2012-07-12 12 views
13

Mam UIViewController z niektórymi kontrolerami i niektórymi widokami. Dwa z tych widoków (Grid Cell) to inne stalówki. Mam gniazda od komórek siatki do właściciela pliku, ale nie są one ładowane automatycznie.Nieskończona pętla podczas przeskakiwania initWithCoder

Więc staram się przesłonić 's initWithCoder. Spowoduje to uruchomienie nieskończonej pętli.

Wiem, że możliwe jest zastąpienie initWithFrame i dodanie podglądu z kodu, ale to nie jest to, czego chcę. Chcę móc przenosić widok w Konstruktorze interfejsów i mieć Xcode zainicjować widok z prawą ramką.

Jak mogę to osiągnąć?

EDIT 1

Próbuję dostać pracy z pomocą Alexander. Tak właśnie ustawiłem: MainView ma UIView z klasą Custom ustawiony jako GridCell. Dostał gniazdo w MainView/Właściciel pliku.

Pic 1

Usunięto cały kod startowy z GridCell.m i skonfigurować wylot do mojego niestandardowej klasy

Pic 2

Pic 3

MAINVIEW nadal nie wyświetla GridCell chociaż. Nie ma błędu, tylko samotna, pusta przestrzeń, w której powinien znajdować się czerwony przełącznik. Co ja robię źle?

Jestem bardzo blisko robienia tego programowo. Bardzo chciałbym się tego nauczyć ze stalówkami.

+0

'[NSBundle loadNibNamed]' zwraca 'BOOL'! – trojanfoe

+2

Dziękuję za odpowiedź. NSBundle loadNibNamed powtarza tablicę, którą otrzymuję jako pierwszy obiekt: [programista Apple] (https://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSBundle_UIKitAdditions/Introduction/Introduction.html) –

+1

Ah good punkt - szukałem odniesienia do OS X :) – trojanfoe

Odpowiedz

42

Ładowanie stalówkę powoduje initWithCoder nazywać ponownie, więc chcesz tylko zrobić jeśli podklasa obecnie nie ma żadnych subviews.

-(id)initWithCoder:(NSCoder *)aDecoder { 
    self = [super initWithCoder:aDecoder]; 
    if (self) { 
     if (self.subviews.count == 0) { 
      UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil]; 
      UIView *subview = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0]; 
      subview.frame = self.bounds; 
      [self addSubview:subview]; 
     } 
    } 
    return self; 
} 
+1

Ta metoda jest zgodna z ARC - i jedyną odpowiedzią, która zapamiętuje ustawienie subview.frame = self.bounds. –

+0

Dzięki, to zadziałało dla mnie. –

+0

To jest wspaniałe – Eugene

1

loadNibNamed :: wezwie initWithCoder:

Czemu nie skorzystasz z tego wzoru?

-(id)initWithCoder:(NSCoder *)coder 
{ 

    if (self = [super initWithcoder:coder]) { 

     // do stuff here ... 

    } 

    return self;     
} 

Czy rzeczy, których chcesz uniknąć, są wykonywane pod numerem [super initWithcoder:coder]?

11

Ładowanie stalówkę spowoduje odpowiedni właściciel w

-(id) initWithCoder:(NSCoder *) coder; 

rozmowy

Dlatego Twój coude w tej metodzie:

self = [[[NSBundle mainBundle] loadNibNamed: @"GridCell" 
owner: self 
options: nil] objectAtIndex:0]; 

spowoduje ponownie połączenie metody initWithCoder. To dlatego, że próbujesz ponownie załadować stalówkę. Jeśli zdefiniujesz niestandardowe UIView i utworzysz plik nib, aby ułożyć jego podskładki, nie możesz po prostu dodać UIView do innego pliku nib, zmień nazwę klasy w IB na twoją niestandardową klasę i spodziewaj się, że system ładowania nib zdecyduje o tym. Co można zrobić, to:

Plik końcówki Twojego niestandardowego widoku musi mieć klasę "Właściciel pliku" ustawioną na niestandardową klasę widoku i musisz mieć gniazdo w niestandardowej klasie o nazwie 'toplevelSubView' połączone z widok w niestandardowym widoku pliku stalówki, który działa jako pojemnik dla wszystkich widoków podrzędnych.Dodaj dodatkowe punkty do swojej klasy widoku i połącz subviews z "Właścicielem pliku" (twój własny UIView). (Patrz https://stackoverflow.com/a/7589220/925622)

EDIT Ok, aby odpowiedzieć edytowane pytanie chciałbym wykonać następujące czynności:

Przejść do pliku nib gdzie chcesz dołączyć widok niestandardowy z jego plik stalówka layouting go. Nie przechodź do widoku niestandardowego (GridCell), zamiast tego utwórz widok, który będzie zawierał komórkę siatki (na przykład gridCellContainer, ale powinien to być UIView) Dostosuj metodę initWithFrame w niestandardowym widoku, tak jak w initWithCoder :

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GridCell" owner:self options:nil];  
     self = [nib objectAtIndex:0]; 
     self.frame = frame; 
    } 
    return self; 
} 

