使用MVC方法进行iOS应用程序开发,我希望通过发布到NotificationCenter
来观察模型的更改。对于我的示例,Person.swift
模型是:
class Person {
static let nameDidChange = Notification.Name("nameDidChange")
var name: String {
didSet {
NotificationCenter.default.post(name: Person.nameDidChange, object: self)
}
}
var age: Int
var gender: String
init(name: String, age: Int, gender: String) {
self.name = name
self.age = age
self.gender = gender
}
}
观察模型的视图控制器如下所示:
class ViewController: UIViewController {
let person = Person(name: "Homer", age: 44, gender: "male")
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var ageLabel: UILabel!
@IBOutlet weak var genderLabel: UILabel!
@IBOutlet weak var nameField: UITextField!
@IBOutlet weak var ageField: UITextField!
@IBOutlet weak var genderField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = person.name
ageLabel.text = String(person.age)
genderLabel.text = person.gender
NotificationCenter.default.addObserver(self,
selector: #selector(self.updateLabels),
name: Person.nameDidChange, object: nil)
}
@IBAction func updatePerson(_ sender: Any) {
guard let name = nameField.text, let age = ageField.text, let gender = genderField.text else { return }
guard let ageNumber = Int(age) else { return }
person.name = name
person.age = ageNumber
person.gender = gender
}
@objc func updateLabels() {
nameLabel.text = person.name
ageLabel.text = String(person.age)
genderLabel.text = person.gender
}
}
示例应用程序的工作原理如下:
updatePerson
按钮从文本字段值updateLabels
函数来更新用户界面。此方法需要最后设置person.name
,否则必须按两次updatePerson
按钮才能更新整个用户界面。由于我只观察一个属性,因此通知并不代表整个类。有没有更好的方法来观察Swift模型(类或结构)的变化?
注意 - 我对此示例使用RxSwift不感兴趣。
答案 0 :(得分:1)
这更像是一个倾销评论,而不是一个令人满意的答案。但长话短KVO是你应该使用的功能,而不是NotificationCenter
。在Swift4中,绑定过程变得非常简单
至于KVO是什么:请参阅here和here。对于一些关注MVVM的示例,您可以看到here和here。并且不要让MVVM摇摆你。它只是带有绑定的MVC,你试图做同样的事情+将表示逻辑移动到不同的层。
Swift 4中的一个简单的KVO示例如下所示:
@objcMembers class Foo: NSObject {
dynamic var string: String
override init() {
string = "hotdog"
super.init()
}
}
let foo = Foo()
// Here it is, kvo in 2 lines of code!
let observation = foo.observe(\.string) { (foo, change) in
print("new foo.string: \(foo.string)")
}
foo.string = "not hotdog"
// new foo.string: not hotdog
您还可以创建自己的Observable
类型,如下所示:
class Observable<ObservedType>{
private var _value: ObservedType?
init(value: ObservedType) {
_value = value
}
var valueChanged : ((ObservedType?) -> ())?
public var value: ObservedType? {
get{
return _value // The trick is that the public value is reading from the private value...
}
set{
_value = newValue
valueChanged?(_value)
}
}
func bindingChanged(to newValue : ObservedType){
_value = newValue
print("value is now \(newValue)")
}
}
然后创建一个可观察的属性:
class User {
// var name : String <-- you normally do this, but not when you're creating as such
var name : Observable<String>
init (name: Observable<String>){
self.name = name
}
}
上述课程(Observable
)将从Swift Designs patterns book
答案 1 :(得分:0)
要简单地显示图片,您应该意识到您只观察name
更改。因此更新Person
的所有其他属性没有意义。您正在观察name
更改并相应更新,更不用说其他人了。
因此,age
和gender
在更改name
的过程中可能已被更改,这并不是一个理想的假设。话虽如此,您应该考虑逐个观察所有属性并以不同方式绑定操作,并仅修改映射到该特定属性的UI组件。
这样的事情:
override func viewDidLoad() {
...
NotificationCenter.default.addObserver(self,
selector: #selector(self.updateName),
name: Person.nameDidChange, object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(self.updateAge),
name: Person.ageDidChange, object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(self.updateGender),
name: Person.genderDidChange, object: nil)
...
}
@objc func updateName() {
nameLabel.text = person.name
}
@objc func updateAge() {
ageLabel.text = String(person.age)
}
@objc func updateGender() {
genderLabel.text = person.gender
}