Trong bảng phân cảnh, làm cách nào để tạo một ô tùy chỉnh để sử dụng với nhiều bộ điều khiển?


216

Tôi đang cố gắng sử dụng bảng phân cảnh trong một ứng dụng tôi đang làm việc. Trong ứng dụng có Danh sáchNgười dùng và mỗi người chứa một bộ sưu tập khác (thành viên của danh sách, danh sách do người dùng sở hữu). Vì vậy, theo đó, tôi có ListCellUserCellcác lớp học. Mục tiêu là để những thứ đó có thể được sử dụng lại trong suốt ứng dụng (nghĩa là trong bất kỳ bộ điều khiển xem bảng nào của tôi).

Đó là nơi tôi đang gặp vấn đề.

Làm cách nào để tạo một ô xem bảng tùy chỉnh trong bảng phân cảnh có thể được sử dụng lại trong bất kỳ trình điều khiển xem nào?

Dưới đây là những điều cụ thể tôi đã thử cho đến nay.

  • Trong Bộ điều khiển số 1, đã thêm một ô nguyên mẫu, đặt lớp vào UITableViewCelllớp con của tôi , đặt id sử dụng lại, thêm nhãn và nối chúng với các ổ cắm của lớp. Trong Trình điều khiển # 2, đã thêm một ô nguyên mẫu trống, đặt nó vào cùng một lớp và sử dụng lại id như trước. Khi nó chạy, các nhãn không bao giờ xuất hiện khi các ô được hiển thị trong Bộ điều khiển # 2. Hoạt động tốt trong Bộ điều khiển # 1.

  • Được thiết kế mỗi loại tế bào trong một NIB khác nhau và được nối với lớp tế bào thích hợp. Trong bảng phân cảnh, đã thêm một ô nguyên mẫu trống và đặt lớp của nó và sử dụng lại id để tham chiếu đến lớp ô của tôi. Trong viewDidLoadcác phương thức của bộ điều khiển , đã đăng ký các tệp NIB đó cho id sử dụng lại. Khi được hiển thị, các ô trong cả hai bộ điều khiển đều trống như nguyên mẫu.

  • Giữ nguyên mẫu trong cả hai bộ điều khiển trống và đặt lớp và sử dụng lại id cho lớp ô của tôi. Xây dựng giao diện người dùng của các tế bào hoàn toàn trong mã. Các tế bào hoạt động hoàn hảo trong tất cả các bộ điều khiển.

Trong trường hợp thứ hai, tôi nghi ngờ rằng nguyên mẫu luôn ghi đè NIB và nếu tôi giết các ô nguyên mẫu, đăng ký NIB của tôi cho id sử dụng lại sẽ hoạt động. Nhưng sau đó tôi sẽ không thể thiết lập các khoảng cách từ các ô sang các khung khác, đây thực sự là toàn bộ vấn đề sử dụng bảng phân cảnh.

Vào cuối ngày, tôi muốn có hai điều: kết nối các luồng dựa trên bảng trong bảng phân cảnh và xác định bố cục ô một cách trực quan hơn là trong mã. Tôi không thể thấy làm thế nào để có được cả hai cho đến nay.

Câu trả lời:


205

Theo tôi hiểu, bạn muốn:

  1. Thiết kế một ô trong IB có thể được sử dụng trong nhiều cảnh phân cảnh.
  2. Định cấu hình các phân cảnh bảng phân cảnh duy nhất từ ​​ô đó, tùy thuộc vào cảnh mà ô đó nằm trong.

Thật không may, hiện tại không có cách nào để làm điều này. Để hiểu lý do tại sao các lần thử trước của bạn không hoạt động, bạn cần hiểu thêm về cách các bảng phân cảnh và các ô xem bảng nguyên mẫu hoạt động. (Nếu bạn không quan tâm đến lý do tại sao những nỗ lực khác này không hoạt động, hãy thoải mái rời đi ngay bây giờ. Tôi không có cách giải quyết kỳ diệu nào cho bạn, ngoài việc đề nghị bạn nộp lỗi.)

Một bảng phân cảnh về bản chất là không nhiều hơn một tập hợp các tệp .xib. Khi bạn tải lên một bộ điều khiển xem bảng có một số ô nguyên mẫu từ bảng phân cảnh, đây là điều xảy ra:

  • Mỗi tế bào nguyên mẫu thực sự là mini-nib nhúng của riêng nó. Vì vậy, khi trình điều khiển xem bảng đang tải lên, nó sẽ chạy qua từng ngòi và cuộc gọi của ô nguyên mẫu -[UITableView registerNib:forCellReuseIdentifier:].
  • Khung nhìn bảng yêu cầu bộ điều khiển cho các ô.
  • Bạn có thể gọi -[UITableView dequeueReusableCellWithIdentifier:]
  • Khi bạn yêu cầu một ô có mã định danh tái sử dụng nhất định, nó sẽ kiểm tra xem nó có đăng ký ngòi hay không. Nếu có, nó khởi tạo một thể hiện của ô đó. Điều này bao gồm các bước sau:

    1. Nhìn vào lớp của ô, như được định nghĩa trong ngòi của ô. Gọi [[CellClass alloc] initWithCoder:].
    2. Các -initWithCoder:phương pháp đi qua và cho biết thêm subviews và bộ thuộc tính đã được định nghĩa trong nib. ( IBOutletcó lẽ cũng bị mắc kẹt ở đây, mặc dù tôi đã không kiểm tra điều đó; nó có thể xảy ra trong -awakeFromNib)
  • Bạn cấu hình ô của bạn theo cách bạn muốn.

Điều quan trọng cần lưu ý ở đây là có sự phân biệt giữa lớp của tế bào và sự xuất hiện trực quan của tế bào. Bạn có thể tạo hai ô nguyên mẫu riêng biệt của cùng một lớp, nhưng với các ô của chúng được đặt hoàn toàn khác nhau. Trong thực tế, nếu bạn sử dụng các UITableViewCellkiểu mặc định , đây chính xác là những gì đang xảy ra. Ví dụ, kiểu "Mặc định" và kiểu "Phụ đề" đều được đại diện bởi cùng một UITableViewCelllớp.

Điều này rất quan trọng : Lớp của ô không có mối tương quan một-một với phân cấp khung nhìn cụ thể . Hệ thống phân cấp khung nhìn được xác định hoàn toàn bởi những gì trong ô nguyên mẫu đã được đăng ký với bộ điều khiển cụ thể này.

Cũng lưu ý rằng, định danh tái sử dụng của tế bào không được đăng ký trong một số phòng cấp phát tế bào toàn cầu. Mã định danh tái sử dụng chỉ được sử dụng trong ngữ cảnh của một UITableViewthể hiện duy nhất .


Đưa ra thông tin này, chúng ta hãy xem những gì đã xảy ra trong những nỗ lực trên của bạn.

Trong Bộ điều khiển số 1, đã thêm một ô nguyên mẫu, đặt lớp thành lớp con UITableViewCell của tôi, đặt id sử dụng lại, thêm nhãn và nối chúng với các ổ cắm của lớp. Trong Trình điều khiển # 2, đã thêm một ô nguyên mẫu trống, đặt nó vào cùng một lớp và sử dụng lại id như trước. Khi nó chạy, các nhãn không bao giờ xuất hiện khi các ô được hiển thị trong Bộ điều khiển # 2. Hoạt động tốt trong Bộ điều khiển # 1.

Điều này được mong đợi. Mặc dù cả hai ô có cùng một lớp, nhưng hệ thống phân cấp khung nhìn được truyền đến ô trong Bộ điều khiển # 2 hoàn toàn không có các bản xem xét. Vì vậy, bạn có một ô trống, đó chính xác là những gì bạn đặt trong nguyên mẫu.

Được thiết kế mỗi loại tế bào trong một NIB khác nhau và được nối với lớp tế bào thích hợp. Trong bảng phân cảnh, đã thêm một ô nguyên mẫu trống và đặt lớp của nó và sử dụng lại id để tham chiếu đến lớp ô của tôi. Trong các phương thức viewDidLoad của bộ điều khiển, đã đăng ký các tệp NIB đó cho id sử dụng lại. Khi được hiển thị, các ô trong cả hai bộ điều khiển đều trống như nguyên mẫu.

Một lần nữa, điều này được mong đợi. Mã định danh tái sử dụng không được chia sẻ giữa các cảnh trong truyện hoặc ngòi, vì vậy thực tế là tất cả các ô riêng biệt này có cùng định danh tái sử dụng là vô nghĩa. Ô bạn nhận được từ bảng xem sẽ có giao diện khớp với ô nguyên mẫu trong cảnh đó của bảng phân cảnh.

Giải pháp này đã gần, mặc dù. Như bạn đã lưu ý, bạn chỉ có thể gọi theo chương trình -[UITableView registerNib:forCellReuseIdentifier:], chuyển qua ô UINibchứa và bạn sẽ lấy lại cùng một ô đó. (Điều này không phải vì nguyên mẫu đã "ghi đè" ngòi bút không có cách nào để nối các bảng phân cảnh vào một ô trong một ngòi độc lập.

Giữ nguyên mẫu trong cả hai bộ điều khiển trống và đặt lớp và sử dụng lại id cho lớp ô của tôi. Xây dựng giao diện người dùng của các tế bào hoàn toàn trong mã. Các tế bào hoạt động hoàn hảo trong tất cả các bộ điều khiển.

Một cách tự nhiên. Hy vọng, điều này là không ngạc nhiên.


Vì vậy, đó là lý do tại sao nó không hoạt động. Bạn có thể thiết kế các ô của mình trong các ngòi độc lập và sử dụng chúng trong nhiều cảnh phân cảnh; hiện tại bạn không thể nối các bảng phân cảnh vào các ô đó. Tuy nhiên, hy vọng rằng bạn đã học được điều gì đó trong quá trình đọc này.


À, tôi hiểu rồi. Bạn đóng đinh sự hiểu lầm của tôi, hệ thống phân cấp khung nhìn hoàn toàn độc lập với lớp của tôi. Rõ ràng khi nhìn lại! Cảm ơn câu trả lời tuyệt vời.
Vách đá

Không còn là không thể, dường như: stackoverflow.com/questions/8574188/ từ
Rich Apodaca

7
@RichApodaca Tôi đã đề cập đến giải pháp đó trong câu trả lời của tôi. Nhưng nó không có trong bảng phân cảnh; đó là trong một Nib riêng biệt. Vì vậy, bạn không thể kết nối sự khác biệt hoặc làm những việc khác trong cốt truyện. Do đó, nó không hoàn toàn giải quyết câu hỏi ban đầu.
BJ Homer

Kể từ XCode8, cách giải quyết sau đây dường như hoạt động nếu bạn muốn một giải pháp duy nhất cho bảng phân cảnh. Bước 1) Tạo ô nguyên mẫu của bạn trong chế độ xem bảng trong ViewContoder # 1 và liên kết với lớp UITableViewCell tùy chỉnh Bước 2) Sao chép / Dán ô đó trong chế độ xem bảng của ViewContoder # 2. Theo thời gian, bạn sẽ phải nhớ truyền thủ công các bản cập nhật cho các bản sao của ô bằng cách xóa các bản sao bạn đã tạo trong bảng phân cảnh và dán lại trong nguyên mẫu đã cập nhật.
jengelsma

Câu trả lời tuyệt vời, mặc dù tôi đã có một câu hỏi tiếp theo:> "Về bản chất, cốt truyện không khác gì một bộ sưu tập các tệp .xib" nếu đây là trường hợp, tại sao việc nhúng xib vào lại rất khó khăn bảng phân cảnh?
willcwf

58

Mặc dù câu trả lời tuyệt vời của BJ Homer tôi cảm thấy như mình có một giải pháp. Theo như thử nghiệm của tôi, nó hoạt động.

Khái niệm: Tạo một lớp tùy chỉnh cho ô xib. Ở đó bạn có thể chờ đợi một sự kiện chạm và thực hiện chương trình segue theo chương trình. Bây giờ tất cả những gì chúng ta cần là một tham chiếu đến bộ điều khiển thực hiện Phân đoạn. Giải pháp của tôi là để thiết lập nó trong tableView:cellForRowAtIndexPath:.

Thí dụ

Tôi có một ô DetailedTaskCell.xibchứa bảng mà tôi muốn sử dụng trong nhiều chế độ xem bảng:

Chi tiếtTaskCell.xib

Có một lớp tùy chỉnh TaskGuessTableCellcho ô đó:

nhập mô tả hình ảnh ở đây

Đây là nơi phép màu xảy ra.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end

// TashGuessTableCell.m
#import "TaskGuessTableCell.h"

@implementation TaskGuessTableCell

@synthesize controller;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSIndexPath *path = [controller.tableView indexPathForCell:self];
    [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
    [super touchesEnded:touches withEvent:event];
}

@end

Tôi có nhiều Phân đoạn nhưng tất cả chúng đều có cùng tên : "FinishedTask". Nếu bạn cần phải linh hoạt ở đây, tôi đề nghị thêm một tài sản khác.

ViewContoder trông như thế này:

// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"

@implementation LogbookViewController

- (void)viewDidLoad
{
    [super viewDidLoad]

    // register custom nib
    [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TaskGuessTableCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
    cell.controller = self; // <-- the line that matters
    // if you added the seque property to the cell class, set that one here
    // cell.segue = @"TheSegueYouNeedToTrigger";
    cell.taskTitle.text  = [entry title];
    // set other outlet values etc. ...

    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"FinishedTask"])
    {
        // do what you have to do, as usual
    }

}

@end

Có thể có nhiều cách thanh lịch hơn để đạt được điều tương tự nhưng - nó hoạt động! :)


1
Cảm ơn, tôi đang thực hiện phương pháp này trong dự án của tôi. Thay vào đó, bạn có thể ghi đè phương thức này để không phải lấy indexPath và tự chọn hàng: - (void) setSelected: (BOOL) đã chọn hoạt hình: (BOOL) hoạt hình {[super setSelected: chọn hoạt hình: hoạt hình]; if (được chọn) [self.controll biểu diễnSegueWithIdentifier: self.segue sender: self]; } Tôi nghĩ super sẽ chọn ô khi gọi [super touchesndnd: touches withEvent: event];. Bạn có biết khi nào nó được chọn nếu không có?
thejaz

9
Lưu ý rằng với giải pháp này, bạn đang kích hoạt segue mỗi khi một lần chạm kết thúc bên trong một tế bào. Điều này bao gồm nếu bạn chỉ đơn giản là cuộn ô, không thực sự cố gắng chọn nó. Bạn có thể có may mắn hơn ghi đè -setSelected:lên ô và chỉ kích hoạt segue khi chuyển từ NOsang YES.
BJ Homer

Tôi có may mắn hơn với setSelected:, BJ. Cảm ơn. Thật vậy, đó là một giải pháp không phù hợp ( cảm thấy sai), nhưng đồng thời, nó hoạt động, vì vậy tôi đang sử dụng nó cho đến khi điều này được khắc phục (hoặc có gì đó thay đổi trong tòa án của Apple).
Ben Kreeger

16

Tôi đang tìm kiếm điều này và tôi đã tìm thấy câu trả lời này của Richard Venable. Nó làm việc cho tôi.

iOS 5 bao gồm một phương thức mới trên UITableView: registerNib: forCellReuseIdentifier:

Để sử dụng nó, đặt một UITableViewCell trong một ngòi. Nó phải là đối tượng gốc duy nhất trong ngòi.

Bạn có thể đăng ký ngòi sau khi tải bảngView của mình, sau đó khi bạn gọi dequeueReabilitiesCellWithIdentifier: với mã định danh ô, nó sẽ kéo nó từ ngòi, giống như khi bạn đã sử dụng ô nguyên mẫu Storyboard.


10

BJ Homer đã đưa ra một lời giải thích tuyệt vời về những gì đang xảy ra.

Từ quan điểm thực tế tôi muốn nói thêm rằng, với điều kiện bạn không thể có các ô dưới dạng xibs VÀ kết nối các phân biệt, cách tốt nhất để chọn là có một ô như một xib - việc chuyển đổi dễ dàng hơn nhiều so với bố trí và các thuộc tính của nhiều ô và phân biệt của bạn có thể khác với các bộ điều khiển khác nhau của bạn. Bạn có thể xác định segue trực tiếp từ bộ điều khiển xem bảng của mình sang bộ điều khiển tiếp theo và thực hiện nó trong mã. .

Một lưu ý nữa là việc có ô của bạn dưới dạng tệp xib riêng biệt ngăn bạn có thể kết nối trực tiếp mọi hành động, v.v. với bộ điều khiển xem bảng (dù sao tôi cũng không làm việc này - bạn không thể định nghĩa chủ sở hữu tệp là bất cứ điều gì có ý nghĩa ). Tôi đang giải quyết vấn đề này bằng cách định nghĩa một giao thức mà bộ điều khiển xem bảng của ô dự kiến ​​sẽ tuân thủ và thêm bộ điều khiển làm thuộc tính yếu, tương tự như một đại biểu, trong cellForRowAtIndexPath.


10

Swift 3

BJ Homer đã đưa ra một lời giải thích tuyệt vời, Nó giúp tôi hiểu khái niệm này. Để make a custom cell reusable in storyboard, có thể được sử dụng trong bất kỳ TableViewControll nào chúng ta phải mix the Storyboard and xibtiếp cận. Giả sử chúng ta có một ô có tên CustomCelllà sẽ được sử dụng trong TableViewControllerOneTableViewControllerTwo. Tôi đang làm cho nó trong các bước.
1. Tệp> Mới> Nhấp vào Tệp> Chọn Lớp cảm ứng ca cao> bấm Tiếp theo> Đặt tên cho lớp của bạn (ví dụ CustomCell)> chọn Lớp con là UITableVieCell> Đánh dấu vào hộp kiểm cũng tạo tệp XIB và nhấn Next.
2. Tùy chỉnh ô theo ý muốn và đặt mã định danh trong trình kiểm tra thuộc tính cho ô, ở đây chúng tôi sẽ đặt là CellIdentifier. Mã định danh này sẽ được sử dụng trong ViewContoder của bạn để xác định và sử dụng lại Cell.
3. Bây giờ chúng ta phảiregister this celltrong ViewControll của chúng tôiviewDidLoad. Không cần bất kỳ phương pháp khởi tạo.
4. Bây giờ chúng ta có thể sử dụng ô tùy chỉnh này trong bất kỳ bảngView nào.

Trong TableViewControllOne

let reuseIdentifier = "CellIdentifier"

override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell
    return cell!
}

5

Tôi tìm thấy một cách để tải tế bào cho cùng một VC, không được kiểm tra cho các phân biệt. Đây có thể là một cách giải quyết để tạo ô trong một ngòi riêng

Giả sử bạn có một bảng VC và 2 bảng và bạn muốn thiết kế một ô trong bảng phân cảnh và sử dụng nó trong cả hai bảng.

(ví dụ: một bảng và trường tìm kiếm có UISearchControll có bảng cho kết quả và bạn muốn sử dụng cùng một ô trong cả hai)

Khi bộ điều khiển yêu cầu ô làm điều này:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * identifier = @"CELL_ID";

    ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
  // Ignore the "tableView" argument
}

Và ở đây bạn có tế bào của bạn từ bảng phân cảnh


Tôi đã thử điều này và nó dường như hoạt động TUY NHIÊN các tế bào không bao giờ được sử dụng lại. Hệ thống luôn tạo và xử lý các ô mới mỗi lần.
GilroyKilroy

Đây là cùng một đề xuất có thể được tìm thấy trong Thêm Thanh tìm kiếm vào Chế độ xem bảng với Bảng phân cảnh . Nếu bạn quan tâm, có một lời giải thích sâu hơn về giải pháp này ở đó (tìm kiếm tableView:cellForRowAtIndexPath:).
Ý thức 7/07/2015

nhưng đây là ít văn bản và trả lời câu hỏi
João Nunes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.