Ios 展开时发现无可选
我试图创建一个聊天应用程序,但我遇到的问题是,我试图让tableview中的单元格显示用户的displayname。但出于某种原因,它一直在说零。这让我相信我的结构或引用数据的方式有问题 StructIos 展开时发现无可选,ios,swift,uitableview,firebase-realtime-database,Ios,Swift,Uitableview,Firebase Realtime Database,我试图创建一个聊天应用程序,但我遇到的问题是,我试图让tableview中的单元格显示用户的displayname。但出于某种原因,它一直在说零。这让我相信我的结构或引用数据的方式有问题 Struct struct User { let name: String! let uid: String! } 表格视图单元格 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPa
struct User {
let name: String!
let uid: String!
}
表格视图单元格
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
// **** Crash is on the following line ****
let nameTxtField = cell.viewWithTag(1) as! UILabel
nameTxtField.text = users[indexPath.row].name
return cell
}
表格视图viewDidload
override func viewDidLoad() {
super.viewDidLoad()
let uid = Auth.auth().currentUser?.uid
let ref: DatabaseReference! = Database.database().reference()
ref.child("users").child(uid!).queryOrderedByKey().observe(.childAdded) { (snapshot) in
let name = (snapshot.value as? NSDictionary)?["name"] as? String ?? ""
let uid = (snapshot.value as? NSDictionary)?["uid"] as? String ?? ""
self.users.append(User(name: name, uid: uid))
self.tableView.reloadData()
}
tableView.reloadData()
}
数据库结构
仅当您希望根据某些条件从列表中获取多个子项时,才需要使用由…和
排序的查询。但在本例中,您知道子节点的确切路径,因此不需要查询
因此,您正在执行直接读取,但使用的是queryOrderedByKey()
和.childAdded
。这意味着您的闭包将在/users/rPJSO…
下与每个子项一起调用,每个属性调用一次:email
,name
,password
,uid
。由于在这些属性下都没有name
property,因此对它们的查找将返回nil
解决方案非常简单:删除queryOrderedByKey
并侦听.value
:
ref.child("users").child(uid!).observe(.value) { (snapshot) in
...
您的其余代码可以保持不变。仅当您希望根据某些条件从列表中获取多个子项时,才需要使用由…
和定义的查询。但在本例中,您知道子节点的确切路径,因此不需要查询
因此,您正在执行直接读取,但使用的是queryOrderedByKey()
和.childAdded
。这意味着您的闭包将在/users/rPJSO…
下与每个子项一起调用,每个属性调用一次:email
,name
,password
,uid
。由于在这些属性下都没有name
property,因此对它们的查找将返回nil
解决方案非常简单:删除queryOrderedByKey
并侦听.value
:
ref.child("users").child(uid!).observe(.value) { (snapshot) in
...
您的代码的其余部分可以保持不变。根据您的评论,您的代码正在崩溃
let nameTxtField = cell.viewWithTag(1) as! UILabel
这意味着viewWithTag(1)
要么返回nil
,要么返回UILabel
以外的内容。您没有将标签的标记设置为1,或者您有多个带有标记1的UI元素(viewWithTag
将返回带有该标记的层次结构中的第一个视图)
您应该创建一个适当的UITableViewCell
子类,并将UILabel
链接到该类上的属性。然后你可以这样说:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyTableViewCell
cell.nameTxtField.text = users[indexPath.row].name
其他一些评论;你应该试着消除尽可能多的!尽可能的
- 结构的属性应该是可选的或非可选的,而不是隐式展开的可选
- 您应该使用
guard
语句检索uid
,而不是稍后强制展开它
- 如果您将获取用户的代码分散在几行上,它将更清晰
- 避免在Swift中使用
NS…
对象
- 我还注意到Frank关于您访问Firebase的方式的回答,但我对Firebase了解不够,不知道这是否是一个问题李>
根据你的评论,你在电话线上遇到了车祸
let nameTxtField = cell.viewWithTag(1) as! UILabel
这意味着viewWithTag(1)
要么返回nil
,要么返回UILabel
以外的内容。您没有将标签的标记设置为1,或者您有多个带有标记1的UI元素(viewWithTag
将返回带有该标记的层次结构中的第一个视图)
您应该创建一个适当的UITableViewCell
子类,并将UILabel
链接到该类上的属性。然后你可以这样说:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyTableViewCell
cell.nameTxtField.text = users[indexPath.row].name
其他一些评论;你应该试着消除尽可能多的!尽可能的
- 结构的属性应该是可选的或非可选的,而不是隐式展开的可选
- 您应该使用
guard
语句检索uid
,而不是稍后强制展开它
- 如果您将获取用户的代码分散在几行上,它将更清晰
- 避免在Swift中使用
NS…
对象
- 我还注意到Frank关于您访问Firebase的方式的回答,但我对Firebase了解不够,不知道这是否是一个问题李>
问题似乎就在这一行:
let nameTxtField = cell.viewWithTag(1) as! UILabel
尚未从情节提要加载单元格,因此cell.viewWithTag(1)
为零。零+!=撞车
通常我用以下算法填充单元格:
为每种类型的单元格创建单独的类
实现方法customizeUI()
,以填充单元格上必要的UI控件
添加要在此单元格中显示的数据类型的iVar,并在其setter中调用customizeUI()
变量项:历史项目?{
迪塞特{
()
}
}
此方法检查单元格是否已加载:
func customizeUI() {
guard let label = self.nameLabel else {
return
}
// Fill UI controlls
label.text = item.name
...
}
此外,必须在单元格awakeFromNib()
方法中调用此方法
最后,在您的cellAt…
方法中,您必须编写类似于cell.item=dataItem
的内容,而不是直接填充UI控件。问题似乎出在这一行:
let nameTxtField = cell.viewWithTag(1) as! UILabel
尚未从情节提要加载单元格,因此cell.viewWithTag(1)
为零。零+!=撞车
通常我用以下算法填充单元格:
为每种类型的单元格创建单独的类
实现方法customizeUI()
,以填充单元格上必要的UI控件