【问题标题】:Swift: Cocoa binding value to a computed property does not workSwift:Cocoa 绑定到计算属性的值不起作用
【发布时间】:2016-03-09 13:16:00
【问题描述】:

我正在学习 KVC 和绑定。目前,我正在尝试将 NSTextField 绑定到计算属性 colorWallStr。我已将滑块的值绑定到相应的颜色变量,并将标签的值绑定到计算属性。

但是,当我移动幻灯片时,标签的内容并没有改变。

// Inside MainWindowController
dynamic var colorRed: CGFloat = 1.0
dynamic var colorGreen: CGFloat = 1.0
dynamic var colorBlue: CGFloat = 0.0

dynamic var colorWallStr: String {
    get {
        return "R: \(colorRed) G: \(colorGreen) B: \(colorBlue)"
    }
}

当我将标签直接绑定到颜色变量时,它工作正常。

感谢@vadian 的回答。现在我可以使用属性的didSet 更新标签来触发更新标签方法(见下文)。

dynamic var colorBlue: CGFloat = 0.0 {
    didSet {
        updateLabel()
    }
}

func updateLabel() {
    colorWall = "R: \(colorRed) G: \(colorGreen) B: \(colorBlue)"
}

如果字符串插值中使用的属性不更新封闭的计算属性,那么为什么下面的代码 sn-p 不起作用?

dynamic var colorWall: String {
    get {
        let red = colorRed
        let green = colorGreen
        let blue = colorBlue
        return "R: \(red) G: \(green) B: \(blue)"
    }
}

【问题讨论】:

    标签: swift cocoa cocoa-bindings


    【解决方案1】:

    Key-Value Observering API 允许您通过注册依赖键来处理此类情况。以下是文档介绍该主题的方式:

    在很多情况下,一个属性的值取决于另一个对象中的一个或多个其他属性的值。如果一个属性的值发生更改,则派生属性的值也应标记为更改。

    在这种情况下,colorWallString 的值取决于三个颜色变量的值,所以你需要做的就是实现一个明确的类方法:

    // It's crucial that you get the signature of this method correct, 
    // otherwise it'll just be ignored.
    class func keyPathsForValuesAffectingColorWallStr() -> Set<NSObject> {
        return Set<NSObject>(arrayLiteral: "colorRed", "colorBlue", "colorGreen")
    }
    

    正如代码 sn-p 中所述,用于标记相关键的方法的格式至关重要;您可以(并且应该)阅读relevant documentation here

    【讨论】:

    • 我刚刚试过你的 sn-p 但我还没有工作。感谢您提供额外的资源。
    • 好的,不知道是什么问题。在发布之前,我在一个小应用程序中进行了设置,它运行良好。我的代码与您问题中第一个代码块中的代码以及我的答案中的代码块完全相同。您应该通过在return 语句处放置断点来查看是否调用了keyPathsForValues.. 方法。当您的应用程序启动时,如果定义正确,程序应在此时暂停执行。
    • 您的解决方案完全按照预期工作。在 Swift 5.4 中,它需要用 @objc 进行标记,以便运行时可以获取它。
    【解决方案2】:

    字符串插值中使用的属性不会更新封闭的计算属性。

    你可以这样做

    dynamic var colorRed: CGFloat = 1.0  { didSet { updateLabel() } }
    dynamic var colorGreen: CGFloat = 1.0  { didSet { updateLabel() } }
    dynamic var colorBlue: CGFloat = 0.0  { didSet { updateLabel() } }
    
    dynamic var colorWallStr = ""
    
    func updateLabel()
    {
      colorWallStr = String(format:"R: %.2f G: %.2f  B: %.2f ", colorRed, colorGreen, colorBlue)
    }
    

    【讨论】:

    • 谢谢@vadian,这就像一个魅力。我没有这样做,因为我通过在每个变量都很难看之后加上{didSet{ functionName()}}。 =P
    • 还有一个问题,看我在问题末尾的更新问题。
    • 我必须承认 Paul 的解决方案要好得多。我不知道 keyPathsForValuesAffecting&lt;Property&gt;() 在 Swift 中可用,我在 ObjectiveC 中经常使用它
    • 实际上,当我向 Apple 提交错误报告并抱怨它不可用时,我才知道它是可用的 Swift。他们很快指出是的,但我的签名有点错误。
    【解决方案3】:

    对于 Xcode 9.0、Swift 4:

    class SampleViewController: NSViewController {
    
      @objc dynamic var colorRed: CGFloat = 1.0
      @objc dynamic var colorGreen: CGFloat = 1.0
      @objc dynamic var colorBlue: CGFloat = 0.0
    
      @objc dynamic var colorWallStr: String {
        get {
          return "R: \(colorRed) G: \(colorGreen) B: \(colorBlue)"
        }
      }
    
      override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
        switch key {
        case "colorWallStr" :
          return Set(["colorRed", "colorGreen", "colorBlue"])
        default :
          return super.keyPathsForValuesAffectingValue(forKey: key)
        }
      }
    
    }
    

    小提示:

    • 在这种情况下使用NSViewController,而不是NSWindowController

    更多信息:

    【讨论】:

      【解决方案4】:

      “在某些情况下,一个值可能依赖于另一个值。例如,如果您有一个 Person 类,其计算属性 fullName 依赖于属性 firstName 和 lastName,如果在 firstName 或 lastName 更改时可以通知 fullName 的观察者,那不是很好吗? KVO 的设计者也是这么认为的,这就是他们实现定义依赖键的约定的原因。 要定义键的依赖关系,您必须实现一个特殊命名的类方法,该方法返回一组键路径。在上面的示例中,您将实现keyPathsForValuesAffectingFullName():

      摘自:Hillegass,Aaron。 “用于 OS X 的 Cocoa 编程:大书呆子牧场指南,5/e(大书呆子牧场指南)。”电子书。

      【讨论】:

        猜你喜欢
        • 2014-06-04
        • 2015-08-19
        • 1970-01-01
        • 1970-01-01
        • 2012-06-24
        • 2015-01-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多