Color attribute is ignored in NSAttributedString with NSLinkAttributeName
在
在带有 Swift 2 的 Xcode 7 中,它可以工作:
在带有 Swift 3 的 Xcode 8 中,链接的自定义属性颜色总是被忽略(在屏幕截图中应该是橙色)。
这是测试代码。
Swift 2,Xcode 7:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import Cocoa import XCPlayground let text ="Hey @user!" let attr = NSMutableAttributedString(string: text) let range = NSRange(location: 4, length: 5) attr.addAttribute(NSForegroundColorAttributeName, value: NSColor.orangeColor(), range: range) attr.addAttribute(NSLinkAttributeName, value:"http://somesite.com/", range: range) let tf = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 50)) tf.allowsEditingTextAttributes = true tf.selectable = true tf.stringValue = text tf.attributedStringValue = attr XCPlaygroundPage.currentPage.liveView = tf |
Swift 3,Xcode 8:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import Cocoa import PlaygroundSupport let text ="Hey @user!" let attr = NSMutableAttributedString(string: text) let range = NSRange(location: 4, length: 5) attr.addAttribute(NSForegroundColorAttributeName, value: NSColor.orange, range: range) attr.addAttribute(NSLinkAttributeName, value:"http://somesite.com/", range: range) let tf = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 50)) tf.allowsEditingTextAttributes = true tf.isSelectable = true tf.stringValue = text tf.attributedStringValue = attr PlaygroundPage.current.liveView = tf |
我已经向 Apple 发送了错误报告,但与此同时,如果有人对 Xcode 8 中的修复或解决方法有想法,那就太好了。
苹果开发者已经回答:
Please know that our engineering team has determined that this issue behaves as intended based on the information provided.
他们解释了为什么它以前有效但不再有效:
Unfortunately, the previous behavior (attributed string ranges with NSLinkAttributeName rendering in a custom color) was not explicitly supported. It happened to work because NSTextField was only rendering the link when the field editor was present; without the field editor, we fall back to the color specified by NSForegroundColorAttributeName.
Version 10.12 updated NSLayoutManager and NSTextField to render links using the default link appearance, similar to iOS. (see AppKit release notes for 10.12.)
To promote consistency, the intended behavior is for ranges that represent links (specified via NSLinkAttributeName) to be drawn using the default link appearance. So the current behavior is the expected behavior.
(强调我的)
此答案不能解决
通过这种解决方法,我们根本不使用
相反,我们使用自定义属性,并且子类化
显然有几个限制:您必须能够子类化字段/视图、覆盖
在准备
1 2 3 | theAttributedString.addAttribute("CUSTOM", value: theLink, range: theLinkRange) theAttributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.orange, range: theLinkRange) theAttributedString.addAttribute(NSCursorAttributeName, value: NSCursor.arrow(), range: theLinkRange) |
设置了链接的颜色和内容。现在我们必须让它可点击。
为此,将您的
我们将获取鼠标事件在窗口中的位置,在该位置找到文本视图中的字符索引,并在文本视图的属性字符串中询问该索引处字符的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class MyTextView: NSTextView { override func mouseDown(with event: NSEvent) { // the location of the click event in the window let point = self.convert(event.locationInWindow, from: nil) // the index of the character in the view at this location let charIndex = self.characterIndexForInsertion(at: point) // if we are not outside the string... if charIndex < super.attributedString().length { // ask for the attributes of the character at this location let attributes = super.attributedString().attributes(at: charIndex, effectiveRange: nil) // if the attributes contain our key, we have our link if let link = attributes["CUSTOM"] as? String { // open the link, or send it via delegate/notification } } // cascade the event to super (optional) super.mouseDown(with: event) } } |
就是这样。
在我的例子中,我需要用不同的颜色和链接类型来定制不同的词,所以不是只将链接作为字符串传递,而是传递一个包含链接和其他元信息的结构,但想法是一样的。 如果您必须使用
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var referenceView: NSTextView {
let theRect = self.cell!.titleRect(forBounds: self.bounds)
let tv = NSTextView(frame: theRect)
tv.textStorage!.setAttributedString(self.attributedStringValue)
return tv
}
override func mouseDown(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
let charIndex = referenceView.textContainer!.textView!.characterIndexForInsertion(at: point)
if charIndex < self.attributedStringValue.length {
let attributes = self.attributedStringValue.attributes(at: charIndex, effectiveRange: nil)
if let link = attributes["CUSTOM"] as? String {
// ...
}
}
super.mouseDown(with: event)
}
}