3

My goal is, to have a subclassed UIView (lets call it infoView) designed in his own XIB so that I can present it in many UIViewController's.

The Problem: So far, when I was adding UIView's to a UIViewController I always had to make an UIViewController the file's owner of the UIView's .xib file to load the view with something like:

...
//this is inside the calling UIViewController's method
// InfoView *infoView is ivar and a subclass of UIView
infoView = nil; 
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"InfoView"
                                                owner:self options:nil];

for (id object in bundle) {
    if ([object isKindOfClass:[InfoView class]])
        infoView = (InfoView *)object;
}   

[[self view] addSubview:infoView]; 
...

But I want to use the same UIView in many different UIViewController's, so I actually don't want a file's owner except maybe the class itself. In ThomasM's question he was setting the UIView itself to be the file's owner but without success.

In the answers there I found a solution to set the file's owner to nil. To do so I had to add all calling UIViewController objects from the Interface Builder object library to the InfoView.xib file and connect them with their infoView outlets.

But this doesn't feel right. So here I would like to collect solutions to
encapsulate a UIView together with his xib-file to use it in many different view controllers. How do you guys handle that?

Thx for any help.

EDIT:

The infoView is something like an overlay which appears when the user presses a button on one of the view controllers. It's NOT the View controllers "main" view. It gives detailed informations about the view of his superviews view controller and will disappear afterwards. I only fill the infoView with different contents threw out all the calling view controllers.

Community
  • 1
  • 1
d.ennis
  • 3,445
  • 2
  • 28
  • 36
  • 1
    You may be interested in my solution to the "reusable UIView widget with xib" problem as described in my answer to this question: http://stackoverflow.com/questions/5056219/uiview-and-initwithframe-and-a-nib-file-how-can-i-get-the-nib-file-loaded/5056886#5056886 – Bogatyr Apr 23 '12 at 06:36

4 Answers4

3

Like Hollance answer was pointing out I am using UINib.

To use it, leave the .xib files owner nil and place all customization of the infoView inside the initWithCoder: method of your InfoView class implementation. This will get called if you obtain the InfoView.xib like:

// here InfoView is the name of the .xib file
UINib *infoNib = [UINib nibWithNibName:@"InfoView" bundle:nil];

NSArray *topLevelObjects = [infoNib instantiateWithOwner:self options:nil];

QInfoView *infoView = [topLevelObjects objectAtIndex:0];
Community
  • 1
  • 1
d.ennis
  • 3,445
  • 2
  • 28
  • 36
1

And yet another way is to create your own "controller" based on NSObject to define your own life-circle (instead of standard UIViewController life-circle).

For example:

BaseSubview.h:

@interface BaseSubview : NSObject {
    UIView* _view;
}

@property (nonatomic, retain) IBOutlet UIView* view;

- (void)myMethod;

@end

BaseSubview.m:

#import "BaseSubview.h"

@implementation BaseSubview

@synthesize view = _view;

- (id)init
{
    self = [super init];
    if (self) {
        // ...
    }
    return self;
}

- (void)dealloc
{
    [_view removeFromSuperView];
    [super dealloc];
}

- (void)myMethod
{
    // view specific logic here
    _view.backgroundColor = [UIColor redColor];
}

@end

InfoView.h:

#import "BaseSubview"
@interface InfoView : BaseSubview {
    UILabel* _labelInfo;
}

@property (nonatomic, retain) IBOutlet UILabel* labelInfo;

@end

InfoView.m:

#import "InfoView.h"

@implementation InfoView

@synthesize labelInfo = _labelInfo;

- (id)init
{
    self = [super init];
    if (self) {
        // ...
    }
    return self;
}

- (void)myMethod
{
    // view specific logic here
    _labelInfo.text = @"current time...";
    [super myMethod];
}

@end

InfoView.xib:

  • file owner is InfoView
  • assign of outlets as usual
  • view is parent all other controls (such as labels, etc)

