"Attempt to present while already presenting" still appearing after checks? - ios

let appDelegate = UIKit.UIApplication.shared.delegate!
if let tabBarController = appDelegate.window??.rootViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let signInVC = storyboard.instantiateViewController(withIdentifier: "SignInVC") as! SignInVC
guard !signInVC.isBeingPresented else {
log.warning("Attempt to present sign in sheet when it is already showing")
return
}
signInVC.modalPresentationStyle = UIModalPresentationStyle.formSheet
tabBarController.present(signInVC, animated: true, completion: nil)
}
This code can be called multiple times despite signInVC being presented. I already added this check:
guard !signInVC.isBeingPresented else {
log.warning("Attempt to present sign in sheet when it is already showing")
return
}
but it doesn't seem to prevent this error:
Warning: Attempt to present <App.SignInVC: 0x101f2f280> on <UITabBarController: 0x101e05880> which is already presenting <App.SignInVC: 0x101f4e4c0>

Your guard isn't a valid check. The isBeingPresented is being called on a brand new view controller instance that hasn't yet been presented. So isBeingPresented will always be false. Besides that, that property can only be used from within a view controller's view[Will|Did]Appear method.
What you want to check is to see if the tabBarController has already presented another view controller or not.
And lastly, only create and setup the sign-in view controller if it should be presented.
let appDelegate = UIKit.UIApplication.shared.delegate!
if let tabBarController = appDelegate.window?.rootViewController as? UITabBarController {
if tabBarController.presentedViewController == nil {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let signInVC = storyboard.instantiateViewController(withIdentifier: "SignInVC") as! SignInVC
signInVC.modalPresentationStyle = UIModalPresentationStyle.formSheet
tabBarController.present(signInVC, animated: true, completion: nil)
}
}

Related

Bar item not show when I use segue programmatically

I’m building an app which has a main vc that only existing user can log in to. The main page is the initial vc so I embed in the navigation bar to it. When I open the app in the first time I can see the bar items, but when I sign out, and log in again I can’t see the bar items, someone know why? Should I add some code? or maybe change some definitions?
I’m using segue programmatically like this:
private var handle: AuthStateDidChangeListenerHandle?
handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
if user == nil{
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Home") as? MainVC
{
self.present(vc, animated: true, completion: nil)
}
}else{
//keep going with the code...
})
In the simulator it starts with the main vc then I sign out (pic 1), then I log in (pic 2) and get back to the main vc but now I can’t see the bar items (pic 3).
link to the app pictures
you must present your main vc in navigationcontroller
private var handle: AuthStateDidChangeListenerHandle?
handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
if user == nil{
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Home") as? MainVC
{
let navVc = UINavigationController(rootViewController: vc)
self.present(navVc, animated: true, completion: nil)
}
}else{
//keep going with the code...
})

Swift Multiple Storyboard - How to access specific one

