加载动画仅在按钮功能后显示(Swift)
Posted
技术标签:
【中文标题】加载动画仅在按钮功能后显示(Swift)【英文标题】:Loading Animation Only Showing After Button Function (Swift) 【发布时间】:2016-01-23 00:32:59 【问题描述】:我试图在生成新数组并将其保存到 NSUserDefaults 时显示加载动画。动画函数在如下所示的函数中调用:
func getOrCreateCalendar(forYear: Int) -> [DetailedDate]
if forYear == date.getDateComponents(todaysDetailDate.date).year && calendar.count > 0
print("forYear == today's year and the current calendar has been RETURNED FROM MEMORY")
return calendar
else
let existingCalendar = getExistingCalendar(forYear)
if existingCalendar != nil
return existingCalendar!
else
SwiftSpinner.show("Generating new calendar for \(forYear)", animated: true)
let calendar = generateCalendarFor(forYear)
print("GENERATED NEW calendar for \(forYear)")
SwiftSpinner.show("Saving calendar", animated: false)
saveNew(calendar, year: forYear)
SwiftSpinner.hide()
return calendar
我正在使用如上所示的开源 SwiftSpinner 动画。 我在整个代码中的多个位置调用此函数(尤其是整个应用程序中的按钮)。
但是,每当调用此函数时,动画才会显示,直到整个过程完成。 为什么是这样?如何在生成和保存新内容时显示动画,然后在完成后将其关闭?
【问题讨论】:
您是否尝试在后台线程中创建并保存您的日历?整个 UI 在主线程中处理;如果你阻塞了主线程,用户界面就会卡住。 【参考方案1】:尝试使用以下代码在后台队列中执行操作:
SwiftSpinner.show("Generating new calendar for \(forYear)", animated: true)
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
let calendar = generateCalendarFor(forYear)
print("GENERATED NEW calendar for \(forYear)")
dispatch_async(dispatch_get_main_queue(),
SwiftSpinner.show("Saving calendar", animated: false)
)
saveNew(calendar, year: forYear)
dispatch_async(dispatch_get_main_queue(),
SwiftSpinner.hide()
onCompletion(calendar)
)
)
你在这里做什么:
您创建了一个后台队列 创建日历 从主队列更新 UI(dispatch_async(dispatch_get_main_queue(), SwiftSpinner.show("Saving calendar", animated: false) )
(主队列很重要,因为 UI 只能从主队列修改)
保存日历(再次在后台队列中)
调用一个自定义函数(这里我在主队列中将它命名为onCompletion
,它对日历做一些事情(替换return calendar
)
您应该确保在 dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ... )
被调用并在 onCompletion
中再次启用之前禁用您的 UI 按钮等,否则用户将能够在您的应用程序运行时按下某些按钮(并且可能会更改某些数据被队列使用 -> 不好)。
原因: UI 从主队列更新,但通常不会在您调用代码更新后立即更新;如果您阻止主队列,您的 UI 将无法更新。因此,我们在后台队列中完成工作,以便您的主队列可以更新 UI。
详细说明:
你调用dispatch_async
(来自GrandCentralDispatch)创建一个优先级为QOS_CLASS_USER_INITIATED
(~正常优先级)的新队列。然后它将花括号中的代码传递给这个队列;操作系统将在单独的线程中执行队列。
由于只能从主队列修改 UI,因此我们再次使用 dispatch_async
将更新 UI (SwiftSpinner.show("Saving calendar", animated: false)
) 的代码传递到主队列。
完成函数的使用是必要的,因为代码/队列在后台执行并且独立于getOrCreateCalendar
;您的函数可能会在队列完成并创建并保存日历之前返回。
【讨论】:
感谢@k-biermann 的详细解释和详细回答。我认为这正是我需要做的事情。我是异步代码的新手,感谢您的帮助。我仍然有一个问题。我在许多地方返回这个数组(又名日历)来替换我的主日历以及一些额外的日历。如何使用完成函数来替换该函数每个块的返回值?我是否也需要在调用此函数的每个地方都重构我的代码? 嗯,这将是最简单的解决方案。您还可以将空引用作为参数传递或使用全局变量。但这并不像听起来那么容易,因为您必须避免碰撞等。所以,是的,我会重构你的代码,并用对onComplete
的调用替换每个返回。
重新格式化了我的代码。使用了一个完成函数,我在每次调用时作为尾随闭包传入。完美运行。以上是关于加载动画仅在按钮功能后显示(Swift)的主要内容,如果未能解决你的问题,请参考以下文章