【问题标题】:How can I use a searchBar in a TableView with multiple sections?如何在具有多个部分的 TableView 中使用 searchBar?
【发布时间】:2020-01-05 10:48:54
【问题描述】:

这是我的情况:我在导航器中有一个包含 4 个部分的表格视图,每个部分都有一些单元格。我想使用搜索栏来查看我的表格视图的内容,但我无法按照我的意愿这样做,并且我遵循的教程仅描述了 1 个带有 1 个部分的表格视图和一个“ n" 行数,但没有关于具有 n 部分和每部分 m 行的表视图。而我发现的那些还没有答案。所以想请各位大侠帮忙看看如何在我之前提到的条件下实现搜索栏。

正如我之前介绍的,我有一个包含 4 个部分的表格视图,每个部分中有一些单元格,当我单击一个单元格时,它会将我带到另一个视图控制器,该控制器有一个 textView 显示该部分和行的数据。我想实现一个搜索栏,以便用户不必向下滚动整个表格视图来查找她/他需要的内容,但到目前为止,我还没有找到使用带有部分的搜索栏的教程/示例,他们使用带有一个部分和许多单元格的单个表格视图。我已经尝试实现 SearchBarDelegate (使用我找到的教程),但它没有像我预期的那样工作。 所以我来找你,看看你是否可以分享你的一些知识和智慧来帮助我解决这个问题,拜托。

这是我的 tableViewController 的代码(/* */ 之间的代码是没有实现 searchBar 的代码,因此它只会显示带有部分和单元格的表格视图):

import UIKit

class TableViewController: UITableViewController {
// MARK: - Variables and Constants
var selectedIndex = Int()
var selectedSection = Int()
var textKind = [
    ["""
    SECTION 1, ROW 0 \n
    Esto debe venir de section 1 row 0, y para hacerlo realmente largo es necesario meter un montón de cosas irrelevantes para ver si respeta los espacios y los renglones del textView. Gracias de nada.
    """,
     """
    SECTION 1, ROW 1 \n
    Esto debe venir de section 1 row 1. Una vez más es necesario asegurarse de que los márgenes y formatos de texto son respetados. De otro modo habrá que corregirlos. \n
    Aquí hay un salto de línea. A ver qué tal sale.
    """,

    """
    SECTION 1, ROW 2 \n
    Esto debe venir de section 1 row 2. \n \n

    OTRO SUBTÍTULO DENTRO DE LA SECCIÓN. \n
    Se usarán 2 nuevas líneas cuando haya que añadir algún título o subtítulo dentro del textView. Por cuestiones de estética visual.
    """,

    """
    SECTION 1, ROW 3 \n
    Esto debe venir de section 1 row 3
    """],

    ["""
    SECTION 2 ROW 0 \n
    Esto debe venir de section 2 row 0. Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. \n \n \n \n \n \n

        Aquí sigue la onda pedorra.
    """,
     "Esto debe venir de section 2 row 1",
     "Esto debe venir de section 2 row 2",
     "Esto debe venir de section 2 row 3",
     "Esto debe venir de section 2 row 4",
     "Esto debe venir de section 2 row 5",
     "Esto debe venir de section 2 row 6"],

    ["Esto debe venir de section 3 row 0",
     "Esto debe venir de section 3 row 1",
     "Esto debe venir de section 3 row 2"],

    ["Esto debe venir de section 4 row 0",
     "Esto debe venir de section 4 row 1",
     "Esto debe venir de section 4 row 2",
     "Esto debe venir de section 4 row 3",
     "Esto debe venir de section 4 row 4",
     "Esto debe venir de section 4 row 5",
     "Esto debe venir de section 4 row 6",
     "Esto debe venir de section 4 row 7"]
]

var sectionsNames:[String] = ["section 1", "section 2", "section 3" , "section 4"]
var section1Content:[String] = ["uno", "dos", "tres", "cuatro"]
var section2Content:[String] = ["a", "be", "ce", "de", "e", "efe", "ge"]
var section3Content:[String] = ["alpha", "beta", "gamma"]
var section4Content:[String] = ["ichi", "ni", "san", "shi", "go", "roku", "nana", "hachi"]
@IBOutlet weak var searchBarVariable: UISearchBar!
var searching = false
var searchingData = [String]()

// MARK: - viewDidLoad()
override func viewDidLoad() {
    super.viewDidLoad()
    searchBarVariable.delegate = self
}

// MARK: - TableView configuration (sections, cellForRowAt, etc.).
// MARK: - Number of sections
override func numberOfSections(in tableView: UITableView) -> Int {
    return sectionsNames.count
}

// MARK: - Sections' titles
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sectionsNames[section]
}

