【问题标题】:How can I use queryStartingAtValue in my searchcontroller to search for users?如何在我的搜索控制器中使用 queryStartingAtValue 来搜索用户?
【发布时间】:2018-10-24 17:18:29
【问题描述】:

我之前曾问过一个关于以最经济高效的方式搜索用户的问题(无需加载整个数据库中的每个用户。

问题之前我的代码是

class FollowUsersTableViewController: UIViewController{

@IBOutlet var tableView: UITableView!

private var viewIsHiddenObserver: NSKeyValueObservation?
let searchController = UISearchController(searchResultsController: nil)
var usersArray = [UserModel]()
var filteredUsers = [UserModel]()
var loggedInUser: User?
//
var databaseRef = Database.database().reference()
//usikker på den koden over

override func viewDidLoad() {
    super.viewDidLoad()
    //large title
    self.title = "Discover"
    if #available(iOS 11.0, *) {
        self.navigationController?.navigationBar.prefersLargeTitles = true
    } else {
        // Fallback on earlier versions
    }

    self.tableView?.delegate = self
    self.tableView?.dataSource = self
    searchController.searchResultsUpdater = self
    searchController.dimsBackgroundDuringPresentation = false
    self.searchController.delegate = self;

    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar

    self.loadProfileData()
}

func loadProfileData() {
    databaseRef.child("profile").queryOrdered(byChild: "username").observe(.childAdded, with: { (snapshot) in
        print(snapshot)
        let userObj =  Mapper<UserModel>().map(JSONObject: snapshot.value!)
        userObj?.uid = snapshot.key

        guard snapshot.key != self.loggedInUser?.uid else { return }

        self.usersArray.append(userObj!)
    })
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let dest = segue.destination as! UserProfileViewController
    let obj = sender as! UserModel
    let dict = ["uid": obj.uid!, "username": obj.username!, "photoURL": obj.photoURL, "bio": obj.bio]
    dest.selectedUser = dict as [String : Any]
}

  }

   // MARK: - tableview methods
   extension FollowUsersTableViewController: UITableViewDataSource, 
  UITableViewDelegate {

func tableView(_ tableView: UITableView, numberOfRowsInSection 
 section: Int) -> Int {
    return searchController.searchBar.text!.count >= 2 ? 
 filteredUsers.count : 0
  }

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! FollowTableViewCell

    let user = filteredUsers[indexPath.row]

    cell.title?.text = user.username
    if let url = URL(string: user.photoURL ?? "") {
        cell.userImage?.sd_setImage(with: url, placeholderImage: 
     #imageLiteral(resourceName: "user_male"), options: 
  .progressiveDownload, completed: nil)
        cell.userImage.sd_setIndicatorStyle(.gray)
        cell.userImage.sd_showActivityIndicatorView()
    }

    return cell
}

 func tableView(_ tableView: UITableView, heightForRowAt indexPath: 
   IndexPath) -> CGFloat {
    return 50
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: 
 IndexPath) {
    self.performSegue(withIdentifier: "user", sender: self.filteredUsers[indexPath.row])
}

 }

 // MARK: - search methods
 extension FollowUsersTableViewController:UISearchResultsUpdating, 
   UISearchControllerDelegate {

func updateSearchResults(for searchController: UISearchController) {
    searchController.searchResultsController?.view.isHidden = false
    filterContent(searchText: self.searchController.searchBar.text!)
    self.tableView.reloadData()
}

func filterContent(searchText:String){
    if searchText.count >= 2{
        self.filteredUsers = self.usersArray.filter{ user in
            return(user.username!.lowercased().contains(searchText.lowercased()))
        }
    }
   }
  }

但后来用户“maxwell”回复我并建议像这样使用 queryStartingAtValue:

func searchQueryUsers(text: String, completion: @escaping (_ userNames: [String]) -> Void) {

    var userNames: [String] = []

    databaseRef.child("profile").queryOrdered(byChild: "username").queryStarting(atValue: text).observeSingleEvent(of: .value, with: { snapshot in

        for item in snapshot.children {

            guard let item = item as? DataSnapshot else {
                break
            }

            //"name" is a key for name in FirebaseDatabese model
            if let dict = item.value as? [String: Any], let name = dict["name"] as? String {
                userNames.append(name)
            }
        }

        completion(userNames)
    })
}

如何使用我现有的代码和Mapper&lt;UserModel&gt;().map(JSONObject) 来实现这一点?我尝试实现他的代码,但无法真正找到有效的方法,而且我似乎无法使用 maxwell,所以有人可以帮我解决这个问题吗?

我应该使用 searchController.searchBar.text 作为 maxwells 代码中的“文本”吗?

谢谢,

为杰更新:

 class FollowUsersTableViewController: UIViewController, 
 UISearchBarDelegate {

@IBOutlet var tableView: UITableView!

private var viewIsHiddenObserver: NSKeyValueObservation?
let searchController = UISearchController(searchResultsController: nil)
var usersArray = [UserModel]()
var filteredUsers = [UserModel]()
var loggedInUser: User?
//
var databaseRef = Database.database().reference()
//usikker på den koden over

override func viewDidLoad() {
    super.viewDidLoad()
    //large title
    self.title = "Discover"
    if #available(iOS 11.0, *) {
        self.navigationController?.navigationBar.prefersLargeTitles = true
    } else {
        // Fallback on earlier versions
    }

    self.tableView?.delegate = self
    self.tableView?.dataSource = self
    searchController.searchResultsUpdater = self
    searchController.dimsBackgroundDuringPresentation = false
    self.searchController.delegate = self;



    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar

    //self.loadProfileData()

   //self.searchBar(searchController.searchBar, textDidChange: searchController.searchBar.text)







}

func searchUsers(text: String) {
    if text.count > 0 {
        self.usersArray = [] //clear the array each time
        let endingText = text + "\u{f8ff}"
        databaseRef.child("profile").queryOrdered(byChild: "username")
            .queryStarting(atValue: text)
            .queryEnding(atValue: endingText)
            .observeSingleEvent(of: .value, with: { snapshot in

                for child in snapshot.children {
                    let childSnap = child as! DataSnapshot
                    let userObj =  Mapper<UserModel>().map(JSONObject: childSnap.value!)
                    userObj?.uid = childSnap.key
                    if childSnap.key != self.loggedInUser?.uid { //ignore this user
                        self.usersArray.append(userObj!)
                    }
                }
                self.tableView.reloadData()
            })
    }
} //may need an else statement here to clear the array when there is no text


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let dest = segue.destination as! UserProfileViewController
    let obj = sender as! UserModel
    let dict = ["uid": obj.uid!, "username": obj.username!, "photoURL": obj.photoURL, "bio": obj.bio]
    dest.selectedUser = dict as [String : Any]
}

func searchBar(_ searchBar: UISearchBar,
               textDidChange searchText: String) {

    self.searchUsers(text: searchText)
}




 }

【问题讨论】:

  • 这个问题有点不清楚。当您询问如何在 Firebase 数据库中搜索值时?您的代码中的查询并没有真正查询任何特定的 ...queryOrdered(byChild: "username")。如何将其更改为您正在寻找的 ....queryOrdered(byChild: "username").queryEqual(toValue: "John")。或者您是否尝试在 Firebase 中搜索以用户迄今为止输入的内容开头的用户名?
  • 另外,在实际选择用户之前,您不需要这个 let userObj = Mapper().map(JSONObject: snapshot.value!),对吧?如果这是真的,那么在找到正确的用户之前,您不需要调用该函数。
  • 不,我认为我需要这个,因为我在表格视图中加载用户名和个人资料图片。我只是不太明白如何在我的代码中实现代码 func searchQueryUsers,并将其替换为我的 func loadProfileData ()
  • 另外,我正在尝试在 Firebase 中搜索以用户在 searchcontroller 中输入的内容开头的用户名。在我的代码中,这现在有效,但我加载了数据库中的每个用户。相反,我只想加载用户输入的内容,所以如果用户输入了“Ja”,我希望“Jay”显示出来,而不加载整个数据库,也加载用户的个人资料图片(因为这真的是贵)