I had split a couple profiles into multiple storyboards to try to help with my freeze time because I heard to many storyboards can be the cause of why Xcode would just "freeze" for up to 10 mins sometimes causing a crash in my flow of work. So, I need to access each specific storyboard, yet don't know how. This current code accesses the "Main.Storyboard" file yet, I need to call other storyboard files. Here I am checking if a specific profile is signed in and pushing them to the profile. I had all the profiles in one storyboard but now I separated them. Just need to know how to push to other storyboards. Thanks
func checkIfBusinessLoggedIn() {
Auth.auth().addStateDidChangeListener({ (auth, user) in
if (user != nil) {
Database.database().reference().child("Businesses").child((user?.uid)!).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
print("Business is Signed In")
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Business Profile")
self.present(vc!, animated: true, completion: nil)
}
})
}
})
}
let storyboard = UIStoryboard(name: "your_storyboard_name", bundle: nil)
vc = storyboard.instantiateViewController(withIdentifier: "your_view_controller_name")
//to push that controller on the stack
self.navigationController?.pushViewController(vc, animated: true)
If your storyboard file is "Business.storyboard", "your_storyboard_name" name should be "Business".
If you want to access other storyboards you need to give that name.
self.storyboard will always point to your default storyboard. Instead do this :
let VC = UIStoryboard(name: "VCStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "InstructionScreenController") as! VC
Use below code for multipe storybord and create ENUM for storyboard name that is easy to specify storyboard name.
//MARK:- Enum_StoryBoard
enum enumStoryBoard:String {
case main = "Main"
case home = "HomeSB"
}
let storyBoard = UIStoryboard.init(name: enumStoryBoard. home.rawValue, bundle: nil)
let objLocationSearch = storyBoard.instantiateViewController(withIdentifier: "LocationSearch") as? LocationSearch
self.navigationController?.pushViewController(objLocationSearch!, animated: true)
Using enum and generic method to return the required object.
enum AppStoryboard : String {
case First, Second
var instance : UIStoryboard {
return UIStoryboard(name: self.rawValue, bundle: Bundle.main)
}
func instantiateVC<T : UIViewController>(viewControllerClass : T.Type) throws -> T {
let storyboardID = (viewControllerClass as UIViewController.Type).storyboardID
guard let viewObj = instance.instantiateViewController(withIdentifier: storyboardID) as? T else {
throw ExcpectedError.intantiationErro(msg:"ViewController with identifier \(storyboardID)")
}
return viewObj
}
}
use this code to instantiate your viewController
let second = try! AppStoryboard.Second.viewController(viewControllerClass: SecondViewController.self) self.present(second, animated: true, completion: nil)

How to reopen application after login

I'm an android developer. in android, when user login in application, I will re-open the MainActivity class ( controller ) to refresh some views.
in iOS applications, how to do this scenario ?
You can reopen you default/LandingViewController.
Suppose you have a View Controller with name LandingViewController
When you successfully logged in all you need is to re instantiate the LandingViewController
In AppDelegate class make a function with name
func userDidLoggedIn(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)//Replace Main With your own storyboard name containing LandingViewController
let landingViewController = storyboard.instantiateViewController(withIdentifier: "LandingViewControllerIdentifier")//Pass your LandingViewController Identier that you have set in your storyboard file.
guard let subviews = self.window?.subviews else {
return
}
for view in subviews {
view.removeFromSuperview()
}
self.window?.rootViewController = landingViewController
}
Now Simply Call this Function where ever in the entire project like this In your case write below lines in the completion block of login request API.
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate. userDidLoggedIn()
Once user login, you can change your rootviewcontroller like this:
var nav_VC: UIViewController?
func onSuccessfulLogin()
{
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
nav_VC = nil
if nav_VC == nil {
nav_VC = storyboard.instantiateViewController(withIdentifier: "home_nav")
}
self.window?.rootViewController = nav_VC
self.window?.makeKeyAndVisible()
}

Swift: How to return to the same instance of my UIViewController

I have a button in my SettingsViewController that when pressed, I want to present the same instance of my TimerViewController each time the button is pressed.
I think I have gotten fairly close with this post here, iOS Swift, returning to the same instance of a view controller. So if you could point me to saving the instance of TimerViewController, that would work the best.
This is the code I have right now -
var yourVariable : UIViewController!
if yourVariable == nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
yourVariable = storyboard.instantiateViewControllerWithIdentifier("timer") as! TimerInterface
}
presentViewController(yourVariable, animated: true, completion: nil)
the code you provided should work. if your SettingsViewController gets deallocated though the timerViewController also gets deallocated and recreated the next time you present it. so you have to make sure to save its instance at an appropriate location.
var timerViewController: TimerViewController!
if timerViewController == nil {
let timerViewController = UIStoryboard(name: "Main", bundle: nil)
yourVariable = storyboard.instantiateViewControllerWithIdentifier("timer") as! TimerInterface
}
presentViewController(timerViewController, animated: true, completion: nil)
The best would be to save the ViewController somewhere , and get back to it .
A way to "get back to it" :
add
var tvc: TimerViewController? = nil
inside AppDelegate
when you get to your Timer (the best would be when you left it , in viewDidDisappear)
you add :
(UIApplication.sharedAplication().delegate as! AppDelegate).tvc = self
then when you get to the setting , if you want to segue back to the timer
let tvc = (UIApplication.sharedAplication().delegate as! AppDelegate).tvc
(UIApplication.sharedApplication().delegate? as! AppDelegate).window?.rootViewController?.presentViewController(tvc, animated: true , completion: nil)
if you ask yourself why should you present it with the rootViewController (last line ) it is because you can present an already active viewController , this will not present an already active vc .

XCode 6: I can't select a view by its storyboard ID

This is the code :
let theBoard = self.storyboard!
let vc = theBoard.instantiateViewControllerWithIdentifier("myViewId") as! UIViewController // this line causes the error
The second line causes this error:
unexpectedly found nil while unwrapping an Optional value
* VERY IMPORTANT * : the error only fires when the build configuration is set to "debug". It works well when set to "release".
Why ?
You are casting UIViewController in the let vc = theBoard.instantiateViewControllerWithIdentifier("myViewId") as! UIViewController which is wrong.
You should try:
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc = storyboard.instantiateViewControllerWithIdentifier("myViewId") as! ViewController
It all is a matter of the new syntax, functionality hasn't changed:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
Use like this
var moveToNextVC:ViewController = self.storyboard?.instantiateViewControllerWithIdentifier("myViewId") as! ViewController
self.presentViewController(moveToNextVC, animated: true, completion: nil)
Remember set StoryboardID Identify 'myViewId' in File Inspector for your viewcontroller

Resources