如何将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
}
你应该异步加载你的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中追求异步/同步的研究。这两个主题在开始学习时非常重要。
我尝试使用此代码时遇到以下错误,但它看起来正确吗? 1.无法将类型'JSON'的值转换为期望的参数类型'[[String:AnyObject]]'2.在闭包中引用属性'arrRes'需要明确的'self'。明确捕获语义3.可选类型'Int?'的值不打开;你的意思是使用'!'要么 '?'? 4.类型'ExerciseDatabaseController'的值没有成员'ExerciseTableView' – infernouk
现在修复一些东西,一会儿。 –
如果你看看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)
}
}
但是,如果我把它放在Alamofire请求中,那么它会在不同的范围内,并且不会看到ExercisesTableView能够重新加载它? – infernouk
你为什么要在不同的地方提出要求? – SaylorTwift2
,因为对于代码库的可扩展性,您不希望将您的服务放入viewcontrollers中,请参阅Phil Hudsons上面的响应 – infernouk
您的网络电话是异步的。这是你的问题。当你收到数据时,重新加载tableView。在'getExerciseData()'上使用闭包。 – Larme