HugeAndComplicatedViewController.h:

// ...
// among other var definitions
InfoView* _infoView;
// ...

HugeAndComplicatedViewController.m, most interesting part:

// when you decide to show your view
// probably in loadView
_infoView = [[InfoView alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"InfoView" owner:_infoView options:nil];
[self.view addSubview:_infoView.view];

// possibly perform specific logic
[_infoView myMethod];

// no need sub-view any more
// probably in dealloc
[_infoView release];

So now you have your own sub-view with logic and design separated from "Huge & Complicated" view-controller. It can have any life-circle you need for your current project.

iutinvg
  • 3,479
  • 3
  • 21
  • 18
1

So you want to load a UIView from a nib that you wish to use in more than one UIViewController, and you want to connect it to an outlet on each of those view controllers. Is that correct?

Then make a UIViewController subclass (let's call it FakeViewController) with an IBOutlet property. Set that FakeViewController as the nib's File's Owner and connect your UIView to its outlet.

Done.

You just need to make sure all your other view controllers also have these outlet properties (although they don't need to be IBOutlets), but the nib loader doesn't actually check to make sure the class that you pass into the owner parameter equals the class name you specified in Interface Builder. So you can fake it.

Oh, and if you're OS 4.0 and higher, use UINib to load the nib file.

Hollance
  • 2,958
  • 16
  • 15
  • You got my point! Though I like your idea to use a face ViewController this approach doesn't feel right since in the [apple doc's](http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html#//apple_ref/doc/uid/TP40007457-CH112-SW10) they say: "you should not use multiple custom view controllers to manage different portions of the same view hierarchy". But thanks a lot for pointing out the UINib class! – d.ennis Aug 19 '11 at 12:39
  • In my understanding a UIViewController is something like UIView with additional hierarchical logic which I don't need to present a partial view on screen. – d.ennis Aug 19 '11 at 12:52
  • 1
    The FakeViewController is only used to be able to connect the UIView to the outlet of any view controller that you're loading it into. So you can have ViewControllerA and ViewControllerB and ViewControllerC all load your UIView, even though they are different classes. The UIView thinks its being loaded into FakeViewController but there really isn't an instance of FakeViewController, you're really loading it into ViewControllerA/B/C. – Hollance Aug 19 '11 at 20:13
  • Ok, thx! So do you think this approach is more efficient than the one I was describing above where I was adding View Controller Objects to the xib-file to hook up the outlets of each ViewController with the view? – d.ennis Aug 22 '11 at 14:31
  • Yep. Although I'm not sure exactly how you're adding all those view controllers; as real View Controller objects or as placeholders? – Hollance Aug 22 '11 at 20:34
  • I was using real view controller objects. Today I looked into the WWDC2010 video session "Advanced Performance Optimization on iPhone OS, Part 2" where I learned: "To keep the usage of NSCoding down, don't put top level objects in a nib that aren't associated with the file's owner of that nib." In my understanding you want to keep that usage down, because nib's us NSCoding to deserialize all objects within your nib. So my approach really doesn't sound good! But I have no idea how these placeholders work. Do you have an idea? And if placeholders are better,do you still stick to fakeVC? – d.ennis Aug 23 '11 at 09:07
  • I would probably hook up the outlets programmatically rather than through a nib, so the whole scheme with the FakeViewController isn't necessary. I just pointed it out as a possibility. – Hollance Aug 23 '11 at 09:28
0

does infoView need to be a subview?

in your viewController:

-(id) init {
    self = [super initWithNibName:@"myNib" bundle:nil]; 
    if (self) {
        // code here
    }
}
ader
  • 5,403
  • 1
  • 21
  • 26
  • Yes, it has to be a subview. And this doesn't work for my question. The infoView is not meant to be the "main" view of the viewController. It's more an overlay which appears on demand by pushing for e.g. an info button. More or less like an UIAlertView... – d.ennis Aug 19 '11 at 11:42