// MARK: - Number of rows per section
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    if searching {
        return searchingData.count
    } else {
        switch section {
        case 0:
            return section1Content.count

        case 1:
            return section2Content.count

        case 2:
            return section3Content.count

        case 3:
            return section4Content.count

        default:
            return 0
        }
    }

   /* switch section {
    case 0:
    return section1Content.count

    case 1:
        return section2Content.count

    case 2:
        return section3Content.count

    case 3:
        return section4Content.count

    default:
        return 0
    } */
}

// MARK: - Cells' content
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = self.tableView.dequeueReusableCell(withIdentifier: "sourceCell", for: indexPath)

    if searching {
        cell.textLabel?.text = searchingData[indexPath.row]
    } else {
        switch indexPath.section {
        case 0:
            cell.textLabel!.text = section1Content[indexPath.row]

        case 1:
            cell.textLabel!.text = section2Content[indexPath.row]

        case 2:
            cell.textLabel!.text = section3Content[indexPath.row]

        case 3:
            cell.textLabel!.text = section4Content[indexPath.row]
        default:
            break
        }
    }

    /*switch indexPath.section {
    case 0:
        cell.textLabel!.text = section1Content[indexPath.row]

    case 1:
        cell.textLabel!.text = section2Content[indexPath.row]

    case 2:
        cell.textLabel!.text = section3Content[indexPath.row]

    case 3:
        cell.textLabel!.text = section4Content[indexPath.row]
    default:
        break
    } */

    return cell
}

// MARK: - DidSelectRowAt
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    self.selectedSection = indexPath.section
    self.selectedIndex = indexPath.row
    performSegue(withIdentifier: "goToInfo", sender: self)
}

// MARK: - prepare(for segue:...)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToInfo" {
        let vc: ViewController = segue.destination as! ViewController
        vc.viewTextText = textKind[self.selectedSection][self.selectedIndex]
        print(vc.viewTextText)
    }
}
}

// MARK: - Searchbar in tableview
extension TableViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    searchingData = section1Content.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()}) + section2Content.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()}) + section3Content.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()}) + section4Content.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
    searching = true
    tableView.reloadData()
}

func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
    searching = false
    searchBar.text = ""
    tableView.reloadData()
}
}

这是链接到上一个tableViewController的viewController的代码:

import UIKit

class ViewController: UIViewController {

//MARK: - Variables and Constants
var viewTextText:String = ""

// MARK: - ViewController components
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var viewTextViewText: UITextView!
@IBOutlet weak var imageView: UIImageView!

// MARK: - viewDidLoad()
override func viewDidLoad() {
    super.viewDidLoad()
    arrangingTextView()
}

func arrangingTextView() {
    self.viewTextViewText.text = viewTextText
    self.viewTextViewText.font = UIFont(name: "helvetica", size: 24.0)
    self.viewTextViewText.textAlignment = .justified
}
}

预期内容:在搜索栏中输入时,必须仅显示在搜索栏中输入匹配的部分和单元格。当我点击想要的信息时,它应该将我带到包含该部分和单元格信息的 viewController。

实际情况:在搜索栏中输入时,4 个部分及其内容显示相同的内容。当我单击该行时,它会将我带到与该单元格无关的虚假信息。

如果你复制我的代码,你会更好地理解我的意思。

