在UIViewController中设置属性的位置在哪里?

3

一个相对基础的问题,我不确定如何处理。我通常在viewDidLoad中设置UIViewController相关代码。如果控制器有一些用于标签等的属性,那么这就是我初始化并将它们添加到视图的地方。这些属性通常在.m文件中声明,可以被视为伪私有。

然而-如果控制器在其头文件中公开了其中一个属性(比如UILabel),我发现在设置它时不能依赖它存在。例如:

CustomViewController *controller = [CustomViewController alloc] initWithNibName:nil bundle:nil];
controller.someLabel.text = @"label text goes here";
//then comes the presentation code

我发现我在设置标签文本时太早了 - viewDidLoad 还没有触发,所以标签是空的。
我应该在 init 中创建这个标签,并在 viewDidLoad 中添加它吗?我应该在 init 中做所有的设置吗?或者视情况而定,初始化视图属性?
或者根本原因是我不应该有一个控制器暴露一个视图(标签),而使用其他模式?
我正在寻找一种一致的方法来组织我的代码。

1
你会在哪里尝试调用你在问题中的代码?从另一个控制器吗?如果是这样,你需要将字符串传递给CustomController中的字符串属性,并在viewDidLoad中设置其标签的文本。从MVC的角度来看,让控制器填充自己的UI更好,因此传递所需的数据以执行此操作是正确的方式。 - rdelmar
你可以在属性 getter 中按需创建标签。这样,调用者就可以保证有一个标签,然后你只需要在 viewDidLoad 中调整其大小并添加到视图中。 - Jeremy
我不确定你所说的视图而非视图控制器的上下文。如果视图在设置标签文本的同一控制器中,那么这样做是可以的。您可以重写setter方法,但我不知道这是否比仅仅说myView.label.text = ...更好。 - rdelmar
我在考虑类似于UITableViewCell的东西,它公开了所有子视图(例如textLabel和detailTextLabel)。它不需要你设置' labelString '或类似的东西,就像你建议的那样。我试图理解区别以及它是否是MVC的事情-我的是控制器,单元格是视图。此外,如果不覆盖setter,它感觉非常不一致。实际上,我宁愿根本不公开标签。否则,我必须记住在视图加载之前始终设置字符串,但可以选择在之后设置任何一个。看起来很复杂。 - Ben Packard
1
我不确定我理解你所有的担忧。将子视图暴露给父视图(单元格)与将一个控制器中的标签暴露给另一个控制器是不同的。视图需要能够直接与其父视图交流,但一个控制器不一定需要知道另一个控制器的视图。我认为,出于封装的原因,最好将数据传递到另一个控制器中,以便它需要工作(甚至在其init方法中),而不是从另一个控制器控制其视图。 - rdelmar
显示剩余4条评论
2个回答

4

没错,你已经基本正确了。问题在于,直到实际呈现视图之前,你的控制器的所有视图组件都没有加载。因此,你不能从控制器外部设置任何IBOutlets。

为了传递一个UILabel的文本,例如,一种方法是创建一个新的字符串属性,比如说self.myString,在控制器外部将其赋值,并在viewDidLoad中将该属性设置为标签文本。

CustomViewController *controller = [CustomViewController alloc] initWithNibName:nil bundle:nil];
controller.myString = @"label text goes here";

CustomViewController 中:

- (void)viewDidLoad
{
    [super viewDidLoad];

    (...)
    self.label.text = self.myString;
}

我还需要一个自定义的setter吗?否则[controller setMyString:]将不会更新文本字段。 - Ben Packard
我不这么认为。这个辅助属性只在这种情况下是必要的并被使用,即viewController已经被分配但尚未呈现。在我的例子中,文本已经在viewDidLoad中设置了,所以您不需要自定义setter。如果您想在类外的其他地方再次更改标签的文本,而视图已经被呈现,则可以直接使用IBOutlet。 - Lucas Eduardo
但这样我必须记得,在视图加载之前使用一个属性(字符串),在视图加载之后使用另一个属性(直接标签)(我也是在代码中完成所有这些操作,所以没有使用outlets)。 - Ben Packard
是的,在这个层面上,这只是代码设计的问题,你可以做任何想做的事情。例如,你可以创建一个像setText:这样的方法,在内部执行类似于if (self.label) {self.label.text = text;} else {self.myString.text = text;}的操作,然后只使用这个方法。它将在初始化输出之前和之后都能正常工作。 - Lucas Eduardo

1
我倾向于做以下操作,如果我只想按需更新视图(如果我想更频繁地更新它,那么我会在viewWillAppear或通过KVO或其他通知机制进行更新)。
有一些私有方法,根据属性设置我的UI。
- (void)_updateUIForProperty {
    // Handle UI update
}

实现一个setter方法来设置我的公共属性,如果视图已经被加载了,就调用_updateUIForProperty方法:
- (void)setProperty:(<#property type#>)property {
    _property = property;

    if(self.isViewLoaded) {
        [self _updateUIForProperty];
    }
}

然后,为了处理在视图加载之前设置属性的情况,我们在 viewDidLoad 中执行以下操作:

- (void)viewDidLoad {
    // Other initialization

    if(_property != nil) {
        [self _updateUIForProperty];
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接