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

时间:2015-12-16 23:43:41

标签: ios swift uicollectionview

我创建了一个显示一些信息的UICollectionViewCell的子类。我有一个房产类型天气。当设置了一个实例时,我想更新单元格。方法下面是不好的?我想如果我在加载之前访问UI组件,我可以触发视图提前创建。或者是没有意义,只适用于UIViewController(关于早期使用view属性)?

如果这很糟糕,那么正确的方法是什么?

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

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

2 个答案:

答案 0 :(得分:3)

但是,您可能需要else子句,如果weathernil,则清除文本字段。同样,如果您可以从后台线程更新它,您可能希望将该UI更新分发回主线程。

请注意,当您在单元格的weather中设置init时,不会调用此观察者(此时也不会在@IBOutlet处进行配置)。因此,请确保您不依赖于此。

此外,如果Weather是可变的,请认识到如果更改现有fromDate对象的Weather,则无法捕获该对象。 (如果Weather是可变的,你真的想要通过KVO,委托协议模式或你有什么来捕获它的变化属性。)但如果你使Weather不可变,你应该没事。

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

  1. 人们通常应该努力使松散耦合的不同类型,即一种类型不应过于依赖另一种类型的内部行为。但在这里,我们在细胞内有一个观察者,这取决于Weather的可变性。

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

  3. 最重要的是,不建议在“视图”中使用存储属性作为“模型”信息。

    您可以通过编写代码来解决这两个问题,这些代码清楚地表明您仅仅为了更新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观察者的存储属性是一种有用的模式。但是,只有当属性真正属于视图属性时才应该这样做。例如,考虑绘制一些形状的自定义视图。例如,您可能具有存储的属性,用于指定绘制路径时要使用的笔划的宽度和颜色。然后,存储lineWidthstrokeColor的属性可能有意义,然后您可能会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
                }
            })
        }
    }