How do you handle the initialization order of a view in a UIViewController when building a view manually?

I have a UIViewController that materializes its representation in a loadView (i.e. no nib). In the documentation (and through confirmation in the code), loadView and, therefore, viewDidLoad will not be called until the UIViewController is accessed for the first time.

I have another class that instantiates a UIViewController and calls a number of methods on it before the view itself is mentioned. Many of these methods modify view / routines in the UIViewController. Unfortunately, the submission / subitems are not created at this stage. (I can simply add a link to the view before calling another method, but this requires effort and understanding of the invisible contract for the user of my UIViewController).

How do I handle methods on a UIViewController that change the view / subviews? In each of my UIViewController methods, I can check if the view is loaded, but you should not directly access loadView. I could put "self.view"; in a way to supposedly ensure that the view is loaded, but it seems pretty hacky (and, of course, triggers a warning about the clang).

In addition, this is not just one time, since it is obvious that the view can be unloaded during memory events, which allows this state to occur quite often.

I feel that I am missing something quite simple, but I could not find it. What is a suitable way to deal with this state / initialization problem?

+4
source share
2 answers

Basically, the answer to this question is not to expose the components of your views through your view controller, since other objects probably shouldn't access them. So, if your view controller controls a view that, for example, displays a person’s name, instead of doing something like this:

PersonNameController* pnc = [[PersonNameController alloc] initWithNibName:nil bundle:nil]; [[pnc nameLabel] setText:@"Jason"]; [[self navigationController] pushViewController:pnc animated:YES]; 

You separate the data / model bits from the view bits and output the 'name' property to your view controller. Then you can get this simple example code that always works correctly (it requires a bit more code, but it saves you headaches like this and, to be honest, this is what MVC is talking about):

 PersonNameController* pnc = [[PersonNameController alloc] initWithNibName:nil bundle:nil]; [pnc setName:@"Jason"]; [[self navigationController] pushViewController:pnc animated:YES]; 

And the implementation of such a controller:

 @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 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 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 

Quick note: it was just printed in this small box, and I was really tired, so he did not check the complete syntax, etc. This meant as a quick example, not something to cut and paste.

In addition, if you have several properties that need to be updated each time, you can create one utility method, such as -udpateLabels, or something that updates all of them. Then you call this from each setter and from viewDidLoad or loadView. Thus, you will have everything in one place (due to some performance). If after profiling you spend too much time in this method, you can break it down as needed.

+5
source

In general, for design reasons, I believe that you should not allow another class to directly view the UIViewController (which is why it is called the UIViewController because it is the one who controls the view). Better to say that the UIViewController will change its appearance instead.

If the view is not loaded at this time, you can simply have ivars that save a certain state, for example:

 BOOL viewShrinked; 

and use this kind of vars to restore this state later when the view loads or reloads.

 - (void) loadView{ if(viewShrinked){ .... }else{ ... } } 

BUT: Before you do this: you probably want to review your code and refactoring so that calls that affect the view are made when the view is available as much as possible.

+2
source

Source: https://habr.com/ru/post/1336261/


All Articles