草庐IT

关于 swift:Color 属性在 NSAttributedString 和 NSLinkAttributeName 中被忽略

codeneng 2023-03-28 原文

Color attribute is ignored in NSAttributedString with NSLinkAttributeName

NSAttributedString 中,一系列字母具有链接属性和自定义颜色属性。

在带有 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 中的修复或解决方法有想法,那就太好了。

  • 使用 NSTextView 时,必须设置 linkTextAttributes 属性。当 NSTextField 获得焦点并且可以编辑时,文本显示在使用其 linkTextAttributesNSTextView 中(默认为蓝色,带下划线和手形光标)。
  • @Willeke谢谢,不幸的是,我已经尝试过覆盖linkTextAttributes,这是一个很好的解决方案,但是它将相同的属性应用于属性字符串中的所有链接,并且我需要能够设置不同的属性。问题是,您对我的另一个问题的回答直到最近才起作用,这里最大的谜团是为什么您回答的完全相同的代码不再起作用。 :)
  • 哦。该链接在 Xcode 7 中是橙色的。在 Xcode 8 中它是蓝色的。我正在提交雷达...抱歉打扰您了,@Willeke...
  • "htpp"???????
  • 下划线也是新的。它曾经没有下划线(如您的第一个屏幕截图所示)。基本上他们使文本视图链接看起来像一个标签链接。这可能是故意的;以前很混乱。
  • 下划线并不新鲜。它肯定是 OS X 10.10 的默认设置。
  • @matt 当然只是一个愚蠢的错字。 // 我已经搜索了变更日志,但没有找到任何东西。我理解他们为什么会故意这样做,但我当然希望有办法再次改变这些链接的外观......
  • 我不知道你说的下划线是什么意思。我的机器上没有下划线。我从未在 UITextView 链接上看到过下划线。我什至不确定你为什么有下划线。我无法重现。
  • @matt 试试 NSTextView
  • 哦,我明白了,因为你在桌面上。但是在 iOS 10 中 UITextView 也出现了颜色问题。你在这里有了一个重要的发现。在您的错误报告中,请提及 iOS 10 的 UITextView 中也发生了同样的问题。
  • @EricAya您想要游乐场的解决方案还是应用程序的解决方案?
  • @Willeke 我在当前的应用程序和 Playground 中得到了相同的行为,它不会改变任何东西。如果您有解决方案,或者只是一个想法,请继续。 :) 我只是使用 Playground 进行简单演示。
  • @EricAya 感谢您这样做


苹果开发者已经回答:

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.

(强调我的)

  • 谢谢!非常有用的信息。
  • @Apple:请给我们这个着色器!所以我们可以同时保持一致性和定制化!


此答案不能解决 NSLinkAttributeName 忽略自定义颜色的问题,它是在 NSAttributedString 中使用彩色可点击单词的替代解决方案。

通过这种解决方法,我们根本不使用 NSLinkAttributeName,因为它会强制使用我们不想要的样式。

相反,我们使用自定义属性,并且子类化 NSTextField/NSTextView 来检测鼠标点击下的属性并采取相应的行动。

显然有几个限制:您必须能够子类化字段/视图、覆盖 mouseDown 等,但在等待修复时"它适用于我"。

在准备 NSMutableAttributedString 时,您将在其中设置 NSLinkAttributeName,将链接设置为具有自定义键的属性:

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)

设置了链接的颜色和内容。现在我们必须让它可点击。

为此,将您的 NSTextView 子类化并覆盖 mouseDown(with event: NSEvent)

我们将获取鼠标事件在窗口中的位置,在该位置找到文本视图中的字符索引,并在文本视图的属性字符串中询问该索引处字符的属性。

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)
    }

}

就是这样。

在我的例子中,我需要用不同的颜色和链接类型来定制不同的词,所以不是只将链接作为字符串传递,而是传递一个包含链接和其他元信息的结构,但想法是一样的。

如果您必须使用 NSTextField 而不是 NSTextView,则查找点击事件位置有点棘手。一个解决方案是在 NSTextField 中创建一个 NSTextView 并从那里使用与以前相同的技术。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyTextField: NSTextField {

    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)
    }

}

  • 谢谢@Eric Aya。我刚刚得到了我正在寻找的解决方案。再次感谢!
  • 好点子!最后我需要添加两件事以使其与文本字段一起使用:(1)设置自定义文本视图的文本容器的 lineFragmentPadding ; (2) 将段落样式添加到 lineBreakMode 设置为 NSLineBreakStrategyPushOut 的属性字符串(对于 >= 11.0,使用 NSLineBreakStrategyStandard)。这些东西修复了多行标签的一些故障。

有关关于 swift:Color 属性在 NSAttributedString 和 NSLinkAttributeName 中被忽略的更多相关文章

  1. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  2. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  3. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  4. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  5. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  6. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  7. c - mkmf 在编译 C 扩展时忽略子文件夹中的文件 - 2

    我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。

  8. Ruby - 如何在读取文件时跳过/忽略特定行? - 2

    在读取/解析文件(使用Ruby)时忽略某些行的最佳方法是什么?我正在尝试仅解析Cucumber.feature文件中的场景,并希望跳过不以Scenario/Given/When/Then/And/But开头的行。下面的代码有效,但它很荒谬,所以我正在寻找一个聪明的解决方案:)File.open(file).each_linedo|line|line.chomp!nextifline.empty?nextifline.include?"#"nextifline.include?"Feature"nextifline.include?"Inorder"nextifline.include?

  9. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

  10. ruby - Chef Ruby 遍历 .erb 模板文件中的属性 - 2

    所以这可能有点令人困惑,但请耐心等待。简而言之,我想遍历具有特定键值的所有属性,然后如果值不为空,则将它们插入到模板中。这是我的代码:属性:#===DefaultfileConfigurations#default['elasticsearch']['default']['ES_USER']=''default['elasticsearch']['default']['ES_GROUP']=''default['elasticsearch']['default']['ES_HEAP_SIZE']=''default['elasticsearch']['default']['MAX_OP

随机推荐