3

For a while I've been using custom cells (with their own nibs) for tables without issues. Now in a new project I see the need for a reusable custom view (not a cell) the would have a title, a button, and another UIVIew to hold more views. I'll call it "Section":

enter image description here

The idea would be to be able to use this Section in storyboards (using a UIView and setting the custom class accordingly). That way whatever views I put inside that UIView would actually be contained in the inner UIView of the Section.

I thought the hard part would be to actually get the views put using IB and Storyboard to actually reside in that inner UIView instead of the root UIView of Section. Turns out just making the custom view (without any inner views yet) is not working as I would have expected. Here is the code, which is based off of the dozens of custom cells I've done and have worked (though adjusted for the specific init methods of a generic UIView):

#import "SectionContainer.h"

@implementation SectionContainer

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"SectionContainer" owner:self options:nil];
        self = [nibArray objectAtIndex:0];
        /*NSArray *nibRoot = [[UINib nibWithNibName:@"SectionContainer" bundle:nil] instantiateWithOwner:self options:nil];
        [self addSubview:[nibRoot objectAtIndex:0]];*/
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        // Initialization code
        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"SectionContainer" owner:self options:nil];
        self = [nibArray objectAtIndex:0];
        /*NSArray *nibRoot = [[UINib nibWithNibName:@"SectionContainer" bundle:nil] instantiateWithOwner:self options:nil];
        [self addSubview:[nibRoot objectAtIndex:0]];*/
    }
    return self;

}

The matching XIB has its root view set to this custom class (just like I do in the custom cells)

THE PROBLEM

This custom class causes a EXC_BAD_ACCESS code=2 and from what I can tell by stepping through it, it's as if the class is being called recursively. Call after call after call to initWithDecoder is being made until the EXC_BAD_ACCESS error happens

WHAT I'VE TRIED

  • Given the seeming recursive calls I tried another approach I saw that set the XIB's file owner to the Custom Class instead of the XIB's root View. This caused the following error:

'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key sectionContainerView

  • Tried a slightly different method (commented out in the code above) where the XIB's root is added to the custom class (addSubView) instead of being set to it. This didn't change anything, same recursive calls (or error above if that is set up)

I would REALLY appreciate some guidance on this. Thank you.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
RobertoCuba
  • 881
  • 9
  • 25

2 Answers2

0

You need to use a component called Custom Container View in storyboard. I can't just post code here because it involves some configuration in your storyboard and the code would depend on how you plumb your views / VCs, but you can read the relevant guide here:

https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

  • Hi, thanks for the reply. Is this really the answer? I was under the impression that Custom View controllers were more like for compartmentalizing distinct functional UI elements. For example, separate a list from the content displayed by each option in the list. What I am looking for is just a repeatable wrapper around discrete parts of content of a single screen. Concrete example, a screen with details of a business with sections: Basic Description, gallery preview, contact list, address and hours of operation. A given screen could have any number of these Sections. – RobertoCuba Jun 07 '14 at 06:32
  • It's a compromise. The problem is that unlike nib files that can be loaded as arrays and you can cherry pick what UIView to load from that array, Storyboard files are designed to contain the complete picture with UIViewControllers as root elements, and the paths that connected them all included in one file. You can define what is the root VC to be loaded, or you can custom load an arbitrary VC from a storyboard file, but I don't think that you can have a UIView standing alone as a root element, to be loaded individually. – James Donald Jun 07 '14 at 06:40
  • It still seems odd to me, but I'll look into this. However, just for the sake of argument, assuming I wasn't try to have more views defined in IB/Storyboard inside this Custom View. Do you have any idea why the above code is not working when it works just fine for all the custom cells I've made? I would still like to know what I am doing wrong just making the custom view work (never mind it having other contents). Thanks again for the replies. – RobertoCuba Jun 07 '14 at 06:50
  • Not sure, if I had to guess, I'd look at how everything is wired inside the XIB, that class types are defined correctly, and that classes extend the correct UI classes in code. – James Donald Jun 07 '14 at 07:03
0

First, the recursive call is on initWithCoder:, loading a nib means instatiating its views through initWithCoder:.

That's why you can't use your UIView subclass you've designed on a nib this way (by setting a view's class on a storyboard or even on another nib actually).

The only way to use it is to instantiate it through the nib, in code.

Section *sectionView = [[[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:options] objectAtIndex:index];

Now, with wiring up things from the nib you've made: You can make connections from the objects on your nib to another object which is not found on the nib. That is what File Owner is for. You have to set its(File Owner's) class, and make the connections to it, and use an instance of its class to which you want the connections to be realized, as the owner parameter when loading the nib.

But I guess this is not what you wanted. I think you wanted to make the sub views on the nib accessible through "Section" which I assume is the root view on the nib. You create IBOutlet (or, IBAction, IBOutletCollection) properties on the Section class. To wire these up with the rest of the objects on your nib, control click on the "Section" view on your nib, and you'll see what to do from there.

riadhluke
  • 880
  • 6
  • 18