4

我正在尝试使用 xib 文件实现继承。是的,有点奇怪,但让我告诉你为什么。

我有一个类,SLBaseViewController,我的许多视图控制器都继承自它。当我想要一个子视图控制器时,我以通常的方式创建它:

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

这工作正常。SLHomeViewController 是一个 SLBaseViewController(它是一个 UIViewController)。

我这样做是因为我有其他想要继承 SLBaseViewController 行为的视图控制器。就我而言,我有一个在我的应用程序中通用的导航 UI 小部件,因此 SLSceneViewControll 也从 SLBaseViewController 继承,并且 SLHomeViewController 和 SLSceneViewController 都获得了自定义导航小部件行为。

自定义导航小部件还具有跨 SLBaseViewControllers 通用的位置信息。所以我实现了一个穷人的方式来做xib继承。

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

并且继承在 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;
}

如果我创建一个 SLHomeViewController 是加载 SLBaseViewController 的 xib,然后从中复制有趣的属性。如果 initWithNibName 检测到它正在加载 SLBaseViewController 它什么也不做,从而防止无限循环。

当然,问题是尚未设置出口属性。所以它只是复制零。

那么这些插座属性是什么时候设置的呢?

或者 - 有没有更好的方法来做我想做的事情?在我手动复制属性之前,这一切似乎都很美好。这对我来说似乎很脆弱。

(注意,我对仅限 iOS6 的解决方案很好。)

4

1 回答 1

5

这是因为 UIViewController 的延迟初始化。

UIViewController 的视图只有在调用 view 属性后才会被加载。

像那样:

controller.view

所以,在你的情况下,你可以在controller.view之前打电话self.navBarExtendedFPO = controller.navBarExtendedFPO;

为了更清楚地解释视图生命周期,有一个例子:

您的 SLBaseViewController 中有被覆盖的方法,类似于XIB 文件self.label中定义的方法navBarExtendedFPO

- (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);
}

并且有创建 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);

所以,有我们的日志:

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
于 2013-04-11T19:16:44.300 回答