0
votes

edit #1

please read the edit #1 at the bottom of the question; I'm trying to figure out all the ways nib's can be used in creating custom views so all the examples deal with nib files.

end

I have been trying to figure out all the techniques for creating custom views that use nibs in controllers. I have listed the ways at the bottom of this question.

Here's the code for my TRtestview (one implication of how I have this set-up is that initWithCoder will always be called):

// TRtestview.m
-(id)initWithCoder:(NSCoder *)aDecoder{
    self=[super initWithCoder:aDecoder];
    if (self) {
      if (self.subviews.count == 0) {
        UIView *myView=[[[NSBundle mainBundle] loadNibNamed:@"testview" owner:nil options:nil] lastObject];
        myView.userInteractionEnabled=YES;  // doesn't do anything
        [self addSubview:myView];
      }
    }
    NSLog(@"about to return here in initWithCoder");
    return self;
}

edit 1

-(void)setup
{
       UIView *myView=[[[NSBundle mainBundle] loadNibNamed:@"testview" owner:nil options:nil] lastObject];
      [self addSubview:myView];
}


- (id)initWithFrame:(CGRect)frame
{
    NSLog(@"at top of initWithFrame");
    self = [super initWithFrame:frame];
    if (self) {
      [self setup];
    }
    return self;
}

in TRViewController.m, I tried setting thisView to userInteractionEnabled but it didn't change things:

- (void)viewDidLoad
{
  [super viewDidLoad];
  self.thisView.userInteractionEnabled=YES;

This has worked for the scenario of calling initWithFrame (technique #1) and loadNibNamed (technique #2). It will load the view by dragging it out from the Object Inspector and then setting the Class in the Custom Class attribute inspector (technique #3). However, the button is non-functional and thus this way doesn't work.

How would I create a custom view with a nib using technique #3 and allow for user interaction?

thx

edit 1

I'd really like to understand all the ways that a custom view can be created and instantiated. Ideally, I'd like to be able to have custom views that can be created via initWithFrame, loadNibNamed, or dragged from the object library.

It seems like the scenarios are:

  1. initWithFrame from a ViewController; if using with a nib, I call loadNibNamed in my initWithFrame - I need to prevent recursive loading
  2. loadNibNamed from a ViewController (which will call initWithCoder) and would want to load only once
  3. dragging an instance of a UIView and then setting the custom class to my custom UIView (TRtestview in this case). This will call initWithCoder.
1

1 Answers

1
votes

It looks like you're loading testview from inside the testview class initialiser with a hacky check for subviews to prevent it from becoming recursive. Since the code outside probably also loads the nib, what you're actually doing here is loading the nib twice, once inside the other. If you set up the owner then the inner one will be setting the outlets on the outer one.

It's just a guess as there is not enough information here to know for sure - but if you are setting outlets in your view class for some of the sub-views within it then you must supply an owner object to loadNibNamed:owner:options: with the type of your class. So you could pass self as owner in your code and this might work. However it's really the wrong way to use nibs for single-views.

Instead of having the single view in your nib file being the TRtestView class, have the single-view just as a holder for your other internal views and set the "File's Owner" place-holder object to have the class TRtestview. Create an outlet for the holder and assign it then do something like this:

TRtestview* testview = [[TRtestview alloc] initWithFrame:desiredFrame];
[[NSBundle mainBundle] loadNibNamed:@"testview" owner:testview options:nil];

Now all the outlets in testview will be set when the nib is loaded, including your holder which you can add as a sub-view.

Also, if you want to use a TRtestview within a different nib (for example TestViewController), you can do this by dragging in a view and setting that object's class to TRtestview - this will create a TRtestview and call its initWithCoder and you can assign it to an outlet in TestViewController called testview. Then in the awakeFromNib for TestViewController you would put the second line of code from above, passing in the testview outlet as the owner object.

Edit: It's tricky to see what you're trying to do exactly, but loading the nib from inside any of the view's init methods is wrong (loading the nib creates the view object, not the other way around). I think I misunderstood at first where the IBOutlets that you want to set are, probably the answer you're looking for is actually that you can load the nib but from a class constructor method like this:

+ (MyCustomView*)newCustomView
{
    MyCustomView* view = [[NSBundle mainBundle] loadNibNamed:@"testview" owner:nil options:nil][0];
    // Do custom setup things to view here...
    // Perhaps based on additional inputs to this method.
    view.doStuff;
    return view;
}