加载动画仅在按钮功能后显示(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)的主要内容,如果未能解决你的问题,请参考以下文章

仅在初始渲染后反应 CSS 动画

(Swift)如何通过重新加载 UITableView 根据 UITextfield 输入(动画)显示/隐藏单元格?

Spritekit 动画加载时间

引导按钮和加载动画

如何在 Swift 3 中加载/删除带有动画的自定义视图

如何在swift 3中重新加载没有动画的特定单元格?