标签: swift firebase firebase-realtime-database uisearchcontroller


【解决方案1】:

问题中的代码几乎是正确的,只需要对其进行调整以确保枚举变量是快照,然后将其与 Mapper 一起使用。

当用户输入用户名时,在每次按键时递归调用它;因此,如果他们正在输入 Leroy,则首先输入 L,此代码将检索用户名值以“L”开头的所有节点。然后用户键入“e”使其成为“Le”等。

func searchUsers(text: String) {
    if text.count > 0 {
       self.usersArray = [] //clear the array each time
       let endingText = text + "\u{f8ff}"
       databaseRef.child("profile").queryOrdered(byChild: "username")
                                   .queryStarting(atValue: text)
                                   .queryEnding(atValue: endingText)
                                   .observeSingleEvent(of: .value, with: { snapshot in

           for child in snapshot.children {
               let childSnap = child as! DataSnapshot
               let userObj =  Mapper<UserModel>().map(JSONObject: childSnap.value!)
               userObj?.uid = childSnap.key
               if childSnap.key != self.loggedInUser?.uid { //ignore this user
                  self.usersArray.append(userObj!)
               }
           }
           self.tableView.reloadData()
       })
    }
  } //may need an else statement here to clear the array when there is no text

编辑:

OP 请求了用于通过 searchBar 和 if 语句处理搜索的代码,以防止在没有文本的情况下进行搜索。这是调用上述 searchUsers 函数的委托方法

func searchBar(_ searchBar: UISearchBar,
               textDidChange searchText: String) {

    self.searchUsers(text: searchText)
}

编辑:

OP 想看看我的 viewDidLoad 函数,因为它很乏味,所以就在这里。

class ViewController: UIViewController, UISearchBarDelegate 
    override func viewDidLoad() {
       super.viewDidLoad()

       // Do any additional setup after loading the view.
    }

【讨论】:

    【解决方案2】:

    我将此作为单独的答案添加,因为 OP 还有其他间接相关的问题:

    这是我的整个代码库

    class ViewController: UIViewController, UISearchBarDelegate {
        @IBOutlet var searchBarOutlet: [UISearchBar]!
         override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view, typically from a nib.
            self.searchBar.delegate = self
         }
    
        var userNamesArray = [String]()
        func searchBar(_ searchBar: UISearchBar,
                       textDidChange searchText: String) {
    
            self.searchUsers(text: searchText)
        }
    
        func searchUsers(text: String) {
            self.userNamesArray = []
            if text.count > 0 {
                let ending = text + "\u{f8ff}"
                let databaseRef = self.ref.child("users")
                databaseRef.queryOrdered(byChild: "Name")
                    .queryStarting(atValue: text)
                    .queryEnding(atValue: ending)
                    .observeSingleEvent(of: .value, with: { snapshot in
                        for child in snapshot.children {
                            let childSnap = child as! DataSnapshot
                            let userName = childSnap.childSnapshot(forPath: "Name").value as! String
                            self.userNamesArray.append(userName)
                        }
                        print(self.userNamesArray) //here you would call tableView.reloadData()
                    })
            }
        }
    }
    

    除了将 self.ref 分配给我的 Firebase 之外,就是这样。我的结构是:

    users
      uid_0
        name: "Frank"
      uid_1
        name: "Fred"
      uid_2
        name: "Finay"
    

    等当我在搜索字段中输入“F”时,我得到以下输出

    ["Frank", "Fred", "Friday"]
    

    然后当我输入“Fr”时,我得到以下输出

    ["Frank", "Fred"]
    

    如您所见,它有效。如果它在您的情况下不起作用,则可能是您的 tableView 连接不正确或其他与搜索无关的问题。

    现在,我没有使用 tableView,而只是将名称字符串打印到控制台,因此您需要执行 tableView.reloadData() 来代替我的打印语句。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-13
      • 1970-01-01
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 2011-04-30
      相关资源
      最近更新 更多