A custom subclass of UIView requires awakeFromNIB to be called twice

I am hopelessly stuck and I would appreciate a pointer.

We are trying to create a view controller, including several views that are subclasses of UIView. Everything works "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 ...

UIView subclass 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 

The main file of the UiView subclass:

 #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; } 

Then we initialize the view from the view controller:

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

Here's the problem - just calling this view, the view is displayed correctly, but the header is NULL;

 [43605:1957090] title: (null) 

If, however, we call awakeFromNib again, then the view is correctly initialized

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 it works after initialization. But it defeats the goal 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 to me that we somehow fulfill the condition when the view is not ready yet, but then it works in the second attempt (although the call to awakeFromNib is illegal)

Again the outputs are configured correctly, the owner of the file is set ... he just needs two calls to awakeFromNib.

Any help is appreciated.

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

Thanks guys, I appreciate the pointer. I implemented the initializer as indicated, but I have the same problem. In addition, I used the GitHub example as a pure example and tried to assign a variable to this view, and even this shows the same behavior. So I'm starting to think that I'm doing something wrong elsewhere. 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 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 

------- CONCLUSION -----

 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?

+5
source share
2 answers

You do not need to use awakeFromNib at all. If you want introView to load its own nib, you can do it like this (change the view class 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 settings for your title property,

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

The code in your controller will be the same as the previous one.

+2
source

So, do you have a view in .xib or in a storyboard?

It looks like this is from .xib, so you should see this answer

You should not override the init method, and you should not allocate it in the code. If you do this, the views you made in the interface builder will not be loaded.

So, instead of alloc init, follow the answer I provided and do something like this.

Anyway, I don’t see where you initialize your

 @property (strong, nonatomic) UIView *view; 

This is not an IBOutlet, and you never start it anywhere, so apparently there will always be nil

0
source

Source: https://habr.com/ru/post/1207428/


All Articles