UISearchBar似乎返回正确的计数,但没有正确的行

时间:2018-07-06 06:44:58

标签: ios swift xcode

我正在尝试在我的应用中实现搜索功能。现在,我只是想按State值搜索我的JSON,尽管我也希望最终也包括Category。总共有9行,前7行是State = AZ,后2行是State = CA。当我搜索“ KK”时,表为空,这很有意义。但是,当我搜索“ CA”时,会得到两行,就像我期望的那样,但它们是JSON中的前两行,它们都是AZ,而不是应该的两行CA。

我怀疑我的问题在我的filterContentForSearchText函数中的某个位置,但是由于我不确定您到底需要哪个代码,因此这里是ViewController(我认为是问题的函数快要结束了):

import UIKit
import os.log
import Foundation

class BonusListViewController: UITableViewController {

    var bonuses = [JsonFile.JsonBonuses]()
    var filteredBonuses = [JsonFile.JsonBonuses]()
    var detailViewController: BonusDetailViewController? = nil

    let defaults = UserDefaults.standard

    let searchController = UISearchController(searchResultsController: nil)

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: Search Support
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchBar.placeholder = "Enter two letter state to filter"
        navigationItem.searchController = searchController
        definesPresentationContext = true

        // MARK: Settings Data Struct
        struct Constants {
            struct RiderData {
                let riderNumToH = "riderNumToH"
                let pillionNumToH = "pillionNumToH"
            }
            struct RallyData {
                let emailDestinationToH = "emailDestinationToH"
            }
        }
        //MARK: Load the bonuses
        loadBonuses { [weak self] bonuses in
            self?.bonuses = bonuses ?? []
            DispatchQueue.main.async {
                self?.tableView.reloadData()
            }
            print("loadBonuses called")
        }
    }
    // MARK: - Table View Configuration
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if isFiltering() {
            print("Showing \(filteredBonuses.count) Filtered Results")
            return filteredBonuses.count
        }

        print("Found \(bonuses.count) rows in section.")
        return bonuses.count
    }

    /* Disabling the swipe function until I code it to actually do something
    override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
    {
        let clearAction = UIContextualAction(style: .normal, title: "Clear Data") { (contextAction: UIContextualAction, sourceView: UIView, completionHandler: (Bool) -> Void) in
            print("Clear Action Tapped")
            completionHandler(true)
        }

        clearAction.backgroundColor = .blue
        let swipeConfig = UISwipeActionsConfiguration(actions: [clearAction])
        return swipeConfig
    }
    */

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "BonusListViewCell"
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusListViewCell else {
            fatalError("The dequeued cell is not an instance of BonusListViewCell.")
        }
        let bonus = bonuses[indexPath.row]
        let bonusSet: JsonFile.JsonBonuses
        if isFiltering() {
            bonusSet = filteredBonuses[indexPath.row]
        } else {
            bonusSet = bonus
        }

        let urlString = "http://tourofhonor.com/appimages/"+(bonus.imageName)
        let url = URL(string: urlString)
        cell.primaryImage.downloadedFrom(url: url!)
        cell.nameLabel.text = bonus.name.capitalized
        cell.bonusCodeLabel.text = bonus.bonusCode.localizedUppercase
        cell.categoryLabel.text = bonus.category
        cell.valueLabel.text = "\(bonus.value)"
        cell.cityLabel.text = "\(bonus.city.capitalized),"
        cell.stateLabel.text = bonus.state.localizedUppercase

        return cell
    }

    // MARK: Functions
    // MARK: - Fetch JSON from ToH webserver

    func downloadJSON(completed: @escaping ([JsonFile.JsonBonuses]?) -> ()) {
        let url = URL(string: "http://tourofhonor.com/BonusData.json")!
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error == nil, let data = data {
                do {
                    let posts = try JSONDecoder().decode(JsonFile.self, from: data)
                    completed(posts.bonuses)
                } catch {
                    print("JSON Download Failed")
                }
            } else {
                print("downloadJSON completed")
                completed(nil)
            }
        }.resume()
    }

    func saveBonuses(_ bonuses: [JsonFile.JsonBonuses], to url: URL) {
        try? FileManager.default.removeItem(at: url)
        do {
            let data = try JSONEncoder().encode(bonuses)
            try data.write(to: url)
            print("saveBonuses successful")
        } catch {
            print("Error saving bonuses to file:", error)
        }
    }

    func loadBonusesFromFile(_ url: URL) -> [JsonFile.JsonBonuses]? {
        do {
            let data = try Data(contentsOf: url)
            let bonuses = try JSONDecoder().decode([JsonFile.JsonBonuses].self, from: data)
            print("loadBonusesFromFile successful")
            return bonuses
        } catch {
            print("Error loading bonuses from file:", error)
            return nil
        }
    }

    func loadBonuses(completion: @escaping ([JsonFile.JsonBonuses]?) -> Void) {
        let localBonusesURL = try! FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("Bonuses.json")
        downloadJSON { bonuses in
            if let bonuses = bonuses {
                completion(bonuses)
                self.saveBonuses(bonuses, to: localBonusesURL)
            } else {
                completion(self.loadBonusesFromFile(localBonusesURL))
            }
        }
    }

    func searchBarIsEmpty() -> Bool {
        // Returns true if the text is empty or nil
        return searchController.searchBar.text?.isEmpty ?? true
    }

    func filterContentForSearchText(_ searchText: String, scope: String = "All") {
        filteredBonuses = bonuses.filter({( bonus: JsonFile.JsonBonuses) -> Bool in
            return bonus.state.lowercased().contains(searchText.lowercased())
        })
        tableView.reloadData()
    }

    func isFiltering() -> Bool {
        return searchController.isActive && !searchBarIsEmpty()
    }

    // MARK: - Navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? BonusDetailViewController {
            destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
        }
    }
}

