2012-07-12 47 views
13

Tôi có một số UIViewController với một số bộ điều khiển và một số chế độ xem. Hai trong số các khung nhìn này (Grid Cell) là các ngòi khác. Tôi đã có các cửa hàng từ Grid Cells đến File's Owner, nhưng chúng không được nạp tự động.Vòng lặp vô hạn khi ghi đè initWithCoder

Vì vậy, tôi cố gắng ghi đè GridCell.m 's initWithCoder. Điều này bắt đầu một vòng lặp vô hạn.

Tôi biết có thể chỉ cần ghi đè initWithFrame và thêm chế độ xem phụ từ mã, nhưng đây không phải là điều tôi muốn. Tôi muốn có thể di chuyển khung nhìn xung quanh trong Interface Builder và có Xcode khởi tạo khung nhìn với khung bên phải.

Tôi làm cách nào để đạt được điều này?

EDIT 1

Tôi đang cố gắng để làm cho nó làm việc với sự giúp đỡ của Alexander. Đây là cách tôi đã thiết lập nó: MainView có UIView với một lớp Custom được đặt làm GridCell. Nó có một lối thoát trong MainView/File's Owner.

Pic 1

Removed tất cả init-mã từ GridCell.m và thiết lập một lối thoát để lớp tùy chỉnh của tôi

Pic 2

Pic 3

Các MainView không vẫn hiển thị GridCell Tuy nhiên. Không có lỗi, chỉ là một không gian cô đơn, trống rỗng, nơi công tắc màu đỏ nên được. Tôi đang làm gì sai?

Tôi rất gần với việc thực hiện chương trình này. Tôi rất thích học cách này với nibs.

+0

'[NSBundle loadNibNamed]' trả về 'BOOL'! – trojanfoe

+2

Cảm ơn bạn đã trả lời. NSBundle loadNibNamed trả về một mảng mà tôi nhận được đối tượng đầu tiên của: [Nhà phát triển Apple] (https://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSBundle_UIKitAdditions/Introduction/Introduction.html) –

+1

Ah tốt điểm - Tôi đang xem tham chiếu OS X :) – trojanfoe

Trả lời

42

Loading the ngòi gây initWithCoder được gọi là một lần nữa, vì vậy bạn chỉ muốn làm như vậy nếu lớp con hiện không có bất kỳ 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

Phương pháp này phù hợp với ARC - và cũng là câu trả lời duy nhất ghi nhớ để đặt subview.frame = self.bounds. –

+0

Cảm ơn, điều này đã làm việc cho tôi. –

+0

Đây là điều tuyệt vời – Eugene

1

loadNibNamed :: will call initWithCoder:

Tại sao bạn không theo mẫu này?

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

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

     // do stuff here ... 

    } 

    return self;     
} 

[super initWithcoder:coder] có làm những việc bạn muốn tránh không?

11

tải một ngòi sẽ làm cho chủ sở hữu tương ứng trong một

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

gọi

Do đó coude của bạn trong phương pháp này:

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

sẽ gây ra một lần nữa một cuộc gọi của phương pháp initWithCoder. Đó là bởi vì bạn cố gắng tải lại ngòi. Nếu bạn định nghĩa một UIView tùy chỉnh và tạo một tập tin nib để bố trí các subviews của nó, bạn không thể chỉ thêm UIView vào một tệp nib khác, thay đổi tên lớp trong IB thành lớp tùy chỉnh của bạn và mong đợi hệ thống tải nib để tìm ra. Những gì bạn có thể làm là như sau:

Tệp nib của chế độ xem tùy chỉnh cần phải đặt lớp 'Chủ sở hữu tệp' thành lớp chế độ xem tùy chỉnh của bạn và bạn cần có ổ cắm trong lớp tùy chỉnh của bạn được gọi là 'toplevelSubView' được kết nối với một chế độ xem trong tệp nib chế độ xem tùy chỉnh của bạn hoạt động như một vùng chứa cho tất cả các bản xem trước.Thêm các cửa hàng bổ sung vào lớp xem của bạn và kết nối các bản xem trước với 'Chủ sở hữu tệp' (UIView tùy chỉnh của bạn). (Xem https://stackoverflow.com/a/7589220/925622)

EDIT Được rồi, để trả lời câu hỏi đã chỉnh sửa của bạn tôi sẽ làm như sau:

Chuyển đến tập tin nib nơi bạn muốn bao gồm các giao diện tùy chỉnh với nó là tập tin nib layouting nó. Không biến nó thành chế độ xem tùy chỉnh (GridCell), thay vào đó tạo chế độ xem sẽ chứa ô lưới của bạn (gridCellContainer chẳng hạn, nhưng nó phải là UIView) Tùy chỉnh phương thức initWithFrame trong chế độ xem tùy chỉnh của bạn như bạn đã làm trong 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; 
} 

