iOS 使用自定义 Xib 和视图覆盖 initWithFrame 的正确方法?
Posted
技术标签:
【中文标题】iOS 使用自定义 Xib 和视图覆盖 initWithFrame 的正确方法?【英文标题】:iOS Proper Way To Override initWithFrame With Custom Xib and View? 【发布时间】:2014-07-21 20:06:38 【问题描述】:现在,我对我为尝试创建自定义视图而创建的这个 hacky 解决方法不太满意。它在另一个视图控制器中使用 initWithFrame 以编程方式初始化,因此我在代码中覆盖了它。这似乎不是初始化我的视图的正确方法,但我不确定还能做什么。
- (instancetype)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
self = [[[NSBundle mainBundle] loadNibNamed:@"OTGMarkerDetailView" owner:self options:nil] lastObject];
[self setFrame:frame];
return self;
如果没有 setFrame 方法,自定义视图似乎总是在窗口顶部创建,而不是在我使用 CGRectMake 指定的坐标处。
我在这里用视图控制器中的方法初始化视图。
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker
if (!self.detailView)
self.detailView = [[OTGMarkerDetailView alloc] initWithFrame:CGRectMake(0, 568, 320, 55)];
[self.view addSubview:self.detailView];
self.detailView.backgroundColor = [UIColor whiteColor];
NSRange range = [self.startAddress rangeOfString:@","];
NSString *mainAddress = [self.startAddress substringToIndex:range.location];
NSString *subAddress = [self.startAddress substringFromIndex:range.location + 1];
[self.detailView setLabelsWithMainAddress:mainAddress subAddress:subAddress];
[UIView animateWithDuration:0.5 animations:^
self.detailView.frame = CGRectMake(0, 513, 320, 55);
];
return YES;
【问题讨论】:
【参考方案1】:一般来说,处理此类事情的最佳方式是使用类工厂方法:
+(instancetype)markerDetailView
[[[NSBundle mainBundle] loadNibNamed:@"OTGMarkerDetailView" owner:nil options:nil] lastObject];
然后你创建视图:
self.detailView = [OTGMarkerDetailView markerDetailView];
请注意,我也更喜欢采用更安全的方法从 nib 文件加载视图:
+(instancetype)markerDetailView
for(OTGMarkerDetailView* view in [[NSBundle mainBundle] loadNibNamed:@"OTGMarkerDetailView" owner:nil options:nil])
if([view isKindOfClass:[OTGMarkerDetailView class]])
return view;
NSAssert(false, @"Failed to load OTGMarkerDetailView");
return nil;
如果您需要在 InterfaceBuilder 中使用自定义视图的实例,您必须覆盖 initWithCoder:
和 initWithFrame:
。既然你要替换自己,真的没有必要打电话给super
:
-(id)initWithCoder:(NSCoder*)aDecoder
self = [[self class] markerDetailView];
// I think this will work to inherit settable properties in IB
if((self = [super initWithCoder:aDecoder]))
// support any additional settable properties in IB
return self;
【讨论】:
如果我需要在界面生成器中创建它怎么办?我是否应该将 self 设置为 markerDetailView 的返回值,因为我必须重写 initWithCoder 和 initWithFrame? 是的,如果您想从 Interface Builder 实例化自定义类的实例并通过从 nib 文件加载来创建自定义视图,那么您必须覆盖 initWithCoder 和 initWithFrame,但是在这两种情况下,您都不会打扰调用 super ,因为无论如何您都要把它扔掉。 在这种情况下,我可能仍会使用工厂方法,并将其包装在 initWithCoder 和 initWithFrame 中。 我看到了一些示例,他们在自定义初始化程序的末尾执行 [self addSubview:customView]。如果我可以将 self 设置为视图本身,是否有任何理由这样做? 向前迈进,IB 6 和它对 IBDesignable 的支持,你可能想要避免这个比喻,但我不确定这将如何真正发挥作用。部分原因是我现在在代码中为我的自定义视图创建子视图,尽管我不喜欢这样做。【参考方案2】:为什么mapView
方法不做你的init
方法做的事情,即直接将视图拉出它的笔尖?换句话说,你为什么要对mapView
隐瞒获取该视图实例的方法是加载一个笔尖这一事实?没有理由隐瞒这个事实。
或者,如果您确实想隐藏这个事实,为什么不编写一个方便的构造函数作为视图的类方法,而不是滥用initWithFrame
?例如[OTGMarkerDetailView makeADetailView]
...
【讨论】:
没有理由这样做。我会解决这个问题 - 但至于其他一切,这是创建自定义视图的可接受方式吗? 一般来说...没有。您正在初始化您的自定义类,然后通过为 self 分配一个不同的实例立即将其丢弃。更好的解决方案是从 nib 文件加载实例的类工厂方法。 对,@David,这就是我所说的便利构造函数作为类方法的意思。 我主要是在回答他关于这是一种合理方法的问题,完全错过了您的构造函数评论。 是的,我明白,我喜欢你的回答。我只是在澄清我们同意的 OP。以上是关于iOS 使用自定义 Xib 和视图覆盖 initWithFrame 的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章
如何:在iOS中使用StackView View从XIB自我调整自定义视图