iOS: how to delay the launch screen? - ios

When launch an app, the LaunchScreen.xib is removed as soon as all the assets are initialized.
I want to make the launch screen stay for at least 1 sec.
Is there a way to achieve this?
Thank you!

You can create a view controller that uses the LaunchScreen storyboard, present it (not animated) on applicationDidFinishLaunching or applicationWillFinishLaunching, and dismiss it whenever you want.
Keep in mind this is discouraged by Apple because it gives the impression that your app takes a lot longer to launch, which is bad user experience and might cause some of your users to delete your app.

Swift 4 Update
Just write one line of code
Thread.sleep(forTimeInterval: 3.0)
in the method of didfinishLauching.... in appdelegate class.
Example
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 3.0)
// Override point for customization after application launch.
return true
}

Never sleep on the main thread. Doing this could actually cause iOS to kill your app for taking too long to start up

Thought I chip in my thoughts on this, I wanted to write in comments but it won't allow many lines. I believe many apps developer want to do this (delay the launch screen) is because they want to build a brand presence of the apps/games for their company.
Having said that, launch screen is NOT designed for that, as Rick Maddy explained in the comment section in one of the other answers. Launch screen's purpose is to make users feel the app is instantly running by showing the empty UI while the actual data is loading at the back (willAppear and so on).
So to achieve what many developers want, while being in-line with Apple's HIG, what you can do is:
Display UI template in the launchscreen as intended by Apple HIG.
Upon main screen load, load up another VC that shows "intro" of your brand. Make sure this runs only ONCE (a simple flag in
NSUserDefaults should do the trick).
Users should be allowed to skip this if it is a long "intro".
The same "intro" VC should be available to user by tapping on a "View Intro" button somewhere (maybe in about page).

If you want to go with simple, you can use NSThread:
[NSThread sleepForTimeInterval:(NSTimeInterval)];
You can put this code into the first line of applicationDidFinishLaunching method.
For example, display default.png for 1.0 seconds.
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
[NSThread sleepForTimeInterval:1.0];
}
It will stop splash screen for 1.0 seconds.

Alhamdulellah Solution is find
Only copy and paste this code in AppDelegate Class
Call this SplashScreenTiming() in didFinishLaunchingWithOptions()
private func SplashScreenTiming(){
let LunchScreenVC = UIStoryboard.init(name: "LaunchScreen", bundle: nil)
let rootVc = LunchScreenVC.instantiateViewController(withIdentifier: "splashController")
self.window?.rootViewController = rootVc
self.window?.makeKeyAndVisible()
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(DismissSpalshController), userInfo: nil, repeats: false)
}
#objc func DismissSpalshController(){
let mainVC = UIStoryboard.init(name: "Main", bundle: nil)
let rootvc = mainVC.instantiateViewController(withIdentifier: "SignInVC")
self.window?.rootViewController = rootvc
self.window?.makeKeyAndVisible()
}

Related

Show window that covers everything when apps enters background

In iOS12 and below I used to use something similar to this to show a window on top of everything to cover my app contents. This use to work but in iOS13 betas this does not work anymore.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var coverWindow: UIWindow?
func applicationDidEnterBackground(_ application: UIApplication) {
if self.coverWindow != nil {
// Skip since cover window is already showing
return
}
let vc = UIViewController()
let label = UILabel(frame: window!.bounds)
label.text = "CoverWindow. Tap to app see contents"
vc.view = label
vc.view.backgroundColor = UIColor.lightGray
let coverWindow = UIWindow(frame: window!.bounds)
coverWindow.rootViewController = vc
coverWindow.windowLevel = .alert
coverWindow.makeKeyAndVisible()
self.coverWindow = coverWindow
}
}
Apparently window changes are not reflected in screen until app enters foreground again.
Question
Does anyone know how fix or workaround this? or maybe this approach is incorrect?
Any help would be highly appreciated
Notes
I don't use a simple view because my app might be showing other windows too and my requirement is to cover everything.
I don't use applicationWillResignActive because we want to only show coverWindow when it enters background. (TouchID authentication and other stuff might trigger applicationWillResignActive and coverWindow would incorrectly show)
Example code
Download Full working example code in Github (Run in iOS simulator 12 and 13 to see the difference)
You have to implement application life cycle, you just delete it , add those app life cycle functions and implement your codes , it ll be run without error
Answer to myself.
I reported this to Apple and it was fixed in iOS 13.1 or so. Latest version of iOS13 does NOT have this bug :)

