चरण 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:
की जाँच करने के लिए Storyboard डिकोडिंग प्रक्रिया या XIB डिकोडिंग प्रक्रिया में कहा जाता है। आपके पास ऐसा करने के कई तरीके हैं:
a) निजी का उपयोग करें @property
जो केवल NIB में सेट किया गया है।
@interface MyCustomView : UIView
@property (assign, nonatomic) BOOL xib
@end
और "MyCustomView.xib 'में केवल" उपयोगकर्ता परिभाषित रनटाइम अटेंडेस "सेट करें।
पेशेवरों:
विपक्ष:
- बस काम नहीं करता है: AFTER
setXib:
कहा जाएगा awakeAfterUsingCoder:
बी) अगर 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];
}
}
पेशेवरों:
- इंटरफ़ेस बिल्डर में कोई चाल नहीं।
विपक्ष:
- आपके स्टोरीबोर्ड में आपके साक्षात्कार नहीं हो सकते हैं।
ग) 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;
}
}
पेशेवरों:
- सरल
- इंटरफ़ेस बिल्डर में कोई चाल नहीं।
विपक्ष:
- सुरक्षित नहीं: स्थिर साझा ध्वज खतरनाक है
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
पेशेवरों:
- कोई स्पष्ट
if
मेंMyCustomView
विपक्ष:
- Xib
_NIB_
इंटरफ़ेस बिल्डर में प्रीफ़िक्सिंग ट्रिक
- अपेक्षाकृत अधिक कोड
ई) स्टोरीबोर्ड में प्लेसहोल्डर के रूप में उपवर्ग का उपयोग करें
करने के लिए इसी तरह की 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:
स्टोरीबोर्ड:
परिणाम: