子类化和覆盖使用 nib 文件创建的 UIView

Posted

技术标签:

【中文标题】子类化和覆盖使用 nib 文件创建的 UIView【英文标题】:Subclassing and overriding a UIView created with a nib file 【发布时间】:2012-03-07 23:43:25 【问题描述】:

假设我创建了一个 UIView 的子类,并且正在使用 nib 文件加载它。 我这样做:

MySubView.m  

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MySubView" owner:self options:nil];

        [self release];
        self = [[nib objectAtIndex:0] retain];
        self.tag = 1;
        [self fire];
    
    return self;


- (void)fire 
   NSLog(@"Fired MySubView");

现在我想创建一些变体,但我不想复制 nib 文件,所以我尝试像这样子类化 MySubView,更改背景颜色:

RedMySubView.m  


- (id)initWithFrame:(CGRect)frame
    
        self = [super initWithFrame:frame];
    if (self) 
       self.backgroundColor = [UIColor redColor];
       [self fire];
    
    return self;


- (void)fire 
   NSLog(@"Fired RedMySubView");

视图已创建,背景颜色已更改,但子类未覆盖开火动作。如果我调用fire 方法,结果是控制台中的Fired MySubView。 我该如何解决这个问题? 我想保留笔尖布局,但给它一个新的类。

【问题讨论】:

检查这个***.com/questions/1923720/…> 【参考方案1】:

我会说,使用 MySubview 初始化程序 initWithFrame 中的 [self release],您将丢弃要使用初始化程序创建的类。该类由 loadNibName 方法加载,因此与 nib 中定义的类相同。 所以在子类中调用初始化器是没有用的。

尝试在 MySubview 中实现自己的 nib 构造函数(例如 initWithNibFile):

- (id) initWithNibFile:(NSString *) nibName withFrame:(CGRect) frame

等等。并在 RedMySubview 中调用此构造函数

- (id) initWithNibFile:(NSString *) nibName withFrame:(CGRect) frame 
self = [super initWithNibFile:mynib withFrame:MyCGRect];
if (self)
....

如果您现在查找您的 nib 文件确实将 RedMySubview 作为类,则应该是 fire 可覆盖。如果您同时使用 MySubview 和 RedMySubview,则必须复制 xib。 或者您创建一个仅实现 initWithNibFile 初始化程序的抽象类(存根),而您要创建的 UIView 是它的子类:

MyAbstractNibUIView initWithNibFile:withFrame:
MyRedSubview : MyAbstractNibUIView        red.xib
MyGreenSubview :MyAbstractNibUIView       green.xib 
MyBlueSubview : MyAbstractNibUIView       blue.xib

【讨论】:

【参考方案2】:

当您调用self = [[nib objectAtIndex:0] retain] 时,您基本上会覆盖您的“self”对象以成为 MySubView,因为 MySubView 是 nib 文件中的基础对象。这是不希望的,因为如果调用类是 RedMySubView,那么它将被覆盖为 MySubView。

相反,您想将 MySubView 中的 - (id)initWithFrame:(CGRect)frame 更改为:

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MySubview" owner:self options:nil];

        // The base of the nib file. Don't set self to this, instead copy all of its
        // subviews, and "self" will always be the class you intend it to be.
        UIView *baseView = [nib objectAtIndex:0];

        // Add all the subviews of the base file to your "MySubview". This
        // will make it so that any subclass of MySubview will keep its properties.
        for (UIView *v in [baseView subviews])
            [self addSubview:v];

        self.tag = 1;
        [self fire];
    
    return self;

现在,一切都应该在“MyRedSubView”的初始化程序中工作,除了 fire 会触发两次,因为您在 MySubView 和 RedMySubView 中都调用它。

【讨论】:

以上是关于子类化和覆盖使用 nib 文件创建的 UIView的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的 NSWindowController。使用 Nib 进行子类化和初始化

如何在 UIView 的子类中同时覆盖 initWithFrame: 和 initWithCoder:?

自定义 UIView 子类添加 nib 子视图

iOS:使用 nib 子类化 UITableViewCell,进入 UIView(不是视图控制器)子类

将子类 UIView 添加到具有自动布局约束的 Nib

为 UIView 子类加载 Nib 的正确方法