【问题标题】:Problems while trying to filter dictionary - index out of range error尝试过滤字典时出现问题 - 索引超出范围错误
【发布时间】:2018-01-17 21:41:51
【问题描述】:

我有一个日记应用程序,它有一个名为 Entry 的对象。它有自己的名为 Entry.swift 的 Swift 文件,这些日记条目使用字典数组保存。

我在UITableViewController 中添加了一个搜索栏,每当我输入一个字母时,应用程序都会在调用tableView.reloadData() 后崩溃。我认为这与过滤器错误地返回我的名为entries 的字典数组有关,当调用tableView.reloadData() 时,由于信息格式错误,dequeueReusableCell 上的两个标签都无法填充在字典数组中。

Entry.swift

//
//  Entry.swift
//  Journal
//
//  Created by handje on 6/17/17.
//  Copyright © 2017 Rob Hand. All rights reserved.
//

import Foundation

class Entry {
    static fileprivate let titleKey = "title"
    static fileprivate let bodyTextKey = "bodyText"
    static fileprivate let dateKey = "date"

    var title: String
    var bodyText: String
    var date: String

    init(title: String, bodyText: String, date: String ) {
        self.title = title
        self.bodyText = bodyText
        self.date = date
    }

    func dictionaryRepresentation() -> [String: Any] {
        return [Entry.titleKey: title, Entry.bodyTextKey: bodyText, Entry.dateKey: date]
    }

    convenience init?(dictionary: [String: Any]) {
        guard let title = dictionary[Entry.titleKey] as? String,
            let bodyText = dictionary[Entry.bodyTextKey] as? String, let date = dictionary[Entry.dateKey] as? String else { return nil
        }
        self.init(title: title, bodyText: bodyText, date: date)
    }
}

extension Entry: Equatable {
    static func == (lhs:Entry, rhs:Entry) -> Bool {
    return
        lhs.title == rhs.title && 
        lhs.bodyText == rhs.bodyText
    }
}

EntryListTableViewController.swift

//
//  EntryListTableViewController.swift
//  Journal
//
//  Created by handje on 6/17/17.
//  Copyright © 2017 Rob Hand. All rights reserved.
//

import UIKit

class EntryListTableViewCell: UITableViewCell {
    @IBOutlet weak var dreamTitle: UILabel!
    @IBOutlet weak var dreamDate: UILabel!
}

extension EntryListTableViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        filterContentForSearchText(searchText: searchController.searchBar.text!)
    }
}

class EntryListTableViewController: UITableViewController {
    @IBOutlet weak var searchBar: UISearchBar!

    var dreamTitle: UILabel!
    let searchController = UISearchController(searchResultsController: nil)
    let dreams = EntryController.shared.entries
    var filteredDreams = [Entry]()

    func filterContentForSearchText(searchText: String, scope: String = "All") {
        let filteredDreams = EntryController.shared.entries.filter{ $0.title.contains(searchController.searchBar.text!) }
        tableView.reloadData()

        print(filteredDreams)
    }

    override func viewDidLoad() {
        //cell setup
        super.viewDidLoad()
        let backgroundImage = UIImage(named: "DreamPageLucidity.jpg")
        let imageView = UIImageView(image: backgroundImage)
        imageView.contentMode = .scaleAspectFill
        self.tableView.backgroundView = imageView
        tableView.separatorInset = .zero
        tableView.separatorColor = UIColor.lightGray

        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false
        definesPresentationContext = true
        tableView.tableHeaderView = searchController.searchBar
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.tableView.reloadData()
    }

    // MARK: - Table view data source

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return EntryController.shared.entries.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "entryCell", for: indexPath) as! EntryListTableViewCell
        let entry: Entry
        if searchController.isActive && searchController.searchBar.text != "" {
            entry = filteredDreams[indexPath.row] /////////ERROR HERE///////

        } else {
            entry = EntryController.shared.entries[indexPath.row]
        }

        cell.dreamTitle.text = entry.title
        cell.dreamDate.text = entry.date
        if cell.dreamTitle.text == "" {
            cell.dreamTitle.text = "Untitled Dream"
        }
        return cell
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            let entry = EntryController.shared.entries[indexPath.row]
            EntryController.shared.deleteEntry(entry: entry)
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
    }

    // MARK: - Navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let detailVC = segue.destination as? EntryDetailViewController

        guard let indexPath = tableView.indexPathForSelectedRow else { return }
        let entry = EntryController.shared.entries[indexPath.row]

        detailVC?.entry = entry 
    }
}

EntryContoller.swift

//
//  EntryController.swift
//  Journal
//
//  Created by handje on 6/17/17.
//  Copyright © 2017 Rob Hand. All rights reserved.
//

import Foundation

class EntryController {
    var entries = [Entry]()
    static fileprivate let entriesKey = "entriesKey"
    static let shared = EntryController()

    init() {
        load()
    }

    // MARK: - CRUD 

    func addNewEntryWith(title: String, bodyText: String, date: String) {
        let entry = Entry(title: title, bodyText: bodyText, date: date)
        entries.append(entry)
        save()
    }

    func updateEntry(entry: Entry, title: String, bodyText: String, date: String) {
        entry.title = title
        entry.bodyText = bodyText
        save()
    }

    // Set up search bar  
    func deleteEntry(entry: Entry) {
        guard let index = entries.index(of: entry) else { return }
        entries.remove(at: index)
        save()
    }

    // MARK: - save/load UserDefaults

    private func save() {
        let entryDictionaries = entries.map {$0.dictionaryRepresentation()}
        UserDefaults.standard.set(entryDictionaries, forKey: EntryController.entriesKey)
    }

    private func load() {
        guard let entryDictionaries = UserDefaults.standard.object(forKey: EntryController.entriesKey) as? [[String: Any]] else { return }
        entries = entryDictionaries.flatMap ({ Entry(dictionary: $0) })
    }
}

【问题讨论】:

标签: ios swift uitableview dictionary


【解决方案1】:

我觉得有问题

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return EntryController.shared.entries.count
    }

您应该在这里检查一下,如果搜索控制器处于活动状态,则从filteredDreams 返回计数,否则从EntryController.shared.entries.count 返回计数,(根据您的具体实现进行代码更改) 类似:

if searchController.isActive && searchController.searchBar.text != "" {
            return filterDreams.count
   } else {
            return EntryController.shared.entries.count
 }

【讨论】:

  • 你返回的列表仍然比过滤后的列表大,表格仍然会在这里崩溃
  • 我已经指出了错误和问题的逻辑解决方案,实际的实现留给用户:)
【解决方案2】:

您返回的列表大于委托函数 numberOfRowsInSection 中的过滤列表

在搜索时试试这个:

    func filterContentForSearchText(searchText: String, scope: String = "All") {
            // update the list that is a class property, you were creating a new one

            if searchText.isEmpty {
                filteredDreams = EntryController.shared.entries
            } else {
                filteredDreams = EntryController.shared.entries.filter{ $0.title.contains(searchController.searchBar.text!) }
            }    
            tableView.reloadData()
        }

numberOfRowsInSection

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // use the filtered list to determine count
        return filteredDreams.count
}

为了更安全,您可以返回一个空单元格而不是崩溃:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard indexPath.row < filteredDreams.count else { return UITableViewCell() }
       // your code here 
    }

【讨论】:

  • 谢谢!!!那行得通,但是当 tableview 加载时,它没有填充任何条目。我刚刚在 viewDidLoad 中添加了“filteredDreams = EntryController.shared.entries”,一切都很好!
猜你喜欢
  • 2020-01-14
  • 1970-01-01
  • 2022-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多