Swift 3- 从主线程更新 UI
Posted
技术标签:
【中文标题】Swift 3- 从主线程更新 UI【英文标题】:Swift 3- Update UI from main thread 【发布时间】:2017-01-08 21:43:57 【问题描述】:我想在后台线程中加载数据并在主线程上更新 tableview/UI。根据here 关于线程的指示,我想知道下面的代码是否是解决问题的方法。我正在尝试在用户滚动到特定索引时加载更多数据,并希望确保 UI 不会由于线程而冻结。谢谢!
func loadMore ()
guard !self.reachedEndOfItems else
return
self.offset = self.offset! + 10
print("load more offset: \(self.offset)")
var start = 0
var end = 0
isPullToRefresh = false
let userCreds = UserDefaults.standard
var getReqString = ""
if userCreds.bool(forKey: "client_journal") == true || userCreds.bool(forKey: "user_journal") == true
var pageNum = ""
if let pgNum = currentPgNum
print(pgNum)
pageNum = String(pgNum)
var filterEntryType = ""
if let entryTypeStr = filtEntryType
filterEntryType = entryTypeStr
var filterUserId = ""
if let userId = filtUserId
filterUserId = userId
getReqString = "https://gethealthie.com/selected_entries.json?page=\(pageNum)&user_id=\(filterUserId)&entry_type=\(filterEntryType)&entry_filter="
else
if let pgNum = currentPgNum
print(pgNum)
getReqString = "https://gethealthie.com/entries.json?page=\(pgNum)"
BXProgressHUD.showHUDAddedTo(self.view)
let request = Alamofire.request(getReqString, method: .get, headers: [
"access-token": userCreds.object(forKey: "access-token")! as! String,
"client": userCreds.object(forKey: "client")! as! String,
"token-type": userCreds.object(forKey: "token-type")! as! String,
"uid": userCreds.object(forKey: "uid")! as! String,
"expiry": userCreds.object(forKey: "expiry")! as! String
]).responseJSON (response:DataResponse<Any>) in
print(response.response)
let json = JSON(data: response.data!)
print(json)
print("yes")
print(json.count)
if userCreds.bool(forKey: "client_journal") == true || userCreds.bool(forKey: "user_journal") == true
self.totalEntries = json["entries"].count
let totalEntryCount = json["entries"].count
start = 0
end = totalEntryCount
else
self.totalEntries = json["entries"].count
let totalEntryCount = json["entries"].count
start = 0
end = totalEntryCount
if self.totalEntries == 0
BXProgressHUD.hideHUDForView(self.view);
else if end <= self.totalEntries
var jourIdx = 0
let newPatient = Patient()
let newDietitian = Dietitian()
for i in start ..< end
let allEntries = json["entries"]
print(allEntries)
print("Entry count in loadMore is \(allEntries.count)")
let entry = allEntries[i]
print(entry)
let category = entry["category"]
print(category)
let name = entry["entry_comments"]
let k = name["id"]
var indexStr = String(i)
//entry attributes
self.jsonIdx.add(indexStr)
self.type.add(entry["type"].stringValue)
self.desc.add(entry["description"].stringValue)
self.category.add(entry["category"].stringValue)
//food cell- metric stat == healthy int
self.metric_stat.add(entry["metric_stat"].stringValue)
self.dateCreate.add(entry["created_at"].stringValue)
self.viewed.add(entry["viewed"].stringValue)
self.seenStatusArr.add(entry["viewed"].stringValue)
self.comments.add(entry["entry_comments"].rawValue)
self.entryType.add(entry["category"].stringValue)
// "category" : entryType as AnyObject]
let posterInfo = entry["poster"]
let first = posterInfo["first_name"].stringValue
let last = posterInfo["last_name"].stringValue
let full = first + " " + last
self.captionName.add(full)
//food cell subcat
self.hungerInt.add(entry["percieved_hungriness"].stringValue)
self.prehunger.add(entry["ed_prehunger_string"].stringValue)
self.posthunger.add(entry["ed_posthunger_string"].stringValue)
self.emotions.add(entry["emotions_string"].stringValue)
self.reflection.add(entry["reflection"].stringValue)
print(self.comments)
self.id.add(entry["id"].stringValue)
self.entryImages.add(entry["image_url"].stringValue)
if i == end - 1
userCreds.set(json.count, forKey: "oldJsonCount")
BXProgressHUD.hideHUDForView(self.view)
DispatchQueue.main.async
self.tableView.reloadData()
else
var reachedEndOfItems = true
BXProgressHUD.hideHUDForView(self.view);
print("reached the end")
【问题讨论】:
如何运行这段代码,看看它是否真的冻结了 UI?它会给你肯定的答案:) 似乎您只在主队列中执行“reloadData”。您还需要将所需的所有内容放入后台线程,因为如果您不放入,它仍然会在主线程中。尽管例如网络默认情况下在后台线程中运行,但在您的情况下它不是。 @TungFam 我已经尝试运行它,当我在特定索引处加载更多数据时,它似乎冻结了 UI。这就是为什么我试图确定问题是否是线程。之前有人告诉我,我正在后台线程上更新 UI。 @Rob 你能澄清一下你所说的冗余是什么意思吗?我应该在重新加载 tableview 时删除DispatchQueue.main.async
吗?
@Rob 感谢您的详细回复。那么我需要将 Alamofire 请求添加到后台线程吗?以前它被包裹在DispatchQueue.global(qos: .background).async
中以在后台线程中加载数据,DispatchQueue.main.async
以在主线程上重新加载表视图。但是,有人告诉我 Alamofire 是异步的,我不需要全局线程。另外,据说我在这里从后台线程更新了我的 UI -> ***.com/questions/41524332/…
【参考方案1】:
在您的代码示例中,您将reloadData
分派到主队列。但这是不必要的,因为 responseJSON
的关闭已经在主队列上运行,所以不需要调度任何东西。因此,您应该将reloadData
的分派移除到主队列。
现在,如果您使用URLSession
,它默认在后台队列上运行闭包,或者如果您明确提供后台队列作为responseJSON
的queue
参数,那么,是的,您将调度@ 987654328@ 到主队列。但这并不是您唯一需要确保分派到主队列的事情,因为您的模型更新和 HUD 更新也应该在主队列上运行。但这没有实际意义,因为这个 responseJSON
已经在主队列上运行其完成处理程序。
然后,在 cmets 中,您稍后会询问是否所有这些都在主队列上运行,是否应该像在 a previous question 中所做的那样将所有这些分派到后台队列(大概是为了避免阻塞主队列)。
事实证明,这不是必需的(也不可取),因为当responseJSON
完成处理程序中的响应处理在主队列上运行时,网络请求本身是异步执行的。如果您在闭包中执行计算密集型操作,您只会将完成处理程序代码分派到后台队列(或将后台队列指定为responseJSON
的参数)。但是您不必担心网络请求会阻塞主队列。
归根结底,Alamofire 让这一切变得简单,它异步运行请求,但在主队列上运行其完成处理程序。它消除了您在使用URLSession
时弄乱的大部分手动 GCD 代码。
【讨论】:
以上是关于Swift 3- 从主线程更新 UI的主要内容,如果未能解决你的问题,请参考以下文章
swift 笔记:iOS与JavaScript的交互(二):JavaScriptCore:20。更新UI相关的操作需要放到主线程中
从主线程中删除 AlamofireImage responseImage
MFC:如何从主线程使用 MsgWaitForMultipleObjects() 来等待多个线程完成使用 SendMessage()?