使用 xib 创建可重用的 UIView(并从故事板加载) [英] Creating a reusable UIView with xib (and loading from 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
——一个完全独立的类 - 类中的插座允许我设置/获取视图的属性
我目前的做法是:
覆盖
-(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:
- 在故事板的父视图上放置一个
UIView
- 将其自定义类设置为
MyCustomView
覆盖
-(id)initWithCoder:
——我见过的最常见的代码符合如下模式:
- Place a
UIView
on a parent view in the storyboard - Set its custom class to
MyCustomView
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屋!