Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom UIView subclass requires awakeFromNIB to be called twice

I am hopelessly stuck and I would appreciate a pointer.

We are trying to build a view controller including multiple views that are a subclasses of UIView. Everything is working "well" except for the fact that we need to either manually initialize the view or call awakeFromNib manually again which is not allowed.

Here is the problem...

Subclassed UIView header file:

#import <UIKit/UIKit.h>
@interface introView : UIView
@property (strong, nonatomic) UIView *view;
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) IBOutlet UILabel *titleLabel;
@end

Subclassed UiView main file:

#import "introView.h"

@interface introView ()
@end

@implementation introView

-(id)init
{
    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"introView" owner:self options:nil];
    id mainView = [subviewArray objectAtIndex:0];
    return mainView;
}
- (void) awakeFromNib
{
    [super awakeFromNib];
    [self initialize];
    [self addSubview:self.view];
}

-(void)initialize
{
    NSLog(@"title: %@", self.title);
    self.titleLabel.text = self.title;
}

We then initialize the view from a view controller doing this:

introView *view = [[introView alloc]init];
view.title = @"Test";
[self addSubview:view];

Here is the problem - by just calling this view, the view shows up correctly but title is NULL;

[43605:1957090] title: (null)

If however we call awakeFromNib again then the view initializes correctly

In the view controller:

introView *view = [[introView alloc]init];
view.title = @"Test;
[self addSubview:view];
[view awakeFromNib];

Then it works:

2014-11-21 09:33:03.500 Test[43706:1972060] title: (null)
2014-11-21 09:33:03.500 Test[43706:1972060] title: Test

Alternatively if I make the initialize method public and call that after the initialization it works as well. But that defeats the purpose in my eyes...

In the view controller:

introView *view = [[introView alloc]init];
view.title = @"Test;
[self addSubview:view];
[view initialize]; //calling the method directly...

It seems me that we are running somehow into a condition where the view is not ready yet but then it works at the second attempt (even though the awakeFromNib call is illegal)

Again the outlets are setup correctly, the file owner is set...it just takes two awakeFromNib calls.

Any help is appreciated.

----------------UPDATE------------------

Thanks guys, I appreciate the pointer. I implemented the initializer as outlined but I have the same problem. Furthermore I used the GitHub example as a clean example and tried to assign a variable to this view and even that shows the same behavior. Hence I am starting to think that I am doing something wrong somewhere else. See here:

//
//  SubClassView.h
//  CustomView
//
//  Created by Paul Solt on 4/28/14.
//  Copyright (c) 2014 Paul Solt. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "PSCustomViewFromXib.h"

@interface SubClassView : PSCustomViewFromXib
@property (strong, nonatomic) NSString *title;
@end

SubclassView.m

//
//  SubClassView.m
//  CustomView
//
//  Created by Paul Solt on 4/28/14.
//  Copyright (c) 2014 Paul Solt. All rights reserved.
//

#import "SubClassView.h"

@interface SubClassView()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UISwitch *onSwitch;


@end

@implementation SubClassView

// Note: You can customize the behavior after calling the super method

// Called when loading programatically
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if(self) {
        // Call a common method to setup gesture and state of UIView
        [self setup];
    }
    return self;
}

// Called when loading from embedded .xib UIView
- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if(self) {
        // Call a common method to setup gesture and state of UIView
        [self setup];
    }
    return self;
}

- (void)setup {
    // Add a gesture to show that touch input works on full bounds of UIView
    NSLog(@"%@", self.title);
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self addGestureRecognizer:panGesture];
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
    NSLog(@"Pan: %@", NSStringFromCGPoint([gesture locationInView:gesture.view]));
}
- (IBAction)switchChanged:(UISwitch *)sender {
    NSLog(@"Switch: %d", sender.on);
}

@end

View Controller class

//
//  ViewController.m
//  CustomView
//
//  Created by Paul Solt on 4/28/14.
//  Copyright (c) 2014 Paul Solt. All rights reserved.
//

#import "ViewController.h"
#import "SubClassView.h"
#import "LabelMadness.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];


    // Add a custom view's programmatically (position using 1/2 width and height)

    SubClassView *oneView = [[SubClassView alloc] init];
    oneView.center = CGPointMake(80 + 80, 282 + 40);
    oneView.backgroundColor = [UIColor blueColor];
    oneView.title = @"Test2";
    [self.view addSubview:oneView];

}

@end

-------OUTPUT-----

2014-11-21 11:49:38.893 CustomView[45653:2123668] LabelMadness.initWithFrame:
2014-11-21 11:49:38.893 CustomView[45653:2123668] (null)

What am I doing wrong here?

like image 929
user3757285 Avatar asked Nov 01 '22 14:11

user3757285


1 Answers

You don't need to use awakeFromNib at all. If you want introView to load its own nib, you can do that like so (change the class of the view to introView in the xib file),

-(instancetype)init{
    if (self = [super init]) {
        NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"introView" owner:self options:nil];
        self = [subviewArray objectAtIndex:0];
    }
    return self;
}

To set the title, you can override the setter of your title property,

-(void)setTitle:(NSString *)title {
    _title = title;
    self.titleLabel.text = title;
}

The code in your controller would be the same as what you tried initially.

like image 91
rdelmar Avatar answered Nov 13 '22 14:11

rdelmar