A potem, w viewController który jest fileowner do widoku, w którym chcesz dołączyć swój własny widok (jeden z widokiem gridCellContainer) zrobić w viewDidLoad np

//... 
GridCell *gridCell = [[GridCell alloc] initWithFrame:self.viewGridCellContainer.bounds]; 
[self.viewGridCellContainer addSubview:gridCell]; 

Teraz eveything powinien działać zgodnie z oczekiwaniami

+0

Dziękuję za odpowiedź. W twoim linku Robin wydaje się ładować nib w initWithCoder jako ja; ale to właśnie powoduje nieskończoną pętlę - jak to jest dla mnie problemem, ale rozwiązaniem dla niego? Aktualizuję moje pytanie z rozwiązaniem, ponieważ nadal mam problemy. Mogę dodawać zdjęcia i rzeczy tam .. –

+0

Zaktualizowałem moje pytanie. Jeśli masz chwilę, proszę spojrzeć. –

+0

Zobacz moje zmiany ... – Alexander

0

miałem ten sam problem, gdy próbuję zastąpić initWithsomething metodę, musimy

-(id)initWithsomething:(Something *)something 
{ 
    if (self = [super initWithsomething:something]) { 
     // do stuff here ... 
    } 

    return self; 
} 

zamiast

-(id)initWithsomething:(Something *)something 
{ 
    if (self = [super init]) { 
     // do stuff here ... 
    } 

    return self; 
} 
+0

To naprawdę nie jest pytanie, ale jesteś daleko, więc chciałem ci pomóc. super odnosi się do klasy, którą rozszerzasz: kiedy plik nagłówka zaczyna się od MyCustomLabel: UILabel, super to UIlabel. Dlatego, gdy zainicjujesz własny obiekt, musisz również zainicjować super za pomocą [super init]. Nigdy nie powinieneś używać initWithsomething w MyCustomLabel AND call super initWith coś, ponieważ wtedy nadpisujesz metodę super z własną, ponieważ jest to ta sama nazwa. Oznacza to, że twoja metoda nie powinna mieć nazwy initWithCoder ani initWithFrame. Wymyśl coś nowego –

2

Właścicielem plik nie dostanie wezwanie do

-(id) initWithCoder:(NSCoder *) coder; 

podczas ładowania pliku xib.

Jednak każdy widok określone w tym XIb dostanie wezwanie do

-(id) initWithCoder:(NSCoder *) coder; 

podczas ładowania XIb.

Jeśli masz podklasę UIView (to znaczy GridCell) zdefiniowaną w XIB, a także próbujesz załadować to samo XIB w initWithCoder twojej podklasy, skończysz z nieskończoną pętlą. Jednak nie widzę, co będzie w przypadku użycia.

Zazwyczaj projektujesz swoją podklasę UIView (to znaczy GridCell) w jednym xib, a następnie używasz tej podklasy na przykład w xib kontrolera widoku.

Również nie widzę przypadku użycia, gdzie zwyczaj widok będzie mieć podrzędny w to initWithCoder, tj

-(id)initWithCoder:(NSCoder *)aDecoder { 
    self = [super initWithCoder:aDecoder]; 
    if (self) { 
     if (self.subviews.count == 0) { 
      UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil]; 
      UIView *subview = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0]; 
      subview.frame = self.bounds; 
      [self addSubview:subview]; 
     } 
    } 
    return self; 
} 

Chyba, że ​​chcesz być w stanie zastąpić jej zdaniem hierarchii na żądanie w innej XIb . Która IMO zakłada zewnętrzną zależność (tj. Hierarchię zdefiniowaną w innym xib) i w pewnym sensie pokonuje cel posiadania wielokrotnego użytku UIView w pierwszej kolejności.

Należy pamiętać, że podczas ładowania pliku xib, przekazanie instancji jako właściciela pliku, spowoduje ustawienie wszystkich zestawów IBOlet. W takim przypadku zastąpiłbyś siebie (tzn. GridCell) tym, co jest widokiem root w tym GridCell.xib, tracąc wszystkie połączenia IBOutlet w procesie.

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GridCell" owner:self options:nil];  
     self = [nib objectAtIndex:0]; 
     self.frame = frame; 
    } 
    return self; 
} 

Jest bardziej szczegółowy post „How to implement a reusable UIView.”, która przechodzi w nieco bardziej szczegółowo, jak dobrze i mam nadzieję, że czyści rzeczy.