How to add a view controller to window programmatically and bring it to front

I want to do a screen saver for the whole app, but i only can do for 1 screen...now i am changing to appdelegate to start the timer and show the screen saver as a whole...However, i not sure how to add the video screen saver(video player view) as a whole at app delegate window... Timer i did edi, it really print out "Play Video" after 5 second, but the video view didn't show out.
library used:
1. https://github.com/piemonte/Player
2. timer
Please use this if it works for you.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "LoginSignupVC")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
If Not works, then I suggest using one initial UIViewController for this video screen and change to next screen when a video is completed.
UIApplication.shared.keyWindow?.rootViewController?.addChildViewController(self.player)
UIApplication.shared.keyWindow?.addSubview(self.player.view)
this is the best solution.
Credit to : senior Desmond
Not sure but you can try adding video on launcher screen, or if you want the video should run, in that case you need to follow these simple steps
Add a Movie player on applicationDidLaunch
when video finishes on it return back method you need to load you app controller.
I hope this helps do let me know in case you need anything more to help.
[Edit as per the requirement]
I think you need to create a controller in the app delegate
you need to create a class function in your app delegate where you can add a movie player to the screen with auto play on.
also you need to maintain a timer, but be careful while using timer and read well about it, as it needs to be manage very carefully else results in various crashes.
You can manage the timer by getting touch events on the screen by the following logic like
if movieplayer is on screen then, remove movie player else,
invalidate timer and restart the timer
The function with timer will does the following things
invalidate timer
add movie player on the window and bring it to the main screen.

DismissViewController Swift

My app has the following flow if the user is logged in
Loading Screen -----> Main Screen -----> Rest of App
and the following flow if he's not :
Loading Screen -----> Login Screen -----> Main Screen -----> Rest of App
Now I am implementing the Logout feature. I have added the following code into main Screen :
func handleLogout() {
if self.presentingViewController != nil {
var vc = self.presentingViewController
while ((vc!.presentingViewController) != nil) {
vc = vc!.presentingViewController
}
vc?.dismissViewControllerAnimated(true, completion: {
})
}
}
This works fine if the 1st path is followed (the user was logged in when the app was launched) as the app returns to the Loading Screen and then loads up the Login Screen as expected. However, if the 2nd path was followed (the user was not logged in when the app was launched, and Login Screen has been used) this code leads to the Login Screen being opened directly and the whole logout process failing. Is there a way I can ensure that the Loading Screen is the one which is always loaded by this code regardless of which of the two paths have been followed.
Use unwind segues!
You basically add an unwind segue connecting your "main screen" and "login screen". Give it an identifier and you can initiate the segue whenever you want. In handleLogout:
func handleLogout() {
self.performSegueWithIdentifier("your identifier", sender: self)
}
For details of how to create an unwind segue: https://www.andrewcbancroft.com/2015/12/18/working-with-unwind-segues-programmatically-in-swift/
This is just a suggestion here goes:
In AppDelegate file you can do something similar to this:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//Implement this method
let userLoggedIn = isUserLoggedIn();
if !userLoggedIn {
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
//Instantiate the login view controller
window?.rootViewController = storyboard.instantiateViewControllerWithIdentifier("login")
window?.makeKeyAndVisible()
}
return true
}
Now when the app starts it'll first check the user's login state and display the appropriate view.
Note: this is assuming you use storyboards and the root view controller is set to the Main Screen
If you are using Storyboards, I would suggest creating storyboard that is used purely for your login view/s. Then in the AppDelegate DidFinishLoading method, you can show the login storyboard if they need to login or show the main storyboard if they are already logged in. You can swap out storyboards at anytime and its easy to do. That will help simplify the flow a bit. This is what I usually do in my apps. If you need sample code just let me know.

Scheduled NSNotification (Instead of UILocalNotification)... Swift solutions?