【问题讨论】:

    标签: ios swift uitableview uisearchbar


    【解决方案1】:

    这种方法不合适。而不是使用标志变量作为searching 并处理不同的场景,您需要做的只是为整个过程定义一个数组。在您的情况下,您的数据源数组将如下所示:

    [["uno", "dos", "tres", "cuatro"],["a", "be", "ce", "de", "e", "efe", "ge"],["alpha", "beta", "gamma"],["ichi", "ni", "san", "shi", "go", "roku", "nana", "hachi"]] 
    

    现在,一切都更容易处理和实施。例如,行数委托函数中的 switch case 将被替换为:

    return dataSource[section].count
    

    您在这里要做的是在点击搜索结果时将两个不同的数组相互映射。当你有多个与一件事相关的属性时,我建议你创建一个类:

    struct Item {
    var title : String // this is cell's title
    var text : String // this is what will be shown on the next view controller when a cell is tapped.
    }
    

    您的数据源将如下所示:

    var dataSource : [[Item]] = [[Item(title: "uno" , text: """
    SECTION 1, ROW 0 \n
    Esto debe venir de section 1 row 0, y para hacerlo realmente largo es necesario meter un montón de cosas irrelevantes para ver si respeta los espacios y los renglones del textView. Gracias de nada.
    """) , Item(title: "dos" , text: """
    SECTION 1, ROW 1 \n
    Esto debe venir de section 1 row 1. Una vez más es necesario asegurarse de que los márgenes y formatos de texto son respetados. De otro modo habrá que corregirlos. \n
    Aquí hay un salto de línea. A ver qué tal sale.
    """ ) , Item(title: "tres" , text: """
    SECTION 1, ROW 2 \n
    Esto debe venir de section 1 row 2. \n \n
    
    OTRO SUBTÍTULO DENTRO DE LA SECCIÓN. \n
    Se usarán 2 nuevas líneas cuando haya que añadir algún título o subtítulo dentro del textView. Por cuestiones de estética visual.
    """ ), Item(title: "cuatro" , text: """
    SECTION 1, ROW 3 \n
    Esto debe venir de section 1 row 3
    """ )] , [Item(title: "a" , text: """
    SECTION 2 ROW 0 \n
    Esto debe venir de section 2 row 0. Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. \n \n \n \n \n \n
    
    Aquí sigue la onda pedorra.
    """ ) , Item(title: "be" , text: "Esto debe venir de section 2 row 1" ) , Item(title: "ce" , text: "Esto debe venir de section 2 row 2" ) , Item(title: "de" , text: "Esto debe venir de section 2 row 3" ) , Item(title: "e" , text: "Esto debe venir de section 2 row 4" ) , Item(title: "efe" , text: "Esto debe venir de section 2 row 1" ) , Item(title: "ge" , text: "Esto debe venir de section 2 row 5" )]]
    

    现在,在您的搜索委托函数中,您需要根据文本从数据源数组中删除不需要的结果,或者在点击取消按钮时简单地将数组重置为默认值。完成后,您需要做的就是重新加载表格视图:

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        self.dataSource = values
        var filteredDataSource : [[Item]] = []
        for items in dataSource {
            let filteredItems = items.filter { (item) -> Bool in
                if item.title.lowercased().prefix(searchText.count) == searchText.lowercased() {
                    return true
                }
                return false
            }
            filteredDataSource.append(filteredItems)
        }
        self.dataSource = filteredDataSource
        tableView.reloadData()
    
    }
    
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        self.dataSource = values //reseting data source to defaults
        tableView.reloadData()
    }
    

    当您选择一行时,您在代码中错误地传递了节和行号。为了将相应的数据适当地传递给下一个视图控制器,您需要在准备 segue 函数中执行此操作:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //assuming your next view controller class's name is ViewController.
        guard let detailViewController = segue.destination as? ViewController,
            let row = tableView.indexPathForSelectedRow?.row,
            let section = tableView.indexPathForSelectedRow?.section
            else {
                return
        }
        detailViewController.item = dataSource[section][row] //passing selected item object.
    }
    

    最后,在您的下一个视图控制器中,您拥有同时具有标题和文本属性的项目对象。在您的情况下,您希望显示其文本属性。 希望对您有所帮助!

    编辑

    github 上的完整解决方案。

    【讨论】:

    • 您好 Soroush,感谢您的回复。我能够复制到return dataSource[section].count,但后来我迷失了你的解释。因此,我能够使用您的建议重现我所做的事情(包含内容的部分并显示与单击的单元格相对应的信息)。您能否(如果您同意)分享搜索委托函数的代码以及为我的项目定义 id 以使它们可追踪到下一个视图控制器的部分?感谢您的帮助。
    • 我用完整的实现编辑了我的答案。 @J_A_F_Casillas
    • 谢谢 Soroush,我已经检查过了,不幸的是你的程序不正确。如果您将我的代码与您的方法结合起来并对其进行测试,您就会明白我的意思。我必须在 textKind 数组中显示信息,并且您的代码显示单元格的内容,这不是目标:) 谢谢。
    • 请注意,至少在大多数情况下,该社区不会为您提供可复制粘贴的代码。目标是给出一个概念或要点,说明什么可以帮助您解决问题。既然我在这个上已经走了这么远,为什么不尝试另一个呢?这次我包括了你想要的目标。 @J_A_F_Casillas
    • 您好 Soroush,感谢您抽出宝贵时间帮助我解决这个问题。我知道这里的动态是关于授鱼而不是授鱼,但我要求澄清两件事:我是 Swift 菜鸟,我已经尝试过其他不适合我的情况的方法。我真的很感谢你的观察。现在还有一个问题:当我实现您的结构案例时,我的 cellForRowAt 方法不再起作用,我该如何解决?我尝试使用ìndexpath.section/row,但它不起作用。再次感谢您的支持和耐心。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 2011-08-17
    • 2018-04-16
    相关资源
    最近更新 更多