在手动构建视图时,如何处理UIViewController中的视图初始化顺序?

时间:2011-01-20 05:36:25

标签: iphone objective-c cocoa-touch ios uiviewcontroller

我有一个UIViewController,它在loadView中实现了它的视图(即没有nib)。根据文档(并通过代码中的确认),在第一次访问UIViewController的视图之前,不会调用loadView,因此不会调用viewDidLoad。

我有另一个实例化UIViewController的类,并在引用视图本身之前调用它上面的许多方法。其中许多方法都修改了UIViewController中的视图/子视图。不幸的是,此时未对视图/子视图进行实例化。 (我可以很容易地在其他方法调用之前添加对视图的引用,但这需要努力并理解我的UIViewController用户的不可见契约。)

如何处理修改视图/子视图的UIViewController上的方法?在我的每个UIViewController方法中,我可以检查视图是否已加载,但您不应该直接调用loadView。我可以把“自我观点”;在方法中可能确保视图被加载,但这看起来非常h​​ackish(当然导致clang警告)。

此外,这不仅仅是一次性的事情,因为显然可以在内存事件期间卸载视图,从而允许这种状态经常发生。

我觉得我错过了一些相当简单的东西,但我找不到它。处理此状态/初始化问题的适当方法是什么?

2 个答案:

答案 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{
        ...
    }
}

但是:在执行此操作之前:您可能希望修改代码并重构,以便在视图可用时尽可能完成影响视图的调用。