cocos2d-objc icon indicating copy to clipboard operation
cocos2d-objc copied to clipboard

[iOS] Attributed String does not work in CCLabelTTF

Open Zammy opened this issue 9 years ago • 18 comments

[SpriteBuilder 1.3.6, Cocos2D-Swift version 3.3.0-develop]

I have not manage to get CCLabelTTF to work with attributedString. If you create a starting project with SpriteBuilder. Add Code Connection from the label and add label property in MainScene. Run this code:

    NSMutableAttributedString * attributedString = [[NSMutableAttributedString alloc] initWithString:@"Hello"];
    [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,2)];
    self.label.attributedString = attributedString;

What is expected is "He" to become red. What happens is a crash.

I can post more details but I believe everyone will be able to reproduce this.

Zammy avatar Jan 08 '15 10:01 Zammy

Please can you retest in SB 1.4 Beta http://forum.spritebuilder.com/t/spritebuilder-1-4-beta-release/2573

Thanks

cocojoe avatar Jan 08 '15 10:01 cocojoe

This is still broken in Cocos2d 3.4.3. Properties of an AttributedString are not being honored (such as font and paragraph styles) and setting a text foreground color causes a crash. It seems like CCLabelTTF is just seriously broken since v3.3. It was fine in v3.2.1 and maybe has something to do with very deep code in the renderer or drawing routines.

If you create a CCLabelTTF with an attributed string, there's no crash until you actually add it to the stage (scene). If you create one with an attributed string that has paragraph and font attributes but no NSForegroundColor attributes and -- for instance -- color the text using label.fontColor, then you will actually not crash but you will get a so-colored label that's missing the paragraph and font attrubutes set on the attributed string.

It is not sufficient to rely on using the CCLabel fontName, fontSize, fontColor becuase we want to be able to use an attributed string with varying attributes across different ranges of the string. We want to be able to have different colors, font sizes, paragraph setting, etc. like we used to have in Cocos2d 3.2.1. Please fix this -- it has caused me lots of grief in trying to get an app ready for the App Store on a high-profile project. Thanks.

ghost avatar Jan 25 '15 19:01 ghost

@Birkemose Would you be able to look at this ? Thanks

cocojoe avatar Jan 26 '15 09:01 cocojoe

Sure

On 26 Jan 2015, at 10:17, cocojoe [email protected] wrote:

@Birkemose https://github.com/Birkemose Would you be able to look at this ? Thanks

— Reply to this email directly or view it on GitHub https://github.com/cocos2d/cocos2d-swift/issues/1153#issuecomment-71432198.

Birkemose avatar Jan 26 '15 09:01 Birkemose

I'm kind of depending on this feature too... well, I will test any fixes :-)

jonnyijapan avatar Feb 07 '15 18:02 jonnyijapan

I looked at this in 3.3, but was unable to find out why CCLabelTTF crashes. I was under the impression it had been fixed in 3.4?

On 07 Feb 2015, at 19:30, jonnyijapan [email protected] wrote:

I'm kind of depending on this feature too... well, I will test any fixes :-)

— Reply to this email directly or view it on GitHub https://github.com/cocos2d/cocos2d-swift/issues/1153#issuecomment-73376422.

Birkemose avatar Feb 08 '15 15:02 Birkemose

I have been looking into this again, and my bad for thinking it was fixed.

Just to recap. When launching cocos2d-tests-ios on ex. iPad2 (8.1), an exception is thrown in line 75 of CCLabelTTF.m. I do not know the reason why, or if it has anything to do with this problem.

After that, almost any attempt to add a string with attributes, causes an unrecoverable crash in line 402 of CCLabelTTF. Very basic attributed strings will work, but adding pretty much any attribute, will cause the crash.

Googling around a bit, leads me to believe it has something to do with the font setup, but I do not have sufficient experience with Core Text functionality, to know what to look for.

On 08 Feb 2015, at 16:06, Lars Birkemose [email protected] wrote:

I looked at this in 3.3, but was unable to find out why CCLabelTTF crashes. I was under the impression it had been fixed in 3.4?

On 07 Feb 2015, at 19:30, jonnyijapan <[email protected] mailto:[email protected]> wrote:

I'm kind of depending on this feature too... well, I will test any fixes :-)

— Reply to this email directly or view it on GitHub https://github.com/cocos2d/cocos2d-swift/issues/1153#issuecomment-73376422.

Birkemose avatar Feb 09 '15 11:02 Birkemose

