UITableViewController에서 UISearchDisplayController를 사용할 때 어설 션 실패


80

내 앱의 TableViewController에 간단한 검색 기능을 추가하려고했습니다. Ray Wenderlich의 튜토리얼을 따랐습니다. 일부 데이터가있는 tableView가 있고 스토리 보드에 검색 막대 + 디스플레이 컨트롤러를 추가 한 다음이 코드가 있습니다.

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

표준 항목이지만 검색 창에 텍스트를 입력하면 매번이 오류와 함께 충돌합니다.

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'

iOS 6에서 셀 처리 및 대기열에서 빼기 시스템이 변경되었고 검색이 다른 tableView를 사용한다는 것을 알고 있으므로 필터링 된 결과가있는 검색 tableView가 셀에 대해 알지 못한다는 문제가 있다고 생각했기 때문에 이것은 내 viewDidLoad에서 :

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

그리고 짜잔! 효과가 ... 처음 검색했을 때만. 원래 결과로 돌아가 다시 검색하면 앱이 동일한 오류와 함께 충돌합니다. 나는 아마 모든 것을 추가하는 것에 대해 생각했다.

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

물건을 cellForRow 메서드에 추가하지만 dequeueReusableCellWithIdentifier : forIndexPath : 메서드를 갖는 전체 목적에 위배되지 않습니까? 어쨌든 길을 잃었습니다. 내가 무엇을 놓치고 있습니까? 도와주세요. 시간 내 주셔서 미리 감사드립니다 (:

알렉스.

답변:


194

dequeueReusableCellWithIdentifier에서 tableView 대신 self.tableView를 사용해보십시오.

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

이 코드는 꽤 잘 작동합니다.

노트

사용자 지정 높이 셀이있는 경우 사용하지 마십시오.

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

대신 사용

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

4
한 테이블에서 다른 테이블로 새로운 셀을 무기한으로 인스턴스화하지 않습니까? 하나 개의 테이블에서 세포를 대기열에서와 같은 다른 소리에 나쁜 아이디어를 제공 한 경우
maltalef

1
나는 이것도 작동 할 것이라고 생각했다. 그렇지 않습니다. 제 경우에는 검색 테이블의 기본 테이블에서 셀을 대기열에서 빼려고하면 때때로 충돌이 발생합니다.
Lee Probert

1
작동하지 않습니다. 검색을 시도한 다음 취소 (오버레이 결과 모드 종료) 한 다음 다시 검색하십시오. 충돌.
다니엘

8
제안 된 솔루션은 여전히 ​​저에게 예외를 발생시킵니다. 내가 대체 할 경우 예상대로하지만, 그것은 작동하는 것 같다 dequeueReusableCellWithIdentifier:forIndexPath:dequeueReusableCellWithIdentifier:.
jweyrich

3
감사합니다! tableview 대신 self.tableview가 문제였습니다! 생명의 은인!
adamteale 2014 년

14

첫 번째 실행에서는 훌륭하게 작동했지만 결과 테이블을 종료하고 다른 검색을 위해 다시 UITableView들어가면 충돌이 발생하는 이유는 검색 모드에 들어갈 때마다 검색 디스플레이 컨트롤러가 새 항목을로드하기 때문 입니다.

검색 모드 란 텍스트 필드를 탭하고 입력을 시작했습니다.이 시점에서 결과를 표시하기 위해 테이블보기가 생성되고 취소 버튼을 눌러이 모드를 종료합니다. 두 번째로 텍스트 필드를 탭하고 다시 입력을 시작하면 두 번째로 "검색 모드"로 전환됩니다.

따라서 충돌을 피하려면 컨트롤러 메서드 대신 searchDisplayController:didLoadSearchResultsTableView:대리자 메서드 (from UISearchDisplayDelegate) 에서 사용할 테이블 뷰의 셀 클래스를 등록해야합니다 viewDidLoad.

다음과 같이 :

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

이것은 iOS 7에서 테이블 뷰가 재사용되고 있기 때문에 나를 놀라게했습니다. 따라서 viewDidLoad원하는 경우 수업을 등록 할 수 있습니다 . 레거시를 위해 내가 언급 한 델리게이트 메서드에 등록을 유지하겠습니다.


2
불행히도 Storyboard 동적 프로토 타입 셀에서는 작동하지 않습니다.
Ortwin Gentz ​​2014 년

그러나 그것은 nib에 의해 등록 된 세포와 잘 작동합니다. 좋은 해결 방법입니다.
Thomas Verbeek 2014 년

12

검색 후 cellForRowAtIndexPath 메서드의 'tableView'가 정의한 Table의 인스턴스가 아닌 것 같습니다. 따라서 셀을 정의하는 테이블의 인스턴스를 사용할 수 있습니다. 대신에:

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

사용하다:

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

( cellForRowAtIndexPath 메서드 의 tableView 를 사용하지 말고 self.tableView를 사용 하십시오 .)


2
+1. 이 개념을 신속하게 구현하려면 self.tableView가 필요했습니다.
davidethell

3

'indexPath'를 사용하지 않고 셀을 대기열에서 빼고 nil 요소를 얻은 경우 수동으로 할당해야합니다.

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

셀을 대기열에서 빼기 위해 self.tableView를 사용하려고하면 섹션화 된 기본 목록과 일반 검색 목록이있을 때 충돌이 발생할 수 있습니다. 이 코드는 대신 어떤 상황에서도 작동합니다.


1
셀이 Storyboard에서 사용자 지정되어 있고 클래스가 아닌 셀에서 인스턴스화되어야하는 경우에는 작동하지 않습니다.
Lee Probert 2013 년

3

이 문제가 발생했을 때 해결책은 tableViewdequeueReusableCellWithIdentifier : @yourcell을self.tableView


2

나는 그 튜토리얼도 작업하고 있습니다. 기본 TableViewController에는 "forIndexPath"가 있으며 그의 예에서는 존재하지 않습니다. 제거하면 검색이 작동합니다.

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

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

1
불행히도 Storyboard 동적 프로토 타입 셀에서는 작동하지 않습니다.
Ortwin Gentz ​​2014 년

2

신속한 3의 경우 self를 추가하면됩니다.

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

    ...
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.