9

tableView의 셀 내용을 소싱하여 CoreData에서 SearchDisplayController (대체)을 새로운 SearchController으로 바 꾸었습니다. 동일한 tableView 컨트롤러를 사용하여 개체의 전체 목록과 필터링 된/검색 한 개체를 모두 표시합니다.UISearchController의 필터링 된 검색 결과에서 삭제

필자는 검색/필터링 작업이 정상적으로 처리되었으며 필터링 된 목록에서 해당 항목에 대한 상세보기로 이동 한 다음 변경 사항을 편집하고 필터링 된 tableView에 성공적으로 다시 저장할 수 있습니다. 필터링 된 목록에서 셀을 삭제하도록 내 문제가 발생하면 런타임 오류가 발생합니다. 불행하게도 그러한있는 tableView가에서 UISearchController 및 임 노출되지

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    // If the search is active do this 
      searchDisplayController!.searchResultsTableView.endUpdates() 
    // else it isn't active so do this 
      tableView.endUpdates() 
    } 
} 

을 : 나는 그래서 다음 (의사) 코드가 잘 작동 할 것 SearchDisplayController's 결과를 tableView에 대한 액세스 권한을 가지고로 이전에 SearchDisplayController와 나는 쉽게 할 수 손실. 나는 tableView.beginUpdates()tableView.endUpdates()을 tableView에 대한 조건부 검색을 tableView가 아니지만 성공으로 만들려고했습니다. 기록을 위해

이 내 오류 메시지입니다 :

Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-3318.65/UITableView.m:1582

* EDIT *

내있는 tableView는 CoreData에서 자신을 채울 FetchedResultsController를 사용합니다. 이 tableViewController는 SearchController가 필터링 된 결과를 표시하는 데 사용되는 컨트롤러이기도합니다.

var searchController: UISearchController! 

다음의 viewDidLoad

searchController = UISearchController(searchResultsController: nil) 
searchController.dimsBackgroundDuringPresentation = false 
searchController.searchResultsUpdater = self 
searchController.searchBar.sizeToFit() 
self.tableView.tableHeaderView = searchController?.searchBar 
self.tableView.delegate = self 
self.definesPresentationContext = true 

func updateSearchResultsForSearchController(searchController: UISearchController) { 
    let searchText = self.searchController?.searchBar.text 
    if let searchText = searchText { 
     searchPredicate = searchText.isEmpty ? nil : NSPredicate(format: "locationName contains[c] %@", searchText) 
     self.tableView.reloadData() 
    } 
} 

지금까지 오류 메시지에 관한 한, 내가 추가 할 수 있습니다 얼마나 확실하지 않다. 앱은 스 와이프하여 표시되는 빨간색 삭제 버튼 (계속 표시됨)을 누르면 즉시 멈 춥니 다. 이것은 1 - 5에 대한 스레드 오류 로그입니다. 응용 프로그램이 번호 4에 걸려있는 것 같습니다.

#0 0x00000001042fab8a in objc_exception_throw() 
#1 0x000000010204b9da in +[NSException raise:format:arguments:]() 
#2 0x00000001027b14cf in -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]() 
#3 0x000000010311169a in -[UITableView _endCellAnimationsWithContext:]() 
#4 0x00000001019b16f3 in iLocations.LocationViewController.controllerDidChangeContent (iLocations.LocationViewController)(ObjectiveC.NSFetchedResultsController) ->() at /Users/neilmckay/Dropbox/Programming/My Projects/iLocations/iLocations/LocationViewController.swift:303 
#5 0x00000001019b178a in @objc iLocations.LocationViewController.controllerDidChangeContent (iLocations.LocationViewController)(ObjectiveC.NSFetchedResultsController) ->()() 

나는 이것이 도움이되기를 바랍니다.

