如何初始化自定义视图(控制器),以便它以编程方式和Interface Builder中工作?

时间:2012-09-21 12:57:24

标签: ios uitableview uiview uiviewcontroller interface-builder

假设您实现了一个自定义表视图和一个自定义视图控制器(主要模仿UITableViewController的行为,但在以编程方式初始化时,...

@interface Foo : MyCustomTableViewController ...

Foo *foo = [[Foo alloc] init];

... foo.view类似于MyCustomTableView而不是UITableView

// MyCustomTableView.h

@protocol MyTableViewDelegate <NSObject, UITableViewDelegate>

// ...

@end

@protocol MyTableViewDataSource <NSObject, UITableViewDataSource>

// ...

@end

@interface MyCustomTableView : UITableView

// ...

@end

// MyCustomTableViewController.h

@interface MyCustomTableViewController : UIViewController

// ...

@end

如何以正确的顺序/方式实现/覆盖init方法,以便您可以通过编程方式或从任何自定义nib文件继承MyCustomTableView来创建和使用MyCustomTableViewController的实例通过在Interface Builder中将自定义类类型设置为MyCustomTableView

重要的是要注意,这正是UITableView(主要是UIKit)现在的工作原理:开发人员可以通过编程方式或通过从nib创建来创建和使用,无论是File owner主要view或更复杂的层次结构中的某个子视图,只需分配数据源或代理,您就可以了...

到目前为止,如果您继承MyCustomTableViewController,我将设法使其工作,我将创建MyCustomTableView的实例,并在self.view方法中将其分配给loadView;但无法弄清楚initWithNibName:bundle:initWithCoder:awakeFromNibawakeAfterUsingCoder:或其他任何操作方式。我迷失在生命周期链中,每次都以黑色视图/屏幕结束。

感谢。

3 个答案:

答案 0 :(得分:0)

UITableViewController如何加载其表是一个真正的谜,无论是否在界面构建器中连接它,但是我想出了一种非常好的方法来模拟该行为。

我想用一个包含MKMapView的可重用视图控制器实现这一点,我想通过检查视图的背景颜色来实现它。

这很难的原因是因为对self.view的任何调用导致故事板加载或加载默认的UIView(如果不存在)。如果用户确实没有设置视图,则无法确定是否介于这两个步骤之间。所以诀窍是来自故事板的那个有颜色,默认颜色是零颜色。

所以现在我有一个mapViewController可以在代码或故事板中使用,甚至不关心是否设置了地图。很酷。

- (void)viewDidLoad
{
    [super viewDidLoad];

    //magic to work without a view set in the storboard or in code.
    //check if a view has been set in the storyboard, like what UITableViewController does.
    //check if don't have a map view
    if(![self.view isKindOfClass:[MKMapView class]]){
        //check if the default view was loaded. Default view always has no background color.
        if([self.view isKindOfClass:[UIView class]] && !self.view.backgroundColor){
            //switch it for a map view
            self.view = [[MKMapView alloc] initWithFrame:CGRectZero];
            self.mapView.delegate = self;
        }else{
            [NSException raise:@"MapViewController didn't find a map view" format:@"Found a %@", self.view.class];
        }
    }

答案 1 :(得分:-1)

我在编写这样的类时使用的策略是尽可能晚地推迟我的自定义初始化代码。如果我可以等viewDidLoadviewWillAppear进行任何设置,而不是在initinitWithNibName:bundle:或类似方法中编写任何自定义代码,我就知道我的对象是初始化就像父类一样,无论实例化的方式是什么。我经常设法编写我的类,而不会覆盖这些init方法。

如果我发现我需要将初始化代码放在init方法中,我的策略是只编写一个版本的初始化代码,将其放在一个单独的方法中,然后覆盖所有的init方法。重写的方法调用自己的超类版本,检查是否成功,然后调用我的内部初始化方法。

如果这些策略失败了,那么这个类的对象被实例化的方式确实有所不同,我会为各种init方法编写自定义方法。

答案 2 :(得分:-1)

这就是我解决自己问题的方法:

- (void)loadView
{
    if (self.nibName) {
        // although docs states "Your custom implementation of this method should not call super.", I am doing it instead of loading from nib manually, because I am too lazy ;-)
        [super loadView];
    }
    else {
        self.view = // ... whatever UIView you'd like to create
    }
}