【问题标题】:UISearchBar is not responding in UITableView with JSON dataUISearchBar 在 UITableView 中没有响应 JSON 数据
【发布时间】:2021-03-16 18:17:57
【问题描述】:

根据标题,UISearchBar 在 UITableView 中没有响应 JSON 数据。我无法让搜索字段工作,您能帮帮我吗?

TableView 工作正常,数据显示它,但是当我在搜索字段中输入单词时没有任何反应。

也许问题可能出在这个扩展中? 扩展 ViewController: UISearchBarDelegate

import UIKit
  

          struct GalleryData: Decodable {
            
            let localized_name: String
            let primary_attr: String
            let attack_type: String
            let img: String
            
        }
    
    
    
    class ViewController: UIViewController {
        
        
        @IBOutlet weak var tableView: UITableView!
        
        
        var dataArray = [GalleryData]()
        var filteredArray = [String]()
        var shouldShowSearchResults = false
        @IBOutlet weak var searchBar: UISearchBar!
        
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            downloadJSON {
                self.tableView.reloadData()
            }
            
            tableView.delegate = self
            tableView.dataSource = self
            
            searchBar.delegate = self
            searchBar.placeholder = "Search here..."
            
        }
        
        
    }
    
    
    
    
    extension ViewController: UITableViewDelegate, UITableViewDataSource {
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            
            if shouldShowSearchResults {
                return filteredArray.count
            } else {
                return dataArray.count
            }
            
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
            
            if shouldShowSearchResults {
                cell.textLabel?.text = filteredArray[indexPath.row]
            }
            else {
                cell.textLabel?.text = dataArray[indexPath.row].localized_name.capitalized
            }
            
            
            return cell
            
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            performSegue(withIdentifier: "showDetails", sender: self)
        }
        
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let destination = segue.destination as? DetailViewController {
                destination.galleryDataDetail = dataArray[(tableView.indexPathForSelectedRow?.row)!]
            }
        }
        
        func downloadJSON(completed: @escaping () -> ()) {
            
            let url = URL(string: "https://api.opendota.com/api/heroStats")
            
            URLSession.shared.dataTask(with: url!) { (data, response, error) in
                
                do {
                    self.dataArray = try JSONDecoder().decode([GalleryData].self, from: data!)
                    DispatchQueue.main.async {
                        completed()
                    }
                }
                catch {
                    print("JSON error")
                }
                
            }.resume()
            
        }
        
    }
    
    
    
    
    extension ViewController: UISearchBarDelegate {
        
        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            
            let searchString = searchBar.text
            
            filteredArray = dataArray.filter({ (country) -> Bool in
                                let countryText: NSString = country as NSString
                                return (countryText.range(of: searchString!, options: .caseInsensitive).location) != NSNotFound
                            })
            tableView.reloadData()
                }
            
        
        
        func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
            shouldShowSearchResults = true
            tableView.reloadData()
        }
        
        func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
            searchBar.text = ""
            shouldShowSearchResults = false
            tableView.reloadData()
        }
        
    
    }

【问题讨论】:

  • filteredArray = dataArray.filter(...) 甚至被调用了吗?如果是,当tableView(_:, cellForRowA:) 被调用时,发生了什么? shouldShowSearchResults 值是否正确? filteredArray 值是否正确(我的意思是只有所需的值)?

标签: ios json swift xcode searchbar


【解决方案1】:

尝试像这样更改您的代码

import UIKit
  
    struct GalleryData: Decodable 
    {    
        let localized_name: String
        let primary_attr: String
        let attack_type: String
        let img: String        
    }
    
    class ViewController: UIViewController 
    {    
        @IBOutlet weak var tableView: UITableView!
        @IBOutlet weak var searchBar: UISearchBar!
        
        var dataArray = [GalleryData]() 
        var filteredArray = [GalleryData]() {
            didSet {
                tableView.reloadData()
            }
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            tableView.delegate = self
            tableView.dataSource = self
            
            searchBar.delegate = self
            searchBar.placeholder = "Search here..."
            
            downloadJSON()
        } 
    }
    
    extension ViewController: UITableViewDelegate, UITableViewDataSource {
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return filteredArray.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
            
            cell.textLabel?.text = filteredArray[indexPath.row].localized_name.capitalized
            
            return cell            
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            performSegue(withIdentifier: "showDetails", sender: self)
        }
        
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let destination = segue.destination as? DetailViewController {
                destination.galleryDataDetail = filteredArray[(tableView.indexPathForSelectedRow?.row)!]
            }
        }
        
        func downloadJSON() {
            let url = URL(string: "https://api.opendota.com/api/heroStats")
            
            URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
                do {
                    self.dataArray = try JSONDecoder().decode([GalleryData].self, from: data!)
                    self.filteredArray = self.dataArray
                }
                catch {
                    print("JSON error")
                }
            }.resume()
        }
    }
    
    extension ViewController: UISearchBarDelegate 
    {    
        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            guard let searchText = searchBar.text else { return } 

            if (searchText == "") {
                    filteredArray = dataArray 
                    return
            }
                
            filteredArray = dataArray.filter { $0.localized_name.uppercased.contains(searchText.uppercased) }
        }

        func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
            searchBar.text = ""
        }
    }

【讨论】:

  • 嗨卢卡,感谢您的帮助。你的代码很好用,谢谢! :-) 我只需要添加 DispatchQueue.main.async 否则它会给我模拟器错误。只是好奇,[无主的自己]是什么。
  • 它保持对它引用的实例的弱引用,以避免实例之间的强引用循环并优化内存分配。使用unowned 假定引用将始终引用分配的实例。如果您认为引用可能为 nil 或已解除分配,则可以使用 [weak self]。有关 Swift 内存管理的更多信息,请参阅docs.swift.org/swift-book/LanguageGuide/…
【解决方案2】:

首先将过滤后的数组声明为与数据源数组相同的类型会更高效

var dataArray = [GalleryData]()
var filteredArray = [GalleryData]()

textDidChange 中检查搜索字符串是否为空并相应地设置shouldShowSearchResults

并且根本不需要投射到NSString 的桥

extension ViewController: UISearchBarDelegate {
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        
        if searchText.isEmpty {
            shouldShowSearchResults = false
            filteredArray = []
        } else {
            filteredArray = dataArray.filter{ $0.localized_name.range(of: searchText, options: .caseInsensitive) != nil }
            shouldShowSearchResults = true
        }
        tableView.reloadData()
    }
    
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        searchBar.text = ""
        shouldShowSearchResults = false
        tableView.reloadData()
    }
}

searchBarTextDidBeginEditing 也不需要。

cellForRowAt 中为单元格添加标识符并重复使用单元格

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
      let cell = tableView.dequeueReusableCell(withIdentifier: "GalleryCell", for: indexPath)
        
      let item = shouldShowSearchResults ? filteredArray[indexPath.row] : dataArray[indexPath.row]         
      cell.textLabel?.text = item.localized_name.capitalized            
      return cell  
}

请注意,如果显示搜索结果,segue 将不起作用(甚至可能崩溃)

更复杂的解决方案是UITableViewDiffableDataSource (iOS 13+)

【讨论】:

  • 您好 Vadian,感谢您的帮助。您的代码给了我一条错误消息:“GalleryData”类型的值没有成员“countryText”
  • 我从您的代码中复制了属性名称,这显然是错误的。请查看编辑。
猜你喜欢
  • 1970-01-01
  • 2014-05-26
  • 2012-06-12
  • 1970-01-01
  • 2019-02-13
  • 1970-01-01
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多