extension BonusListViewController: UISearchResultsUpdating {
    // MARK: - UISearchResultsUpdating Delegate
    func updateSearchResults(for searchController: UISearchController) {
        filterContentForSearchText(searchController.searchBar.text!)
    }
}

这是JsonFile.swift:

import Foundation

struct JsonFile: Codable {
    struct Meta: Codable {
        let fileName: String
        let version: String
    }
    struct JsonBonuses: Codable {
        let bonusCode: String
        let category: String
        let name: String
        let value: Int
        let city: String
        let state: String
        let flavor: String
        let imageName: String
    }
    let meta: Meta
    let bonuses: [JsonBonuses]
}

编辑:JSON本身可以在http://www.tourofhonor.com/BonusData.json

中找到

此外,在let bonusSet: JsonFile.JsonBonuses行(在cellForRowAt下)上,我收到一条警告,指出“即使使用Immutable值,bonusSet也从未使用过;请考虑删除它”在下一行。

3 个答案:

答案 0 :(得分:3)

我猜问题出在您的cellForRow方法中,您应该使用bonusSet而不是bonus分配v alues。当您从花红dara结构初始化值时,该值应该来自花红Set。

尝试将cellForRow更改为:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier = "BonusListViewCell"
    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusListViewCell else {
        fatalError("The dequeued cell is not an instance of BonusListViewCell.")
    }
    let bonus = bonuses[indexPath.row]
    let bonusSet: JsonFile.JsonBonuses
    if isFiltering() {
        bonusSet = filteredBonuses[indexPath.row]
    } else {
        bonusSet = bonus
    }

    //CHANGE IS REQUIRED HERE: REPLACE THE bonus WITH bonusSet :
    let urlString = "http://tourofhonor.com/appimages/"+(bonusSet.imageName)
    let url = URL(string: urlString)
    cell.primaryImage.downloadedFrom(url: url!)
    cell.nameLabel.text = bonusSet.name.capitalized
    cell.bonusCodeLabel.text = bonusSet.bonusCode.localizedUppercase
    cell.categoryLabel.text = bonusSet.category
    cell.valueLabel.text = "\(bonusSet.value)"
    cell.cityLabel.text = "\(bonusSet.city.capitalized),"
    cell.stateLabel.text = bonusSet.state.localizedUppercase

    return cell
}

答案 1 :(得分:2)

问题出在行索引路径的单元格

从filterContentForSearchText获得的

搜索结果,存储在filteredBonuses中,但是存储在cellForRowAt中,您仍在设置来自 bouns变量

  

奖金=红利[indexPath.row]

if isFiltering() {
        bonusSet = filteredBonuses[indexPath.row] //even though you are creating bonusSet you are not using it while setting cell values below so use that bonusSet
    } else {
        bonusSet = bonus
    }


//Like this
let urlString = "http://tourofhonor.com/appimages/"+(bonusSet.imageName)
let url = URL(string: urlString)
cell.primaryImage.downloadedFrom(url: url!)
cell.nameLabel.text = bonus.name.capitalized
cell.bonusCodeLabel.text = bonusSet.bonusCode.localizedUppercase
cell.categoryLabel.text = bonusSet.category
cell.valueLabel.text = "\(bonusSet.value)"
cell.cityLabel.text = "\(bonusSet.city.capitalized),"
cell.stateLabel.text = bonusSet.state.localizedUppercase

答案 2 :(得分:1)

此代码无用:

user2

您将创建一个局部变量ModelA,其值取决于您是否要过滤;但是,正如编译器正确观察到的那样,以后您什么也不会使用它。因此,无论是否进行过滤,您的代码的行为方式都完全相同。