2017-08-09 52 views
0

我有一个日志应用程序,其名称为Entry。它有自己的Swift文件,名为Entry.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) }) 
    } 
} 
+3

欢迎#1,请阅读[如何创建一个最小化,完整和可验证的示例](https://stackoverflow.com/help/mcve)。 –

回答

0

你返回一个列表,它比委托函数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 
    } 
+0

谢谢! !这工作,但是当tableview加载它没有填充任何条目。我只是将'filteredDreams = EntryController.shared.entries'添加到viewDidLoad中,所有内容都是膨胀的! –

2

我觉得有一个与

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 
} 
+0

你仍然返回一个比过滤列表更大的列表,表格仍然会在这里崩溃 – Hakim

+0

我已经指出了错误和逻辑解决方案的问题,实际执行情况留给用户:) –