Xác nhận thất bại khi sử dụng UISearchDisplayController trong UITableViewController


80

Tôi đã cố gắng thêm chức năng Tìm kiếm đơn giản vào TableViewController trong ứng dụng của mình. Tôi đã làm theo hướng dẫn của Ray Wenderlich. Tôi có một tableView với một số dữ liệu, tôi đã thêm thanh tìm kiếm + bộ điều khiển hiển thị trong bảng phân cảnh và sau đó tôi có mã này:

#pragma mark - Table View
     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BreedCell" forIndexPath:indexPath];

        //Create PetBreed Object and return corresponding breed from corresponding array
        PetBreed *petBreed = nil;

        if(tableView == self.searchDisplayController.searchResultsTableView)
            petBreed = [_filteredBreedsArray objectAtIndex:indexPath.row];
        else
            petBreed = [_breedsArray objectAtIndex:indexPath.row];

        cell.accessoryType  = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.text = petBreed.name;

        return cell;
    }

#pragma mark - Search
    -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
        [_filteredBreedsArray removeAllObjects];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",searchString];
        _filteredBreedsArray = [[_breedsArray filteredArrayUsingPredicate:predicate] mutableCopy];

        return YES;
    }

    -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
        // Tells the table data source to reload when scope bar selection changes

        [_filteredBreedsArray removeAllObjects];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",self.searchDisplayController.searchBar.text];
        _filteredBreedsArray = [[_breedsArray filteredArrayUsingPredicate:predicate] mutableCopy];
        return YES;
    }

Nội dung tiêu chuẩn, nhưng khi tôi nhập văn bản vào thanh tìm kiếm, nó bị treo mỗi lần với lỗi này:

