Swift 3 Breaks Cell For Row At Index Path

2019-09-05 02:31发布

This code was from a now inactive tutorial that helped me load in data to a table view. Since the tutorial was written in Swift 2.0, I believe that this was changed in Swift 3. I know that the override function itself was changed, which I handled. But now, it brings me a Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0) error.

Update: I have tried multiple things including creating a custom class for the cell. I still either get the same error I listed above, or a Thread 1: Signal SIGABRT error on the first line of my App Delegate file. Creating a breakpoint hasn't helped me because I know where the error is coming from.

import UIKit
import Firebase
import FirebaseDatabase


struct postStruct {
    let title : String!
    let message : String!
}

class LoggedInController: UITableViewController {

    var posts = [postStruct]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.delegate = self
        self.tableView.dataSource = self

        let databaseRef = FIRDatabase.database().reference()

        databaseRef.child("Posts").queryOrderedByKey().observe(.childAdded, with: {
            snapshot in

            let snapshotValue = snapshot.value as? NSDictionary
            let title = snapshotValue!["title"] as? String

            let message = snapshotValue!["message"] as? String

            self.posts.insert(postStruct(title: title, message: message), at: 0)
            self.tableView.reloadData()
        })

        post()
    }

    func post(){

        let title = "Title"
        let message = "Message"

        let post : [String : AnyObject] = ["title" : title as AnyObject,
                                           "message": message as AnyObject]

        let databaseRef = FIRDatabase.database().reference()

        databaseRef.child("Posts").childByAutoId().setValue(post)

    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return posts.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell")

    let label1 = cell?.viewWithTag(1) as! UILabel
    label1.text = posts[indexPath.row].message

    let label2 = cell?.viewWithTag(2) as! UILabel
    label2.text = posts[indexPath.row].message

    return cell!
    }
}

Update 2: Here is the new code I used. It's not pretty and only gets the title.

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return posts.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")

    if cell == nil {
        cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
        cell?.textLabel?.text = posts[indexPath.row].title
        cell?.detailTextLabel?.text = posts[indexPath.row].message
        return cell!
    } else {
        let label1 = cell?.viewWithTag(1) as? UILabel
        label1?.text = posts[indexPath.row].title

        let label2 = cell?.viewWithTag(2) as? UILabel
        label2?.text = posts[indexPath.row].message
        return cell!
    }
}

3条回答
男人必须洒脱
2楼-- · 2019-09-05 03:18

Ok this code let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") produces an optional called cell that may or may not contain a a valid instance of UITableViewCell. Optionals in Swift are a way to safeguard against nil values, you can read more about optionals here: Optionals

On the first run when your table view wants to load its data it calls all the required methods of your UITableViewDataSource. The first run is a critical one because there aren't any instances of the UITableViewCell the table view can dequeue yet. To solve your problem you have to do something similar to the code below:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return posts.count
    }

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: "cell")

    if cell == nil {
        cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell?.textLabel?.text = "New value"
        cell?.detailTextLabel?.text = "New value"
        return cell!
    } else {
        cell?.textLabel?.text = "" //reset value
        cell?.detailTextLabel?.text = "" // resetValue
        cell?.textLabel?.text = "New value"
        cell?.detailTextLabel?.text = "New value"
        return cell!
    }
}

Code similar to the one above are usually used to programmatically add an instance of UITableViewCell. However, if you used interface builder to add a prototype cell use the let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) method to dequeue your cells in which case it does not return an optional and you do not need to do all the if / else blocks. Something else I wanted to mention about your code is finding sub views buy their ID will not produce a very object oriented code and that maybe the source of your errors where the compiler can not find the sub views. The better way would be to use one of the built in instances of UITableViewCell such as .default or alliteratively you could subclass the said class and make your very own custom cells.

Hope this helped!

查看更多
成全新的幸福
3楼-- · 2019-09-05 03:23

Try this

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
 {
    let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!

    let label1 = cell.viewWithTag(1) as! UILabel
    label1.text = posts[indexPath.row].message

    let label2 = cell.viewWithTag(2) as! UILabel
    label2.text = posts[indexPath.row].message

    return cell
}

Edited

Make Sure you did these things

UITableViewController

In viewDidLoad()

self.tableView.register(UITableViewCell.self,     forCellReuseIdentifier:"catCell" )
let catNib : UINib = UINib.init(nibName: "TableViewCategoryCell",  bundle: nil)
self.tableView.register(catNib, forCellReuseIdentifier: "TableViewCategoryCell")

In cellForRowAt indexPath

let cell : OPM_AlarmTableViewCategoryCell = tableView.dequeueReusableCell(withIdentifier:"OPM_AlarmTableViewCategoryCell" ) as! OPM_AlarmTableViewCategoryCell!
cell.categoryLabel?.text = "Some Text"
return cell

UITableviewCell.XIB Hope u did these things Custom Class TableViewCell Outlet

UITableviewCell Tableviewcell code Make sure the dot appears so that the @IBOutlet is connected with the xib label

查看更多
Evening l夕情丶
4楼-- · 2019-09-05 03:27

Using dequeueReusableCell, you are accessing cell which doesn't exists. To make your code work change the below line:

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")

To

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
查看更多
登录 后发表回答