UIView subclass with its own XIB [duplicate] UIView subclass with its own XIB [duplicate] objective-c objective-c

UIView subclass with its own XIB [duplicate]


ThomasM,

We had similar ideas about encapsulating behavior inside a custom view (say, a slider with companion labels for min/max/current values, with value-changed events also handled by the control internally).

In our current best-practice, we would design the ShareView in Interface Builder (ShareView.xib), as described by Eimantas in his answer. We then embed the ShareView to the view hierarchy in MainViewController.xib.

I wrote up how we embed custom-view Nibs inside other Nibs in our iOS developer blog. The crux is overriding -awakeAfterUsingCoder: in your custom view, replacing the object loaded from MainViewController.xib with the one loaded from the "embedded" Nib (ShareView.xib).

Something along these lines:

// ShareView.m- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {    BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);    if (theThingThatGotLoadedWasJustAPlaceholder) {        // load the embedded view from its Nib        ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0];        // pass properties through        theRealThing.frame = self.frame;        theRealThing.autoresizingMask = self.autoresizingMask;        [self release];        self = [theRealThing retain];    }    return self;}


You defined owner of the loaded xib as nil. Since file owner in xib itself has outlets connected and is defined as instance of ShareView you get the exception about unknown keys (nil doesn't have outleted properties you defined for ShareView).

You should define the loader of the xib as owner (i.e. view controller responsible for loading the xib). Then add separate UIView object to xib and define it as instance of ShareView. Then when loading the xib.

ShareView *shareView = [[[[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:self options:nil] objectAtIndex:0] retain];

You can also define shareView as an IBOutlet in view controller's interface (and connect the outlet from file owner to that view in the xib itself). Then when you load the xib there won't be any need for reassigning the shareView instance variable since the xib loading process will reconnect the view to the instance variable directly.


I would like to add to the answer. I hope people would improve this answer though.

First of all it DOES work.

XIB:

enter image description here

Result:

enter image description here

I would like to subclass UIView for a long time especially for tableViewCell.

This is how I did it.

It's succesful, but some part is still "awkward" in my opinion.

First I created a usual .h, .m, and xib file. Notice that Apple do not have the check box to automatically create an xib if the subclass you created is not a subclass of UIViewController. Well create those anyway.

#import <UIKit/UIKit.h>#import "Business.h"@interface BGUIBusinessCellForDisplay : UITableViewCell+ (NSString *) reuseIdentifier;- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz;@end

Really simple UITableViewCell, that I want to initialize latter with biz.

I put reuseidentifier which you should do for UITableViewCell

//#import "Business.h"@interface BGUIBusinessCellForDisplay ()@property (weak, nonatomic) IBOutlet UILabel *Title;@property (weak, nonatomic) IBOutlet UIImageView *Image;@property (weak, nonatomic) IBOutlet UILabel *Address;@property (weak, nonatomic) IBOutlet UILabel *DistanceLabel;@property (weak, nonatomic) IBOutlet UILabel *PinNumber;@property (strong, nonatomic) IBOutlet BGUIBusinessCellForDisplay *view;@property (weak, nonatomic) IBOutlet UIImageView *ArrowDirection;@property (weak, nonatomic) Business * biz;@end@implementation BGUIBusinessCellForDisplay- (NSString *) reuseIdentifier {    return [[self class] reuseIdentifier];};+ (NSString *) reuseIdentifier {    return NSStringFromClass([self class]);};

Then I eliminated most init codes and put this instead:

- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz{    if (self.biz == nil) //First time set up    {        self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right        NSString * className = NSStringFromClass([self class]);        //PO (className);        [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];        [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though    }    if (biz==nil)    {        return self;    }    self.biz = biz;    self.Title.text = biz.Title; //Let's set this one thing first    self.Address.text=biz.ShortenedAddress;    //if([self.distance isNotEmpty]){    self.DistanceLabel.text=[NSString stringWithFormat:@"%dm",[biz.Distance intValue]];    self.PinNumber.text =biz.StringPinLineAndNumber;

Notice that it's really awkward.

First of all the init can be used in 2 ways.

  1. It can be used to right after aloc
  2. It can be used by we having another existing class and then we just want to init that existing cell to another biz.

So I did:

if (self.biz == nil) //First time set up{    self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right    NSString * className = NSStringFromClass([self class]);    //PO (className);    [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];    [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though}

Another icky things that I did is when I do [self addSubview:self.view];

The thing is I want self to be the view. Not self.view. Somehow it works nevertheless. So yea, please help me improve, but that's essentially the way to implement your own subclass of UIView.