2017-08-09 113 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

我已經指出了錯誤和邏輯解決方案的問題,實際執行情況留給用戶:) –