I know this is quite straight forward but after too much hair-pulling I am nowhere near solution.
I have seen tutorials explaining how to create view using XIB and all. But none of them address the situation that I have here.
I have an XIB file, a custom UIView subclass that has few labels and buttons. The UIView subclass is reusable, and that is the reason I can't have outlets inside any single View controller. As a result I store individual controls (subviews) of this view inside my custom UIView itself. This is logical, as no view controller should own the subviews of this custom view which is to be included in every view controller.
The problem is, I don't know how to initialize the entire UI fully.
Here is my code for UIView Subclass:
@interface MCPTGenericView : UIView
+(id)createInstance : (bool) bPortrait;
@property (weak, nonatomic) IBOutlet UIView *topView;
@property (weak, nonatomic) IBOutlet UIView *titleView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *logoButton;
@property (weak, nonatomic) IBOutlet UITextField *searchTextField;
@property (weak, nonatomic) IBOutlet UIButton *menuButton;
@end
Later on, I also plan to use this same XIB file for landscape orientation of this UIView too, and I plan to use the same above outlets with landscape oriented controls in same XIB.
And here is the implementation:
@implementation MCPTGenericView
//@synthesize topView, titleLabel, titleView;
+(id)createInstance : (bool) bPortrait
{
UIView * topLevelView = nil;
MCPTGenericView * instance = [MCPTGenericView new];
NSArray * views = [[NSBundle mainBundle] loadNibNamed:@"MoceptGenericView" owner:instance options:nil];
int baseTag = (bPortrait)?PORTRAIT_VIEW_TAG_OFFSET:LANDSCAPE_VIEW_TAG_OFFSET;
// make sure customView is not nil or the wrong class!
for (UIView * view in views)
{
if (view.tag == baseTag)
{
topLevelView = view;
break;
}
}
instance.topView = (MCPTGenericView *)[topLevelView viewWithTag:baseTag + 1];
instance.searchTextField = (UITextField *)[topLevelView viewWithTag:baseTag + 2];
instance.menuButton = (UIButton *)[topLevelView viewWithTag:baseTag + 3];
instance.logoButton = (UIButton *)[topLevelView viewWithTag:baseTag + 4];
instance.titleView = [topLevelView viewWithTag:baseTag + 5];
instance.titleLabel = (UILabel *)[topLevelView viewWithTag:baseTag + 6];
return instance;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}
-(void)awakeFromNib
{
[super awakeFromNib];
[self addSubview: self.titleView];
[self addSubview:self.topView];
}
- (id) init
{
self = [super init];
if (self)
{
[[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
[self addSubview:self.topView];
[self addSubview:self.titleView];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
[[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
[self addSubview:self.topView];
[self addSubview:self.titleView];
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
@end
Something that worked:
I succeeded in calling initWithFrame:frame
from my viewcontroller. That way, I could see all controls properly initialized. But then, why should I be supplying a frame if I have already drawn an XIB? Shouldn't loadNibNamed be handling frame setting and layout stuff since that is the intended use of XIBs?
I am also baffled at the way loadNibNamed needs an owner object. Why do we already need an object to get the same object from XIB? That too, a half-baked one?
Please help...