简体   繁体   中英

Why I can't populate my uiTableView with cells filled with data fetched from json?

This is my class:

import UIKit
import SwiftyJSON

class ListOfEvents: UIViewController, UITableViewDataSource, UITableViewDelegate {

    var tableView:UITableView?
    var items = NSMutableArray()

    override func viewDidLoad() {
        let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
        self.tableView = UITableView(frame: frame)
        self.tableView?.dataSource = self
        self.tableView?.delegate = self
        self.view.addSubview(self.tableView!)
        print("aaa")
        getAllRequests()

    }


    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.items.count;
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        tableView.registerClass(SingleEventCell.self, forCellReuseIdentifier: "cell")
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SingleEventCell

        let user:JSON =  JSON(self.items[indexPath.row])
       print(user["username"].description)
       // cell.username.text = user["username"].description
       // cell.descr.text = "ggg"
        print(cell)
        return cell
    }

    func getAllRequests() {
        print("getAllRequests")
        RestApiManager.sharedInstance.getRequests { json in
            let results = json//["username"]
            for (index: String, subJson: JSON) in results {
                let user: AnyObject = JSON.object
                self.items.addObject(user)
                dispatch_async(dispatch_get_main_queue(),{
                    self.tableView?.reloadData()
                })
            }
        }
    }

}

I try to fill my custom UITableViewCell s with data fetched from webservice. My problem is that:

print(user["username"].description) //that prints perfectly

but if I uncomment one of the lines below:

// cell.username.text = user["username"].description 
// cell.descr.text = "ggg"

I'm getting the error:

fatal error: unexpectedly found nil while unwrapping an Optional value

I suspect that I try to refer to the cells before they are initialized/visible for the algorithm, but if so - how can I prevent it? I'm using viewDidLoad() method but that doesn't change anything - the error is still the same.

How can I fix it?

====EDIT

@vadian suggested that it's a different table view in storyboard and in the code.I went with that advice and created an outlet for the tableview from storyboard and in all places in code changed the reference from tableview to newly created outlet:

import UIKit import SwiftyJSON

class ListOfEvents: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var tview: UITableView!
    var tableView:UITableView?
    var items = NSMutableArray()

    override func viewDidLoad() {
        let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
        tview = UITableView(frame: frame)
        tview.dataSource = self
        tview.delegate = self
        self.view.addSubview(tview!)
        print("aaa")
        getAllRequests()

    }


    func tableView(tview: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.items.count;
    }

    func tableView(tview: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        tview.registerClass(SingleEventCell.self, forCellReuseIdentifier: "cell")
        let cell = tview.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SingleEventCell

        let user:JSON =  JSON(self.items[indexPath.row])
       print(user["username"].description)
       // cell.username.text = user["username"].description
        cell.descr.text = "ggg"
        print(cell)
        return cell
    }

    func getAllRequests() {
        print("getAllRequests")
        RestApiManager.sharedInstance.getRequests { json in
            let results = json
            for (index: String, subJson: JSON) in results {
                let user: AnyObject = JSON.object
                self.items.addObject(user)
                dispatch_async(dispatch_get_main_queue(),{
                    self.tview.reloadData()
                })
            }
        }
    }

}

But now when I run it I'm getting error;

fatal error: unexpectedly found nil while unwrapping an Optional value

here:

tview.dataSource = self

Is there a way to fix it?

One obvious problem is this line:

tview = UITableView(frame: frame)

That is not how you create a table view in code. You must call its designated initializer , which is initWithFrame:style: . For example:

tview = UITableView(frame: frame, style: .Plain)

Note, however, that your code has various other problems that could also be an issue here. For example, this line:

let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)

That line depends on self.view having a frame, and a frame of sufficient size (ie more than 100 pixels in height). But you don't know this, because in viewDidLoad , self.view merely exists — it has not yet achieved its final frame because it has not yet been put into the interface and laid out. So that line is very risky at that point.

I'm guessing that you probably don't have your username and/or descr outlets set up correctly inside your SingleEventCell .

Take a look here to know how to connect your outlets correctly.

Your problem is, that your JSON object is not yet fetched at the time tableView.cellForRowAtIndexPath wants to load it, therefor giving you an unexpectedly found nil error.

What You can do is this:

Create a new Delegate protocol:

protocol NetworkingDelegate {

    func didFinishLoadingData()
}

Create a class Networking where you put your API request in:

class Networking {

var networkingDelegate: NetworkingDelegate?


// (I am using Alamofire here so the function itself will differ a little.)

    func getAllRequests() {

        Alamofire.request(.GET, "YOUR_API_ENDPOINT")
            .responseJSON { response in

                if response.result.isSuccess {
                    let jsonObject = JSON(response.result.value!)

                    User.populateUserDataSourceFromJSON(jsonObject)

                    self.dispatchAndRefresh()
                }
        }
    }
}


func dispatchAndRefresh() {
    dispatch_async(dispatch_get_main_queue()) {
        if let delegate = self.networkingDelegate {
            delegate.didFinishLoadingData()
        }
    }
}

In your Modelclass have a function called populateUserDataSourceFromJSON

class User {

let name = String()
let age = Int()
//...

init() {
}


class func populateUserDataSourceFromJSON(json: JSON) {

    userDataSource = [User]()

    for (_, user): (String, JSON) in json {

        let currentUser = User()

        currentUser.name = user["name"].stringValue
        currentUser.age = user["age"].intValue
        //...

        userDataSource.append(currentUser)
    }
}

In your ViewController add the Delegate we created:

class ListOfEvents: UIViewController, UITableViewDataSource, UITableViewDelegate, NetworkingDelegate { 
    //...

create a reference of your Networking class:

let networking = Networking()

and set it's delegate to self:

override func viewDidLoad() {
    super.viewDidLoad()

    networking.networkingDelegate = self
    //...

Only thing left to do is to make your ViewController conform to the Delegate by implementing didFinishloadingData()

func didFinishLoadingData() {

    tableView.reloadData()
}

I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM