27

I have put an image in imageView in LaunchStoreyboard. How can I delay the time of image programmatically?

Here is the Launch Screen Guideline from Apple.

Here is code for Launch Screen View controller:

import UIKit
class LaunshViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delay(0.4)
    }

    func delay(_ delay:Double, closure:@escaping ()->()) {
        let when = DispatchTime.now() + delay
        DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Xcodian Solangi
  • 2,342
  • 5
  • 24
  • 52

9 Answers9

80

As of today there is no predefine method from Apple to hold launch screen. Here are some Approaches which are not optimum but works

Approach #1 Create a Separate ViewController which has Launch logo & create a timer or perform some operation (Like Database/Loads some essential network call) depends on your app type this way you can ready with data before hand & hold the launch screen as well :)

Approach #2 Not Optimum

Use Sleep code which holds up the app for a while.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        Thread.sleep(forTimeInterval: 3.0)
        // Override point for customization after application launch.
        return true
    }
Jack
  • 13,571
  • 6
  • 76
  • 98
  • 6
    It is a very bad hack, not the right way to do it. @yoowim answer is also a hack but a good way to achieve the same. Apple otherwise doesn't provide any way to do this. – zeeshan Jan 26 '19 at 14:55
  • 1
    @zeeshan is there is any approach you can recommend instead of this – Munish Kapoor Sep 20 '19 at 02:42
  • 1
    @MunishKapoor I can suggest something, but first the explanation on why this is bad. When you do Thread.sleep (in this particular situation) you are blocking the main thread and preventing any progress the app should be doing naturally, which is, of course, dangerous. Now, what can you do to fix this? Simple: make your first ViewController look like the splash screen. This way you will be free to do any background computations or any kind of control over the normal flow of your app's launch. Once you're done with this ViewController, simply push or present a new ViewController. Hope it helped! – tf.alves Dec 14 '19 at 15:52
24

Would not recommending setting the entire application in a waiting state. If the application needs to do more work before finishing the watchdog could kill the application for taking too long time to start up.

Instead you could do something like this to delay the launch screen.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()
        window?.makeKeyAndVisible()

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
            self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
        }
        return true
    }
oscar
  • 647
  • 1
  • 5
  • 17
23

Swift 4.x

It is Not a good practice to put your application to sleep!

Booting your App should be as fast as possible, so the Launch screen delay is something you do not want to use.

But, instead of sleeping you can run a loop during which the receiver processes data from all attached input sources:

This will prolong the launch-screen's visibility time.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    RunLoop.current.run(until: NSDate(timeIntervalSinceNow:1) as Date)

    return true
}
Forge
  • 6,538
  • 6
  • 44
  • 64
yoowim
  • 239
  • 2
  • 2
  • 4
    Warning: Date/NSDate is subject to leap seconds, timezone changes and winter/summer time adjustments. So trying to wait for 1 second could sometimes become waiting for 1 hour + 1 second. So don't use `RunLoop` like that, better use it with [`addTimer:forMode:`](https://developer.apple.com/documentation/foundation/runloop/1418468-add). – Cœur May 31 '19 at 17:17
23

Swift 5.x, iOS 13.x.x

Modifying the following function in the AppDelegate class does not work in Swift 5.x/iOS 13.x.x.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    return true
}

Instead, you will have to modify the scene function in SceneDelegate class as following. It will delay the LaunchSceen for 3 seconds.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    window?.rootViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()
    window?.makeKeyAndVisible()

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
        self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
    }

    guard let _ = (scene as? UIWindowScene) else { return }
}

The window variable should already be there in SceneDelegate class like the following.

var window: UIWindow?
alvin
  • 459
  • 4
  • 5
3

Definitely your app should not be put to sleep as it may be killed by the OS for being unresponsive for so long.

If you're using a static image for your launch screen, what works for me is to use the image in the LaunchScreen.storyboard, and then when your main controller launches, modally present a VC with the same image as the background in the ViewDidAppear of your main controller (with animated set to false).

You can then use your logic to know when to dismiss the launch screen (dismiss method in the VC with animated set to false).

The transition from the actual LaunchScreen to my VC presenting the same screen looks to me imperceptible.

PS: the ViewDidAppear method might be called more than once, in which case you need to use logic to not present the VC with the launch screen a second time.

raulmf9325
  • 31
  • 3
2

Create a ViewController and use NSTimer to detect the delay time. and when the timer ends push the first UIViewcontroller.

In ViewDidLoad method..

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(fireMethod) userInfo:nil repeats:NO];


-(void)fireMethod
{
// push view controller here..
}
Saranjith
  • 11,242
  • 5
  • 69
  • 122
0

Put one line of code in AppDelegate Class;

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // Override point for customization after application launch.
            Thread.sleep(forTimeInterval: 3.0)
            return true
    }
Furkan E
  • 1
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 07 '22 at 10:21
-1

SwiftUI

For SwiftUI, you can put a very similar code to the accepted answer into ContentView.onAppearing:

struct ContentView: View {

    var body: some View {
        Text("Hello")
        .onAppear {
            Thread.sleep(forTimeInterval: 3.0)
        }
    }
}
adamsfamily
  • 1,746
  • 19
  • 37
-1

Putting a thread to sleep is not a good idea.

I would suggest you go to SceneDelegate's "willConnectTo" function and paste this piece of code and you are good to go.

window?.rootViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()

window?.makeKeyAndVisible()
    
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
        self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
}
    
guard let _ = (scene as? UIWindowScene) else { return }