问题
I want to search in my tableView, set checkmarks and save the objects with the checkmarks in Realm. But if I set a checkmark after a search and cancel the search, the checkmark is at the indexPath that I clicked on, and not at the object. I can't explain it better, so here's an example: After I search an exercise.
After I clicked the cancel button
Here's my code:
class ShowExcercisesTableViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
//Properties
let realm = try! Realm()
var request2: Results<Excercise>?{
didSet{
tableView.reloadData()
}
}
var searchOrNot: Excercise?
var searchResults = try! Realm().objects(Excercise.self)
var resultSearchController: UISearchController!
var shouldShowSearchResults = false
var muscleGroupForSearch: String?
//Searchbar Funktionen
func filterResultsWithSearchString(searchString: String){
let predicate = NSPredicate(format: "name CONTAINS [c]%@ AND muscleGroup =%@ AND copied = false", searchString, muscleGroupForSearch!)
searchResults = realm.objects(Excercise.self).filter(predicate).sorted(byProperty: "name", ascending: true)
}
func updateSearchResults(for searchController: UISearchController) {
let searchString = searchController.searchBar.text
filterResultsWithSearchString(searchString: searchString!)
tableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
shouldShowSearchResults = true
tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = false
tableView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if !shouldShowSearchResults {
shouldShowSearchResults = true
tableView.reloadData()
}
resultSearchController.searchBar.resignFirstResponder()
}
//Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.searchBar.delegate = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
controller.searchBar.placeholder = "Suche Übungen..."
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
//TableView Funktionen
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowSearchResults {
return searchResults.count
}
else{
return request2!.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ShowExcercisesTableViewCell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.showExcercises, for: indexPath) as! ShowExcercisesTableViewCell
if shouldShowSearchResults{
let excercise = searchResults[indexPath.row]
cell.nameLabel.text = excercise.name
if fromTrainingPlan{
if excercise.selected == true{
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
}
return cell
}
else{
let excercise = request2![indexPath.row]
cell.nameLabel.text = excercise.name
if fromTrainingPlan{
if excercise.selected == true{
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
}
return cell
}
}
//Checkmarks
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if fromTrainingPlan == true && request2 != nil{
if shouldShowSearchResults{
searchOrNot = searchResults[indexPath.row]
}
else{
searchOrNot = request2![indexPath.row]
}
tableView.deselectRow(at: indexPath, animated: true)
let cell: ShowExcercisesTableViewCell = tableView.cellForRow(at: indexPath) as! ShowExcercisesTableViewCell
do {
try realm.write {
searchOrNot!.selected = !searchOrNot!.selected
}
}
catch{
print(error)
}
if searchOrNot!.selected {
cell.accessoryType = .checkmark
}
else {
cell.accessoryType = .none
}
}
}
Sorry for so much code, I'm not sure what is relevant and what not. Is there any way to set the checkmarks at the right places after the search? Thanks in advance!
It's working now.
回答1:
In your cellForRowAtIndexPath
, you need to disable the checkmark for cells that do not need it. You are only enabling for cells that do.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ShowExcercisesTableViewCell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.showExcercises, for: indexPath) as! ShowExcercisesTableViewCell
if shouldShowSearchResults{
let excercise = searchResults[indexPath.row]
cell.nameLabel.text = excercise.name
if excercise.selected == true{
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none // Add this code here
}
return cell
}
else{
let excercise = request2![indexPath.row]
cell.nameLabel.text = excercise.name
if excercise.selected == true {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none // Add this code here
}
return cell
}
}
UITableViews
reuse the cells internally. So when you are searching, you are saying cell one has a checkmark
, then when you cancel, it goes back to the table and looks at the cells and your cellForRow
code never tells it that cell one is no longer checked, thus it maintains the checkmark there. The cell is not being recreated, its already exists, so you cannot make an assumption about what state it is in (not checked or checked).
回答2:
the checkmark is at the indexPath that I clicked on
You are not telling the code on what indexPath
you want the checkmark. When your cell gets reused :
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
You need to keep a reference to what indexPath is supposed to be selected and show the checkMark so the cells shows the checkMark on the correct indexPath when the cells are reused internally.
EDIT:
Seems some people downvote and modify my answers as their own without reading how the framework works and reading Apples Documentation.
Reusable Cells
prepareForReuse
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.
来源:https://stackoverflow.com/questions/42056123/uitableview-checkmarks-at-the-wrong-place-after-search