Thanks so much! Does someone else on the team have more insight perhaps? It definitely used to work fine, but whatever changes occurred in 3.3 or thereabouts have caused problems with this. The fact that it doesn't crash unless you actually add your label to the stage makes me think the issue is deep in the renderer, but is that a silly theory? I mean, you can create as many complex labels as you want but it's only if you want to see them that you crash. It worked fine in 3.2.1 at least. Would love for this functionality to return in the future! 😻

ghost avatar Feb 09 '15 13:02 ghost

We should at least have a test case that can show that it did work in 3.2 but not since 3.x... So it will be easier for anyone who wants to take on the task.

jonnyijapan avatar Feb 09 '15 17:02 jonnyijapan

I have a test case which works in older version. This should just be added to the iOS tests.

On 09 Feb 2015, at 18:12, jonnyijapan [email protected] wrote:

We should at least have a test case that can show that it did work in 3.2 but not since 3.x... So it will be easier for anyone who wants to take on the task.

— Reply to this email directly or view it on GitHub https://github.com/cocos2d/cocos2d-swift/issues/1153#issuecomment-73548009.

Birkemose avatar Feb 09 '15 18:02 Birkemose

The problem with attributes not being applied in iOS (all I've looked at so far) seems to be in NSMutableAttributedStringFixPlatformSpecificAttributes in Support/NSAttributedString+CCAdditions.m

For some reason, if not running on Android, it reapplies a font attribute to the string - but only the first font attribute it comes across and to the whole string (fullRange) - removing any changes to font size, etc later on in the string. Commenting out lines 264 to 269 stops this, but the font attribute is then again set on the 'fullRange' on line 333 - so commenting that line is also required to make font size attributes work on iOS.

Hopefully that helps someone who fully understands what this method is trying to achieve to resolve the issues...

RobBoluga avatar Feb 18 '15 14:02 RobBoluga

Just want to change the color? CCLabelBMFont might do what you need.

I did some fixes to CCLabelBMFont recently. That class always promised that you could apply effects to individual font char sprites but it was impossible because of the caching/re-use and new-line mangling that went on mean the index of the child didn't correspond to the string. That now works and I added a convenience method that allows you to get a list of the sprites for a range.

  • https://github.com/cocos2d/cocos2d-swift/pull/1210
  • https://github.com/cocos2d/cocos2d-swift/pull/1240

Note that the second pull request hasn't been accepted yet, and fixes some bugs relating to FooBar.bmfont style fonts used by SpriteBuilder.

sarah-j-smith avatar Feb 19 '15 05:02 sarah-j-smith

One cause of this is a faulty comparison of NSAttributedString equality in CCLabelTTF https://github.com/cocos2d/cocos2d-objc/blob/v3.4/cocos2d/CCLabelTTF.m#L159

if ( _attributedString.hash != attributedString.hash) should be if ( ![_attributedString isEqualToAttributedString: attributedString])

Patching this locally resolves the issue for me

SRandazzo avatar May 07 '15 12:05 SRandazzo

Any update on this issue? The solution from @SRandazzo did not work for me. I am also running v3.4.3 and observe the same crash when setting NSForegroundColorAttributeName on CCLabelTTF attributedStrings. Thanks!

mstorch avatar Jul 13 '15 06:07 mstorch

I am in the process of going through issues for the final release of 3.4.9, so hopefully this week

Birkemose avatar Jul 13 '15 06:07 Birkemose

Excellent, looking forward to the upcoming release. Please let me know if you need any help testing anything out!

mstorch avatar Jul 13 '15 06:07 mstorch

hash is guaranteed to be identical for identical strings, so comparing hash and using isEqualToString, should be the same. No idea why they chose to use hash

Birkemose avatar Jul 13 '15 11:07 Birkemose

@SRandazzo @Birkemose about crash: there is bug in NSMutableAttributedStringFixPlatformSpecificAttributes method in NSAttributedString+CCAdditions.m, as we see in line [string addAttribute:(id)kCTForegroundColorFromContextAttributeName value:(__bridge id)color range:fullRange]; for kCTForegroundColorFromContextAttributeName used 'color' (CGColorRef) as value, but as described in comment for this attribute (kCTForegroundColorFromContextAttributeName), value must be CFBooleanRef. It's why cocos2d code is crashing when user wants to use NSForegroundColorAttributeName attribute in attributed string. Value should be something like [NSNumber numberWithBool:YES].

@mstorch there is workaround (if you still interested :-)) for example if you have attributed string: NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:str]; instead of setting attribute like this: [text addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:somerange]; you should use CoreText attribute name like this:

CGColorRef color = CGColorRetain([UIColor redColor].CGColor);
[text addAttribute:(id)kCTForegroundColorAttributeName value:(__bridge id)color range:somerange];
CGColorRelease(color);

AlexMishagin avatar Feb 21 '17 13:02 AlexMishagin