在应用程序启动之前执行完成处理程序 [英] Performing a completion handler before app launches

查看:31
本文介绍了在应用程序启动之前执行完成处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过以下两种方式之一打开应用:

I am attempting to open an app in one of two ways:

  1. 如果用户没有保存UserDefaults,那么打开一个WelcomeViewController
  2. 如果用户保存了UserDefaults,则打开一个MenuContainerViewController作为主页
  1. If the user has no UserDefaults saved, then open up a WelcomeViewController
  2. If the user has UserDefaults saved, then open up a MenuContainerViewController as a home page

在第 2 步中,如果保存了 UserDefaults,那么我需要使用 Firebase 登录用户,我通过一个带有完成处理程序的函数获得了该用户.如果是第 2 步,我想在完成块中打开 MenuContainerViewController 而不会出现任何 UI 问题.

In step 2, if there are UserDefaults saved, then I need to log a user in using Firebase which I have through a function with a completion handler. If step 2 is the case, I want to open MenuContainerViewController within the completion block without any UI hiccups.

这是我目前拥有的代码:

Here is the code I have currently:

func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    self.window = UIWindow(frame: UIScreen.main.bounds)

    FirebaseApp.configure()

    guard
        let email = UserDefaults.standard.string(forKey: "email"),
        let password = UserDefaults.standard.string(forKey: "password")
    else {
        // User has no defaults, open welcome screen
        let welcomeViewController = WelcomeViewController()
        self.window?.rootViewController = welcomeViewController
        self.window?.makeKeyAndVisible()
        return true
    }

    // User has defaults saved locally, open home screen of app
    let authentificator = Authentificator()
    authentificator.login(with: email, password) { result, _ in
        if result {
            let menuContainerViewController = MenuContainerViewController()
            self.window?.rootViewController = menuContainerViewController
            self.window?.makeKeyAndVisible()
        }
    }

    return true
}

这是当前 UI 的视频,当我需要运行完成处理程序时,过渡到应用程序并不流畅(有一小秒黑屏).

Here is a video of the current UI, when I need to run the completion handler, the transition is not smooth into the app (there is a brief second with a black screen).

请帮助我弄清楚如何使应用程序顺利启动.

Please help me figure out how to make a smooth app launch.

推荐答案

我不得不在 Firebase 应用程序中处理类似的情况.我通常做的是制作一个 InitialViewController.无论如何,这是始终加载的视图控制器.此视图控制器最初设置为与启动屏幕完全一样.

I've had to handle situations similarly in my Firebase applications. What I typically do is make an InitialViewController. This is the view controller that is always loaded, no matter what. This view controller is initially set up to seamlessly look exactly like the launch screen.

这是InitialViewController 在界面构建器中的样子:

This is what the InitialViewController looks like in the interface builder:

这就是我的启动屏幕的样子:

And this is what my launch screen looks like:

所以当我说它们看起来完全一样时,我的意思是它们看起来完全相同.这个 InitialViewController 的唯一目的是处理这个异步检查并决定下一步做什么,同时看起来像启动屏幕.您甚至可以在两个视图控制器之间复制/粘贴界面构建器元素.

So when I say they look exactly the same, I mean they look exactly the same. The sole purpose of this InitialViewController is to handle this asynchronous check and decide what to do next, all while looking like the launch screen. You may even copy/paste interface builder elements between the two view controllers.

因此,在此 InitialViewController 中,您在 viewDidAppear() 中进行身份验证检查.如果用户已登录,我们对主视图控制器执行 segue.如果没有,我们将用户入职元素动画化到位.展示我的意思的 gif 非常大(维度和数据),因此它们可能需要一些时间来加载.您可以在下面找到每一个:

So, within this InitialViewController, you make the authentication check in viewDidAppear(). If the user is logged in, we perform a segue to the home view controller. If not, we animate the user onboarding elements into place. The gifs demonstrating what I mean are pretty large (dimension-wise and data-wise), so they may take some time to load. You can find each one below:

用户以前登录过.

用户之前未登录.

这是我在 InitialViewController 中执行检查的方式:

This is how I perform the check within InitialViewController:

@IBOutlet var loginButton: UIButton!
@IBOutlet var signupButton: UIButton!
@IBOutlet var stackView: UIStackView!
@IBOutlet var stackViewVerticalCenterConstraint: NSLayoutConstraint!

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    //When the view appears, we want to check to see if the user is logged in.
    //Remember, the interface builder is set up so that this view controller **initially** looks identical to the launch screen
    //This gives the effect that the authentication check is occurring before the app actually finishes launching
    checkLoginStatus()
}

func checkLoginStatus() {
    //If the user was previously logged in, go ahead and segue to the main app without making them login again

    guard
        let email = UserDefaults.standard.string(forKey: "email"),
        let password = UserDefaults.standard.string(forKey: "password")
    else {
        // User has no defaults, animate onboarding elements into place
        presentElements()
        return
    }

    let authentificator = Authentificator()
    authentificator.login(with: email, password) { result, _ in
        if result {
            //User is authenticated, perform the segue to the first view controller you want the user to see when they are logged in
            self.performSegue(withIdentifier: "SkipLogin", sender: self)
        }
    }
}

func presentElements() {

    //This is the part where the illusion comes into play
    //The storyboard elements, like the login and signup buttons were always here, they were just hidden
    //Now, we are going to animate the onboarding UI elements into place
    //If this function is never called, then the user will be unaware that the launchscreen was even replaced with this view controller that handles the authentication check for us

    //Make buttons visible, but...
    loginButton.isHidden = false
    signupButton.isHidden = false

    //...set their alpha to 0
    loginButton.alpha = 0
    signupButton.alpha = 0

    //Calculate distance to slide up
    //(stackView is the stack view that holds our elements like loginButton and signupButton. It is invisible, but it contains these buttons.)
    //(stackViewVerticalCenterConstraint is the NSLayoutConstraint that determines our stackView's vertical position)
    self.stackViewVerticalCenterConstraint.constant = (view.frame.height / 2) + (stackView.frame.height / 2)

    //After half a second, we are going to animate the UI elements into place
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        UIView.animate(withDuration: 0.75) {
            self.loginButton.alpha = 1
            self.signupButton.alpha = 1

            //Create correct vertical position for stackView
            self.stackViewVerticalCenterConstraint.constant = (self.view.frame.height - self.navigationController!.navigationBar.frame.size.height - self.signupButton.frame.maxY - (self.stackView.frame.size.height / 2)) / 3
            self.view.layoutIfNeeded()
        }
    }   
}

这篇关于在应用程序启动之前执行完成处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