2013-01-07 19:47:07.330 FindFeedo[3206:c07] *** Assertion failure in -[UISearchResultsTableView dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:4460
2013-01-07 19:47:07.330 FindFeedo[3206:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier BreedCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

Tôi hiểu rằng trong iOS 6, hệ thống xử lý và xếp hàng cho các ô đã thay đổi và tìm kiếm cũng sử dụng một tableView khác, vì vậy tôi nghĩ vấn đề là tableView tìm kiếm với kết quả đã lọc không biết về ô, vì vậy tôi đặt cái này trong viewDidLoad của tôi:

[self.searchDisplayController.searchResultsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"BreedCell"];

Và Voila! Nó hoạt động ... Chỉ lần đầu tiên bạn tìm kiếm. Nếu bạn quay lại kết quả ban đầu và tìm kiếm lại, ứng dụng bị lỗi với cùng một lỗi. Tôi đã nghĩ về việc có thể thêm tất cả

if(!cell){//init cell here};

vào phương thức cellForRow, nhưng điều đó không đi ngược lại toàn bộ mục đích của việc có phương thức dequeueReusableCellWithIdentifier: forIndexPath: sao? Dù sao thì tôi cũng lạc lối. Tôi đang thiếu gì? Giúp tôi với. Cảm ơn bạn trước cho tất cả thời gian của bạn (:

Alex.

Câu trả lời:


194

Hãy thử sử dụng self.tableView thay vì tableView trong dequeueReusableCellWithIdentifier:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"BreedCell"];

    //Create PetBreed Object and return corresponding breed from corresponding array
    PetBreed *petBreed = nil;

    if(tableView == self.searchDisplayController.searchResultsTableView)
        petBreed = [_filteredBreedsArray objectAtIndex:indexPath.row];
    else
        petBreed = [_breedsArray objectAtIndex:indexPath.row];

    cell.accessoryType  = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.text = petBreed.name;

    return cell;
}

Mã này hoạt động khá tốt

Ghi chú

Nếu bạn có ô độ cao tùy chỉnh, không sử dụng

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

Sử dụng cái này thay thế

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

4
điều đó sẽ không chỉ khởi tạo các ô mới từ bảng này sang bảng khác một cách vô thời hạn? nếu dequeuing tế bào từ một bảng và đem lại cho họ với những âm thanh khác giống như một ý tưởng tồi
maltalef

1
Tôi nghĩ điều này cũng sẽ hoạt động. Nó không. Trong trường hợp của tôi, việc cố gắng xóa ô từ bảng chính của tôi cho bảng Tìm kiếm đôi khi gây ra lỗi.
Lee Probert

1
Không hoạt động. Cố gắng tìm kiếm, sau đó hủy (thoát chế độ kết quả lớp phủ) và sau đó tìm kiếm lại. Sự cố.
Daniel

8
Giải pháp được đề xuất vẫn đưa ra một ngoại lệ đối với tôi. Tuy nhiên, nó có vẻ hoạt động như mong đợi nếu tôi thay thế dequeueReusableCellWithIdentifier:forIndexPath:bằng dequeueReusableCellWithIdentifier:.
jweyrich 27/09/13

3
cảm ơn bạn! self.tableview thay vì tableview là vấn đề! tiết kiệm cuộc sống!
adamteale

14

Lý do tại sao nó hoạt động tốt trong lần chạy đầu tiên nhưng sau đó bị lỗi nếu bạn thoát khỏi bảng kết quả và quay lại tìm kiếm khác là do Bộ điều khiển hiển thị tìm kiếm đang tải một tệp mới UITableViewmỗi khi bạn vào chế độ tìm kiếm.

Ý tôi là ở chế độ tìm kiếm, bạn đã nhấn vào trường văn bản và bắt đầu nhập, tại thời điểm đó, chế độ xem bảng được tạo để hiển thị kết quả, thoát khỏi chế độ này mà nó đạt được bằng cách nhấn nút hủy. Khi bạn nhấn vào trường văn bản lần thứ hai và bắt đầu nhập lại - đây là bước vào "chế độ tìm kiếm" lần thứ hai.

Vì vậy, để tránh sự cố, bạn nên đăng ký lớp ô cho dạng xem bảng để sử dụng trong searchDisplayController:didLoadSearchResultsTableView:phương thức ủy nhiệm (from UISearchDisplayDelegate) thay vì trong viewDidLoadphương thức bộ điều khiển của bạn .

Như sau:

- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
    [tableView registerClass:[DPContentTableCell class] forCellReuseIdentifier:cellIdentifier];
    [tableView registerClass:[DPEmptyContentTableCell class] forCellReuseIdentifier:emptyCellIdentifier];
}

Điều này làm tôi ngạc nhiên vì trên iOS 7 ... chế độ xem bảng đang được sử dụng lại. Vì vậy, bạn có thể đăng ký lớp học viewDidLoadnếu bạn thích. Đối với các lỗi kế thừa, tôi sẽ giữ đăng ký của mình trong phương pháp ủy quyền mà tôi đã đề cập.


2
Thật không may, điều này không hoạt động với các ô nguyên mẫu động của Storyboard.
Ortwin Gentz

Nhưng nó hoạt động tốt với các ô được đăng ký bởi nib. Cách giải quyết tốt.
Thomas Verbeek

12

Sau khi tìm kiếm, 'tableView' của phương thức cellForRowAtIndexPath dường như không phải là một phiên bản của Bảng mà bạn xác định. Vì vậy, bạn có thể sử dụng một thể hiện của bảng xác định ô. Thay vì:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

Sử dụng:

UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

(Không sử dụng tableView của phương thức cellForRowAtIndexPath, hãy sử dụng self.tableView .)


2
+1. Cần có self.tableView trong quá trình triển khai nhanh chóng của tôi về khái niệm này.
davidethell

3

Dequeue ô mà không sử dụng 'indexPath' và trong trường hợp bạn lấy được phần tử nil, bạn phải cấp phát nó theo cách thủ công.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YourCellId"];
    if (!cell)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"YourCellId"];

    // fill your cell object with useful stuff :)

    return cell;
}

Việc cố gắng sử dụng self.tableView để xóa ô có thể gây ra lỗi khi bạn có danh sách chính được phân đoạn và danh sách tìm kiếm thuần túy. Thay vào đó, mã này hoạt động trong mọi tình huống.


1
Điều này sẽ không hoạt động nếu ô của bạn là tùy chỉnh trong Bảng phân cảnh và cần được khởi tạo từ nó chứ không phải lớp của nó.
Lee Probert

3

Khi tôi gặp sự cố này, giải pháp là thay thế tableViewdequeueReusableCellWithIdentifier: @yourcell bằngself.tableView


2

Tôi cũng đang làm việc trên hướng dẫn đó. TableViewController mặc định có "forIndexPath" và trong ví dụ của anh ta, nó không tồn tại. Sau khi tôi xóa nó, tìm kiếm sẽ hoạt động.

//Default code
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

//Replace with
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

1
Thật không may, điều này không hoạt động với các ô nguyên mẫu động của Storyboard.
Ortwin Gentz

2

đối với swift 3, bạn chỉ cần thêm bản thân:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = self.tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! YourCell

    ...
}
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.