我有一个UIViewController,它在loadView中实现了它的视图(即没有nib)。根据文档(并通过代码中的确认),在第一次访问UIViewController的视图之前,不会调用loadView,因此不会调用viewDidLoad。
我有另一个实例化UIViewController的类,并在引用视图本身之前调用它上面的许多方法。其中许多方法都修改了UIViewController中的视图/子视图。不幸的是,此时未对视图/子视图进行实例化。 (我可以很容易地在其他方法调用之前添加对视图的引用,但这需要努力并理解我的UIViewController用户的不可见契约。)
如何处理修改视图/子视图的UIViewController上的方法?在我的每个UIViewController方法中,我可以检查视图是否已加载,但您不应该直接调用loadView。我可以把“自我观点”;在方法中可能确保视图被加载,但这看起来非常hackish(当然导致clang警告)。
此外,这不仅仅是一次性的事情,因为显然可以在内存事件期间卸载视图,从而允许这种状态经常发生。
我觉得我错过了一些相当简单的东西,但我找不到它。处理此状态/初始化问题的适当方法是什么?
答案 0 :(得分:5)
基本上,这类事情的答案是不要通过视图控制器公开视图的组件,因为其他对象可能不应该访问它们。因此,如果您的视图控制器管理一个呈现某人姓名的视图,例如,而不是这样做:
PersonNameController* pnc = [[PersonNameController alloc] initWithNibName:nil bundle:nil];
[[pnc nameLabel] setText:@"Jason"];
[[self navigationController] pushViewController:pnc animated:YES];
您可以将数据/模型位与视图位分开,并在视图控制器上显示“name”属性。然后你就可以拥有这个总是正常工作的简单示例代码(它需要更多的代码,但是它可以让你免于这样的麻烦,而且说实话,就是MVC的意思):
PersonNameController* pnc = [[PersonNameController alloc] initWithNibName:nil bundle:nil];
[pnc setName:@"Jason"];
[[self navigationController] pushViewController:pnc animated:YES];
并且执行这样一个控制器:
@interface PersonNameController : UIViewController {
@private
NSString* name_;
}
// other people use this to pass model data to the controller
@property(nonatomic,copy) NSString* name;
@end
@interface PersonNameController()
// for us only
@property(nonatomic,assign) UILabel* nameLabel;
@end
@implementation PersonNameController
// properties
@synthesize nameLabel;
@synthesize name = name_;
- (void)setName:(NSString*)value
{
if( [value isEqualToString:name_] ) return;
[name_ autorelease];
name_ = [value copy];
[[self nameLabel] setText:name_?:@""];
}
// view lifecycle
- (void)loadView
{
// ob fake frame for now
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake( 10, 10, 300, 37)];
[view addSubview:label];
[self setView:view];
[self setNameLabel:label];
// Set the name on the label if one's set already
[label setText:name_?:@""];
// clean up
[view release];
[label release];
}
// take care of unloads
- (void)viewDidUnload
{
// no need to release, but set nil so it's not used while the view
// is unloaded
nameLabel = nil;
[super viewDidUnload];
}
- (void)dealloc
{
// nameLabel is assign (owned by our view) so we don't release here
// but set to nil just to be good citizens
nameLabel = nil;
[name_ release];
[super dealloc];
}
@end
快速注意:这只是在这个小盒子里输入的,我真的很累,所以没有检查完整的语法正确性等等。这是一个快速的例子,不是要削减和粘贴。强> 的
此外,如果每次都需要更新许多属性,您可以创建一个单独的实用程序方法,例如-udpateLabels或更新所有这些方法。然后从每个setter和viewDidLoad或loadView调用它。这样你就可以在一个地方拥有一切(以牺牲一些表现为代价)。如果在分析后,您在该方法上花费了太多时间,则可以根据需要将其分解。
答案 1 :(得分:2)
一般来说,出于设计原因,我相信你不应该让其他类直接修改UIViewController的视图(这就是为什么叫做UIViewController,因为他是控制 视图的人)最好告诉它UIViewController改为修改它的视图。
如果当时未加载视图,您可能只有保存某种状态的ivars,如:
BOOL viewShrinked;
并在加载或重新加载视图时使用此类变量来恢复该状态。
- (void) loadView{
if(viewShrinked){
....
}else{
...
}
}
但是:在执行此操作之前:您可能希望修改代码并重构,以便在视图可用时尽可能完成影响视图的调用。