2014-11-21 12 views
5

Jestem beznadziejnie zatrzymany i byłbym wdzięczny za wskazówkę.Niestandardowa podklasa UIView wymaga dwukrotnego wywołania awakeFromNIB

Próbujemy zbudować kontroler widoku, w tym wiele widoków, które są podklasami UIView. Wszystko działa "dobrze", z wyjątkiem tego, że musimy ręcznie zainicjować widok lub ponownie wywołać metodę awakeFromNib, co nie jest dozwolone.

Tutaj jest problem ...

podklasy UIView plik nagłówka:

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

podklasy UIView główny plik:

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

Następnie zainicjowania widok z kontrolerem widok robi to :

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

Oto problem - po prostu wywołując ten widok, widok wyświetla się poprawnie, ale tytuł ma wartość NULL;

[43605:1957090] title: (null) 

Jeśli jednak nazywamy awakeFromNib ponownie wówczas widok inicjuje poprawnie

W regulatorze Widok:

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

to działa:

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

Ewentualnie jeśli robię zainicjuj metodę public i wywołaj, że po inicjalizacji działa również. Ale to celowość w moich oczach ...

W regulatorze Widok:

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

To mnie wydaje się, że jesteśmy w jakiś sposób działa w warunkach, w których widok nie jest jeszcze gotowy, ale to działa na druga próba (mimo że wywołanie AwakeFromNib jest nielegalne)

Znów gniazdka są ustawione poprawnie, właściciel pliku jest ustawiony ... po prostu bierze dwa przebudzone połączenia FromNib.

Każda pomoc jest doceniana.

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

Dzięki chłopaki, Doceniam wskaźnik . Zaimplementowałem inicjator zgodnie z opisem, ale mam ten sam problem. Ponadto użyłem przykładu GitHub jako czystego przykładu i próbowałem przypisać zmienną do tego widoku, a nawet to pokazuje to samo zachowanie. Dlatego zaczynam myśleć, że robię coś złego gdzie indziej. Zobacz tutaj:

// 
// 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 

klasa View Controller

// 
// 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 

WYJŚCIE ------- -----

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

Co robię źle tutaj?

+0

Kod jest nieco mylący. W klasie introView czym jest self.view (w [self addSubview: self.view])? Jak wygląda mainView (jakie subviews ma)? – rdelmar

Odpowiedz

2

Nie trzeba w ogóle używać awakeFromNib. Jeśli chcesz introView załadować własne stalówkę, można to zrobić jak tak (zmienić klasę celu introView w pliku XIB)

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

Aby ustawić tytuł, można zastąpić setter twoich title property,

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

Kod w kontrolerze będzie taki sam, jak na początku.

+0

Próbowałem, ale potem otrzymuję wyjątek ... *** Kończenie aplikacji z powodu nieprzechwyconego wyjątku "NSInvalidArgumentException", powód: '- [UIView setTitle:]: nierozpoznany selektor wysłany do instancji 0x7fbc4d8c4040' – user3757285

+0

@ user3757285, To brzmi jak nie zmieniłeś klasy widoku w twoim xib na twoją niestandardową klasę (właśnie dlatego błąd mówi UIView, a nie introView). – rdelmar

+0

Masz rację, ale była ona niepoprawna, ale niczego nie zmienia. Dostaję wyjątek. Jednak zaczynam myśleć, że moje całe podejście jest trochę wadliwe. Czy nie jest możliwe przypisanie zmiennej globalnej jako części funkcji init? Czy muszę wywołać funkcję [view initialize], aby zmienna została pobrana? To nie ma dla mnie sensu, ale w ten sposób działa ... – user3757285

0

Masz więc widok w .xib lub w storybooku?

To wygląda jak jest z .xib więc należy spojrzeć na this answer

Nie należy zastąpić metodę init i nie należy Alloc startowych w kodzie. W takim przypadku widoki utworzone w programie budującym interfejs nie zostaną załadowane.

Więc zamiast alokować init, postępuj zgodnie z odpowiedzią wskazałem i zrób coś podobnego.

W każdym razie, nie widzę gdzie jesteś inicjalizacji swojej

@property (strong, nonatomic) UIView *view; 

nie jest IBOutlet i nigdy init, to wszędzie, więc najwyraźniej, że będzie zawsze nil

Powiązane problemy