In my experience, the most robust view/viewcontroller implementations do all of their element creation and layout directly in the UIView subclass (in initWithFrame:
and layoutSubviews
), rather than using a .xib file. While one can certainly use a .xib file to achieve the same result, the complexity and maintenance doesn't seem worth the trouble. I recently started working on a custom view controller and UIView subclass, for which I am using the PFLogInViewController from the ParseUI framework as a reference to create this '.xib-free' robustness that I need.
My custom UIView subclass (which I will call NumPadView
as an example) is responsible for laying out buttons, labels, and other UI elements, while my custom UIViewController subclass (which I will call NumPadViewController
as an example) is responsible for creating the NumPadView
instance and adding target actions to all of its buttons, and then implementing those actions. Here is an example interface for NumPadView
:
//
// NumPadView.h
#import <UIKit/UIKit.h>
@interface NumPadView : UIScrollView
@property (strong, nonatomic, readonly) UIButton *button1;
@property (strong, nonatomic, readonly) UIButton *button2;
@property (strong, nonatomic, readonly) UIButton *button3;
@property (strong, nonatomic, readonly) UIButton *button4;
@property (strong, nonatomic, readonly) UIButton *button5;
@property (strong, nonatomic, readonly) UIButton *button6;
@property (strong, nonatomic, readonly) UIButton *button7;
@property (strong, nonatomic, readonly) UIButton *button8;
@property (strong, nonatomic, readonly) UIButton *button9;
@property (strong, nonatomic, readonly) UIButton *button0;
- (instancetype)init;
@end
Using this interface, NumPadViewController
(with a _numPadView
property) can do things such as:
self.view = _numPadView;
[_numPadView.button0 addTarget:self selector:@selector(button0Pressed) forControlEvents:UIControlEventTouchUpInside];
Now, to get to my question. Eventually, all of the NumPadView
layout will be done in code, similar to the following:
//
// NumPadView.m
#import "NumPadView.h"
@interface NumPadView ()
@property (strong, nonatomic, readwrite) UIButton *button1;
@property (strong, nonatomic, readwrite) UIButton *button2;
@property (strong, nonatomic, readwrite) UIButton *button3;
@property (strong, nonatomic, readwrite) UIButton *button4;
@property (strong, nonatomic, readwrite) UIButton *button5;
@property (strong, nonatomic, readwrite) UIButton *button6;
@property (strong, nonatomic, readwrite) UIButton *button7;
@property (strong, nonatomic, readwrite) UIButton *button8;
@property (strong, nonatomic, readwrite) UIButton *button9;
@property (strong, nonatomic, readwrite) UIButton *button0;
@end
@implementation NumPadView
- (instancetype)init
{
self = [super init];
if (self) {
self.button1 = [[UIButton alloc] initWithFrame:CGRectZero];
self.button1.backgroundColor = [UIColor redColor];
//...
}
return self;
}
- (void)layoutSubviews {
self.button1.frame = //...
//...
}
@end
However, I always do the layout/UI design as the last step because I want to make sure that everything functions first. I will therefore spend my time implementing methods in NumPadViewController
, rather than coding the layout in NumPadView
. Of course, to test the functionality of the view controller, I will need some sort of UI to interact with. I would like to make a quick prototype UI using a .xib file and attaching it to the NumPadView
. I don't want to change the interface of the NumPadView
at all, as that would require changes to my NumPadViewController
. So my problem is figuring out how to create and load a .xib from within the NumPadView
implementation. Of course, .xibs are typically owned by a view controller and IBOutlets are linked to properties in said view controller, but I want to shift all of that to the private implementation of NumPadView
like such:
//
// NumPadView.m
#import "NumPadView.h"
@interface NumPadView ()
//Link private interface properties to .xib file
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button1;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button2;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button3;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button4;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button5;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button6;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button7;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button8;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button9;
@property (strong, nonatomic, readwrite) IBOutlet UIButton *button0;
@end
@implementation NumPadView
- (instancetype)init
{
//I'm not sure if this is the right way to do it, but I'm looking for something similar in effect.
self = [[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil].firstObject;
return self;
}
@end
So, how can I effectively implement this? I've tried using loadFromNib:
, but the button properties always end up being nil
. I've connected all of the IBOutlets in the interface builder and set the file owner on NumPadView.xib
to NumPadView
and the class of the main view to UIScrollView
(I've tried NumPadView
as well). In addition to advice on how to successfully implement this scheme, I am certainly open better ways of doing this whole prototyping scheme if anyone has a better method. The most important thing is that the NumPadView
maintain a consistent interface and that the NumPadViewController
has no knowledge of the .xib file. This concept seems to be missed in similar questions such as this one and this one. Thank you for your input; I look forward to reading your answers.