When are XIB outlet properties set?

2019-06-26 04:36发布

I'm trying to implement inheritance with xib files. Yes, a little strange, but let me tell you why.

I have a class, SLBaseViewController that many of my view controllers inherit from. When I want a child view controller I create it in the usual way:

    SLHomeViewController *controller = [[SLHomeViewController  alloc] initWithNibName:@"SLHomeViewController" bundle:nil];

This works fine. SLHomeViewController is an SLBaseViewController (which is a UIViewController).

I'm doing this because I have other view controllers that I want to inherit SLBaseViewController behavior. In my case, I have a navigation UI widget that is common across my app, so a SLSceneViewControll inherits from SLBaseViewController also and both SLHomeViewController and SLSceneViewController both get the custom nav widget behavior.

The custom nav widget also has position information that is common across SLBaseViewControllers. So I implemented a poor man's way of doing xib inheritance.

@interface SLBaseViewController : UIViewController <SLNavBarViewControllerDelegate>
@property (strong, nonatomic) IBOutlet UIView *navBarExtendedFPO;

and the inheritance is done in initWithNibName

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        Class callingClass = [self class];
        Class slBaseViewControllerClass = NSClassFromString (SL_BASE_VC_CLASS_NAME);
        if (callingClass !=  slBaseViewControllerClass) {
            SLBaseViewController *controller = [[SLBaseViewController  alloc] initWithNibName:@"SLBaseViewController" bundle:nil];
            // now load all the properties by hand
            self.navBarExtendedFPO = controller.navBarExtendedFPO;
        }
    }
    return self;
}

If I create a SLHomeViewController is load the xib of a SLBaseViewController and then copies the interesting property from it. If initWithNibName detects it is loading a SLBaseViewController it just does nothing, preventing an infinite loop.

The problem is, of course, that the outlet properties are not set yet. So it just copies nil.

So when are these outlet properties set?

Or - is there a better way to do what I'm trying to do? It all seemed rosy until I copy the properties by hand. That seems pretty brittle to me.

(Note, I'm fine with iOS6-only solutions.)

标签: ios ios6 xib
1条回答
男人必须洒脱
2楼-- · 2019-06-26 05:21

It happens because of UIViewController's the lazy initialization.

The view of UIViewController will be loaded only after some one call the view property.

like that:

controller.view

So, in your case, you can just call controller.view before self.navBarExtendedFPO = controller.navBarExtendedFPO;

To explaining view life cycle more clear, there is an example:

there is overridden methods in your SLBaseViewController, self.label is analog of navBarExtendedFPO defined in XIB file

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        NSLog(@"initWithNibName: view loaded - %d , IBOuttlet loaded - %d", [self isViewLoaded], self.label != nil);
    }
    return self;
}

- (void)loadView {
    NSLog(@"loadView1: view loaded - %d , IBOuttlet loaded - %d", [self isViewLoaded], self.label != nil);
    [super loadView];
     NSLog(@"loadView2: view loaded - %d , IBOuttlet loaded - %d", [self isViewLoaded], self.label != nil);
}

- (void)viewDidLoad
{
    NSLog(@"viewDidLoad1: view loaded - %d , IBOuttlet loaded - %d", [self isViewLoaded], self.label != nil);
    [super viewDidLoad];
    NSLog(@"viewDidLoad2: view loaded - %d , IBOuttlet loaded - %d", [self isViewLoaded], self.label != nil);
}

and there is method that creates SLBaseViewController

SLBaseViewController *testController = [[SLBaseViewController alloc] initWithNibName:@"SLBaseViewController" bundle:nil];
NSLog(@"after initialization: view loaded - %d , IBOuttlet loaded - %d", [testController isViewLoaded], testController.label != nil);
UIView * testView = testController.view;
NSLog(@"after calling testView.view: view loaded - %d , IBOuttlet loaded - %d", [testController isViewLoaded], testController.label != nil);

so, there is our logs:

initWithNibName: view loaded - 0 , IBOuttlet loaded - 0
after initialization: view loaded - 0 , IBOuttlet loaded - 0
loadView1: view loaded - 0 , IBOuttlet loaded - 0
loadView2: view loaded - 1 , IBOuttlet loaded - 1
viewDidLoad1: view loaded - 1 , IBOuttlet loaded - 1
viewDidLoad2: view loaded - 1 , IBOuttlet loaded - 1
after calling testView.view: view loaded - 1 , IBOuttlet loaded - 1
查看更多
登录 后发表回答