Reputation: 9311
According to Apple's documentation, Interface Builder instantiates objects, which then are serialized and packaged into a NIB/XIB file at design time. At runtime, the whole object graph is deserialized via initWithCoder:.
Here's the documentation:
View instances that are created in Interface Builder don't call initWithFrame: when their nib files are loaded, which often causes confusion. Remember that Interface Builder archives an object when it saves a nib file, so the view instance will already have been created and initWithFrame: will already have been called.
As the objects are instantiated via initWithCoder:, I thought that the object graph is serialized via its counterpart encodeWithCoder:. However, consider the following code:
@interface CustomView : UIView
@end
@implementation CustomView
-(id)initWithCoder:(NSCoder *)aDecoder{
if(self = [super initWithCoder:aDecoder]){
NSNumber * n = [aDecoder decodeObjectForKey:@"DummyKey"];
NSLog(@"%@", n);
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:@YES forKey:@"DummyKey"];
}
@end
If I place this CustomView on a storyboard in Interface Builder, it prints "(null)" instead of "YES". Why is this happening? How does Interface Builder serialize the object graph? Am I misinterpreting the documentation?
Upvotes: 4
Views: 836
Reputation: 6952
encoderWithCoder:
method won't be called because the time when XCode encode the object graph into xib is not running time, it can't call your encoderWithCoder:
method.
How does Interface Builder serialize the object graph?
If you open Xib as source code, you can see it like this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment defaultVersion="1536" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="EMXibViewController">
<connections>
<outlet property="view" destination="1" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="1">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" id="XyJ-CF-vRx">
<rect key="frame" x="68" y="102" width="204" height="403"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina4"/>
</view>
</objects>
</document>
We can see xib only keeps the non-default value of object and the relationships between them.
The xib an UIViewController and has only two UI object , one is the UIViewController's view, the other is it's subview let's say it mysubview
and talks about how XCode archive mysubview
.
I have changed its backgroundColor、frame and autoresizingMask property of mysubview
, take backgroundColor
property as example, the backgroundColor property is saved as "color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"". It will archive the backgroundColor of mysubview
.
Xib just archive the backgroundColor and autoresizingMask and frame property of the mysubview
object and when load the xib, it will call UIView's initWithCoder
method. In the method, it will get the UIColor object for the key @"backgroundColor" and CGRect for the key @"frame" and UIViewAutoresizing for the key @"autoresizingMask", any other property of UIView assigned default value.
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder] ;
UIColor * backgroundColor = [aDecoder decodeIntegerForKey:@"backgroundColor"] ;
if (backgroundColor) {
self.backgroundColor = backgroundColor ;
} else {
self.backgroundColor = $(defaultColor) ; // if no key in xib, use the default value.
}
// more...
return self ;
}
It's a nice question and make me think more.
Upvotes: 1
Reputation: 4005
In simple word if you create your object in xib or Storybord then initWithFrame and initWithCoder will not be able to override by you, they will be called by framework and create the object property from xib or Storybord.
Upvotes: 0
Reputation: 130191
Interface Builder doesn't use your code to generate xibs. It's as simple as that. encodeWithCoder
is never called by the Interface Builder. The Interface Builder doesn't run your code.
Upvotes: 0