在某些情况下,NSTextStorage子类无法处理表情符号字符和更改字体

Sha*_*oop 8 objective-c ios nstextstorage nsmutableattributedstring swift

我继承NSTextStorage做一些链接高亮和我多读我可以对 话题.一切正常,直到我输入 emoji character.

My subclass:

private let ims = NSMutableAttributedString()

override var string: String {
    return ims.string
}

override func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {
    return ims.attributesAtIndex(location, effectiveRange: range)
}

override func replaceCharactersInRange(range: NSRange, withString str: String) {
    ims.replaceCharactersInRange(range, withString: str)
    self.edited(.EditedCharacters, range: range, changeInLength:(str as NSString).length - range.length)
}

override func setAttributes(attrs: [String : AnyObject]?, range: NSRange) {
    ims.setAttributes(attrs, range: range)
    self.edited(.EditedAttributes, range: range, changeInLength: 0)
}
Run Code Online (Sandbox Code Playgroud)

Nothing complicated. Then, when entering the infamous character it switches to Courier New for some random reason:

Anything but Courier New!

Now I'm picking on the 角色,还有其他人也会引起这种疯狂.我打字时查询了字体,它来自System> Apple Emoji> Courier New.

我也试过设置processEditing()半解决问题的字体,它会增加额外的空间(虽然不是在模拟器中).我正在硬编码值==糟糕.

终极问题:

我究竟做错了什么?我不认为这个问题与其他人的实现有关,我确定开发人员已经将子类化为NSTextStorage.

注意:我可以确认在objc.io的演示应用程序中存在同样的问题.

Arc*_*gon 3

这是我的外行人的理解。大多数表情符号仅存在于 Apple 的 AppleColorEmoji 字体中。当您键入表情符号字符时,您的 NSTextStorage 会调用processEditing,然后调用fixAttributesInRange。此方法可确保字符串中任何缺失的字符都替换为支持这些字符的字体。如果您的字符串包含表情符号,则所有包含表情符号的范围都将获得 AppleColorEmoji 字体属性。

不幸的是,没有什么可以阻止这个新的字体属性“感染”在它之后键入的字符。AppleColorEmoji 似乎不包含通常的 ASCII 集,因此那些后续字符会使用等宽字体“固定”自身。

该怎么办?在我的程序中,我想手动管理文本存储的属性,因为我不希望通过复制粘贴文本向文本添加新样式。这意味着我可以简单地这样做:

override func setAttributes(attrs: [String : AnyObject]?, range: NSRange) {
    if self.isFixingAttributes {
        self.attributedString.setAttributes(attrs, range: range)
        self.edited(NSTextStorageEditActions.EditedAttributes, range: range, changeInLength: 0)
    }
}

override func fixAttributesInRange(range: NSRange) {
    self.isFixingAttributes = true
    super.fixAttributesInRange(range)
    self.isFixingAttributes = false
}

override func processEditing() {
    // not really fixing -- just need to make sure setAttributes follows orders
    self.isFixingAttributes = true
    self.setAttributes(nil, range: self.editedRange)
    self.setAttributes(self.dynamicType.defaultAttributes(), range: self.editedRange)
    self.isFixingAttributes = false

    super.processEditing()
}
Run Code Online (Sandbox Code Playgroud)

每当输入新文本时,我只需清除其属性(以防任何先前固定的范围“感染”它)并将其替换为默认属性。之后,super.processEditing()执行其操作并修复该范围内任何新的丢失字符(如果有)。

另一方面,如果您希望能够将样式文本粘贴到文本视图中,则应该可以通过比较 for 之前/之后来跟踪您的固定范围fixAttributesInRange,然后防止这些样式转移到新键入的文本在processEditing