Thiết lập kiểu của UITableViewCell khi sử dụng iOS 6 UITableView dequeueReusableCellWithIdentifier: forIndexPath:


82

Tôi đang cố gắng tìm cách đặt thời UITableViewCellStyleđiểm sử dụng các phương pháp mới trong iOS 6 cho UITableView.

Trước đây, khi tạo một UITableViewCelltôi sẽ thay đổi UITableViewCellStyleenum để tạo ra các loại ô mặc định khác nhau khi gọi initWithStyle:nhưng từ những gì tôi có thể thu thập, điều này không còn xảy ra nữa.

Tài liệu của Apple cho UITableViewcác tiểu bang:

Giá trị trả về : Một đối tượng UITableViewCell với số nhận dạng tái sử dụng được liên kết. Phương thức này luôn trả về một ô hợp lệ.

Thảo luận : Vì lý do hiệu suất, nguồn dữ liệu của chế độ xem bảng thường sử dụng lại các đối tượng UITableViewCell khi nó gán các ô cho các hàng trong phương thức tableView: cellForRowAtIndexPath:. Chế độ xem bảng duy trì hàng đợi hoặc danh sách các đối tượng UITableViewCell mà nguồn dữ liệu đã đánh dấu để sử dụng lại. Gọi phương thức này từ đối tượng nguồn dữ liệu của bạn khi được yêu cầu cung cấp ô mới cho chế độ xem bảng. Phương thức này xếp hàng đợi một ô hiện có nếu một ô có sẵn hoặc tạo một ô mới dựa trên lớp hoặc tệp nib mà bạn đã đăng ký trước đó.

Quan trọng : Bạn phải đăng ký một lớp hoặc tệp nib bằng phương thức registerNib: forCellReuseIdentifier: hoặc registerClass: forCellReuseIdentifier: trước khi gọi phương thức này.

Nếu bạn đã đăng ký một lớp cho số nhận dạng được chỉ định và một ô mới phải được tạo, phương thức này sẽ khởi tạo ô bằng cách gọi phương thức initWithStyle: reuseIdentifier: của nó. Đối với các ô dựa trên nib, phương pháp này tải đối tượng ô từ tệp nib được cung cấp. Nếu một ô hiện có sẵn sàng để sử dụng lại, phương thức này sẽ gọi phương thức readyForReuse của ô đó.

Đây là giao diện mới của tôi cellForRowAtIndexPathsau khi triển khai các phương pháp mới:

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

    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellIdentifier];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    return cell;
}

Mã mà tôi có cho đến nay hoạt động tốt nhưng luôn trả về kiểu mặc định. Làm thế nào tôi có thể thay đổi điều này để tôi có thể tạo ra các tế bào với các phong cách khác như UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2UITableViewCellStyleSubtitle?

Tôi không muốn phân lớp con UITableViewCell, tôi chỉ muốn thay đổi kiểu mặc định như tôi có thể làm trước iOS 6. Có vẻ kỳ lạ là Apple sẽ cung cấp các phương pháp nâng cao nhưng với tài liệu tối thiểu để hỗ trợ việc triển khai chúng.

Có ai nắm vững điều này, hoặc gặp phải một vấn đề tương tự? Tôi đang đấu tranh để tìm bất kỳ thông tin hợp lý nào cả.

Câu trả lời:


106

Tôi biết bạn nói rằng bạn không muốn tạo một lớp con, nhưng nó có vẻ không thể tránh khỏi. Dựa trên mã lắp ráp trong khi thử nghiệm trong trình mô phỏng iOS 6.0, UITableViewtạo các phiên bản mới của UITableViewCell(hoặc các lớp con của nó) bằng cách thực hiện

[[<RegisteredClass> alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:<ReuseIdentifier>]

Nói cách khác, kiểu sent ( UITableViewCellStyleDefault) dường như được mã hóa cứng. Để giải quyết vấn đề này, bạn sẽ cần tạo một lớp con ghi đè bộ khởi tạo mặc định initWithStyle:reuseIdentifier:và chuyển kiểu bạn muốn sử dụng:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    // ignore the style argument, use our own to override
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        // If you need any further customization
    }
    return self;
}

Ngoài ra, có thể tốt hơn là gửi registerClass:forCellReuseIdentifier:vào viewDidLoad, thay vì làm điều đó mỗi khi một ô được yêu cầu:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerClass:<RegisteredClass> forCellReuseIdentifier:<ReuseIdentifier>];
}

4
Tôi bắt đầu cho rằng điều này thực sự sẽ xảy ra. Đó không phải là vấn đề lớn nhưng việc phải phân lớp UITableViewCellđể lấy các kiểu mặc định khác là một điều khó khăn vì nó chỉ tạo ra các tệp không cần thiết. Cảm ơn bình luận của bạn và xác nhận những nghi ngờ của tôi.
CaptainRedmuff

11
Đừng quên rằng thay vì phân lớp con, bạn có thể sử dụng phương pháp iOS5 cũ, vẫn còn hiệu lực. Bằng cách đó, bạn có thể khởi tạo bất kỳ loại kiểu ô nào mà bạn muốn. Xem câu trả lời khác.
SpacyRicochet

60

