如何在我的搜索控制器中使用 queryStartingAtValue 来搜索用户?
How can I use queryStartingAtValue in my searchcontroller to search for users?
我之前问过一个关于以最具成本效益的方式搜索用户的问题(无需加载整个数据库中的每个用户。
问题前我的代码是
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<UserModel>().map(JSONObject)
中已有的代码来实现这一点?我尝试实现他的代码,但找不到真正有效地执行此操作的方法,而且我似乎无法使用 maxwell,所以有人可以帮我解决这个问题吗?
我应该使用 searchController.searchBar.text 作为 maxwells 代码中的 "text" 吗?
谢谢,
为 Jay 更新:
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)
}
}
问题中的代码几乎是正确的,只需要对其进行调整以确保枚举变量是快照,然后将其与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.
}
我将此添加为单独的答案,因为 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() 来代替我的打印语句。
我之前问过一个关于以最具成本效益的方式搜索用户的问题(无需加载整个数据库中的每个用户。
问题前我的代码是
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<UserModel>().map(JSONObject)
中已有的代码来实现这一点?我尝试实现他的代码,但找不到真正有效地执行此操作的方法,而且我似乎无法使用 maxwell,所以有人可以帮我解决这个问题吗?
我应该使用 searchController.searchBar.text 作为 maxwells 代码中的 "text" 吗?
谢谢,
为 Jay 更新:
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)
}
}
问题中的代码几乎是正确的,只需要对其进行调整以确保枚举变量是快照,然后将其与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.
}
我将此添加为单独的答案,因为 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() 来代替我的打印语句。