Swift 使用UITableViewController从Firebase获取当前子ID
我的问题和这里几乎一样:和这个问题稍有不同 我有一个UITableViewController,用户可以在其中创建自己的频道。每个人都可以查看这些频道,每个人都可以点击其中一个频道,进入一个新的视图。现在,当用户点击任何一个单元格时,我想得到当前的频道ID,用户只是点击了 我的数据库如下所示: 现在我的问题是:如何获得当前ID-KdD6[…]Q0Q?问题2的答案对文件室的创建者来说可以起到作用,但其他用户不能使用,因为他们没有创建密钥。第1个问题的答案也许可以使用,但到目前为止,它只是在每个随机键之间循环。但我需要与用户当前所在频道相关的密钥。下面是一些代码,展示了我如何制作频道。提前谢谢。编辑:这是我所有的代码。我现在得到一个类型为[Channel]的错误值,在didSelectRow函数中没有成员“name”Swift 使用UITableViewController从Firebase获取当前子ID,swift,firebase,firebase-realtime-database,Swift,Firebase,Firebase Realtime Database,我的问题和这里几乎一样:和这个问题稍有不同 我有一个UITableViewController,用户可以在其中创建自己的频道。每个人都可以查看这些频道,每个人都可以点击其中一个频道,进入一个新的视图。现在,当用户点击任何一个单元格时,我想得到当前的频道ID,用户只是点击了 我的数据库如下所示: 现在我的问题是:如何获得当前ID-KdD6[…]Q0Q?问题2的答案对文件室的创建者来说可以起到作用,但其他用户不能使用,因为他们没有创建密钥。第1个问题的答案也许可以使用,但到目前为止,它只是在每个随机
import UIKit
import Firebase
import Foundation
enum Section: Int {
case createNewChannelSection = 0
case currentChannelsSection
}
extension multiplayerChannelView: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
class multiplayerChannelView: UITableViewController {
// MARK: Properties
var channels: [Channel] = []
let searchController = UISearchController(searchResultsController: nil)
var filteredChannels = [Channel]()
private lazy var channelRef: FIRDatabaseReference = FIRDatabase.database().reference().child("channels")
private var channelRefHandle: FIRDatabaseHandle?
var senderDisplayName: String?
var newChannelTextField: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
title = "Rooms"
observeChannels()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
deinit {
if let refHandle = channelRefHandle {
channelRef.removeObserver(withHandle: refHandle)
}
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredChannels = channels.filter { Channel in
return (Channel.name.lowercased().range(of: searchText.lowercased()) != nil)
}
tableView.reloadData()
}
@IBAction func createChannel(_ sender: AnyObject) {
if let name = newChannelTextField?.text {
let newChannelRef = channelRef.childByAutoId()
let channelItem = [ // 3
"name": name
]
newChannelRef.setValue(channelItem)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
private func observeChannels() {
channelRefHandle = channelRef.observe(.childAdded, with: { (snapshot) -> Void in // 1
let channelData = snapshot.value as! Dictionary<String, AnyObject> // 2
let id = snapshot.key
if let name = channelData["name"] as! String!, name.characters.count > 0 { // 3
self.channels.append(Channel(id: id, name: name))
self.tableView.reloadData()
} else {
print("Error! Could not decode channel data")
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let currentSection: Section = Section(rawValue: section) {
switch currentSection {
case .createNewChannelSection:
return 1
case .currentChannelsSection:
if searchController.isActive && searchController.searchBar.text != "" {
return filteredChannels.count
}
else{
return channels.count
}
}
} else {
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reuseIdentifier = (indexPath as NSIndexPath).section == Section.createNewChannelSection.rawValue ? "NewChannel" : "ExistingChannel"
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
if (indexPath as NSIndexPath).section == Section.createNewChannelSection.rawValue {
if let createNewChannelCell = cell as? CreateChannelCell {
newChannelTextField = createNewChannelCell.newChannelNameField
}
} else if (indexPath as NSIndexPath).section == Section.currentChannelsSection.rawValue {
if searchController.isActive && searchController.searchBar.text != "" {
cell.textLabel?.text = filteredChannels[(indexPath as NSIndexPath).row].name
} else {
cell.textLabel?.text = channels[(indexPath as NSIndexPath).row].name
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == Section.currentChannelsSection.rawValue {
var channel = channels
if searchController.isActive && searchController.searchBar.text != "" {
channel = [filteredChannels[(indexPath as NSIndexPath).row]]
channelRef.queryOrdered(byChild: "name").queryEqual(toValue: channel.name).observe(.childAdded, with: {snapshot in
let currentID = snapshot.key
print(currentID)
})
}
else
{
channel = [channels[(indexPath as NSIndexPath).row]]
channelRef.queryOrdered(byChild: "name").queryEqual(toValue: channel.name).observe(.childAdded, with: {snapshot in
let currentID = snapshot.key
print(currentID)
})
}
self.performSegue(withIdentifier: "ShowChannel", sender: channel)
}
}
}
internal class Channel {
internal let id: String
internal let name: String
init(id: String, name: String) {
self.id = id
self.name = name
}
}
执行prepare for segue函数,但if let channel=sender as?频道不是,因为它是零 您必须向firebase查询密钥。当前设置的唯一问题是您希望防止名称重复。原因是,如果您没有在应用程序中存储每个频道的所有密钥,并且必须查询密钥,那么查询确切密钥的唯一方法是通过单个节点名称。如果存在重复的名称,那么在我添加到did select row方法的代码中添加的childAdded将返回两个,并且没有其他数据来帮助识别它
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == Section.currentChannelsSection.rawValue {
var channel = channels
if searchController.isActive && searchController.searchBar.text != "" {
channel = [filteredChannels[(indexPath as NSIndexPath).row]]
channelRef.queryOrdered(byChild: "name").queryEqual(toValue: channel.name).observe(.childAdded, with: {snapshot in
let currentID = snapshot.key
})
}
else
{
channel = [channels[(indexPath as NSIndexPath).row]]
channelRef.queryOrdered(byChild: "name").queryEqual(toValue: channel.name).observe(.childAdded, with: {snapshot in
let currentID = snapshot.key
})
}
self.performSegue(withIdentifier: "ShowChannel", sender: channel)
}
}
我建议您获取查看时加载的所有频道的数据:
override func viewDidLoad(){
channelRef.observe(.childAdded, with: {snapshot in
let data = snapshot.value as? [String:Any] ?? [:]
let key = snapshot.key
let name = data["name"]
let chnl = Channel.init(id:key, name:name)
self.channels.append(chnl)
self.tableView.reloadData()
})
}
当您使用.childAdded时,此VC的侦听器将处于活动状态,因此无论何时创建新频道,都会调用此代码并重新加载表
否则,您可以在创建通道时直接获取它们的密钥:
@IBAction func createChannel(_ sender: AnyObject) {
if let name = newChannelTextField?.text {
let newChannelRef = channelRef.childByAutoId()
let channelItem = [
"name": name
]
newChannelRef.setValue(channelItem)
let channelKey = newChannelRef.key
let newChannel = Channel.init(id:channelKey, name:name)
self.channels.append(newChannel)
self.tableView.reloadData()
}
}
请注意,如果您选择此路线,则添加的任何新项目都没有活动侦听器,这意味着如果其他设备正在添加频道,则其他设备将无法获得更新。最好通过查询从firebase获取新创建的数据,并在将数据添加到数组时重新加载表
下面是一个示例通道类,该类应适用于上述情况:
import Foundation
// import FirebaseDatabase // import this if you would like to use this class to handle data transactions
class Channel: NSObject {
// change either of these to var if they should be mutable
let id:String
let name:String
// initialize
init (_ id:String, name:String){
self.id = id
self.name = name
}
}
这样您就有了一个可以使用的模型对象。如果您不熟悉MVC编码风格,您应该查看它。它使应用程序更加高效。您可以采用此通道模型并添加数据检索编码,以便此类处理控制器的数据。这远远超出了你的文章范围。现在这门课应该有用了
从上一条评论编辑:
在通道选择期间,而不是在if块之后,在两个查询块中添加prepareforsegue方法,因为在查询完成之前,prepare for segue正在执行。但有一件事需要注意,在我意识到这一点之前,我曾经和你做过同样的事情;这意味着此时不必运行查询来获取所选单元格的数据,因为它位于本地数组中。嗯,目前您只收听添加的孩子。您还可以侦听已删除和已更改的子项,以便始终更新本地数据,然后在选择单元格时,您只需直接访问FilteredChannel/channel阵列,并通过PrepareForegue方法推送通道即可。我开始这样做,因为在选择表的过程中查询总是会导致一些奇怪的错误,我必须找到创造性的解决方法
childRemoved和childChanged查询的实现方式可以与当前的.childAdded方法相同,因为您希望保留侦听器的处理程序,以便在页面deinit上删除它们。但是,如果不允许删除或更新频道,则可以忽略此项 在频道中添加对象的位置显示代码在设置频道对象的名称时,是否设置了该对象的id属性?如果您设置的频道id比您想将该频道id传递到新屏幕ShowChannel?我添加了更多代码,现在您可以确切地看到我在做什么。我只想检索文件室的ID,不仅是为了创建者,也是为了想加入文件室的人。谢谢你抽出时间回答这个问题。当我尝试将您的第一个代码块添加到我的项目中时,我得到类型为[Channel]的错误值没有成员“name”。您知道如何修复此错误吗?通道类是同一swift文件的一部分吗?最好创建一个名为Channel.swift的新swift文件作为NSObject的子类。我将用一个例子更新我的答案,这个例子应该对你有用。是的,内部类已经是一个单独的文件,但是如果我没有说的话,很抱歉。我在我的Channel.swift文件上复制了您的代码,但我再次遇到相同的错误…它只是将频道视为一个数组,没有名称或id属性。我试了很多,但是
没有任何效果:我添加了一个prepare for segue函数,该函数是从其他地方获得的,但该函数无法正确执行。。。我再次更新了问题。可能在查询返回数据之前调用了perform segue方法。请参阅我的更新答案,它太长,无法添加为评论。
import Foundation
// import FirebaseDatabase // import this if you would like to use this class to handle data transactions
class Channel: NSObject {
// change either of these to var if they should be mutable
let id:String
let name:String
// initialize
init (_ id:String, name:String){
self.id = id
self.name = name
}
}