I'm implementing a custom UILabel
with link clicking support. To do so, I've got the following methods: one to initialize the structures (called only once) and one to detect clicks in a determinate range of text within a string:
- (void) sharedInit { // Remember, this method is only called once self.layoutManager = [[NSLayoutManager alloc] init]; self.textContainer = [[NSTextContainer alloc] initWithSize:self.frame.size]; [self.layoutManager addTextContainer:self.textContainer]; self.myGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)]; self.textContainer.lineFragmentPadding = 0.0; self.textContainer.lineBreakMode = self.lineBreakMode; self.textContainer.maximumNumberOfLines = self.numberOfLines; self.textContainer.size = self.frame.size; } - (void) onTap:(UITapGestureRecognizer*) tapGesture { CGPoint location = [tapGesture locationInView:tapGesture.view]; NSInteger index = [self.layoutManager characterIndexForPoint:locationOfTouchInLabel inTextContainer:self.textContainer fractionOfDistanceBetweenInsertionPoints:nil]; NSLog(@"index of tapped character: %li", index); // ... and some more code to work with that index } - (void) setFrame:(CGRect)frame { [super setFrame:frame]; self.textContainer.size = self.frame.size; } - (void) setAttributedText:(NSAttributedString *)attributedText { [super setAttributedText:attributedText]; self.textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText]; [self.textStorage addLayoutManager:self.layoutManager]; } - (void) setNumberOfLines:(NSInteger)numberOfLines { [super setNumberOfLines:numberOfLines]; self.textContainer.maximumNumberOfLines = numberOfLines; }
The key variable here is index
(onTap:
mehtod). That variable returns the index of the tapped character so we can work with it. This worked flawlessly with iOS 8, but as for iOS 9 I'm seeing the following behavior:
- If the label has one line, it returns the correct index
- If the label has more than one line, it always returns zero no matter where did you click.
I faced a similar issue when I was developing the feature for iOS 8, and I solved it by setting the maximumNumberOfLines
of the NSTextContainer
to the correct value, as you can see in the initialization. The configuration parameters (size, maxNumOfLines, etc) for the TextContainer are correct when the onTap
method is run. I've checked the documentation (https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/NSTextContainer_Class_TextKit/index.html#//apple_ref/occ/instm/NSTextContainer/lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:) and apparently nothing's changed in this version, so I'm quite lost. So far, this looks like a iOS9 bug, but I don't want to discard any options. I've come with a couple of workarounds as well, but if it's possible I'd like to know what's going on with that method.
So, does anybody know what's going on? Thanks in advance...
EDIT:
Two things:
- I tried consulting
glyphIndex
instead ofcharIndex
and so far it's returning identical results (ok with one line, zero with more than one line) - I've noticed that the
firstUnlaidCharIndex
is equal to 0 when the result is incorrect. In cases where it works fine (iOS8 and iOS9 with just one line) it's returning the correct value, the first out-of-bounds character.