如何将JSON加载到TableView中?

问题描述:

我想将一个练习JSON加载到表视图中,我有一个服务函数从一个源代码获取数据作为JSON,并有一个视图控制器和一个表,我想要加载信息到。代码中没有错误,但表格加载空白行,调试部分通过打印命令显示JSON数据。我是一个初学者,所以我很确定我错过了一个核心元素,但无法解决它!如何将JSON加载到TableView中?

API服务

class ApiService { 

static var swiftyJsonVar:JSON? 

class func getExerciseData() { 

    Alamofire.request("https://wger.de/api/v2/exercise/?format=json").responseJSON { (responseData) -> Void in 
     if((responseData.result.value) != nil) { 
      swiftyJsonVar = JSON(responseData.result.value!) 
      print(swiftyJsonVar ?? nil) 
     } 

    } 
} 

视图控制器

class ExerciseDatabaseController: UIViewController, UITableViewDataSource, UITableViewDelegate { 

@IBOutlet weak var ExerciseSearchField: UISearchBar! 
@IBOutlet weak var ExercisesTableView: UITableView! 

var arrRes = [[String:AnyObject]]() // Array of dictionary 

override func viewDidLoad() { 
    super.viewDidLoad() 

    let arrRes = ApiService.getExerciseData() 

    if let resData = ApiService.swiftyJsonVar?["exercise"].arrayObject { 
     self.arrRes = resData as! [[String:AnyObject]] 
    } 
    if self.arrRes.count > 0 { 
     self.ExercisesTableView.reloadData() 
    } 

    print(arrRes) 
    // Do any additional setup after loading the view. 
} 

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 
    var dict = arrRes[indexPath.row] 
    cell.textLabel?.text = dict["name"] as? String 
    cell.detailTextLabel?.text = dict["description"] as? String 
    return cell 
} 
+1

您的网络电话是异步的。这是你的问题。当你收到数据时,重新加载tableView。在'getExerciseData()'上使用闭包。 – Larme

你应该异步加载你的JSON,这意味着你应该在,让您的alamofire调用方法关闭。

class ApiService { 

class func getExerciseData(completion: @escaping ([[String: AnyObject]]) ->()) { 

    Alamofire.request("https://wger.de/api/v2/exercise/?format=json").responseJSON { (responseData) -> Void in 
     guard let jsonResponse = responseData.result.value else { 
     //possibly put some sort of protection, or what you want to do if there is not a response here 
     return 
     } 
     //instead of creating a variable for swiftyJsonVar in this class, 
     //you want to use a completion to send the array of dictionaries to the tableview asynchronously, 
     //that way it doesn't load blank 
     //I'm not super familiar with swifty json(sorry). What I normally do is below. 
     let swiftyJsonVar = JSON(jsonResponse) 
     guard let dictArray = swiftyJsonVar["exercise"].arrayObject as? [[String: AnyObject]] else { 
     //some sort of protection here if this fails 
     return 
     } 
     completion(dictArray) 
    } 
} 

所以,现在我们已经取得了我们的异步调用(通常要做到这一点,只要你在视觉上,这不是已经预装了互联网电话/应用程序中保存的地方显示信息)。

接下来,我们希望在tableview加载时在tableview中显示这些信息。

class ExerciseDatabaseController: UIViewController, UITableViewDataSource, UITableViewDelegate { 
    //these should start with lower cases(exerciseSearchField), never uppercased 
    @IBOutlet weak var ExerciseSearchField: UISearchBar! 
    @IBOutlet weak var ExercisesTableView: UITableView! 

    var arrRes = [[String:AnyObject]]() // Array of dictionary 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     //you said you would use these delegates up top when you created the class, so you have to set them 
     ExercisesTableView.delegate = self 
     ExercisesTableView.dataSource = self 
     fetchData() 
     // Do any additional setup after loading the view. 
    } 

    //this method will make the api call 
    //you'll notice that if you set breakpoints, it will reach the end of the method before hitting self?.arrRes = dictArray 
    //this is normal and how asynchronous calls work, look into tableview threading for a deeper explanation of why that is. It is super important to understand threading in iOS 
    //once it gets data back from the API call, it will go to the main thread and tell the tableview to reload with that data 

    func fetchData() { 
     ApiService.getExerciseData { [weak self] (dictArray) in 
     self?.arrRes = dictArray 
     print(self?.arrRes) 
     if self?.arrRes.count > 0 { 
      DispatchQueue.main.async { 
      self?.ExercisesTableView.reloadData() 
      } 
     } 
     } 
    } 

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 
     var dict = arrRes[indexPath.row] 
     cell.textLabel?.text = dict["name"] as? String 
     cell.detailTextLabel?.text = dict["description"] as? String 
     return cell 
    } 

你会看到我在上面使用[weak self]。欲了解更多的为什么是必要的异步网络电话的解释/无论何时使用闭包,你可以在这里阅读: http://krakendev.io/blog/weak-and-unowned-references-in-swift

有很多其他资源的阅读弱和强引用/父子东西在iOS版用快速的谷歌搜索。并且,在iOS中追求异步/同步的研究。这两个主题在开始学习时非常重要。

+0

我尝试使用此代码时遇到以下错误,但它看起来正确吗? 1.无法将类型'JSON'的值转换为期望的参数类型'[[String:AnyObject]]'2.在闭包中引用属性'arrRes'需要明确的'self'。明确捕获语义3.可选类型'Int?'的值不打开;你的意思是使用'!'要么 '?'? 4.类型'ExerciseDatabaseController'的值没有成员'ExerciseTableView' – infernouk

+0

现在修复一些东西,一会儿。 –

+0

如果你看看API调用,我们需要说明JSON响应是以字典数组的形式返回的。你会看到我添加为? [[String:AnyObject]]。只要答案是这种格式,那就应该有效。如果这不起作用,请给我一个折点,然后为您提供JSON响应,以便我可以查看它。 arrRes的事情相当简单,只需添加自我?在它之前。如果你点击它,编译器警告可能会为你做。没有成员的事是因为在ExercisesTableView.reload数据中的拼写错误。我写了ExerciseTableView(忘了s) –

刷新您的tableView一旦收到来自异步请求的JSON数据。所以,你的

self.ExercisesTableView.reloadData() 

将里面去

Alamofire.request("https://wger.de/api/v2/exercise/?format=json").responseJSON { (responseData) -> Void in 
    if((responseData.result.value) != nil) { 
     swiftyJsonVar = JSON(responseData.result.value!) 
     print(swiftyJsonVar ?? nil) 
    } 
} 
+0

但是,如果我把它放在Alamofire请求中,那么它会在不同的范围内,并且不会看到ExercisesTableView能够重新加载它? – infernouk

+0

你为什么要在不同的地方提出要求? – SaylorTwift2

+0

,因为对于代码库的可扩展性,您不希望将您的服务放入viewcontrollers中,请参阅Phil Hudsons上面的响应 – infernouk