dequeueReusableCellWithIdentifierkhông còn được dùng nữa nên bạn không bắt buộc phải sử dụng cái mới dequeueReusableCellWithIdentifier:forIndexPath:.

Sử dụng cách mới cùng với phương thức đăng ký thích hợp (trong viewDidLoad) nếu bạn đang sử dụng một lớp ô tùy chỉnh nhưng sử dụng cách cũ nếu bạn muốn sử dụng một trong các enum UITableViewCellStyle.


1
Được ủng hộ vì chỉ ra rằng bạn không cần phải sử dụng các phương pháp mới lạ. Chỉ khi chúng phù hợp với mục đích của bạn hoặc nếu các lựa chọn thay thế không được dùng nữa.
SpacyRicochet

Nếu bạn đặc biệt quan tâm, bạn có thể ghi đè dequeueReusableCellWithIdentifier:forIndexPath:để cung cấp cho một số mã nhận dạng xây dựng các ô theo cách cũ (và trả lại chúng). Các định danh khác sẽ gọi super và trả về điều đó. Có thể có ý nghĩa nếu có một NSDictionarytrong các số nhận dạng cho các khối khởi tạo cho loại số nhận dạng đó.
Benjohn

11

Bạn có thể tránh một lớp con không liên quan bằng cách sử dụng trình tạo giao diện bảng phân cảnh:

  1. Trong dạng xem Bảng phân cảnh, hãy chọn ô nguyên mẫu của ô chế độ xem bảng (trên chế độ xem bảng)
  2. Trong dạng xem Tiện ích, trong trình kiểm tra Thuộc tính, hãy sửa đổi giá trị Kiểu
  3. (Tùy chọn) Sửa đổi các giá trị khác như Lựa chọn và Phụ kiện

IOS 6.0 mới dequeueReusableCellWithIdentifier:forIndexPath:sử dụng các giá trị đó khi phân bổ các ô mới và trả lại chúng. (Đã thử nghiệm trên bản biên dịch iOS 6.0 sử dụng Xcode 4.5.2)


7

Một giải pháp thay thế khác giúp lưu một tệp là tạo Nib và sử dụng registerNib:forCellReuseIdentifier:thay thế.

Tạo Nib thật dễ dàng: Tạo tệp .xib mới trong Trình tạo giao diện. Xóa chế độ xem mặc định. Thêm đối tượng Ô Dạng xem Bảng. Sử dụng Trình kiểm tra thuộc tính, thay đổi kiểu cho ô. (Tại đây bạn cũng có cơ hội tùy chỉnh thêm ô bằng cách điều chỉnh các thuộc tính khác.)

Sau đó, trong viewDidLoadphương thức của bộ điều khiển chế độ xem bảng của bạn, hãy gọi một cái gì đó như:

[self.tableView registerNib:[UINib nibWithNibName:@"StyleSubtitleTableCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"Cell"];

initWithStyle: reuseIdentifier không được gọi.
thierryb

0

Câu trả lời của Bolot là chính xác. Đơn giản và bạn không cần tạo bất kỳ tệp XIB nào.

Tôi chỉ muốn cập nhật câu trả lời của anh ấy cho những ai đang làm việc đó bằng Swift thay vì Objective-C:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: .value1, reuseIdentifier: reuseIdentifier)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

-5

Giải pháp của tôi cho điều này là gọi initWithStyle: reuseIdentifier:sau khi tôi đã lấy được nó bằng cách sử dụng [self.tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath]. Rốt cuộc, initnó chỉ là một bộ chọn khác và trình biên dịch không hạn chế việc gọi nó trên một đối tượng đã được khởi tạo. Tuy nhiên, nó sẽ phàn nàn về việc không sử dụng kết quả của việc gọi init, vì vậy tôi làm:

UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath];
cell = [cell initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cellId"];

Tôi tưởng tượng điều này sẽ không hoạt động trong Swift ...


Giải pháp thanh lịch nhất imo. Hy vọng rằng những người ruột của initWithStyle không tạo lại mọi thứ một lần nữa.
Scott Birksted

2
Vâng, tôi đoán nếu you're làm việc đó như thế này, bạn có thể thả những thứ dequeuing hoàn toàn ...
stk

1
Sử dụng lại các ô là chìa khóa để có một UITableView hiệu quả. Giải pháp của bạn đề xuất không sử dụng lại các ô.
Berik

2
Khi bạn gọi initWithStyle: reuseIdentifierlần thứ hai, bạn thực sự đang ghi đè ô bằng một đối tượng mới được tạo. Có, việc cấp phát bộ nhớ đã được thực hiện và ô mới sẽ ghi đè lên cùng vị trí bộ nhớ đó, nhưng bạn thực sự đang tạo một đối tượng hoàn toàn mới khi bạn khởi tạo lại nó. Điều này phủ nhận tất cả sự tối ưu hóa là toàn bộ điểm của việc sử dụng lại các ô ngay từ đầu.
AnthonyMDev

1
Không phải là một giải pháp tốt để làm theo - nếu bạn đang sử dụng giải pháp này, tôi nghĩ rằng đó là ít nhất tốt hơn để thả dequeuing các tế bào hoàn toàn
Sudara
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.