使用 firebase 连接到 Tableview 的搜索栏

Posted

技术标签:

【中文标题】使用 firebase 连接到 Tableview 的搜索栏【英文标题】:Search bar connected to a Tableview with firebase 【发布时间】:2019-05-22 12:00:57 【问题描述】:

我有一个连接到 Tableview 的搜索栏,但我不知道如何使它工作! 表格视图是“记忆”的表格视图。记忆有标题、用户 ID、正文,我希望它也能够按标题搜索。 问题是我正在使用 firebase,但我真的不知道如何在特定用户的特定记忆中搜索以及如何从 Firebase 中获取所有内容。

我尝试了这么多代码行,但没有任何效果。我需要帮助!

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase

class MemoryViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, UISearchBarDelegate, UISearchResultsUpdating 

    func updateSearchResults(for searchController: UISearchController) 
        self.memories = Array.filter( (array: String) -> Bool in

            if array.contains(searchController.searchBar.text!)
            
                return true
            
            else
            
                return false
            

        )

        self.resultController.tableView.reloadData()

    
    var filteredArray = [Memory]()

    var searchController = UISearchController()
    var resultController = UITableViewController()

    @IBOutlet weak var tbl: UITableView!

    @IBOutlet weak var searchBar: UISearchBar!

    var memories : [Memory] = []

    var ref : DatabaseReference!
    let sref = Storage.storage().reference()

    var lastIndex : Int = 0
    var strMode : String = ""

    var filteredData = [String]()



    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 

        return memories.count
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let identifier = "iden"

        var cell : UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)

        if cell == nil
        
            cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: identifier)
        

        let temp = memories[indexPath.row]
        cell?.textLabel?.text = temp.title
        cell?.imageView?.image = temp.image
        return cell!
    

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool 

        return true

    


    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) 

        if editingStyle == .delete
        
            let temp = self.memories[indexPath.row]

            self.memories.remove(at: indexPath.row)

            self.ref.child("MEmories/\(temp.key)").removeValue()

            tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
        
    


    override func viewDidLoad() 
        super.viewDidLoad()
        ref = Database.database().reference()
        let rightButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(MemoryViewController.barButtonItemClicked(_:)))

        self.navigationItem.setRightBarButton(rightButton, animated: true)
        // Do any additional setup after loading the view.

        self.loadMemories()

        self.searchController = UISearchController(searchResultsController: resultController)
        tbl.tableHeaderView = self.searchController.searchBar

        self.searchController.searchResultsUpdater = self

        self.resultController.tableView.delegate = self
        self.resultController.tableView.dataSource = self



    

    @objc func barButtonItemClicked(_ sender:UIBarButtonItem)
    
        print("+ clicked")
        let addMemoryViewController = self.storyboard?.instantiateViewController(withIdentifier: "AddMemoryViewController") as! AddMemoryViewController

        self.strMode = "newMemory"


        self.navigationController?.pushViewController(addMemoryViewController, animated: true)



    


    func readFromNSUSerDefault()-> Memory

    

        let d : UserDefaults = UserDefaults.standard

        let strTitle = d.object(forKey: "title") as? String

        let strBody = d.object(forKey: "body") as? String

        let strImageRef = d.object(forKey: "imageRef") as? String

        let uid = d.object(forKey: "uid") as? String

        let imageData = d.object(forKey: "imageData") as? Data

        let key = d.object(forKey: "key") as? String



        let m = Memory(title: strTitle!, body: strBody!, key: key!, uid: uid!, imageRef: strImageRef!)

        m.image = UIImage(data: imageData!)

        m.key = key!

        return m

    

    override func viewDidAppear(_ animated: Bool) 

        let d = UserDefaults.standard
        let newMemory = readFromNSUSerDefault()
        let userAdded = d.bool(forKey: "userAdded") //key new user = true
        let userEdited = d.bool(forKey: "userEdited")//key  user edited = true

        if self.strMode == "newMemory" &&  userAdded

        
            self.memories.append(newMemory)
            self.tbl.reloadData()
        

        else if self.strMode == "edit" && userEdited
        
            memories[lastIndex] = newMemory
            self.tbl.reloadData()
        


        d.set(false, forKey: "userAdded")
        d.set(false, forKey: "userEdited")

        d.synchronize()

        self.strMode = " "


    

    func loadMemories()

    

        self.ref.child("MEmories").queryOrderedByKey().observe(.value, with: 

            snapShot in

            if let dict = snapShot.value as? NSDictionary

            

                for d in (dict as? Dictionary<String,AnyObject>)!

                

                    let title = d.value["title"] as?String

                    let body = d.value["body"] as? String

                    let uid = d.value["uid"] as? String

                    let imageRef = d.value["imageRef"] as? String



                    let m = Memory(title: title!, body: body!, uid: uid!,imageRef:imageRef!)

                    m.key = d.key

                    let tempImageRef = self.sref.child(m.imageRef)



                    tempImageRef.getData(maxSize: 1*1024*1024, completion: (data,error) in



                        if error == nil

                        

                            if let imageData = data

                            

                                m.image = UIImage(data: imageData)

                                self.memories.append(m)

                                self.tbl.reloadData()

                            

                        

                    )

                



            //end of if

            self.ref.child("MEmories").removeAllObservers()



        )

    

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) 

        if let identifier = segue.identifier

        

            if identifier == "goToEdit"

            

                let indexPath = self.tbl.indexPathForSelectedRow

                let addMemoryViewController = segue.destination as! AddMemoryViewController

                self.strMode = "edit"

                self.lastIndex = (indexPath?.row)!

                addMemoryViewController.mode = self.strMode

                addMemoryViewController.current = memories[(indexPath?.row)!]

            

        

    


【问题讨论】:

我需要更改这一行:self.memories = Array.filter( (array: String) 所以它会检查内存的 TITLE 而不仅仅是 MEMORY 你有两个问题合二为一;如何查询 Firebase 实时数据库,然后如何过滤该数据。这两个问题都解决了,实在是太宽泛了。但是要开始,我们需要知道您是要在代码中过滤还是在 Firebase 中过滤,以及您想使用什么流程进行过滤?输入一个字符串并根据该字符串进行过滤,或者逐个字符进行过滤。一旦我们知道了这一点并且您有了解决方案,那么在您的 searchBox 和/或 tableView 中使用它就是另一个问题。 【参考方案1】:

您应该将过滤后的值存储在过滤数组中,并在表视图数据委托、数据源方法中使用该数组

将memory数组的所有值存储到loadMemories方法中的filteredArray数组中

if let dict = snapShot.value as? NSDictionary

    for d in (dict as? Dictionary<String,AnyObject>)!
    
       //append in memories
    
    self.filteredArray = self.memories
    self.tbl.reloadData()
//end of if

在表视图委托数据源方法中使用filteredArray数组

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return filteredArray.count

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let identifier = "iden"
    var cell : UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
    if cell == nil
    
        cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: identifier)
    
    let temp = filteredArray[indexPath.row]
    cell?.textLabel?.text = temp.title
    cell?.imageView?.image = temp.image
    return cell!

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) 
    if editingStyle == .delete
    
        let temp = self.filteredArray[indexPath.row]
        self.filteredArray.remove(at: indexPath.row)
        if let index = self.memories.firstIndex(where:  $0.title == temp.title ) 
            self.memories.remove(at: index)
        
        self.ref.child("MEmories/\(temp.key)").removeValue()
        tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
    

在 updateSearchResults 方法中,通过比较 searchBar 文本和内存对象标题来过滤内存数组

func updateSearchResults(for searchController: UISearchController) 
    if searchController.searchBar.text!.isEmpty 
        self.filteredArray = self.memories
     else 
        self.filteredArray = self.memories.filter(
            $0.title.localizedCaseInsensitiveContains(searchController.searchBar.text!)
        )
    
    self.resultController.tableView.reloadData()

【讨论】:

首先,非常感谢您!您的意思是当我在表视图委托数据源方法中使用filteredArray 数组时,它将不是在那里使用内存数组吗?因为我需要那里的记忆来给特定用户他的特定记忆...... 是的。在表视图委托数据源方法中使用过滤数组数组,而不是使用内存数组。而filteredArray 只是内存数组的副本。所以不会有问题。试试看 抱歉,现在 TableView 中根本不显示记忆了。可能是因为我在表视图委托数据源方法中用过滤数组替换了内存数组。有任何想法吗? :) @ABM 你是否像我提到的那样在loadMemories 方法中添加了self.filteredArray = self.memories self.tbl.reloadData() 是的...你知道发生了什么吗?【参考方案2】:

根据评论,这个问题与 Firebase 没有直接关系,但原发帖人在问这个

我想通过内存的标题过滤代码,我还在 卡住了...有什么想法吗? – 反导

所以,这里有一个完整的例子,说明如何按内存标题过滤代码。

让我们从一个简单的类开始来保存一段记忆和它的标题

class MemoryClass 
    var title = ""
    init(withTitle: String) 
        self.title = withTitle
    

然后我们将构建一个数组,将其用作例如 tableView 数据源

let m0 = MemoryClass(withTitle: "Gone Fishing")
let m1 = MemoryClass(withTitle: "Wedding")
let m2 = MemoryClass(withTitle: "France Vacation")
let m3 = MemoryClass(withTitle: "Italy Vacation")

let memoriesArray = [m0, m1, m2, m3]

然后是按标题查找内存的代码。在这种情况下,我们要找到标题为“婚礼”的内存

let memoryTitleToFind = "Wedding"
let filteredArray = memoriesArray.filter  $0.title == memoryTitleToFind 

为了验证是否找到了内存,让我们遍历找到的内存。请记住,如果有多个名为“婚礼”的记忆,那么它会将所有记忆过滤到过滤后的数组中

for foundMemory in filteredArray 
    print(foundMemory.title)

显示过滤结果的输出是

Wedding

我为此答案创建了一个简单的 MemoryClass,在您的问题中您注意到还有其他属性

内存有标题、用户 ID、正文,我希望它也能够搜索 按标题

因此可以将它们添加到类中并以相同的方式过滤。

【讨论】:

非常感谢你,但我坚持的事情是记忆阵列是动态的。用户可以创建新的记忆或删除记忆,因此使用 firebase 就是它的全部意义所在。非常感谢你。 @ABM 这不是您的评论所说的或您的问题所要求的。您问如何在代码中过滤,这就是解决方案。它适用于“动态”数据,因为只要您的 Firebase 数据发生更改,memoryArray 就会更新。如果您要问其他问题,请更新并澄清问题的真正含义。

以上是关于使用 firebase 连接到 Tableview 的搜索栏的主要内容,如果未能解决你的问题,请参考以下文章

使用 Laravel 5.2 连接到 Firebase -“创建资源时出错”

无法将 Firebase 连接到 Android 应用 |此应用无权使用 Firebase 身份验证

如何连接到 tableview 容器?

Firebase连接到后端时如何上传到前端的Firebase存储?

无法连接到 Firebase 以进行云消息传递

Dialogflow 使用 Firebase 连接到 MQTT 代理?