Do you use Interface Builder ? Even if you don’t like IB, read this short tip and referenced articles.
If you have used IB before, you probably needed to reference one Xib view from another Xib / Storyboard.
You probably wrote some code to create that view from code ? You don’t need to do that.
Intro
People often argue if you should use IB/Storyboards or not, I’ve heard that you can’t create advanced UI in IB and you need to write code. What’s interesting is that I’ve always heard that argument from people that are opposed to using IB, I guess there are tons of people creating such amazing and custom UI ?
Some people obviously have valid reasons not to use IB as it’s not magic solution and some stuff is better and easier done with code. The thing is, to be able to be pragmatic about it and understand when to use which, you actually need to give IB a try and use it for quite some time.
I’ve been doing only code layout for 2 years, because I believed it’s better than IB and my UI’s are too complicated to use IB. After 2 years I’ve made decision to really give IB a try, I’ve been using it for over a year now, guess what? Most of the layout code I’ve written in 2 years could be replaced with Xib’s and it would be less buggy, faster to develop and cheaper to maintain.
My current approach is pretty much aligned with this blog post, you should also read this as it shows how to pass custom attributes to views.
One thing that I didn’t agree with was the line “reusing a UI control from a different Xib is not yet possible“. Well that’s not exactly true, it’s not perfect but it is very much possible.
Referencing Xib’s from other Xib’s
There are many hidden gems in iOS frameworks that many people miss, one of such gems is method:
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
Documentation states:
Overridden by subclasses to substitute another object in place of the object that was decoded and subsequently received this message.
And it so happens that Xib/Storyboards are decoded from NSCoder, let’s use that to our advantage.
We need to distinguish when awakeAfterUsingCoder is called from our referencing Xib and when it’s called from the real, simplest way to do that will be setting some arbitrary Tag on the placeholder view in IB/Storyboard.
Simple category method might look like this:
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
//! loading from placeholder
if (self.tag == kNibReferencingTag) {
UIView *realView = [[self class] loadInstanceFromNib];
realView.frame = self.frame;
realView.alpha = self.alpha;
realView.backgroundColor = self.backgroundColor;
realView.autoresizingMask = self.autoresizingMask;
realView.autoresizesSubviews = self.autoresizesSubviews;
for (UIView *view in self.subviews) {
[realView addSubview:view];
}
return realView;
}
return [super awakeAfterUsingCoder:aDecoder];
}
If we are loading from placeholder view, we create a real view and then we transfer some common properties from it and all it’s subviews, then we just return that instance in place of placeholder, otherwise we just return normal view (This method is implemented on NSObject so we can call super, but this still should be done with method swizzling instead of category smashing).
Conclusion and Sample
Create some simple Xib view, set your custom class on it. Create storyboard / other xib and create new UIView on it, set it’s class to your custom class and it’s tag to 616 ( as that’s my special tag in sample ). Run your project.