问题描述
好的,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:
-
覆盖
- (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:
- 在故事板中的父视图上放置
UIView
/ li>
- 将自定义类设置为
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 并从文件中加载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问题涉及此问题,例如,,和。但是,没有给出令人满意的答案解决问题:
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加载)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!