ステップ1。self
ストーリーボードからの置き換え
メソッドの置換self
はinitWithCoder:
次のエラーで失敗します。
'NSGenericException', reason: 'This coder requires that replaced objects be returned from initWithCoder:'
代わりに、デコードされたオブジェクトをawakeAfterUsingCoder:
(ではなくawakeFromNib
)に置き換えることができます。お気に入り:
@implementation MyCustomView
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
owner:nil
options:nil] objectAtIndex:0];
}
@end
ステップ2。再帰呼び出しの防止
もちろん、これは再帰的な呼び出しの問題も引き起こします。(ストーリーボードデコード-> awakeAfterUsingCoder:
-> loadNibNamed:
-> awakeAfterUsingCoder:
-> loadNibNamed:
-> ...)
したがってawakeAfterUsingCoder:
、ストーリーボードデコードプロセスまたはXIBデコードプロセスで現在の呼び出しが呼び出されていることを確認する必要があります。これを行うには、いくつかの方法があります。
a)@property
NIBでのみ設定されているprivateを使用します。
@interface MyCustomView : UIView
@property (assign, nonatomic) BOOL xib
@end
「MyCustomView.xib」でのみ「ユーザー定義のランタイム属性」を設定します。
長所:
短所:
- 単に機能しません:AFTER
setXib:
と呼ばれます awakeAfterUsingCoder:
b)self
サブビューがあるかどうかを確認します
通常、xibにはサブビューがありますが、ストーリーボードにはありません。
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
if(self.subviews.count > 0) {
return self;
}
else {
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
owner:nil
options:nil] objectAtIndex:0];
}
}
長所:
- InterfaceBuilderにはトリックはありません。
短所:
- ストーリーボードにサブビューを含めることはできません。
c)loadNibNamed:
通話中に静的フラグを設定する
static BOOL _loadingXib = NO;
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
if(_loadingXib) {
return self;
}
else {
_loadingXib = YES;
typeof(self) view = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
owner:nil
options:nil] objectAtIndex:0];
_loadingXib = NO;
return view;
}
}
長所:
- シンプル
- InterfaceBuilderにはトリックはありません。
短所:
d)XIBでプライベートサブクラスを使用する
たとえば_NIB_MyCustomView
、のサブクラスとして宣言しMyCustomView
ます。また、XIBでのみ_NIB_MyCustomView
代わりに使用MyCustomView
してください。
MyCustomView.h:
@interface MyCustomView : UIView
@end
MyCustomView.m:
#import "MyCustomView.h"
@implementation MyCustomView
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
owner:nil
options:nil] objectAtIndex:0];
}
@end
@interface _NIB_MyCustomView : MyCustomView
@end
@implementation _NIB_MyCustomView
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
return self;
}
@end
長所:
短所:
- 接頭辞
_NIB_
XIB Interface Builderでトリック
- 比較的多くのコード
e)ストーリーボードのプレースホルダーとしてサブクラスを使用する
d)
ストーリーボードのサブクラス、XIBの元のクラスと似ていますが使用します。
ここではMyCustomViewProto
、のサブクラスとして宣言しMyCustomView
ます。
@interface MyCustomViewProto : MyCustomView
@end
@implementation MyCustomViewProto
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self superclass])
owner:nil
options:nil] objectAtIndex:0];
}
@end
長所:
- とても安全
- 掃除; に余分なコードはありません
MyCustomView
。
if
と同じ明示的なチェックはありませんd)
短所:
- ストーリーボードでサブクラスを使用する必要があります。
私e)
はそれが最も安全でクリーンな戦略だと思います。そこで、ここでそれを採用します。
ステップ3。プロパティをコピーする
loadNibNamed:
'awakeAfterUsingCoder:'の後でself
、ストーリーボードのインスタンスをデコードするいくつかのプロパティをコピーする必要があります。frame
自動レイアウト/自動サイズ変更のプロパティは特に重要です。
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
typeof(self) view = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])
owner:nil
options:nil] objectAtIndex:0];
view.frame = self.frame;
view.autoresizingMask = self.autoresizingMask;
view.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
NSMutableArray *constraints = [NSMutableArray array];
for(NSLayoutConstraint *constraint in self.constraints) {
id firstItem = constraint.firstItem;
id secondItem = constraint.secondItem;
if(firstItem == self) firstItem = view;
if(secondItem == self) secondItem = view;
[constraints addObject:[NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant]];
}
for(UIView *subview in self.subviews) {
[view addSubview:subview];
}
[view addConstraints:constraints];
return view;
}
最終的解決
ご覧のとおり、これはちょっとした定型コードです。それらを「カテゴリ」として実装できます。ここでは、一般的に使用されるUIView+loadFromNib
コードを拡張します。
#import <UIKit/UIKit.h>
@interface UIView (loadFromNib)
@end
@implementation UIView (loadFromNib)
+ (id)loadFromNib {
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self)
owner:nil
options:nil] objectAtIndex:0];
}
- (void)copyPropertiesFromPrototype:(UIView *)proto {
self.frame = proto.frame;
self.autoresizingMask = proto.autoresizingMask;
self.translatesAutoresizingMaskIntoConstraints = proto.translatesAutoresizingMaskIntoConstraints;
NSMutableArray *constraints = [NSMutableArray array];
for(NSLayoutConstraint *constraint in proto.constraints) {
id firstItem = constraint.firstItem;
id secondItem = constraint.secondItem;
if(firstItem == proto) firstItem = self;
if(secondItem == proto) secondItem = self;
[constraints addObject:[NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant]];
}
for(UIView *subview in proto.subviews) {
[self addSubview:subview];
}
[self addConstraints:constraints];
}
これを使用して、次のMyCustomViewProto
ように宣言できます。
@interface MyCustomViewProto : MyCustomView
@end
@implementation MyCustomViewProto
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
MyCustomView *view = [MyCustomView loadFromNib];
[view copyPropertiesFromPrototype:self];
return view;
}
@end
XIB:
ストーリーボード:
結果: