5

UILabel でタップされた文字のインデックスを取得したいと思います。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)

最初の文字をタップし、touchesEnded で 0 のインデックスを取得するように、ラベルの最初の 5 文字に対して希望どおりに機能しています。

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

しかし、UILabel の最初の 4 文字を超えてどこかをタップすると、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
}

、 、layoutSubviews()、、awakFromNibのセッターでこれを呼び出します。boundsframeattributedTexttext

そして、テキストの長さが21の場合、最初の文字をタップすると6になるなど、奇妙なインデックスが表示されます.

4

3 に答える 3

11

TLDR: 表示される文字列がlayoutManager、実際に画面に表示されるものとは異なるレイアウトで表示されるため、奇妙なインデックスが表示されます。


あなたはいくつかの奇妙なインデックスを取得しています:

- (NSUInteger)characterIndexForPoint:(CGPoint)point
    inTextContainer:(NSTextContainer *)container
    fractionOfDistanceBetweenInsertionPoints:(nullable CGFloat *)partialFraction;

あなた自身がシステムlayoutManagerの内部と同期している必要があるからです。つまり、ラベルにフォント、サイズなどを設定すると、によって処理されるため、内部はそれを認識しますが、インスタンスは認識しません。layoutManagerUILabellayoutManagerUILabellayoutManager

したがって、 layoutManager によって「見られる」テキスト レイアウトは、画面上のラベルによってレンダリングされる実際のテキストとは異なる位置にあります (デフォルトのフォントとサイズに違いありません)。

複雑な部分は、これらのプロパティの同期を維持することです。できることは、属性付きテキストに次のように設定することです。

[mutableAttributedString addAttribute:NSFontAttributeName 
                                value:self.font 
                                range:NSMakeRange(0, mutableAttributedString.string.length)];

そして、この属性付き文字列をNSTextStorageインスタンスに渡します。これですべての問題が解決しました。


情報: デバッグを支援するために、これを使用して文字列 " seen "をレンダリングできますlayoutManager

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

2 つの文字列が重なり合って配置されており、不具合がある場合は、これが問題です。それらが完全に整列していて、あなたが良いからです。

于 2015-12-12T09:37:53.797 に答える
2

この問題は、NSTextContainer が UITextView のようにテキストをレイアウトするという事実に起因していると思います。つまり、UILabel のように垂直方向に中央揃えされません。

次のコードを実行すると、グリフが配置されている場所を確認できます。

var location = layoutManager.locationForGlyphAtIndex(0);

グリフが textContainer の上部に配置されていることを示す必要があります。

どこに電話しても

var point = tapGesture.locationInView(self);

UILabel の垂直シフトを考慮して調整する必要があります。

于 2015-06-24T16:02:43.317 に答える