【问题标题】:swift property observer on reference property参考属性上的快速属性观察器
【发布时间】:2017-06-29 23:31:05
【问题描述】:

如何获取引用属性来触发属性观察器?

为了演示我的问题,我编写了一个带有一个按钮和一个标签的简单 MVC 程序。该按钮增加模型中的计数器并在视图控制器的标签中显示计数器的值。

问题是计数器增量(在模型中)不会触发 didSet 观察者(在视图控制器中)

这是模型文件:

import Foundation

class MvcModel {
    var counter: Int
    var message: String

    init(counter: Int, message: String) {
        self.counter = counter
        self.message = message
    }
}

// create instance
var model = MvcModel(counter: 0, message: "" )

// counting
func incrementCounter() {
    model.counter += 1
    model.message = "Counter Value:  \(model.counter)"
    //print(model.message)
}

这是视图控制器文件:

import Cocoa

class ViewController: NSViewController {

    let model1 = model

    var messageFromModel = model.message {
        didSet {
            updateDisplayCounterLabel()
        }
    }

    // update Label
    func updateDisplayCounterLabel() {
        DisplayCounterLabel.stringValue = model1.message
    }

    // Label
    @IBOutlet weak var DisplayCounterLabel: NSTextField! {
        didSet {
            DisplayCounterLabel.stringValue = "counter not started"
        }
    }

    // Button
    @IBAction func IncrementButton(_ sender: NSButton) {
        incrementCounter()
        print("IBAction:  \(model1.message)")
    }
}

我猜这个问题与引用属性有关(因为我已经能够使这个程序与基于结构的模型一起工作)。

如果有人能告诉我如何处理属性观察者和引用属性并使这种 MVC 像我计划在实际程序中使用它一样工作,我将不胜感激。

【问题讨论】:

  • 请将解决方案发布为答案,而不是更新您的问题。这是为了帮助未来的访问者理解并避免混淆。

标签: swift properties


【解决方案1】:

您可以为MvcModel 创建一个委托

protocol MvcModelDelegate {
    func didUpdateModel(counter:Int)
}

接下来将委托属性添加到MvcModel

class MvcModel {
    var counter: Int {
        didSet {
            delegate?.didUpdateModel(counter: counter)
        }
    }

    var message: String
    var delegate: MvcModelDelegate?

    init(counter: Int, message: String) {
        self.counter = counter
        self.message = message
    }
}

然后你使ViewController 类符合MvcModelDelegate,最后你将model.delegate = self 设置为viewDidLoad

class Controller: UIViewController, MvcModelDelegate {

    let model = MvcModel(counter: 0, message: "hello")

    override func viewDidLoad() {
        super.viewDidLoad()
        self.model.delegate = self
    }

    func didUpdateModel(counter: Int) {
        print("new value for counter \(counter)")
    }

}

【讨论】:

  • @Noam 我已根据您的建议编辑了模型和 ViewController 文件。我不熟悉协议和委托(因为我刚刚开始编程)。所以我不确定我是否进行了正确的编辑。在任何情况下,程序都会编译,但标签没有更新,控制台只显示 IBAction: hello。我需要回到我的书本上去理解其中的逻辑。
  • @C.Merger 我的代码只是一个例子。您需要将 print("new value for counter \(counter)") 替换为 self.myTextLabel.text = counter.description
  • @Noam 是的,这就是我所理解的。看起来更新计数器时没有调用 didUpdateModel 函数。
  • @Noam - 我发现了我的错误。现在标签已更新。唯一剩下的问题是标签中显示的值是以前的值,而不是当前值。一旦我修复它,我会发布代码。
【解决方案2】:

如果有人对此感兴趣,请参考 Noam 建议的代码。

模型文件:

    import Foundation

    protocol MvcModelDelegate {
        func didUpDateModel(message: String)
    }

    class MvcModel {
        var counter: Int
        var message: String {
            didSet {
                delegate?.didUpDateModel(message: message)
            }
        }
        var delegate: MvcModelDelegate?

        init(counter: Int, message: String) {
            self.counter = counter
            self.message = message
        }
    }

    // create instance
    var model = MvcModel(counter: 0, message: "" )

    // counting
    func incrementCounter() {
        model.counter += 1
        model.message = "Counter Value:  \(model.counter)"
    }
}

查看控制器文件

import Cocoa

class ViewController: NSViewController, ModelDelegate {
    // communication link to the model
    var model1 = model
    var messageFromModel = messageToLabel

    override func viewDidLoad() {
        super.viewDidLoad()
        self.model1.delegate = self
    }

    // update display
    func didUpdateModel(message: String) {
        //self.Label1.stringValue = model1.message
        self.Label1.stringValue = messageFromModel
    }

    // Label
    @IBOutlet weak var Label1: NSTextField! {
        didSet {
            Label1.stringValue = " counter not started"
        }
    }

    // Button
    @IBAction func testButton(_ sender: NSButton) {
        incrementCounter()
    }    
}

【讨论】:

  • @Noam - 我试图修改模型以将其从一个类移动到一个结构,在模型文件中进行一些调整(视图控制器文件没有变化)。一旦我通过结构更改课程,程序就不再起作用。事实上,看起来“delegate?.didUpdateModel(message: message)”没有被执行,因为委托值为 nil。我想问题是我没有正确声明/初始化委托属性。你会建议什么来纠正它?有什么书可以详细而清晰地涵盖这些主题吗?
猜你喜欢
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 2016-04-22
  • 1970-01-01
  • 2015-07-24
  • 2012-04-20
  • 2017-04-25
  • 1970-01-01
相关资源
最近更新 更多