【问题标题】:Filtering Array and displaying in Table View?过滤数组并在表格视图中显示?
【发布时间】:2016-11-14 12:02:11
【问题描述】:

我想在练习字典中搜索名称键,然后在表格视图中显示过滤后的结果。我正在使用这个功能

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    let filtered = exercises.filter { $0["name"] == searchText }
    print(filtered)

    if(filtered.count == 0){
        searchActive = false;
    } else {
        searchActive = true;
    }
    self.exercisesTableView.reloadData()
}

变量:

var exercises = [Exercise]()
var filtered: [NSMutableArray] = []
var searchActive: Bool = false

在搜索功能中出现错误

Type'Exercise' 没有下标成员

然后我遇到的问题是结果是 NSMutableArray,因此我无法将结果名称设置为要显示的单元格文本

无法强制将“NSMutableArray”类型的值转换为“String”类型

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    if (searchActive){
        cell.textLabel?.text = filtered[indexPath.row] as String
    } else {
        let exercise = exercises[indexPath.row]

        cell.textLabel!.text = exercise.name
    }
    return cell
}

这是我的练习词典供参考:

final public class Exercise {
    var id: Int
    var descrip: String
    var name: String
    var muscles: [Int]
    var equipment: [Int]

    public init?(dictionary: [String: Any]) {

        guard
            let id = dictionary["id"] as? Int,
            let descrip = dictionary["description"] as? String,
            let name = dictionary["name"] as? String,
            let muscles = dictionary["muscles"] as? [Int],
            let equipment = dictionary["equipment"] as? [Int]

            else { return nil }

        self.id = id
        self.descrip = descrip
        self.name = name
        self.muscles = muscles
        self.equipment = equipment

    }

我可以通过设置 var filters: [String] = [] 来修复第二个错误,这样它就可以用作单元格标题,但这并不能解决第一个错误,而且我不确定这样做是否正确?

【问题讨论】:

    标签: swift uitableview uisearchbar


    【解决方案1】:

    您的filtered 也是[Exercise] 的类型,您需要对其进行过滤。

    var filtered = [Exercise]() 
    
    self.filtered = exercises.filter { $0.name == searchText }
    

    这里$0Exercise 对象的类型,因此您需要使用$0.name 访问其属性名称。

    编辑:如果您希望filtered 是只有名称的[String] 类型,那么您可以像这样同时使用mapfilter

    self.filtered = exercises.filter { $0.name == searchText }.map { $0.name }
    

    self.filtered = exercises.map { $0.name }.filter { $0 == searchText }
    

    或者按照@dfri 的建议直接使用filterMap

    self.filtered = exercises.flatMap{ $0.name == searchText ? $0.name : nil }
    

    【讨论】:

    • 谢谢你,但是如果我将过滤设置为也 = [Exercise]() 当它不再是字符串时,我如何将它映射到单元格名称?看到我的代码部分 cell.textLabel?.text = filters[indexPath.row] as String
    • 我试过了,但它不喜欢它,我需要编辑变量还是只插入规定的代码?错误是无法将“String”类型的值转换为闭包结果类型“Exercise”,并且在表格单元格名称语句中:无法将“Exercise”类型的值转换为强制类型“String”
    • @NiravD 请注意,使用链式 filtermap 操作的常见替代方法是单个 flatMap 操作:self.filtered = exercises.flatMap{ $0.name == searchText ? $0.name : nil }
    • @infernouk 是的,现在您需要将filtered 更改为[String] 已正确写入Edit 行检查。
    • 感谢我删除了控制器并制作了插座!
    【解决方案2】:

    我昨天告诉过你忘记字典语义时使用自定义类。
    不要使用键下标!

    正如其他答案中提到的,您必须将filtered 也声明为[Exercise]

    var filtered = [Exercise]() 
    

    和过滤

    self.filtered = exercises.filter { $0.name == searchText }
    

    但是,如果您还想搜索部分字符串,请使用

    self.filtered = exercises.filter {$0.name.range(of: searchText, options: [.anchored, .caseInsensitive, .diacriticInsensitive]) != nil }
    

    但是

    由于过滤和未过滤的数组都是 [Exercise] 类型,因此您必须以这种方式更改 cellForRowAtIndexPath

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let exercise : Exercise 
        if (searchActive){
            exercise = filtered[indexPath.row]
        } else {
            exercise = exercises[indexPath.row]
        }
        cell.textLabel!.text = exercise.name
        return cell
    }
    

    【讨论】:

    • 谢谢我试过了,因为保持过滤器类型相同似乎更干净,没有错误只是我在 Nirav 的响应 cmets 中提到的在搜索栏中输入字母时仍然遇到这种奇怪的崩溃
    • 你在使用UISearchController吗?如果是,请使用 searchResultController.isActive 检查搜索状态,而不是自定义布尔变量。
    • 所以我需要将 searchResultController 声明为变量?只是将其换成 searchResultController.isActive 会导致未解决的标识符错误。我现在让 searchController = UISearchController(searchResultsController: nil)
    • 是的,当然var searchResultController: UISearchController! 但是你也应该使用updateSearchResults(for 而不是textDidChange
    • 我已经更新到 searchResultController.isActive 但是现在这会导致致命的崩溃,只是在输入之前单击搜索字段。要更改为 updateSearchResults,它如何适合 textDidChange 语句? func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
    【解决方案3】:

    这里的问题是$0["name"] == searchText$0 在这种情况下属于 Excersise 类型,您将其称为数组。此行应如下所示:

    let filtered = exercises.filter { $0.name == searchText }

    【讨论】:

      【解决方案4】:

      就我而言,我在一些个人项目中使用了以下代码,代码与之前的答案非常相似。我只使用contains

      func filter(query: String){
              if query.isEmpty {
                  self.filteredData = self.originalData
              } else {
                  self.filteredData = self.originalData.filter {
                      $0.prop1!.lowercased().contains(query)
                  }
              }
          }
      

      【讨论】:

        【解决方案5】:

        清洁有效的方法

        在你的类中声明这些数组:

        var filteredData: [Exercise] = []  
        {
            didSet
            { 
                tableView.reloadData()
            }
        }
        
        // once we get our data, we will put it into filtered array
        // user will interact only with filtered array
        var source: [Exercise] = [] 
        {
            didSet
            { 
                filteredData = source
            }
        }
        

        实现你的功能如下:

        extension ViewController: UISearchBarDelegate
        {
            func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
            {        
                let searchText = searchText.lowercased()
                let filtered = source.filter({ $0.lowercased().contains(searchText) })
                filteredData = filtered.isEmpty ? source : filtered
            }
        }
        

        您会注意到,用户永远不会与原始数组交互,而是与过滤后的数组交互。

        // MARK: UITableViewDataSource
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
        {
            return filteredData.count
        }
        
        // MARK: UITableViewDelegate
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
        {
            .
            .
        
            cell.textLabel?.text = filteredData[indexPath.row]
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多