我认为这是我最后要发布1.0版本之前的最后一个问题。这是我最后剩下的“无法启动”问题:按状态筛选时,结果行未打开相应的详细信息视图。

例如,如果我搜索TX,我会看到七个德克萨斯州的奖金。如果我点击第三项,我将进入AK3(如果未过滤,则为列表中的第三项)。奇怪的是,我的滑动操作确实知道我在TX3上。只有在点击进入细节时,它才会跳到错误的奖金上。

这是完整的UITableViewController页面:

import UIKit
import os.log
import Foundation

class BonusListViewController: UITableViewController {

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

    var riderNumToH:String = UserDefaults.standard.string(forKey: Constants.RiderData().riderNumToH) ?? "000"
    var pillionNumToH:String = UserDefaults.standard.string(forKey: Constants.RiderData().pillionNumToH) ?? "000"
    var emailDestinationToH:String = UserDefaults.standard.string(forKey: Constants.RallyData().emailDestinationToH) ?? "[email protected]"

    struct Constants {
        struct RiderData {
            let riderNumToH = "riderNumToH"
            let pillionNumToH = "pillionNumToH"
        }
        struct RallyData {
            let emailDestinationToH = "emailDestinationToH"
        }
    }

    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"
        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
        print("About to call loadBonuses")
        loadBonuses { [weak self] bonuses in
            self?.bonuses = bonuses ?? []
            DispatchQueue.main.async {
                self?.tableView.reloadData()
            }
            print("loadBonuses called")
        }

        // MARK: Set Rider Defaults to Initial Values.
        let defaults = UserDefaults.standard
        print("Setting initial defaults")

        if riderNumToH == "" {
            print("riderNumToH is blank")
            defaults.set("000", forKey: Constants.RiderData().riderNumToH)
        } else if riderNumToH == "000" {
            print("riderNumToH is 000")
        } else {
            print("riderNumToH is custom")
        }

        if pillionNumToH == "" {
            print("pillionNumToH is blank")
            defaults.set("000", forKey: Constants.RiderData().pillionNumToH)
        } else if pillionNumToH == "000" {
            print("pillionNumToH is 000")
        } else {
            print("pillionNumToH is custom")
        }

        if emailDestinationToH == "" {
            print("emailDestinationToH is blank")
            defaults.set("[email protected]", forKey: Constants.RallyData().emailDestinationToH)
        } else if emailDestinationToH == "[email protected]" {
            print("emailDestinationToH is set to default")
        } else {
            print("emailDestinationToH has been customized")
        }


    }
    // 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
    }

    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")
            // Delete the created images
            let bonus: JsonFile.JsonBonuses
            if self.isFiltering() {
                bonus = self.filteredBonuses[indexPath.row]
            } else {
                bonus = self.bonuses[indexPath.row]
            }
            print("Selected Bonus is \(bonus.bonusCode)")
            let fileNameToDeletePri = "\(bonus.bonusCode)_1.jpg"
            let fileNameToDeleteOpt = "\(bonus.bonusCode)_2.jpg"
            var filePathPri = ""
            var filePathOpt = ""
            // Find documents directory on device
            let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
            if dirs.count > 0 {
                let dir = dirs[0] //documents directory
                filePathPri = dir.appendingFormat("/" + fileNameToDeletePri)
                filePathOpt = dir.appendingFormat("/" + fileNameToDeleteOpt)
                print("Local path = \(filePathPri)")
                print("Local path = \(filePathOpt)")
            } else {
                print("Could not find local directory to store file")
                return
            }
            do {
                let fileManager = FileManager.default
                // Check if primary file exists
                if fileManager.fileExists(atPath: filePathPri) {
                    // Delete file
                    try fileManager.removeItem(atPath: filePathPri)
                } else {
                    print("Primary image does not exist")
                }
                // Check if optional file exists
                if fileManager.fileExists(atPath: filePathOpt) {
                    // Delete file
                    try fileManager.removeItem(atPath: filePathOpt)
                } else {
                    print("Optional image does not exist")
                }
            }
            catch let error as NSError {
                print("An error took place: \(error)")
            }
            tableView.reloadData()
            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: JsonFile.JsonBonuses
        if self.isFiltering() {
            bonus = self.filteredBonuses[indexPath.row]
        } else {
            bonus = self.bonuses[indexPath.row]
        }
        // Set Primary Image
        let documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
        let imgUrl =  documentsUrl.appendingPathComponent(bonus.bonusCode + "_1.jpg")
        if(FileManager.default.fileExists(atPath:imgUrl.path))
        {
            do
            {
                let data = try Data.init(contentsOf:imgUrl)
                cell.primaryImage.image = UIImage.init(data:data)
            }
            catch {
                print(error)
            }
        }
        else
        {
            cell.primaryImage.image = #imageLiteral(resourceName: "DefaultImage")
        }

        cell.nameLabel.text = bonus.name
        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)
                    self.defaults.set(posts.meta.version, forKey: "jsonVersion")
                    print("URLSession did not fail")
                    print("JSON Version Set to \(posts.meta.version)")
                } catch {
                    print("Can't decode JSON: \(error)")
                }
            } 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("BonusData.json")
        downloadJSON { bonuses in
            if let bonuses = bonuses {
                completion(bonuses)
                self.saveBonuses(bonuses, to: localBonusesURL)
            } else {
                print("versions did not match")
                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.localizedCaseInsensitiveContains(searchText)
        })
        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!)
    }
}


在cellForRowAt和swipeAction方法中,应该过滤视图的部分代码是相同的:

let bonus: JsonFile.JsonBonuses
            if self.isFiltering() {
                bonus = self.filteredBonuses[indexPath.row]
            } else {
                bonus = self.bonuses[indexPath.row]
            }


最初,cellForRowAt没有self条目,但是我添加了这些条目以查看它是否可以解决它,而没有。

最佳答案

我找到了问题的原因。在UITableViewController的底部附近,我有以下内容:

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


这就是问题所在。我通过添加在cellForRowAt和SwipeAction中使用的相同的if / else逻辑来修复它:

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

        }
    }

10-08 14:00