【问题标题】:Find rect/position of words in NSTextField在 NSTextField 中查找单词的矩形/位置
【发布时间】:2016-01-07 05:25:31
【问题描述】:

我有一个 NSTextField,里面有一个句子。我想为句子中的每个单词找到一个矩形(理想情况下)或位置,以便在这些位置(在 NSTextField 之外)执行操作。

在 NSTextView/UITextView seems achievableNSLayoutManager.boundingRectForGlyphRange 中执行此操作,但没有 NSTextView(和 UITextView)拥有的 NSLayoutManager 似乎更具挑战性。

在 NSTextField 中查找给定单词位置的最佳方法是什么?

【问题讨论】:

标签: swift macos nstextfield


【解决方案1】:

它需要一个几乎没有记录的魔法和另一个完全没有记录的魔法。这是Objective-C代码。抱歉,在 Swift 中不方便使用。

NSRect textBounds = [textField.cell titleRectForBounds:textField.bounds];

NSTextContainer* textContainer = [[NSTextContainer alloc] init];
NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
NSTextStorage* textStorage = [[NSTextStorage alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
textContainer.lineFragmentPadding = 2;
layoutManager.typesetterBehavior = NSTypesetterBehavior_10_2_WithCompatibility;

textContainer.containerSize = textBounds.size;
[textStorage beginEditing];
textStorage.attributedString = textField.attributedStringValue;
[textStorage endEditing];

NSUInteger count;
NSRectArray rects = [layoutManager rectArrayForCharacterRange:matchRange
                                 withinSelectedCharacterRange:matchRange
                                              inTextContainer:textContainer
                                                    rectCount:&count];
for (NSUInteger i = 0; i < count; i++)
{
    NSRect rect = NSOffsetRect(rects[i], textBounds.origin.x, textBounds.origin.y);
    rect = [textField convertRect:rect toView:self];
    // do something with rect
}

typesetterBehavior 记录在 here 中。 lineFragmentPadding 是根据经验确定的。

根据您计划对矩形做什么,您可能希望将{ NSNotFound, 0 } 作为选定的字符范围传递。

为了提高效率,您通常希望保留文本对象,而不是每次都实例化它们。您只需每次将文本容器的containerSize 和文本存储的attributedString 设置为适当的值即可。

【讨论】:

  • 谢谢 - 我会试一试,一旦我快速处理它就会回来找你。很想跳回 ObjC 领域!
  • 太棒了!谢谢你这工作得很好。我不得不将 .attributedString 更改为 textStorage.appendAttributedString 并且我使用了 layoutManager.enumerateEnclosingRectsForGlyphRange 而不是 rectArrayForCharacterRange - 但我认为这些更改可能只是因为我正在使用的基本 SDK。
  • 仅供参考,多年来我一直在愉快地使用它,但在 macOS Monterey 上使用NSTypesetterBehavior_10_2_WithCompatibility 似乎已经停止使用layoutManager.enumerateEnclosingRectsForGlyphRange。我现在正在使用NSTypesetterLatestBehavior,它似乎工作正常。
【解决方案2】:

根据 Ken Thomases 的回答。我为 Swift 4.2 做了一个改编

    guard let textFieldCell = textField.cell,
        let textFieldCellBounds = textFieldCell.controlView?.bounds else{
        return
    }
    let textBounds = textFieldCell.titleRect(forBounds: textFieldCellBounds)
    let textContainer = NSTextContainer()
    let layoutManager = NSLayoutManager()
    let textStorage = NSTextStorage()

    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)

    layoutManager.typesetterBehavior = NSLayoutManager.TypesetterBehavior.behavior_10_2_WithCompatibility

    textContainer.containerSize = textBounds.size
    textStorage.beginEditing()
    textStorage.setAttributedString(textFieldCell.attributedStringValue)
    textStorage.endEditing()

    let rangeCharacters = (textFieldCell.stringValue as NSString).range(of: "string")

    var count = 0
    let rects: NSRectArray = layoutManager.rectArray(forCharacterRange: rangeCharacters,
                                                    withinSelectedCharacterRange: rangeCharacters,
                                                    in: textContainer,
                                                    rectCount: &count)!

    for i in 0...count {
        var rect = NSOffsetRect(rects[i], textBounds.origin.x, textBounds.origin.y)
        rect = textField.convert(rect, to: self.view)
       // do something with rect
    }

所有致谢肯·托马斯

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2010-11-12
    相关资源
    最近更新 更多