简体   繁体   中英

NSLayoutManager characterIndexForPoint method failing on ios9

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 of charIndex 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.

尝试使用相同的UILabel宽度和CGFLOAT_MAX高度初始化NSTextContainer的大小

    self.textContainer.size = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);

Solved! Looks like the problem was the setAttributedText: overload. The TextStorage was re-created every time you changed the label's text, and it looks like the layout manager didn't like that at all. Reusing the textStorage and just changing the text by calling setAttributedString: fixed the issue.

In fact, I solved it quite a long ago :( I edited the post, but forgot to answer myself. Sorry!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM