present viewcontroller on top from app delegate - swift

so i've spent the last 5-6 hours trying to get this one feature to work and i've run into a couple of pitfalls in my implementation and limits of understanding. I've tried a couple of methods:
Method 1 :
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc : UIViewController
vc = storyboard.instantiateViewController(withIdentifier: "PasscodeLockVC")
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
print("app entered foreground")
}
Issue : I can't dismiss the screen from the PasscodeLockVC (it's the passcode VC).
Method 2 :
public extension UIViewController {
func show() {
let win = UIWindow(frame: UIScreen.main.bounds)
let vc = UIViewController()
vc.view.backgroundColor = .clear
win.rootViewController = vc
win.windowLevel = UIWindowLevelAlert + 1
win.makeKeyAndVisible()
vc.present(self, animated: true, completion: nil)
}
func hide() {
dismiss(animated: true, completion: nil)
}
}
Issue : I get a black screen when i dismiss.
**So my questions is ** Can someone direct me on the best method to have my lock screen present upon entering foreground (i know it goes in applicationWillEnterForegroud), but when i do so, it preserves the screen that was active when the app was closed (will all of it's contents since it's a diary app)? I've been stumped all day and could really use some direction.
Running on most recent Xcode

Related

Open first View based on condition

I want my app to check at start conditionaly if a variable is correct or not. Based on that it should either go to an intro screen (where he can select a variable in my case select a team) or it should start the main view. After searching i found this code and edited it.
But there still seems to be problems. First of all I dont have two identifier. The Intro has one but not the main view. My main View is called WeatherViewController and the Intro screen is called FirstScreenViewController.
I also added a picture of my Main.storyboard.
I also googled a lot about conditional UINavigationController but I can only understand with a video and did not found a video about it.
I tried to use the code from here.
var id = hello ? "goToIntro" : "???"
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let WeatherViewController: UIViewController = mainStoryboard.FirstScreenViewController(withIdentifier: WVC has no identifier??) as UIViewController
self.window?.rootViewController = WeatherViewController
self.window?.makeKeyAndVisible()
if hello {
self.performSegue(withIdentifier: "goToIntro", sender: self)
} else {
/here nothing should happen. It should open the Main View
self.performSegue(withIdentifier: "???", sender: self)
}
Simply repeat the steps you did for creating the segue goToIntro.
Add a new UIViewController (MainViewController) in your storyboard.
Add a segue and give it an identifier.
Provide the identifier value in the else condition of the code that you've shown in your post to perform the segue to the UIViewController that you've just created.
The final code should look something like this:
var id = hello ? "goToIntro" : "goToMain"
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let weatherViewController = mainStoryboard.instantiateInitialViewController()
window?.rootViewController = weatherViewController
window?.makeKeyAndVisible()
weatherViewController.performSegue(withIdentifier: id, sender: self)
You don't need the if else block since the id is already determined using the ? ternary operator.

Whose view is not in the window hierarchy only in First Launch

I have an Onboarding screen that I'm showing to the new users when they first open the app.
In my appDelegate I check whether is the first launch or not.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = storyboard.instantiateViewController(withIdentifier: "OnBoarding")
let userDefaults = UserDefaults.standard
if userDefaults.bool(forKey: "onBoardingComplete") {
initialViewController = storyboard.instantiateViewController(withIdentifier: "MainApp")
}
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
}
Also I have a collectionViewCell that I have some buttons and when I click them I get an Alert with informations.
Example of one button
#IBAction func guide3Btn(_ sender: Any) {
let infoVC = infoService.info(title: "Title", body: "Information")
self.window?.rootViewController?.present(infoVC, animated: true, completion: nil)
}
When the user first launches the app if he clicks the info button gets this:
Warning: Attempt to present <MyApp.InfoViewController: 0x7f91db45cfb0> on <MyApp.OnbBoardViewController: 0x7f91db506af0> whose view is not in the window hierarchy!
If the user reopens the app everything is ok. I know that when we have first launch we have onBoarding as root controller but I can't understand how to fix this.
Update
This is the infoService class. I use a new storyboard to create the alert.
class InfoService {
func info(title: String, body: String) -> InfoViewController {
let storyboard = UIStoryboard(name: "InfoStoryboard", bundle: .main)
let infoVC = storyboard.instantiateViewController(withIdentifier: "InfoVC") as! InfoViewController
infoVC.infoBody = body
infoVC.infoTitle = title
return infoVC
}
}
You can try add your storyboard instantiate code blocks to main thread using DispatchQueue.main.async like below:
I solved almost all of my whose view is not in the window hierarchy! problems.
DispatchQueue.main.async {
let infoVC = storyboard.instantiateViewController(withIdentifier: "InfoVC") as! InfoViewController
infoVC.infoBody = body
infoVC.infoTitle = title
}
return infoVC
Referenced from : https://stackoverflow.com/a/45126338/4442254

How to control multiple navigation controller in an iOS project

The structure of my project is as follows.
Initially when the user is registering or trying to login the initial navigation controller should work and after successfully registering / loggin in the user should be taken to first tab of tab bar controller. But the issue that i am facing is that i am getting 2 navigation bars in the tab bar view. Can someone guide me how to implement this in the correct way.
Thanks in advance
At some point, I would assume that the app is able to determine whether the user loggedin or not, based on that you have to set the desired root view controller for the app.
For such a case, the best place to do that is application(_:didFinishLaunchingWithOptions:) method in the AppDelegate file:
Tells the delegate that the launch process is almost done and the app
is almost ready to run.
For simplicity, let's say that you are saving isLoggedin boolean in the UserDefault, so it could be achieved like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// the flag for determining whether the user loggedin or not
let isLoggedin = UserDefaults.standard.bool(forKey: "K_isLoggedin")
// the desired initial view controller (based on the value of `isLoggedin`)
let initialViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: isLoggedin ? "TabbarIdentifier" : "FirstNavigationIdentifier")
// setting the app rootViewController
window?.rootViewController = initialViewController
return true
}
Note that "TabbarIdentifier" represents the tabbar controller at the storyboard and also "FirstNavigationIdentifier" represents the first navigation view controller at the storyboard.
if you are unaware of how to set the view controller identifier, checking this answer should help.
Technically speaking, setting the desired root view controller means setting the rootViewController to the main window of the app (AppDelegate window).
Make one of the two navigation controllers from tabbar to initial viewcontroller and add the following in Appdelegate's didFinishLAunching
if (isDashboardVC == nil || isDashboardVC == false)
{
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController:UINavigationController = mainStoryboardIpad.instantiateViewController(withIdentifier: "navigationmain") as! UINavigationController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
else
{
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarIntial : UITabBarController = mainStoryboardIpad.instantiateViewController(withIdentifier: "Tabbarcontroller") as! UITabBarController
let navigationController2:UINavigationController = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourViewControllerName") as! UINavigationController
let navigationController:UINavigationController = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourViewControllerName") as! UINavigationController
let navigationController3:UINavigationController = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourViewControllerName") as! UINavigationController
tabBarIntial.viewControllers = [navigationController2, navigationController, navigationController3]
tabBarIntial.selectedIndex = 1
}

dismiss passcode lock screen with correct passcode to last view

I currently have a function in my app delegate willEnterForeground method that if the app goes to background, upon coming to the foreground, it presents a passcode lock screen. The code for the willEnterForeground is as such
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc : UIViewController
vc = storyboard.instantiateViewController(withIdentifier: "IdleLockVC")
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
print("app entered foreground")
}
My question is, is there an easy way to dismiss the passcode view (logic for determining the correct code is working) to the screen it was before? If not with my current setup, what would i need to change? am i not presenting the passcode screen and therefore can't just dismiss it?
A possible solution would be awesome.

Have ViewController appear only on first launch

I would like to have my viewController named "DoctorInformationController" appear only when the user launches the app for the first time. Ive seen some people's answers, however i would like to know how to do this in swift please!
Thank you
This code I use that will launch a viewcontoller based on first or second launch of the app.
This in the AppDelegate.
var defaults: NSUserDefaults
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let firstLaunch = defaults.boolForKey("FirstLaunch")
if firstLaunch {
print("Not first launch.")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("navController") as! UINavigationController
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
} else {
print("First launch, setting NSUserDefault.")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("firstrunVCstoryboard") as UIViewController
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
return true
}
You would need to adapt it to fit your storyboard and viewcontrollers names.
EDIT:
This how I set the value in the first view controller to indicate that first run has completed. So that it will not show again.
self.defaults.setBool(true, forKey: "FirstLaunch")
There is no way to tell if it's the first launch built into the UIKit framework, however you can do it another way.
In AppDelegate.swift, go to applicationDidFinishLaunchingWithOptions method (the first one, usually). Now, add this:
//Check for key "first_launch" in User Defaults
if let _ = NSUserDefaults.standardUserDefaults().objectForKey("first_launch") {
//Set your own global variable to be true. Then, when your ViewController
//loads, do a popup window with that DoctorViewController thingy if the
//variable is true
//Example:
isFirstLaunch = true
//Then, set "first_launch" to be a value so your app will never call this block again
NSUserDefaults.standardUserDefaults().setObject("", forKey: "first_launch")
}