我在学习Swift / iOS的过程中很早。我尝试了解异步请求,以及在结果进入时更新tableview的最佳方法。
我正在制作一个简单的电影列表应用并使用' themoviedatabase' API。这是代码,对我来说不起作用。目前它搜索星球大战的力量唤醒了#39;并且工作正常,从API获得预期的单个结果。到目前为止竖起大拇指。
但是当我尝试用我的结果更新tableview时......问题!
以下代码是我在SearchVC.swift自定义视图控制器中创建的函数的一部分。我之前提到过这个我已经研究过的其他帖子(this one和this one)似乎在说你应该只从主线程更新UI - 我是不是已经完全不知道自己是否这样做了?
SearchVC.swift:
import UIKit
class SearchVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
var theResults = [SearchResult]()
@IBOutlet weak var searchField: MaterialTextField!
@IBOutlet weak var searchBtn: MaterialButton!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 106
// do I need to update self.theResults[] and do self.tableView.reloadData() in here?
}
@IBAction func searchBtnPressed(sender: MaterialButton) {
print("Search button pressed!")
performSearch(searchField.text)
}
func performSearch(searchText: String?) {
if let searchString = searchText where searchString != "" {
self.theResults = [] // clear the search results array
print("Searching for: \(searchString)")
let tmdb_apikey = "----just gonna take out my API key----"
let tmdb_query = searchString
let tmdb_query_escaped = tmdb_query.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url = NSURL(string: "http://api.themoviedb.org/3/search/movie?query=\(tmdb_query_escaped)&api_key=\(tmdb_apikey)&page=1")!
let request = NSMutableURLRequest(URL: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
if let response = response, data = data {
do {
// Convert data to JSON
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
// Convert JSON into a Dictionary
if let searchDict = json as? Dictionary<String,AnyObject> {
// Iterate through dictionary and add results to the results array
if let searchResults = searchDict["results"] as? [Dictionary<String,AnyObject>] {
for result in searchResults {
if let title = result["title"] as? String, let posterPath = result["poster_path"] as? String, let id = result["id"] as? Int {
let idStr = String(id) // cast id int to string, for storing in our custom class
let thisResult = SearchResult(title: title, posterUrl: posterPath, id: idStr)
print("Results:")
print(thisResult.movieTitle)
self.theResults.append(thisResult) // adds result to array
}
}
print("Num of results to display inside closure: \(self.theResults.count)")
// if I put 'self.tableView.reloadData()' here, which is where I thought it should go (once all the results have been added to my array) I get an error: "This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release." ... and loads of stack dumps!
} else {
print("ERROR: No 'results'?")
}
} else {
print("ERROR: Got data, converted to JSON, but could not convert into a Dictionary.")
}
} catch {
print("ERROR: Could not convert data into JSON")
}
} else {
print(error)
}
}
task.resume()
// This outputs 0, before results are delivered, because the data request is asynchronous? How do I deal with this?!
print("Num of results to display: \(theResults.count)")
self.tableView.reloadData()
} else {
// no search term specified
print("No search term specified. Doing nothing")
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theResults.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = theResults[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("SearchResultCell") as? SearchResultCell {
print("Reconfiguring cell")
cell.configureSearchCell(post)
return cell
} else {
print("Configuring cell")
return SearchResultCell()
}
}
configureSearchCell()在单独的 SearchResultCell.swift 中定义:
import UIKit
class SearchResultCell: UITableViewCell {
@IBOutlet weak var moviePoster: UIImageView!
@IBOutlet weak var movieTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configureSearchCell(post: SearchResult) {
self.movieTitle.text = post.movieTitle
if post.moviePosterUrl != "" {
// doing nothing here for the moment, just want to get the movie title added to a cell before worrying about adding the poster image!
}
}
}
performSearch 功能在很大程度上似乎正在起作用,因为它输出以下内容:
Search button pressed!
Searching for: star wars force awakens
Num of results to display: 0
Results:
Star Wars: The Force Awakens
Num of results to display inside closure: 1
所以我得到了结果,但是无法更新我的表,显然是因为调用是异步的,并且在结果仍在处理时调用了第3行输出(并且告诉我在那里有0个结果)点)。我知道了。
但是,当我尝试在我的代码中间调用self.tableView.reloadData()
时,在解析结果之后(print()告诉我现在有1个结果),我得到更多上面代码中的注释中提到的错误,警告我通过后台线程更新autolayout!
无论哪种方式,我的tableview中都没有更新任何内容。结果根本没有添加到单元格中。
如何绕过我需要做的事情?
我意识到我可能只是&#39;需要一个关于异步调用的入门知识,以及如何在Swift / xcode中更新表视图,但是到目前为止,很多搜索还没有找到解决这个问题的任何东西,所以我在这里。希望有些知识渊博的人可以对新手iOS程序员表示同情!
提前感谢任何提示。