Ios 使用属性观察者修改Swift中的UI组件

Ios 使用属性观察者修改Swift中的UI组件,ios,swift,uicollectionview,Ios,Swift,Uicollectionview,我已经创建了UICollectionViewCell的一个子类,它显示了一些信息。我有一个天气类型的属性。当设置了一个实例时,我想更新单元格。下面的方法糟糕吗?我想,如果在加载UI组件之前访问它,我可能会提前触发要创建的视图。或者,这是无意义的,并且仅适用于UIViewController(关于使用viewproperty提前查看) 如果这不好,正确的方法是什么 var weather: Weather? { didSet { if let weather

我已经创建了UICollectionViewCell的一个子类,它显示了一些信息。我有一个天气类型的属性。当设置了一个实例时,我想更新单元格。下面的方法糟糕吗?我想,如果在加载UI组件之前访问它,我可能会提前触发要创建的视图。或者,这是无意义的,并且仅适用于UIViewController(关于使用
view
property提前查看)

如果这不好,正确的方法是什么

var weather: Weather? {
        didSet {
            if let weather = weather {                            
                dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)

                // ... more code like this
            }
        }
    }

如果我计划在用户会话期间更新值,我会使用属性观察者。如果这是一个仅在用户首次加载时才更新的值,则我只需在最初加载视图时调用一个方法即可

如果使用属性观察者,则可以在定义它时为其提供初始值,以便在用户需要时数据就在那里。此外,如果要更新用户界面,请确保在主队列上进行更新

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

您可能需要一个
else
子句,如果
weather
nil
,则清除文本字段。类似地,如果您可能从后台线程更新此内容,则可能希望将该UI更新分派回主线程

请注意,当您在单元格的
init
中设置
weather
时,不会调用此观察者(无论如何,
@IBOutlet
也不会在该点进行配置)。所以要确保你没有依赖它

另外,如果
Weather
是可变的,请注意,如果您更改现有
Weather
对象的
fromDate
,则不会捕获该对象。(如果
Weather
是可变的,那么您真的希望通过KVO、代理协议模式或您所拥有的任何东西来捕获其不断变化的属性。)但是如果您使
Weather
不可变,您应该可以


因此,从技术上讲,这就是问题的答案,但这会引起一些设计考虑:

  • 人们通常应该努力使不同的类型松散耦合,即一种类型不应该太依赖于另一种类型的内部行为。但是在这里,我们在细胞内有一个观察者,它依赖于天气的易变性

  • 使用存储属性在视图中存储模型对象是不可取的。单元格在屏幕外滚动时会重复使用,但您可能需要一个单独的模型来捕获相关的模型对象,然后控制器会根据需要处理向视图对象(单元格)提供适当的模型对象的过程

  • 总之,不建议在“视图”中为“模型”信息使用存储属性

    您可以通过编写代码来解决这两个问题,该代码清楚地表明,您仅使用此
    weather
    参数用于更新UI控件,而不用于存储任何内容。因此,我只想使用一个方法,而不是存储的属性:

    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
    observer的存储属性是一种有用的模式。但是,只有当该属性确实是视图属性时,才应该这样做。例如,考虑一个绘制一些形状的自定义视图。您可能已经存储了指定(例如)绘制路径时要使用的笔划的宽度和颜色的属性。然后,为
    lineWidth
    strokeColor
    存储属性可能有意义,然后您可能会有一个
    didSet
    ,它调用
    setNeedsDisplay()
    (这会触发视图的重画)


    因此,您建议的模式确实有实际的应用,只是它应该局限于属性确实是视图对象属性的情况。

    谢谢您的反馈。你有没有其他更好的方法?除了用您描述的要点改进代码之外。好的,谢谢。我把问题“未回答”留到那时。@LuckyLuke-顺便说一句,除了单元和天气之间不幸的紧密耦合之外,使用存储属性存储模型信息的想法也是不可取的。见修订后的答复,