{"id":3178,"date":"2022-08-11T10:33:34","date_gmt":"2022-08-11T08:33:34","guid":{"rendered":"https:\/\/www.albertopasca.it\/whiletrue\/?p=3178"},"modified":"2022-08-11T10:34:39","modified_gmt":"2022-08-11T08:34:39","slug":"swift-simple-full-screen-spinner","status":"publish","type":"post","link":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/","title":{"rendered":"Swift &#8211; Simple full screen loader"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hello,<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">resuming an old post, that help you to create an <strong>UIAlert extension<\/strong> to show a modal popup, <a href=\"https:\/\/www.albertopasca.it\/whiletrue\/objective-c-modal-view-in-navigation-and-tabbar-controller-projects\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.albertopasca.it\/whiletrue\/objective-c-modal-view-in-navigation-and-tabbar-controller-projects\/<\/a> , I&#8217;ve created a new modern implementation that use <strong>UIWindow<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The scope is to show a spinner (or a <em>Lottie<\/em> spinner, or whatever you prefer), centered in the screen with automatic or manually dismission. Background can be blurred or colored, like this screen:<\/p>\n\n\n\n<!--more-->\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png?resize=355%2C768&#038;ssl=1\" alt=\"\" class=\"wp-image-3179\" width=\"355\" height=\"768\" srcset=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51.png?resize=473%2C1024&amp;ssl=1 473w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51.png?resize=139%2C300&amp;ssl=1 139w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51.png?resize=768%2C1662&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51.png?resize=710%2C1536&amp;ssl=1 710w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51.png?resize=946%2C2048&amp;ssl=1 946w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51.png?w=1170&amp;ssl=1 1170w\" sizes=\"auto, (max-width: 355px) 100vw, 355px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Show me the code &#x1f6a7;:<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create a new <strong>SpinnerManager<\/strong> class and make it singleton (singleton because logically, no you can show only one spinner each time&#8230;).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">class SpinnerManager {\n    static let shared: SpinnerManager = { SpinnerManager() }()\n    \n    func showAlert() {\n\n    }\n        \n    func dismiss() {\n\n    }\n    \n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now you can show or hide the spinner, calling <strong>SpinnerManager.shared.showAlert()<\/strong> and dismiss using <strong>SpinnerManager.shared.dismiss()<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>C<\/strong>reate the real Spinner &#x1f680;:<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add a new <strong>UIViewController<\/strong>, in the way you prefer, using Storyboards or programmatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this example we create a simple UIViewController programmatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The next class adds using constraints, two views, <strong>a rounded white view<\/strong> and an <strong>UIActivityIndicator<\/strong> on top. Nothing else.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">class SpinnerViewController: UIViewController {\n\n    override func loadView() {\n        super.loadView()\n        \n        let spinner = UIView(frame: .zero)\n        spinner.backgroundColor = .white\n        spinner.layer.cornerRadius = 18\n        spinner.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]\n        spinner.layer.shadowOffset = CGSize(width: 0, height: 0)\n        spinner.layer.shadowRadius = 6\n        spinner.layer.shadowOpacity = 0.1\n\n        let activity = UIActivityIndicatorView(frame: spinner.bounds)\n        activity.color = .red\n        activity.style = .large\n        activity.startAnimating()\n        spinner.addSubview(activity)\n\n        self.view.addSubview( spinner )\n        \n        spinner.translatesAutoresizingMaskIntoConstraints = false\n        let horizontalContainerConstraint = NSLayoutConstraint(item: spinner, attribute: .centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)\n        let verticalContainerConstraint = NSLayoutConstraint(item: spinner, attribute: .centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)\n        let widthContainerConstraint = NSLayoutConstraint(item: spinner, attribute: .width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100)\n        let heightContainerConstraint = NSLayoutConstraint(item: spinner, attribute: .height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100)\n        NSLayoutConstraint.activate([\n            horizontalContainerConstraint,\n            verticalContainerConstraint,\n            widthContainerConstraint,\n            heightContainerConstraint\n        ])\n\n        activity.translatesAutoresizingMaskIntoConstraints = false\n        let horizontalActivityConstraint = NSLayoutConstraint(item: activity, attribute: .centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)\n        let verticalActivityConstraint = NSLayoutConstraint(item: activity, attribute: .centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)\n        NSLayoutConstraint.activate([\n            horizontalActivityConstraint,\n            verticalActivityConstraint\n        ])\n    }\n\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Now we have the basic SpinnerManager and a new view-controller.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"370\" src=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/3nywsh.jpg?resize=500%2C370&#038;ssl=1\" alt=\"\" class=\"wp-image-3182\" srcset=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/3nywsh.jpg?w=500&amp;ssl=1 500w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/3nywsh.jpg?resize=300%2C222&amp;ssl=1 300w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s add some logic in our manager.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">class SpinnerManager {\n    [...]\n\n    private lazy var spinnerVC: SpinnerViewController = SpinnerViewController()\n    private var blankWindow: UIWindow?\n\n    func showAlert() {      \n        if let currentWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {\n            blankWindow = UIWindow(windowScene: currentWindowScene)\n            blankWindow?.backgroundColor = UIColor.black.withAlphaComponent(0.25)\n            blankWindow?.windowLevel = .alert\n            blankWindow?.rootViewController = spinnerVC\n            blankWindow?.makeKeyAndVisible()\n        }\n    }\n    \n    func dismiss() {\n        blankWindow = nil\n    }\n    \n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">the blankWindow is on &#8220;<strong>windowLevel<\/strong>&#8221; = <strong>.alert<\/strong>, this mean that the spinner is shown at the same layer of the system modal alert, or better, on top of all other windows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Basically <em>this code works<\/em>, you have a new <strong>SpinnerViewController<\/strong>, shown or hidden by the two relative functions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But we want to add more features, like automatic dismission (timeout).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>A<\/strong>dding timeout dismission &#x23f0;<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We need now to create a <strong>Timer<\/strong> and a callback closure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">class SpinnerManager {\n    [...]\n    private var timeoutCallback: (() -&gt; Void)?\n    private var timeoutHandlerTimer: Timer?\n    [...]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">and pass it to the <strong>showAlert<\/strong> function, using if you prefer a default value (in my case 30 seconds):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">func showAlert(\n    autodismissTimeout: Double = 30.0,\n    timeoutCallback: (() -&gt; Void)? = nil\n) {\n    [...]\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Before using the Timer, we need to know if the Spinner is visible on screen or not. To reach this, you can add a <code>isVisible()<\/code> method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">[...]\nvar isVisible: Bool {\n    get {\n       (UIApplication.topMostKeyWindow?.rootViewController as? SpinnerViewController) != nil\n    }\n}\n[...]<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">But you need a simple extension to get the <strong>topMostKeyWindow<\/strong>, as also <a href=\"http:\/\/www.albertopasca.it\/whiletrue\/swift-get-app-keywindow\/\" target=\"_blank\" rel=\"noreferrer noopener\">explained here<\/a>: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">extension UIApplication {\n    \/\/\/ The top most keyWindow\n    static var topMostKeyWindow: UIWindow? {\n        UIApplication.shared.connectedScenes\n            .first(where: { $0.activationState == .foregroundActive })\n            .map({ $0 as? UIWindowScene })\n            .flatMap({ $0 })?.windows\n            .first(where: { $0.isKeyWindow })\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Well, all is ready, let&#8217;s add the timer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">[...]\n    func showAlert(\n        autodismissTimeout: Double = 30.0, \/\/ both optionals\n        timeoutCallback: (() -&gt; Void)? = nil \/\/ both optionals\n    ) {\n\n        if isVisible {\n            if autodismissTimeout &gt; 0 { \/\/ handle timeout\n                timeoutHandlerTimer?.invalidate()\n                timeoutHandlerTimer = Timer.scheduledTimer(\n                    timeInterval: autodismissTimeout, target: self, selector: #selector(dispatchTimeoutHandlerAndDismiss), userInfo: nil, repeats: false\n                )\n            }\n            return\n        }\n        \n        if autodismissTimeout &gt; 0 {\n            self.timeoutCallback = timeoutCallback\n        }\n       \n        if let currentWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {\n            blankWindow = UIWindow(windowScene: currentWindowScene)\n            blankWindow?.backgroundColor = UIColor.black.withAlphaComponent(0.25)\n            blankWindow?.windowLevel = .alert\n            blankWindow?.rootViewController = spinnerVC\n            blankWindow?.makeKeyAndVisible()\n            \n            if autodismissTimeout &gt; 0 {\n                timeoutHandlerTimer?.invalidate()\n                timeoutHandlerTimer = Timer.scheduledTimer(\n                    timeInterval: autodismissTimeout, target: self, selector: #selector(dispatchTimeoutHandlerAndDismiss), userInfo: nil, repeats: false\n                )\n            }\n        }\n    }\n    \n    @objc private func dispatchTimeoutHandlerAndDismiss() {\n        timeoutCallback?()\n        self.dismiss()\n    }\n    \n    func dismiss() {\n        timeoutHandlerTimer?.invalidate()\n        timeoutHandlerTimer = nil\n        timeoutCallback = nil\n        blankWindow = nil\n    }\n\n\n[...]<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>T<\/strong>esting code &#x1f4a5;<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s see if it works, so go back in your main View-Controller and make an <strong>example test<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">class ViewController: UIViewController {\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        \/\/ add this if you want to test the manual dismission\n        Timer.scheduledTimer(\n            timeInterval: 5,\n            target: self,\n            selector: #selector(closeSpinner),\n            userInfo: nil,\n            repeats: false\n        )\n    }\n\n    \/\/ example on how to show a spinner\n    override func touchesEnded(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {\n        SpinnerManager.shared.showAlert(autodismissTimeout: 15) {\n            print( \"closed after timeout\" )\n        }\n    }\n\n    \/\/ the timer callback\n    @objc func closeSpinner() {\n        SpinnerManager.shared.dismiss()\n    }\n\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Perfect! Work is now complete.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nope, I want also a blurred background, that is more cool!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>A<\/strong>dding blurred background &#x1f32b;<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The last part of this tutorial is about configuring and adding an optional blurred view as background of our spinner:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As a commodity, you can reuse this snippet (not mine, founded online), that help you to create a custom <strong>UIVisualEffectView<\/strong> in a few row of code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">class CustomVisualEffectView: UIVisualEffectView {\n    private let theEffect: UIVisualEffect\n    var customIntensity: CGFloat\n    private var animator: UIViewPropertyAnimator?\n\n    init(effect: UIVisualEffect, intensity: CGFloat) {\n        theEffect = effect\n        customIntensity = intensity\n        super.init(effect: nil)\n    }\n    \n    required init?(coder aDecoder: NSCoder) { nil }    \n    deinit { animator?.stopAnimation(true) }\n    \n    override func draw(_ rect: CGRect) {\n        super.draw(rect)\n        effect = nil\n        animator?.stopAnimation(true)\n        animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in\n            effect = theEffect\n        }\n        animator?.fractionComplete = customIntensity\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After that, edit a little bit the previous <em>SpinnerManager<\/em> code, adding the blurred view.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In particular, declare the <strong>CustomVisualEffectView<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">private var blurEffectView: CustomVisualEffectView?<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and add as background:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">    [...]\n    func showAlert(\n        blurBackground: Bool = false,\n        autodismissTimeout: Double = 30.0,\n        timeoutCallback: (() -&gt; Void)? = nil\n    ) {\n        blurEffectView?.removeFromSuperview()\n        [...]\n        blankWindow?.rootViewController = spinnerVC\n\n        \/\/ check if we need to add a blurred view\n        if blurBackground {\n            \/\/ play with effects\n            blurEffectView = CustomVisualEffectView(effect: UIBlurEffect(style: .dark), intensity: 0.0)\n            blurEffectView?.frame = blankWindow!.rootViewController!.view.bounds\n            blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            \n            UIView.animate(withDuration: 0.25) {\n                self.blurEffectView!.customIntensity = 0.15 \/\/ play with intensity\n            }\n            \n            blankWindow?.rootViewController?.view.insertSubview(blurEffectView!, at: 0)\n        }\n\n        blankWindow?.makeKeyAndVisible()\n        [...]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;re really complete now.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h4 class=\"wp-block-heading\">&#x1f389; You can call the new code using these two functions:<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">SpinnerManager.shared.showAlert(blurBackground: true, autodismissTimeout: 15) {\n    print( \"closed after timeout\" )\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"swift\" class=\"language-swift\">SpinnerManager.shared.dismiss()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With or without blurred background:<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"1024\" data-id=\"3183\" src=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1-473x1024.png?resize=473%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-3183\" srcset=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1.png?resize=473%2C1024&amp;ssl=1 473w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1.png?resize=139%2C300&amp;ssl=1 139w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1.png?resize=768%2C1662&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1.png?resize=710%2C1536&amp;ssl=1 710w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1.png?resize=946%2C2048&amp;ssl=1 946w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-1.png?w=1170&amp;ssl=1 1170w\" sizes=\"auto, (max-width: 473px) 100vw, 473px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"1024\" data-id=\"3184\" src=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27-473x1024.png?resize=473%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-3184\" srcset=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27.png?resize=473%2C1024&amp;ssl=1 473w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27.png?resize=139%2C300&amp;ssl=1 139w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27.png?resize=768%2C1662&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27.png?resize=710%2C1536&amp;ssl=1 710w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27.png?resize=946%2C2048&amp;ssl=1 946w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-10.19.27.png?w=1170&amp;ssl=1 1170w\" sizes=\"auto, (max-width: 473px) 100vw, 473px\" \/><\/figure>\n<\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Have fun.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><sub><em>As usually, for lazy people, the code is on <a href=\"https:\/\/github.com\/elpsk\/Swift-SpinnerPopup\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>.<\/em><\/sub><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello, resuming an old post, that help you to create an UIAlert extension to show a modal popup, https:\/\/www.albertopasca.it\/whiletrue\/objective-c-modal-view-in-navigation-and-tabbar-controller-projects\/ , I&#8217;ve created a new modern implementation that use UIWindow. The scope is to show a spinner (or a Lottie spinner, or whatever you prefer), centered in the screen with automatic or manually dismission. Background can <a class=\"read-more\" href=\"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":false,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[67,763,11,2,3,30],"tags":[163,961,1061,46,1062],"class_list":["post-3178","post","type-post","status-publish","format-standard","hentry","category-funny","category-ios","category-mobile","category-objectivec","category-swift","category-xcode","tag-background","tag-blur","tag-modal","tag-swift","tag-timeout"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Swift - Simple full screen loader - while (true) {\u00a0}<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Swift - Simple full screen loader - while (true) {\u00a0}\" \/>\n<meta property=\"og:description\" content=\"Hello, resuming an old post, that help you to create an UIAlert extension to show a modal popup, https:\/\/www.albertopasca.it\/whiletrue\/objective-c-modal-view-in-navigation-and-tabbar-controller-projects\/ , I&#8217;ve created a new modern implementation that use UIWindow. The scope is to show a spinner (or a Lottie spinner, or whatever you prefer), centered in the screen with automatic or manually dismission. Background can Read More\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/\" \/>\n<meta property=\"og:site_name\" content=\"while (true) {\u00a0}\" \/>\n<meta property=\"article:published_time\" content=\"2022-08-11T08:33:34+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-08-11T08:34:39+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png\" \/>\n<meta name=\"author\" content=\"Alberto Pasca\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@lupasca\" \/>\n<meta name=\"twitter:site\" content=\"@lupasca\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alberto Pasca\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/\"},\"author\":{\"name\":\"Alberto Pasca\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\"},\"headline\":\"Swift &#8211; Simple full screen loader\",\"datePublished\":\"2022-08-11T08:33:34+00:00\",\"dateModified\":\"2022-08-11T08:34:39+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/\"},\"wordCount\":472,\"publisher\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\"},\"image\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png\",\"keywords\":[\"background\",\"blur\",\"modal\",\"swift\",\"timeout\"],\"articleSection\":[\"funny\",\"ios\",\"mobile\",\"objectivec\",\"swift\",\"xcode\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/\",\"url\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/\",\"name\":\"Swift - Simple full screen loader - while (true) {\u00a0}\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png\",\"datePublished\":\"2022-08-11T08:33:34+00:00\",\"dateModified\":\"2022-08-11T08:34:39+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png\",\"contentUrl\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/swift-simple-full-screen-spinner\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Swift &#8211; Simple full screen loader\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#website\",\"url\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/\",\"name\":\"while (true) {\u00a0}\",\"description\":\"notes from a developer, tutorials, how-to, ideas\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\",\"name\":\"Alberto Pasca\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\",\"caption\":\"Alberto Pasca\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\"},\"description\":\"Software engineer @ Pirelli &amp; C. S.p.A. with a strong passion for mobile \uf8ff development, security, and connected things.\",\"sameAs\":[\"https:\\\/\\\/www.albertopasca.it\",\"albertopasca\",\"https:\\\/\\\/x.com\\\/lupasca\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Swift - Simple full screen loader - while (true) {\u00a0}","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/","og_locale":"en_US","og_type":"article","og_title":"Swift - Simple full screen loader - while (true) {\u00a0}","og_description":"Hello, resuming an old post, that help you to create an UIAlert extension to show a modal popup, https:\/\/www.albertopasca.it\/whiletrue\/objective-c-modal-view-in-navigation-and-tabbar-controller-projects\/ , I&#8217;ve created a new modern implementation that use UIWindow. The scope is to show a spinner (or a Lottie spinner, or whatever you prefer), centered in the screen with automatic or manually dismission. Background can Read More","og_url":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/","og_site_name":"while (true) {\u00a0}","article_published_time":"2022-08-11T08:33:34+00:00","article_modified_time":"2022-08-11T08:34:39+00:00","og_image":[{"url":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png","type":"","width":"","height":""}],"author":"Alberto Pasca","twitter_card":"summary_large_image","twitter_creator":"@lupasca","twitter_site":"@lupasca","twitter_misc":{"Written by":"Alberto Pasca","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#article","isPartOf":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/"},"author":{"name":"Alberto Pasca","@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac"},"headline":"Swift &#8211; Simple full screen loader","datePublished":"2022-08-11T08:33:34+00:00","dateModified":"2022-08-11T08:34:39+00:00","mainEntityOfPage":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/"},"wordCount":472,"publisher":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac"},"image":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#primaryimage"},"thumbnailUrl":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png","keywords":["background","blur","modal","swift","timeout"],"articleSection":["funny","ios","mobile","objectivec","swift","xcode"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/","url":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/","name":"Swift - Simple full screen loader - while (true) {\u00a0}","isPartOf":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#primaryimage"},"image":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#primaryimage"},"thumbnailUrl":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png","datePublished":"2022-08-11T08:33:34+00:00","dateModified":"2022-08-11T08:34:39+00:00","breadcrumb":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#primaryimage","url":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png","contentUrl":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2022\/08\/Simulator-Screen-Shot-iPhone-13-Pro-2022-08-11-at-09.43.51-473x1024.png"},{"@type":"BreadcrumbList","@id":"https:\/\/www.albertopasca.it\/whiletrue\/swift-simple-full-screen-spinner\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.albertopasca.it\/whiletrue\/"},{"@type":"ListItem","position":2,"name":"Swift &#8211; Simple full screen loader"}]},{"@type":"WebSite","@id":"https:\/\/www.albertopasca.it\/whiletrue\/#website","url":"https:\/\/www.albertopasca.it\/whiletrue\/","name":"while (true) {\u00a0}","description":"notes from a developer, tutorials, how-to, ideas","publisher":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.albertopasca.it\/whiletrue\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac","name":"Alberto Pasca","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g","caption":"Alberto Pasca"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g"},"description":"Software engineer @ Pirelli &amp; C. S.p.A. with a strong passion for mobile \uf8ff development, security, and connected things.","sameAs":["https:\/\/www.albertopasca.it","albertopasca","https:\/\/x.com\/lupasca"]}]}},"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts\/3178","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/comments?post=3178"}],"version-history":[{"count":6,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts\/3178\/revisions"}],"predecessor-version":[{"id":3188,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts\/3178\/revisions\/3188"}],"wp:attachment":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/media?parent=3178"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/categories?post=3178"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/tags?post=3178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}