EDIT * 2 *

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     let location: Location = self.fetchedResultsController.objectAtIndexPath(indexPath) as Location 
     location.removePhotoFile() 

     let context = self.fetchedResultsController.managedObjectContext 
     context.deleteObject(location) 

     var error: NSError? = nil 
     if !context.save(&error) { 
      abort() 
     } 
    } 
} 

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    if self.searchPredicate == nil { 
     let sectionInfo = self.fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo 
     return sectionInfo.numberOfObjects 
    } else { 
     let filteredObjects = self.fetchedResultsController.fetchedObjects?.filter() { 
      return self.searchPredicate!.evaluateWithObject($0) 
     } 
     return filteredObjects == nil ? 0 : filteredObjects!.count 
    } 
} 

// MARK: - NSFetchedResultsController methods 

var fetchedResultsController: NSFetchedResultsController { 
    if _fetchedResultsController != nil { 
     return _fetchedResultsController! 
    } 

    let fetchRequest = NSFetchRequest() 
    // Edit the entity name as appropriate. 
    let entity = NSEntityDescription.entityForName("Location", inManagedObjectContext: self.managedObjectContext!) 
    fetchRequest.entity = entity 

    // Set the batch size to a suitable number. 
    fetchRequest.fetchBatchSize = 20 

    // Edit the sort key as appropriate. 
    if sectionNameKeyPathString1 != nil { 
     let sortDescriptor1 = NSSortDescriptor(key: sectionNameKeyPathString1!, ascending: true) 
     let sortDescriptor2 = NSSortDescriptor(key: sectionNameKeyPathString2!, ascending: true) 
     fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2] 
    } else { 
     let sortDescriptor = NSSortDescriptor(key: "firstLetter", ascending: true) 
     fetchRequest.sortDescriptors = [sortDescriptor] 
    } 

    var sectionNameKeyPath: String 
    if sectionNameKeyPathString1 == nil { 
     sectionNameKeyPath = "firstLetter" 
    } else { 
     sectionNameKeyPath = sectionNameKeyPathString1! 
    } 

    // Edit the section name key path and cache name if appropriate. 
    // nil for section name key path means "no sections". 
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil /*"Locations"*/) 
    aFetchedResultsController.delegate = self 
    _fetchedResultsController = aFetchedResultsController 

    var error: NSError? = nil 
    if !_fetchedResultsController!.performFetch(&error) { 
     fatalCoreDataError(error) 
    } 

    return _fetchedResultsController! 
} 

var _fetchedResultsController: NSFetchedResultsController? = nil 

func controllerWillChangeContent(controller: NSFetchedResultsController) { 
    if searchPredicate == nil { 
     tableView.beginUpdates() 
    } else { 
     (searchController.searchResultsUpdater as LocationViewController).tableView.beginUpdates() 
    } 

// tableView.beginUpdates()}

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    var tableView = UITableView() 
    if searchPredicate == nil { 
     tableView = self.tableView 
    } else { 
     tableView = (searchController.searchResultsUpdater as LocationViewController).tableView 
    } 

    switch type { 
    case .Insert: 
     tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) 
    case .Delete: 
     tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) 
    default: 
     return 
    } 
} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath) { 
    var tableView = UITableView() 
    if searchPredicate == nil { 
     tableView = self.tableView 
    } else { 
     tableView = (searchController.searchResultsUpdater as LocationViewController).tableView 
    } 

    switch type { 
    case .Insert: 
     println("*** NSFetchedResultsChangeInsert (object)") 
     tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade) 

    case .Delete: 
     println("*** NSFetchedResultsChangeDelete (object)") 
      tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
    case .Update: 
     println("*** NSFetchedResultsChangeUpdate (object)") 
     if searchPredicate == nil { 
      let cell = tableView.cellForRowAtIndexPath(indexPath) as LocationCell 
      let location = controller.objectAtIndexPath(indexPath) as Location 
      cell.configureForLocation(location) 
     } else { 
      let cell = tableView.cellForRowAtIndexPath(searchIndexPath) as LocationCell 
      let location = controller.objectAtIndexPath(searchIndexPath) as Location 
      cell.configureForLocation(location) 
     } 
    case .Move: 
     println("*** NSFetchedResultsChangeMove (object)") 
     tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
     tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade) 
    } 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    if searchPredicate == nil { 
     tableView.endUpdates() 
    } else { 
     (searchController.searchResultsUpdater as LocationViewController).tableView.endUpdates() 
    } 
} 
+0

