使用 xib 创建可重用的 UIView(并从故事板加载) [英] Creating a reusable UIView with xib (and loading from storyboard)

查看:20
本文介绍了使用 xib 创建可重用的 UIView(并从故事板加载)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,StackOverflow 上有很多关于这个的帖子,但没有一个特别清楚解决方案.我想创建一个带有随附的 xib 文件的自定义 UIView.要求是:

OK, there are dozens of posts on StackOverflow about this, but none are particularly clear on the solution. I'd like to create a custom UIView with an accompanying xib file. The requirements are:

  • 没有单独的UIViewController——一个完全独立的类
  • 类中的插座允许我设置/获取视图的属性

我目前的做法是:

  1. 覆盖-(id)initWithFrame:

-(id)initWithFrame:(CGRect)frame {
    self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
                                          owner:self
                                        options:nil] objectAtIndex:0];
    self.frame = frame;
    return self;
}

  • 在我的视图控制器中使用 -(id)initWithFrame: 以编程方式实例化

    MyCustomView *myCustomView = [[MyCustomView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
    [self.view insertSubview:myCustomView atIndex:0];
    

  • 这很好用(尽管从不调用 [super init] 并且简单地使用加载的笔尖的内容设置对象似乎有点可疑 – 这里有关于 在这种情况下添加一个子视图,它也可以正常工作).但是,我也希望能够从情节提要中实例化视图.所以我可以:

    This works fine (although never calling [super init] and simply setting the object using the contents of the loaded nib seems a bit suspect – there is advice here to add a subview in this case which also works fine). However, I'd like to be able to instantiate the view from the storyboard also. So I can:

    1. 在故事板的父视图上放置一个 UIView
    2. 将其自定义类设置为 MyCustomView
    3. 覆盖-(id)initWithCoder:——我见过的最常见的代码符合如下模式:

    1. Place a UIView on a parent view in the storyboard
    2. Set its custom class to MyCustomView
    3. Override -(id)initWithCoder: – the code I've seen the most often fits a pattern such as the following:

    -(id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self initializeSubviews];
        }
        return self;
    }
    
    -(id)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self initializeSubviews];
        }
        return self;
    }
    
    -(void)initializeSubviews {
        typeof(view) view = [[[NSBundle mainBundle]
                             loadNibNamed:NSStringFromClass([self class])
                                    owner:self
                                  options:nil] objectAtIndex:0];
        [self addSubview:view];
    }
    

    当然,这行不通,因为无论我是使用上面的方法,还是以编程方式实例化,最终都会在输入 - 时递归调用 -(id)initWithCoder:(void)initializeSubviews 并从文件中加载笔尖.

    Of course, this doesn't work, as whether I use the approach above, or whether I instantiate programatically, both end up recursively calling -(id)initWithCoder: upon entering -(void)initializeSubviews and loading the nib from file.

    其他几个 SO 问题与此有关,例如 这里这里此处 和此处.但是,给出的答案都不能令人满意地解决问题:

    Several other SO questions deal with this such as here, here, here and here. However, none of the answers given satisfactorily fixes the problem:

    • 一个常见的建议似乎是将整个类嵌入到 UIViewController 中,并在那里加载笔尖,但这对我来说似乎并不理想,因为它需要添加另一个文件作为包装器

    任何人都可以就如何解决这个问题提供建议,并在自定义 UIView 中获得工作插座,而无需大惊小怪/没有瘦控制器包装器?或者有没有替代的、更简洁的使用最少的样板代码做事的方法?

    Could anyone give advice on how to resolve this problem, and get working outlets in a custom UIView with minimum fuss/no thin controller wrapper? Or is there an alternative, cleaner way of doing things with minimum boilerplate code?

    推荐答案

    您的问题是从 initWithCoder:(的后代)调用 loadNibNamed:.loadNibNamed: 在内部调用 initWithCoder:.如果您想覆盖故事板编码器,并始终加载您的 xib 实现,我建议使用以下技术.向您的视图类添加一个属性,并在 xib 文件中将其设置为预定值(在用户定义的运行时属性中).现在,在调用 [super initWithCoder:aDecoder]; 后检查属性的值.如果是预定值,则不调用[self initializeSubviews];.

    Your problem is calling loadNibNamed: from (a descendant of) initWithCoder:. loadNibNamed: internally calls initWithCoder:. If you want to override the storyboard coder, and always load your xib implementation, I suggest the following technique. Add a property to your view class, and in the xib file, set it to a predetermined value (in User Defined Runtime Attributes). Now, after calling [super initWithCoder:aDecoder]; check the value of the property. If it is the predetermined value, do not call [self initializeSubviews];.

    所以,像这样:

    -(instancetype)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
    
        if (self && self._xibProperty != 666)
        {
            //We are in the storyboard code path. Initialize from the xib.
            self = [self initializeSubviews];
    
            //Here, you can load properties that you wish to expose to the user to set in a storyboard; e.g.:
            //self.backgroundColor = [aDecoder decodeObjectOfClass:[UIColor class] forKey:@"backgroundColor"];
        }
    
        return self;
    }
    
    -(instancetype)initializeSubviews {
        id view =   [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
    
        return view;
    }
    

    这篇关于使用 xib 创建可重用的 UIView(并从故事板加载)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