Tuesday, 9 April 2013

The dangerous UIView


I don't even know how many times I was struggling with UIView. The thing is, that's normal, I mean the complexity it carries. It's not a procedural model, it's all threaded and asynchron at the backend.


Usually the problem is that you don't have the UIView actually at the point of the interaction. The mistake I did several times was the following. I had a referenced UIView instance on the main XIB file of the UIViewController:

#import <UIKit/UIKit.h>

@interface MyUIViewController

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

@end


Then in the implementation file I tried to access it and do something:

#import "MyUIViewController.h"

@implementation MyUIViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        [self.myView setBackgroundColor:[UIColor redColor]];
    }
    return self;
}

@end


Why no effect? Because at this point the view object is not initialized. I guess it's the NSCoder (not sure at all) that responsible to put together the UIViewController and the referenced NIB file. So it assigns the view object to the instance variable, but it's not initialized yet. The correct way of doing it is to put it to the viewDidLoad call:

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view.

  [self.myView setBackgroundColor:[UIColor redColor]];
}


Today's lesson was not to transfer UIView directly. On one of my view I'm adding a new subview, but only on certain actions, so it's not necessary to keep it together, it's enough to bundle it and create one when necessary. So as a good boy I added the property to the header:

#import <UIKit/UIKit.h>

@interface MyUIViewController {
  UIView *subView;
}

@end


And initialized when it was the right time:

- (void)togglePanel {
    if (self->subView == nil) {
        self->subView = [[[MySubViewController alloc] initWithNibName:@"MySubView" bundle:nil] view];
        [self.view addSubview:self->subView];
    }
}


The funny thing is that it's there, and almost functioning. I could toggle it and see just fine. However when did any interaction with event handling it failed with BAD EXEC. It took me a while till I figured out I'm passing an empty view object at that point, which obviously won't lead to any fun. The proper way is to use the controller and access the view afterwards:

#import <UIKit/UIKit.h>

@interface MyUIViewController {
  UIViewController *subViewController;
}

@end


- (void)togglePanel {
    if (self->subViewController == nil) {
        self->subViewController = [[MySubViewController alloc] initWithNibName:@"MySubView" bundle:nil];
        [self.view addSubview:self->subViewController.view];
    }
}


Yeah, just never trust in UIView.

---

Peter

No comments:

Post a Comment