SearchController 구성, 'updateSearchResultsForSearchController' 메소드 및 오류 메시지에 대한 자세한 정보에 대해 자세히 알려주십시오. 감사. – pbasdf

+0

원본 답변에 몇 가지 정보를 추가했습니다. 희망이 도움이됩니다. – Magnas

+0

감사합니다. tableView.endUpdates()가 호출 될 때 오류가 발생하더라도 문제는 다른 곳에 있다고 생각합니다. 나는 그 문제가 삭제 후'numberOfRowsInSection'에 의해 반환 된 숫자가 이전 값과 일치하지 않는다고 의심한다. 만약 당신이 한 행을 삭제했다면, 이전 값 -1이어야한다. 질문) 코드에서'commitEditingStyle'와'numberOfRowsInSection'과 다른 FRC 델리게이트 메소드 – pbasdf

답변

5

문제 때문에 결과 페치 제어기에 의해 사용되는 indexPath 간의 불일치 발생 및 tableView의 대응하는 행의 indexPath

검색 컨트롤러가 활성화되어 있지만 기존 tableView는 검색 결과를 표시하기 위해 다시 사용됩니다. 따라서 두 개의 tableViews를 구별하는 논리는 다음과 같습니다.

if searchPredicate == nil { 
    tableView = self.tableView 
} else { 
    tableView = (searchController.searchResultsUpdater as LocationViewController).tableView 
} 

은 필요하지 않습니다.searchController를 초기화 할 때 searchController.searchResultsUpdater = self을 설정했기 때문에 작동하지만 변경하지 않아도되지만 두 경우 모두 동일한 tableView가 사용됩니다.

차이점은 searchController가 활성화되어있는 동안 tableView가 채워지는 방식에 있습니다. 이 경우 필터링 된 결과가 모두 한 섹션에 표시되는 것처럼 (numberOfRowsInSection 코드에서) 찾습니다. (나는 cellForRowAtIndexPath도 비슷하게 작동한다고 가정합니다.) 필터링 된 결과에서 섹션 0, 7 행의 항목을 삭제한다고 가정합니다. 그런 다음 commitEditingStyle는 indexPath 0-7, 다음 줄 호출됩니다

let location: Location = self.fetchedResultsController.objectAtIndexPath(indexPath) as Location 

는 FRC에서 인덱스 0-7에서 객체를 얻을하려고합니다. 그러나 FRC의 색인 0-7에있는 항목은 완전히 다른 대상 일 수 있습니다. 따라서 잘못된 개체를 삭제합니다. 그런 다음 FRC 대리자 메서드가 실행되고 tableView에 인덱스 0-7의 행을 삭제하도록 지시합니다. 이제 실제로 삭제 된 객체가 필터링 된 결과에없는 경우 행이 삭제 되었더라도 행 수는 변경되지 않습니다. 따라서 오류가 발생합니다.

그래서, 수정, 그것을 해결하는 방법 commitEditingStyle 그래서이 searchController가 활성화되어있는 경우 올바른 객체가 삭제 발견 한 : 나는 위의 테스트 할 수 없었다

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     var location : Location 
     if searchPredicate == nil { 
      location = self.fetchedResultsController.objectAtIndexPath(indexPath) as Location 
     } else { 
      let filteredObjects = self.fetchedResultsController.fetchedObjects?.filter() { 
       return self.searchPredicate!.evaluateWithObject($0) 
      } 
      location = filteredObjects![indexPath.row] as Location 
     } 
     location.removePhotoFile() 

     let context = self.fetchedResultsController.managedObjectContext 
     context.deleteObject(location) 

     var error: NSError? = nil 
     if !context.save(&error) { 
      abort() 
     } 
    } 
} 

; 약간의 실수가 끼어 들면 사과드립니다. 그러나 적어도 올바른 방향으로 향해야합니다. 도움이되기를 바랍니다. 유사한 변경 사항이 다른 tableView 대리자/데이터 소스 메서드 중 일부에서 필요할 수 있습니다.

+0

이것을 명확히 설명 할 시간과 노력을 가져 주셔서 감사합니다. – Magnas