首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Swift中用NSLayoutManager隐藏标记字符

在Swift中用NSLayoutManager隐藏标记字符
EN

Stack Overflow用户
提问于 2019-08-12 20:39:16
回答 1查看 2K关注 0票数 11

我正在一个使用Markdown语法的Mac应用程序中开发一个富文本编辑器。我使用NSTextStorage在Markdown语法中监视匹配,然后将样式实时应用到NSAttributedString中,如下所示:

在这一点上,我已经在我的头上在这件事上,但我很高兴取得进展。) 本教程非常有用。

作为下一步,我希望在呈现字符串时隐藏标记字符。因此,在上面的示例中,一旦输入了最后一个星号,我希望隐藏* *字符,并以粗体显示sample

我使用的是NSLayoutManager委托,我可以看到匹配的字符串,但我不清楚如何使用shouldGenerateGlyphs方法生成修改后的符号/属性。到目前为止,我所拥有的是:

代码语言:javascript
复制
func layoutManager(_: NSLayoutManager, shouldGenerateGlyphs _: UnsafePointer<CGGlyph>, properties _: UnsafePointer<NSLayoutManager.GlyphProperty>, characterIndexes _: UnsafePointer<Int>, font _: NSFont, forGlyphRange glyphRange: NSRange) -> Int {
    let pattern = "(\\*\\w+(\\s\\w+)*\\*)" // Look for stuff like *this*
    do {
        let regex = try NSRegularExpression(pattern: pattern)
        regex.enumerateMatches(in: textView.string, range: glyphRange) {
            match, _, _ in
            // apply the style
            if let matchRange = match?.range(at: 1) {
                print(matchRange) <!-- This is the range of *sample*

                // I am confused on how to provide the updated properties below...
                // let newProps = NSLayoutManager.GlyphProperty.null
                // layoutManager.setGlyphs(glyphs, properties: newProps, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange)
                // return glyphRange.length
            }
        }
    } catch {
        print("\(error.localizedDescription)")
    }

    return 0
}

如何根据我找到的隐藏星号的文本范围来修改传递给setGlyphs的内容?

EN

回答 1

Stack Overflow用户

发布于 2020-06-04 17:10:44

我决定提交另一个解决方案,因为这方面的信息很少,也许有人会发现它是有用的。一开始,我被layoutManager(_:shouldGenerateGlyphs:properties:characterIndexes:font:forGlyphRange:)完全搞糊涂了,直到我找到了纪尧姆·阿吉斯( Guillaume )非常彻底的解释(上图)。再加上25‘18英寸的幻灯片,进入2018年世界互联网大会演示版"TextKit最佳做法“,并研究不安全的指针是如何起作用的,这对我起了很大作用。

我的解决方案并不直接处理隐藏标记字符的问题,而是隐藏具有特定值(DisplayType.excluded)的自定义属性(DisplayType.excluded)的字符。(这正是我所需要的。)但是代码相当优雅,所以它可能很有教育意义。

以下是自定义属性定义:

代码语言:javascript
复制
extension NSAttributedString.Key { static let displayType = NSAttributedString.Key(rawValue: "displayType") }

要进行检查,可以在视图控制器的ViewDidLoad (设置为NSLayoutManagerDelegate)中这样做:

代码语言:javascript
复制
textView.layoutManager.delegate = self
        
let text = NSMutableAttributedString(string: "This isn't easy!", attributes:  [.font: UIFont.systemFont(ofSize: 24), .displayType: DisplayType.included])
let rangeToExclude = NSRange(location: 7, length: 3)
text.addAttribute(.displayType, value: DisplayType.excluded, range: rangeToExclude)
textView.attributedText = text

最后,下面是完成所有工作的函数:

代码语言:javascript
复制
func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSLayoutManager.GlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: UIFont, forGlyphRange glyphRange: NSRange) -> Int {
        
    // Make mutableProperties an optional to allow checking if it gets allocated
    var mutableProperties: UnsafeMutablePointer<NSLayoutManager.GlyphProperty>? = nil
        
    // Check the attributes value only at charIndexes.pointee, where this glyphRange begins
    if let attribute = textView.textStorage.attribute(.displayType, at: charIndexes.pointee, effectiveRange: nil) as? DisplayType, attribute == .excluded {
            
        // Allocate mutableProperties
        mutableProperties = .allocate(capacity: glyphRange.length)
        // Initialize each element of mutableProperties
        for index in 0..<glyphRange.length { mutableProperties?[index] = .null }
    }
        
    // Update only if mutableProperties was allocated
    if let mutableProperties = mutableProperties {
            
        layoutManager.setGlyphs(glyphs, properties: mutableProperties, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange)
            
        // Clean up this UnsafeMutablePointer
        mutableProperties.deinitialize(count: glyphRange.length)
        mutableProperties.deallocate()
            
        return glyphRange.length
            
    } else { return 0 }
}

在字符和字形计数不匹配的情况下,上面的代码似乎很健壮:attribute(_:at:effectiveRange:)只使用charIndexesmutableProperties只使用glyphRange。此外,由于mutableProperties在主函数中被赋予与props相同的类型(实际上,它是可变的和可选的),所以以后不需要转换它。

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57467951

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档