My app (prefix "AAS") is basically a game where users lose points every day they don't play. I use UILocalNotifications to alert the user that they've lost points, and invite them back to play. One of my view controllers displays when the points have changed, and it's pretty simple to send out an NSNotification when a UILocalNotification is fired while the app is open).
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
if notification.userInfo != nil {
if let notificationName = notification.userInfo![AASNotification.ActionKey] as? String {
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil, userInfo: nil)
}
}
}
When the app is reopened after being inactive, one of the classes calculates how many points are lost. Great. Bulletproof, except when the user disallows my app to use NotificationCenter, the app will not be updated if it's open when the notification is supposed to fire. For this case, I wrote my own implementation of a timed notification queue that would mimic UILocalNotification to a certain extent while my app is open. But I thought, someone must have had this problem before, and maybe there is a cocoapod for it.
So my question to the community is, does someone know of a library that dispatches timed NSNotifications? Or a different approach to this problem? Here's my solution, which is barebones and works for the purpose I need:
https://github.com/JamesPerlman/JPScheduledNotificationCenter
I'd love to use one that was coded by a professional and is well tested and feature rich. (I was made aware that this request is off topic for SO.)
Edits:
I want to be able to queue up any amount of NSNotifications to be fired at arbitrary dates. Obviously the NSNotifications can only be received by my app while it is open, that's fine. I do not know the expense of using one NSTimer for each NSNotification (could be hundreds of NSTimers all on the run loop), so my solution only uses one NSTimer at a time. I want the ability to schedule and cancel NSNotifications just like you can do with UINotifications.
You could try NSTimer (NSTimer class reference). In your AppDelegate you can create a method similar to your didReceiveLocalNotification method to execute when the timer is triggered. Also, create an NSUserDefault to store the next time you need to trigger the timer. Finally, at the point where you want to begin the countdown, get the time interval from the current time until the time you want to trigger the event, and set the timer.
So in your AppDelegate, register the default and implement the notifyPlayer:
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
let userDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.registerDefaults(["alertTime": NSDate()]) //initial value
return true
}
func notifyPlayer() {
// Calculate points and notify relevant viewcontroller to alert player.
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
let lastNotificationTime = defaults.objectForKey("alertTime") as! NSDate
let nextNotificationTime = lastNotificationTime.dateByAddingTimeInterval(86400)
defaults.setObject(nextNotificationTime, forKey: "alertTime")
}
}
Now set the timer wherever it makes sense, probably in your app's initial view controller.
class InitialVewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
let savedTime = defaults.objectForKey("alertTime") as! NSDate
let countDownTime = savedTime.timeIntervalSinceNow
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
NSTimer.scheduledTimerWithTimeInterval(countDownTime,
target: appDelegate,
selector: #selector(AppDelegate.notifyPlayer()),
userInfo: nil,
repeats: false)
}
}
It's not perfect, as I haven't tested it, but I think the concept will work for you.
Edit: Just to clarify, this would solve your problem of alerting the user while he is using the app, but won't do anything when the app is not in use. I don't know of any way to send users notification center notifications when permission hasn't been granted.

iPhone 6 Slide Out Menu

I am trying to make a collapsible slide out menu bar from scratch. I decided to try a UISplitViewController(). When I use an iPad simulator, I get a collapsible menu on the right side like desired. However, when I try it on my iPhone 6, the master view and detail view are shown on totally different screens instead of partially overlapping each other until a decision is made. Is it possible to get a split screen overlap when viewed on both the smaller devices (iPhone 5/6) and bigger devices?
The posted UISplitViewController is below:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let splitVC = UISplitViewController()
window?.rootViewController = splitVC
print(splitVC.collapsed)
var nav1 = UINavigationController(rootViewController: MasterViewControllerTableViewController())
var nav2 = UINavigationController(rootViewController: ViewController())
splitVC.viewControllers = [nav1, nav2]
splitVC.preferredDisplayMode = .PrimaryOverlay // this does not help?
window?.makeKeyAndVisible()
return true
}
I have built one from scratch but I would advise just using one of the pre-existing libraries. https://www.cocoacontrols.com/ is a good place to check. In the filter above select Sort: rating, Platform: iOS. You can even run the code online to test it. Most of the code for this controls is hosted on github.
If you do decide to build one there are plenty of tutorials if you google for “swift build slide out menu”. The code you have does a small subset of what you’ll need so there’s no simple “add this” or “change that” to get it to work.

Resources