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

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

问题描述

好的,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 - 一个完全独立的类

  • 允许我设置/获取视图属性的类中的插座

  • No separate UIViewController – a completely self-contained class
  • Outlets in the class to allow me to set/get properties of the view

我的当前这样做的方法是:

My current approach to doing this is:


  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] 并且只是使用加载的nib的内容设置对象似乎有点怀疑 - 这里有建议在这种情况下添加一个子视图也可以正常工作)。但是,我希望能够从故事板中实例化视图。所以我可以:

    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 / li>
    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 并从文件中加载nib。

    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问题涉及此问题,例如这里 here here 此处。但是,没有给出令人满意的答案解决问题:

    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中,在那里加载nib,但这对我来说似乎不是最理想的,因为它需要添加另一个文件作为包装器

    任何人都可以给出建议如何解决这个问题,并在自定义 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?

    推荐答案

    您的问题是调用 loadNibNamed:来自(后代) initWithCoder: loadNibNamed:在内部调用 initWithCoder:。如果你想覆盖storyboard编码器,并且总是加载你的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(并从storyboard加载)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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