开发者

iOS accessing view's variable from view controller

I'm developing on iOS and I'm building my views programmatically. I noticed that when I try to access variables that have to be changed in my view from the view controller they are null. I'll post both the view and its view controller:

RootViewController.h

 #import <UIKit/UIKit.h>

@class RootView;

@interface RootViewController : UIViewController {
    RootView *rootView;
}

@property (nonatomic,retain) RootView *rootView;


@end

RootViewController.m

    #import "RootViewController.h"
#import "RootView.h"

@implementation RootViewController

@synthesize rootView;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom init开发者_StackOverflowialization
    }
    return self;
}



- (void)dealloc
{
    [rootView release];
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)loadView{
    RootView *rootV = [[RootView alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
    rootV.rootViewController = self;
    self.view = rootV;
    [rootV release];
}

- (void)viewDidLoad{
    NSLog(@"TEXT: %@",self.rootView.label.text);
    self.rootView.label.text=@"HELLO!";
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    [self setRootView:nil];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end

RootView.h

   #import <UIKit/UIKit.h>

@class RootViewController;

@interface RootView : UIView {
    RootViewController *rootViewController;
    UILabel *label;
}

@property (nonatomic,assign) RootViewController *rootViewController;
@property (nonatomic,retain) UILabel *label;


@end

RootView.m

   #import "RootView.h"
#import "RootViewController.h"

@implementation RootView
@synthesize rootViewController;
@synthesize label;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //Create the label
        UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100,100, 50)];
        //Set the font bold 
        testLabel.font = [UIFont boldSystemFontOfSize:20.0];
        //Set the backgroundcolor of the label to transparent
        testLabel.backgroundColor = [UIColor clearColor];
        //Set the text alignment of the text label to left
        testLabel.textAlignment = UITextAlignmentLeft;
        //Set the text color of the text label to black
        testLabel.textColor = [UIColor blackColor];
        testLabel.text = @"01:30";

        self.label = testLabel;

        [self addSubview:label];
        [testLabel release];
    }
    return self;
}


- (void)dealloc
{
    [label release];
    rootViewController = nil;
    [super dealloc];
}

@end

I changed the code but it seems not working.....

Ok solved I forgot this line "self.rootView = rootV;"


Your view doesn't find out what its controller is until after its -initRootView method returns, but you're trying to use the controller from within that method.

That said, it would be much better if you followed the usual Cocoa Touch pattern for a view controller creating its view. View controllers are supposed to create their views lazily, which is to say that they defer view creation and initialization until the -loadView method is called. You can override -loadView to create your view, and also override -viewDidLoad to do any setup work that needs to be done after the view is created.

Also, it's generally not advisable for a view to know about its controller. The controller should tell the view what to do, not the other way around. If you need the view to send some information to the controller, you usually provide the controller to the view as the view's delegate. But if you just need the view controller to be able to find some subview, like your label, it's probably a good idea to either provide some accessors in the container view for that (so that the view controller can just say something like self.view.label.text = @"some text";. Another options is to set the subview's tag property to some unique value and have the controller use that to find the subview.


The problem is easy to spot, but requires some work to fix.

Looking at your code, something that I immediately want to suggest is to put all your RootView initialization code the loadView method of your RootViewController. That's where it should be (see here why).

Also, if you absolutely need your RootView to have a reference back at RootViewController, you should probably do that in viewDidLoad. But I wouldn't recommend doing that.

When using the MVC pattern, it is the controller's responsibility to initialize and update views. The line self.rootViewController.rootViewLabel = testLabel; should be removed from RootView's implementation. It's not clear what your intention is there, but if you want the rootViewLabel updated, you should let the controller do that.

To sum it all up:

// RootViewController.m

- (id)initRootViewController{

    self = [super init];

    if(self){
        // other init code here
    }

    return self;
}

- (void)loadView {
    RootView *rootV = [[RootView alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];        
    self.view = rootV;
    [rootV release];     
}

- (void)viewDidLoad {
    [super viewDidLoad];        
    // etc...
}

// etc.

Now, as for RootView, here is what it would look like:

RootView.h

#import <UIKit/UIKit.h>

@interface RootView : UIView {    
    UILabel *rootViewLabel;
}

// moved from RootViewController
@property (nonatomic, retain) UILabel *rootViewLabel;

@end

RootView.m

#import "RootView.h"

@implementation RootView

@synthesize rootViewLabel;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Whatever initialization code you might have
        //Create the label
        UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100,100, 50)];
        //Set the font bold 
        testLabel.font = [UIFont boldSystemFontOfSize:20.0];
        //Set the backgroundcolor of the label to transparent
        testLabel.backgroundColor = [UIColor clearColor];
        //Set the text alignment of the text label to left
        testLabel.textAlignment = UITextAlignmentLeft;
        //Set the text color of the text label to black
        testLabel.textColor = [UIColor blackColor];
        testLabel.text = @"01:30";

        self.rootViewLabel = testLabel;      
        [testLabel release];

        // add rootViewLabel as a subview of your this view
        [self addSubView:rootViewLabel];
    }
    return self;
}

- (void)dealloc
{
    [rootViewLabel release];
    [super dealloc];
}

@end

I hope this gives you an idea on how to structure your view initialization code...

(Disclaimer, I can't test this code now, please point out any errors! Thanks)


Not sure why you're doing it like that, but you could probably make it work if you pass the controller into the view's init:

RootView *rootV = [[RootView alloc] initRootView:self];

view's init:

- (id)initRootView:(UIViewController*)controller
{
     self.rootViewController = controller;
     self.rootViewController.rootViewLabel = testLabel;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