我创建了一个显示一些信息的UICollectionViewCell的子类。我有一个房产类型天气。当设置了一个实例时,我想更新单元格。方法下面是不好的?我想如果我在加载之前访问UI组件,我可以触发视图提前创建。或者是没有意义,只适用于UIViewController(关于早期使用view
属性)?
如果这很糟糕,那么正确的方法是什么?
var weather: Weather? {
didSet {
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
}
}
}
答案 0 :(得分:3)
但是,您可能需要else
子句,如果weather
为nil
,则清除文本字段。同样,如果您可以从后台线程更新它,您可能希望将该UI更新分发回主线程。
请注意,当您在单元格的weather
中设置init
时,不会调用此观察者(此时也不会在@IBOutlet
处进行配置)。因此,请确保您不依赖于此。
此外,如果Weather
是可变的,请认识到如果更改现有fromDate
对象的Weather
,则无法捕获该对象。 (如果Weather
是可变的,你真的想要通过KVO,委托协议模式或你有什么来捕获它的变化属性。)但如果你使Weather
不可变,你应该没事。
因此,从技术上讲,这就是问题的答案,但这引出了一些设计考虑因素:
人们通常应该努力使松散耦合的不同类型,即一种类型不应过于依赖另一种类型的内部行为。但在这里,我们在细胞内有一个观察者,这取决于Weather
的可变性。
使用存储属性在视图中存储模型对象是不可取的。单元格在屏幕外滚动时可以重复使用,但是您可能需要一个单独的模型来捕获相关的模型对象,然后控制器根据需要处理向视图对象(单元格)提供适当的模型对象。
最重要的是,不建议在“视图”中使用存储属性作为“模型”信息。
您可以通过编写代码来解决这两个问题,这些代码清楚地表明您仅仅为了更新UI控件而使用此weather
参数,而不是为了存储任何内容。所以更确切地说是存储属性,我只想使用一种方法:
func updateWithWeather(weather: Weather?) {
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
} else {
dayLabel.text = nil
// ... more code like this
}
}
这可能只会在collectionView:cellForItemAtIndexPath:
内调用。
但是,这清楚地表明您只是根据天气参数更新控件,而不是尝试除此之外的任何事情。而且,巧合的是,天气对象的可变性现在无关紧要,应该如此。如果模型发生变化,请拨打reloadItemsAtIndexPaths:
,这会触发您的collectionView:cellForItemAtIndexPath:
被调用。
有时,didSet
观察者的存储属性是一种有用的模式。但是,只有当属性真正属于视图属性时才应该这样做。例如,考虑绘制一些形状的自定义视图。例如,您可能具有存储的属性,用于指定绘制路径时要使用的笔划的宽度和颜色。然后,存储lineWidth
和strokeColor
的属性可能有意义,然后您可能会didSet
调用setNeedsDisplay()
(触发重新绘制视图)。< / p>
因此,您建议的模式确实具有实际应用,它只是应该限制在属性真正属于视图对象属性的情况下。
答案 1 :(得分:0)
如果我计划在用户会话期间更新值,我会使用属性观察器。如果这是一个只在用户第一次加载时才更新的值,我只需在最初加载视图时调用方法。
如果使用属性观察器,则可以在定义属性观察器时为其指定初始值,以便在用户需要时存储数据。此外,如果您要更新用户界面,请确保在主队列中执行此操作。
var weather: Weather = data {
didSet {
dispatch_async(dispatch_get_main_queue(),{
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
}
})
}
}