Và sau đó, trong viewController đó là fileOwner cho quan điểm mà bạn muốn bao gồm giao diện tùy chỉnh của bạn (một với quan điểm gridCellContainer) làm điều này trong viewDidLoad ví dụ

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

Bây giờ eveything nên làm việc như bạn mong đợi

+0

Cảm ơn bạn đã trả lời. Trong liên kết của bạn, Robin dường như đang tải một nib trong initWithCoder như tôi; nhưng đây là những gì gây ra vòng lặp vô hạn - làm thế nào đây là một vấn đề đối với tôi, nhưng một giải pháp cho anh ta? Tôi đang cập nhật câu hỏi của mình với giải pháp của bạn vì tôi vẫn gặp sự cố. Tôi có thể thêm ảnh và nội dung ở đó .. –

+0

Đã cập nhật câu hỏi của tôi. Nếu bạn có một phút, hãy xem. –

+0

Xem các chỉnh sửa của tôi ... – Alexander

0

tôi đã cùng một vấn đề khi tôi đang cố gắng để ghi đè initWithsomething phương pháp, chúng ta cần

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

    return self; 
} 

thay

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

    return self; 
} 
+0

Đây không thực sự là một câu hỏi, nhưng bạn đang ở xa, vì vậy tôi muốn giúp bạn. siêu đề cập đến lớp bạn đang mở rộng: khi tệp tiêu đề bắt đầu bằng MyCustomLabel: UILabel, super là UIlabel. Đó là lý do tại sao, khi bạn khởi tạo đối tượng của riêng mình, bạn cũng cần phải khởi tạo siêu với [super init]. Bạn không bao giờ nên sử dụng initWithsomething trong MyCustomLabel AND gọi super's initWithsomething, bởi vì bạn đang ghi đè phương thức của siêu với phương thức của riêng bạn, vì nó giống với tên. Nghĩa là, phương thức của bạn không nên được gọi là initWithCoder hoặc initWithFrame. Hãy đến với một cái gì đó mới –

2

chủ sở hữu Các tập tin sẽ không nhận được một cuộc gọi đến

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

khi tải xib.

Tuy nhiên, mỗi điểm xác định trong xib rằng sẽ nhận được một cuộc gọi đến

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

khi tải một xib.

Nếu bạn có một lớp con của UIView (nghĩa là GridCell) được định nghĩa trong xib và cũng cố gắng tải cùng xib đó trong initWithCoder của lớp con, bạn sẽ kết thúc bằng một vòng lặp vô hạn. Tuy nhiên, tôi không thể thấy trường hợp sử dụng sẽ là gì.

Thông thường bạn thiết kế lớp con của UIView (ví dụ: GridCell) bằng một xib, sau đó sử dụng phân lớp đó trong xib của bộ điều khiển chế độ xem.

Ngoài ra, không thể nhìn thấy một trường hợp sử dụng nơi giao diện tùy chỉnh của bạn sẽ có một subview trong nó initWithCoder, tức là

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

Trừ khi bạn muốn để có thể ghi đè lên hệ thống phân cấp quan điểm của mình theo yêu cầu ở một số xib khác . IMO nào giả định một phụ thuộc bên ngoài (nghĩa là một hệ thống phân cấp được định nghĩa trong xib khác) và kinda đánh bại mục đích của việc có một UIView có thể tái sử dụng ngay từ đầu.

Hãy nhớ rằng khi tải một xib, chuyển một cá thể làm chủ sở hữu tệp, sẽ có tất cả các bộ IBOutlet của nó. Trong trường hợp đó, bạn sẽ thay thế self (nghĩa là GridCell) với bất kỳ cái nhìn gốc nào trong GridCell.xib đó, mất tất cả các kết nối IBOutlet trong tiến trình.

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

Có một bài đăng chi tiết hơn về "How to implement a reusable UIView.", chi tiết hơn một chút và hy vọng xóa mọi thứ.

Các vấn đề liên quan