解包时发现零

时间:2018-12-03 05:39:22

标签: ios swift uitableview firebase-realtime-database

我正在尝试创建一个聊天应用程序,但是发生在我身上的问题是我试图在表格视图中获取单元格以显示用户的显示名称。但是出于某种原因,它总是说零。这使我相信我的结构或引用数据的方式有问题。

结构

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()
}

数据库结构 enter image description here

3 个答案:

答案 0 :(得分:1)

queryOrderedBy....childAdded仅在您希望基于某种条件从列表中获取多个子代时才需要。但是在您的情况下,您知道子节点的确切路径,因此您不需要查询。

因此,您正在执行直接读取,但是正在使用queryOrderedByKey().childAdded。这意味着您的闭包将在/users/rPJSO...下的每个子级中被调用,对于每个单独的属性分别被调用一次:emailnamepassworduid。而且,由于其中没有任何一个属性{strong},因此该查找将返回name

解决方案非常简单:删除nil并收听queryOrderedByKey

.value

其余代码可以保持不变。

答案 1 :(得分:1)

根据您的评论,您可能会遇到崩溃

let nameTxtField = cell.viewWithTag(1) as! UILabel

这意味着viewWithTag(1)返回nilUILabel以外的内容。您可能尚未将标签的标签设置为1,或者您有多个带有标签1的UI元素(viewWithTag将返回带有该标签的层次结构中的第一个视图)。

您应该创建一个适当的UITableViewCell子类,并将您的UILabel链接到该类的属性。然后您可以说:

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyTableViewCell
cell.nameTxtField.text = users[indexPath.row].name

其他一些评论;您应该尝试消除尽可能多的信息!尽可能。

  • 结构的属性应该是可选的或非可选的,而不是隐式解包的可选。
struct User {
    let name: String
    let uid: String
}
  • 您应该使用uid语句检索guard,而不是在以后强行打开包装
  • 如果您将其分散在几行中,则获取用户的代码将更加清晰
  • 避免在Swift中使用NS...对象
  • 我还注意到Frank关于您访问​​Firebase的方式的答案,但是我对Firebase的了解还不够,还不知道这是否是一个问题;

override func viewDidLoad() {
    super.viewDidLoad()
    guard let uid = Auth.auth().currentUser?.uid else {
        return
    }

    let ref: DatabaseReference! = Database.database().reference()

    ref.child("users").child(uid).queryOrderedByKey().observe(.childAdded) { (snapshot) in
        guard let result = snapshot.value as? [String:Any] else {
            return
        }

        let name = (result["name"] as? String) ?? ""

        let uid = (result["uid"] as? String) ?? ""

        self.users.append(User(name: name, uid: uid))

        self.tableView.reloadData()
    }

}

答案 2 :(得分:1)

问题似乎在此行中:

let nameTxtField = cell.viewWithTag(1) as! UILabel

该单元尚未从情节提要中加载,因此cell.viewWithTag(1)为零。零+! =崩溃。

通常,我使用以下算法填充单元格:

  1. 为每种类型的单元格创建一个单独的类
  2. 实施方法customizeUI(),以填充单元格上必要的UI控件
  3. 添加要在此单元格中显示的数据类型的iVar,并在其设置方法中调用customUI()。

    var item:HistoryItem? {     didSet {        customUI()     } }

此方法检查单元是否已加载:

func customizeUI() {
    guard let label = self.nameLabel else {
        return
    }
    // Fill UI controlls
    label.text = item.name
    ...
}

还必须在单元格awakeFromNib()方法中调用此方法。

最后,在您的cellAt...方法中,您必须编写类似cell.item = dataItem的内容,而不是直接填充UI控件。