何时设置 XIB 插座属性?

Posted

技术标签:

【中文标题】何时设置 XIB 插座属性?【英文标题】:When are XIB outlet properties set? 【发布时间】:2013-04-11 14:37:55 【问题描述】:

我正在尝试使用 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,它什么也不做,防止无限循环。

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

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

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

(请注意,我可以使用仅限 ios6 的解决方案。)

【问题讨论】:

好的,所以你有两个UIViewControllers,它们继承自第三个UIViewController,对吗?这里的问题是您想在您通常从子类中使用的所有UIViewController's + UIViewController's XIB 中使用一个小部件?是这样吗? 在视图控制器中有延迟初始化。因此,只有在调用属性 controller.view 后才会加载视图 - 在 vi​​ewController 中将调用 loadView 方法和 viewDidLoad 方法。 是的 Jacky Boy,但可能不止 2 个。 您的导航工具有多复杂?您是否考虑过一种以编程方式生成它的方法?可以在基本视图控制器的子类的每个实例中自反调用的东西? @PaulCezanne,对不起我的英语,在您调用 controller.view 属性后,您的 IBOutlet 将不为零 :) 只需在 self.navBarExtendedFPO = controller.navBarExtendedFPO 之前调用 controller.view; 【参考方案1】:

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

UIViewController 的视图只会在一些之后加载 调用视图属性。

这样:

controller.view

所以,在你的情况下,你可以在self.navBarExtendedFPO = controller.navBarExtendedFPO;之前调用controller.view

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

在您的 SLBaseViewController 中有被覆盖的方法,self.label 是模拟的 navBarExtendedFPO 在 XIB 文件中定义

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

【讨论】:

以上是关于何时设置 XIB 插座属性?的主要内容,如果未能解决你的问题,请参考以下文章

笔尖已装好,插座不见了

使用 xib 创建可重用的 UIView(并从情节提要中加载)

从外部引用设置属性上的 .attributedText(可通过 NIB 加载的 UIView 内的插座访问)似乎失败

自定义控件设置属性并实时展现并预览在xib中

我的 Outlet 何时与 IB 元素绑定?

从 xib 加载子类时如何设置子类 UIView 属性的值