从tap中获取UILabel中的角色索引

Sir*_*III 4 nslayoutmanager uilabel ios nstextstorage nstextcontainer

我想得到UILabel上的tapped字符的索引.我已经将UILabel分类了.在我的awakeFromNib()中我有这个:

    layoutManager = NSLayoutManager()
    textStorage = NSTextStorage(attributedString: self.attributedText)
    textStorage.addLayoutManager(layoutManager)

    textContainer = NSTextContainer(size: CGSizeMake(self.frame.size.width, self.frame.size.height))
    textContainer.lineFragmentPadding = 0
    textContainer.maximumNumberOfLines = self.numberOfLines
    textContainer.lineBreakMode = self.lineBreakMode
    textContainer.size = self.frame.size

    layoutManager.addTextContainer(textContainer)
Run Code Online (Sandbox Code Playgroud)

这是我想要它的标签的前5个字符的方式,因为我点击第一个字符,在我的touchesEnded我得到一个0的索引:

var touchedCharacterIndex = layoutManager.characterIndexForPoint(touch.locationInView(self), inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

但是当我点击UILabel的前四个字符的任何地方时,我得到4或0,这是不正确的.

谢谢,

更新

根据评论中的建议,我添加了这个:

 func updateTextStorage() {
    if let attributedText = self.attributedText {
        textStorage = NSTextStorage(attributedString: attributedText)
    }
    textStorage?.addLayoutManager(layoutManager)

    textContainer = NSTextContainer(size: CGSizeMake(self.frame.size.width, self.frame.size.height))
    textContainer?.lineFragmentPadding = 7
    textContainer?.maximumNumberOfLines = self.numberOfLines
    textContainer?.lineBreakMode = self.lineBreakMode
    textContainer?.size = self.bounds.size

    layoutManager.addTextContainer(textContainer!)
    layoutManager.textStorage = textStorage
}
Run Code Online (Sandbox Code Playgroud)

我称这种现象layoutSubviews(),awakFromNib和的制定者bounds,frame,attributedTexttext.

它现在给了我一些奇怪的索引,比如文本长度是21,点击第一个字母给我6.

Lud*_*dry 8

TLDR:你得到一些奇怪的索引,因为你看到的字符串layoutManager与你在屏幕上看到的字符串不同.


您将获得一些奇怪的索引:

- (NSUInteger)characterIndexForPoint:(CGPoint)point
    inTextContainer:(NSTextContainer *)container
    fractionOfDistanceBetweenInsertionPoints:(nullable CGFloat *)partialFraction;
Run Code Online (Sandbox Code Playgroud)

因为你自己layoutManager必须与layoutManager系统内部同步UILabel.这意味着当你在标签上设置字体,大小等时,内部就layoutManager知道这一点,因为它是由UILabel你照顾的,但是你的layoutManager实例没有.

因此,layoutManager " 看到 " 的文本布局与屏幕上标签呈现的实际文本位于不同的位置(我打赌的默认字体和大小).

复杂的部分是保持这些属性同步,你可以做的是在你的属性文本上设置如下:

[mutableAttributedString addAttribute:NSFontAttributeName 
                                value:self.font 
                                range:NSMakeRange(0, mutableAttributedString.string.length)];
Run Code Online (Sandbox Code Playgroud)

并将此属性字符串传递给您的NSTextStorage实例.这为我解决了所有问题.


信息:为了帮助您调试,您可以使用以下方法呈现字符串" see " layoutManager:

- (void)drawTextInRect:(CGRect)rect
{
    [super drawTextInRect:rect];
    [self.layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, self.textStorage.length) atPoint:CGPointMake(0, 0)];
}
Run Code Online (Sandbox Code Playgroud)

如果你看到2个字符串叠加在一起并带有一些故障,那么这就是你的问题.如果你只能看到一个应该是因为它们完全对齐并且你很好!


swi*_*eti 2

我认为这个问题源于 NSTextContainer 像 UITextView 一样布局文本的事实,这意味着它不会像 UILabel 那样垂直居中。

如果运行以下代码,您可以看到字形的布局位置。

var location = layoutManager.locationForGlyphAtIndex(0);
Run Code Online (Sandbox Code Playgroud)

它应该显示字形被放置在文本容器的顶部。

所以无论你打电话

var point = tapGesture.locationInView(self);
Run Code Online (Sandbox Code Playgroud)

需要进行调整以考虑 UILabel 的垂直移位。