Show two ViewController from AppDelegate - ios

When APP is Launching - start SigninView - it's Okey. Next if success - I need showTripController(). Function work but nothing show? What's a problem?
func showSigninView() {
let controller = self.window?.rootViewController!.storyboard?.instantiateViewControllerWithIdentifier("DRVAuthorizationViewController")
self.window?.rootViewController!.presentViewController(controller!, animated: true, completion: nil)
}
func showTripController() {
let cv = self.window?.rootViewController!.storyboard?.instantiateViewControllerWithIdentifier("DRVTripTableViewController")
let nc = UINavigationController()
self.window?.rootViewController!.presentViewController(nc, animated:true, completion: nil)
nc.pushViewController(cv!, animated: true);
}

First of all you must add this before you use window :
self.window.makeKeyAndVisible()
Another thing to keep in mind is:
Sometimes keyWindow may have been replaced by window with nil rootViewController (showing UIAlertViews, UIActionSheets on iPhone, etc), in that case you should use UIView's window property.
So, instead of using rootViewController, use the top one presented by it:
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
if let topController = UIApplication.topViewController() {
topController.presentViewController(vc, animated: true, completion: nil)
}

Replace last 3 lines of showTripController as below:
let nc = UINavigationController(rootViewController: cv));
self.window!.rootViewController = nc

Related

Check to specific VC in AppDelegate

I'm trying to check specific VC if app is running in foreground. My root view controller class is SWRevealViewController. After that I have a TabBarController and under it there is NavigationController and ViewController under it.
My heirachy is,
SWRevealViewController --> TabBar Controller --> Navigation Controller --> MessageVC --> ChatVC
I want to check in app delegate if app is on ChatVC or not if running on foreground.I have tried this code,
let tabBar:UITabBarController = self.window?.rootViewController as! UITabBarController
let navInTab:UINavigationController = tabBar.viewControllers?[1] as! UINavigationController
let storyboard = UIStoryboard(name: "Dashboard", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "ChatDetailViewController") as? ChatDetailViewController
if destinationViewController?.restorationIdentifier == "ChatDetailViewController"
{
print("Yes")
}
else
{
print("No")
}
But app crashes with this error,
Could not cast value of type 'SWRevealViewController' (0x100dc4b20) to 'UITabBarController' (0x211b289f0).
How i can check if app is on ChatVC or not?
Screenshot of storyboard :
I have an extension for it
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController, top.view.window != nil {
return topViewController(base: top)
} else if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
USAGE:
if UIApplication.topViewController is YourViewController {
// do smth
}
I never used SWRevealViewController but you can try this.
As you said in your first line "SWRevealViewController is the RooVC" and you are converting the SWRevealViewController to UITabBarController (check below line of your code). This is your crash reason.
let tabBar:UITabBarController = self.window?.rootViewController as! UITabBarController
Now you need to change, UITabBarController to SWRevealViewController
let rootVC = self.window?.rootViewController as! SWRevealViewController
Now get all ViewControllers
if let navController = rootVC.navigationController { // for safety check
for controller in navController.viewControllers {
if controller is ChatVC {
print("Chat VC is available")
break
}
}
}
For safe coding and keeping swift optional binding in mind, you can do code like below,
Updated answer
var haveChatVC = false
if let rootVC = self.window?.rootViewController as? SWRevealViewController,
let tabbar = rootVC.frontViewController as? UITabBarController {
if let requiredNC = tabbar.viewControllers?[1] as? UINavigationController {
for vc in requiredNC.viewControllers {
if vc is ChatVC {
// ...
haveChatVC = true
break
}
}
}
else {
print("Navigation controller not found.")
}
}
else {
print("Unable to get root controller or navigation controller")
}
if haveChatVC {
// do task here when chat vc available
}
else {
// do task here when chat vc not available
}
Note: This is only pseudo code.

iOS Swift3 check nil value for ViewController Object

let viewControllers: [UIViewController] = self.navigationController!.viewControllers
for VC in viewControllers {
if (VC.isKind(of: HomeViewController.self)) {
bScreen = true
self.navigationController?.popToViewController(VC, animated: true)
}
}
if bScreen == false {
let homeVC = HomeViewController()
self.navigationController?.pushViewController(homeVC, animated: false)
}
I loop through navigation controller array to move to HomeViewController.above code is working fine.some times i am getting crash as “fatal error: unexpectedly found nil while unwrapping an Optional value”.I know the cause for this crash.Please help me how to check nil value for view controller object.any help will be appreciated.thanks in advance
-- Swift 3 --
for vc in (self.navigationController?.viewControllers)! {
if vc is HomeViewController {
_ = self.navigationController?.popToViewController(vc, animated: true)
}
}
Use this code. this is helpful for you.
let viewControllers: [UIViewController] = self.navigationController!.viewControllers
for VC in viewControllers {
if (VC.isKind(of: HomeViewController.self)) {
bScreen = true
self.navigationController?.popToViewController(VC, animated: true)
break;
}
}
if bScreen == false
{
let homeVC = HomeViewController()
self.navigationController?.pushViewController(homeVC, animated: false)
}
let getCurrentVCIndex = self.navigationController?.viewControllers.indexOf({ (viewController) -> Bool in
if let _ = viewController as? HomeViewController {
return true
}
return false
})
if getCurrentVCIndex
{
let HomeVC = self.navigationController?.viewControllers[getCurrentVCIndex!] as! HomeViewController
self.navigationController?.popToViewController(HomeVC, animated: true)
}
else
{
// use push
}
or use like
if let HomeVC = self.navigationController?.viewControllers.filter({$0 is HomeViewController}).first
{
self.navigationController?.popToViewController(HomeVC!, animated: true)
}else
{
// use push
}
Never use directly ! until you are damn sure that it will not be nil. Replace your code as below. You can use if let or guard let to unwrap optionals.
if let viewControllers: [UIViewController] = self.navigationController?.viewControllers {
for VC in viewControllers {
if (VC.isKind(of: ViewController.self)) {
bScreen = true
self.navigationController?.popToViewController(VC, animated: true)
}
}
if bScreen == false
{
let homeVC = ViewController()
self.navigationController?.pushViewController(homeVC, animated: false)
}
}
else {
// IF VC is nil
}
This is better to use to if let / guard for an optional value to avoid crashing.
if let viewControllers: [UIViewController] = self.navigationController.viewControllers{
for VC in viewControllers {
if (VC.isKind(of: HomeViewController.self)) {
bScreen = true
self.navigationController?.popToViewController(VC, animated: true)
}
}
if bScreen == false
{
let homeVC = HomeViewController()
self.navigationController?.pushViewController(homeVC, animated: false)
}
}
Based on your code, In the loop, if the navigation stack contains the respective view controller will be popped to the respective page. But the thing is if the same view controller is present two times, will lead to execute the loop for the same time. This may cause crash. So Add a break after the poptoviewcontroller will avoid this issue. Please check the below code, will help you.
if (VC.isKind(of: HomeViewController.self)) {
bScreen = true
self.navigationController?.popToViewController(VC, animated: true)
break
}

Can not tap on presented ViewController on TopViewController XCode 8.1, iphone6s/6s+/7/7+

I want present a ViewController on top application so I use the codes below
let nextStoryBoard = UIStoryboard(name: "Menu", bundle: nil)
let menu = nextStoryBoard.instantiateViewControllerWithIdentifier("MenuView") as! MenuPanelViewController
UIApplication.sharedApplication().keyWindow?.rootViewController!.getTopViewController().modalPresentationStyle = .FullScreen
UIApplication.sharedApplication().keyWindow?.rootViewController!.getTopViewController().presentViewController(menu, animated: false, completion: nil)
Everything fine when I use xCode 7, when I update xCode to 8.1, on iPhone 6s/6s+/7/7+ I cannot use tapRecognizer.
I think my problem related 3D Touch on these above devices.
Please check your getTopViewontroller() returns correctly.
This is the method I am using to find the top most presented view controller.
extension UIApplication {
class func topViewController() -> UIViewController {
return UIApplication.topViewController(base: nil)
}
class func topViewController(base: UIViewController?) -> UIViewController {
var baseView = base
if baseView == nil {
baseView = (UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController
}
if let vc = baseView?.presentedViewController {
return UIApplication.topViewController(base: vc)
} else {
return baseView!
}
}
}

I have multiple storyboards. How can I use AppDelegate to open another ViewController in another storyboard? (Segue)

Here is the code I have. I have tried a few different approaches and some of them gives me the error that the view is not in the hierarchy.
The code snippet below goes in the correct else but can't perform the segue or presentViewController
func applicationDidTimout(notification: NSNotification) {
if let vc = self.window?.rootViewController as? UINavigationController {
if let myTableViewController = vc.visibleViewController as? AccountsOverviewViewController {
// Call a function defined in your view controller.
myTableViewController.signOffUser()
} else {
// We are not on the main view controller. Here, you could segue to the desired class.
let storyboard = UIStoryboard(name: "Accounts", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("AccountsNavigationController") as UIViewController
let vc2 = getVisibleViewController(nil)
vc2?.presentViewController(vc, animated: true, completion: nil)
}
}
}
func getVisibleViewController(var rootViewController: UIViewController?) -> UIViewController? {
if rootViewController == nil {
rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
}
if rootViewController?.presentedViewController == nil {
return rootViewController
}
if let presented = rootViewController?.presentedViewController {
if presented.isKindOfClass(UINavigationController) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKindOfClass(UITabBarController) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
Use the func below to get the visible view controller,
func getVisibleVC() -> UIViewController? {
if var visibleVC = window?.rootViewController {
while let presentedVC = visibleVC.presentedViewController {
visibleVC = presentedVC
}
return visibleVC
}
return nil
}

Warning: Attempt to present * on * whose view is not in the window hierarchy - swift

I'm trying to present a ViewController if there is any saved data in the data model. But I get the following error:
Warning: Attempt to present * on *whose view is not in the window hierarchy"
Relevant code:
override func viewDidLoad() {
super.viewDidLoad()
loginButton.backgroundColor = UIColor.orangeColor()
var request = NSFetchRequest(entityName: "UserData")
request.returnsObjectsAsFaults = false
var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext!
var results:NSArray = context.executeFetchRequest(request, error: nil)!
if(results.count <= 0){
print("Inga resultat")
} else {
print("SWITCH VIEW PLOX")
let internVC = self.storyboard?.instantiateViewControllerWithIdentifier("internVC") as internViewController
self.presentViewController(internVC, animated: true, completion: nil)
}
}
I've tried different solutions found using Google without success.
At this point in your code the view controller's view has only been created but not added to any view hierarchy. If you want to present from that view controller as soon as possible you should do it in viewDidAppear to be safest.
In objective c:
This solved my problem when presenting viewcontroller on top of mpmovieplayer
- (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
Swift 3
I had this keep coming up as a newbie and found that present loads modal views that can be dismissed but switching to root controller is best if you don't need to show a modal.
I was using this
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard?.instantiateViewController(withIdentifier: "MainAppStoryboard") as! TabbarController
present(vc, animated: false, completion: nil)
Using this instead with my tabController:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let view = storyboard.instantiateViewController(withIdentifier: "MainAppStoryboard") as UIViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//show window
appDelegate.window?.rootViewController = view
Just adjust to a view controller if you need to switch between multiple storyboard screens.
Swift 3.
Call this function to get the topmost view controller, then have that view controller present.
func topMostController() -> UIViewController {
var topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while (topController.presentedViewController != nil) {
topController = topController.presentedViewController!
}
return topController
}
Usage:
let topVC = topMostController()
let vcToPresent = self.storyboard!.instantiateViewController(withIdentifier: "YourVCStoryboardID") as! YourViewController
topVC.present(vcToPresent, animated: true, completion: nil)
for SWIFT
func topMostController() -> UIViewController {
var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!
while (topController.presentedViewController != nil) {
topController = topController.presentedViewController!
}
return topController
}
You just need to perform a selector with a delay - (0 seconds works).
override func viewDidLoad() {
super.viewDidLoad()
perform(#selector(presentExampleController), with: nil, afterDelay: 0)
}
#objc private func presentExampleController() {
let exampleStoryboard = UIStoryboard(named: "example", bundle: nil)
let exampleVC = storyboard.instantiateViewController(withIdentifier: "ExampleVC") as! ExampleVC
present(exampleVC, animated: true)
}
Swift 4
func topMostController() -> UIViewController {
var topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while (topController.presentedViewController != nil) {
topController = topController.presentedViewController!
}
return topController
}
Use of main thread to present and dismiss view controller worked for me.
DispatchQueue.main.async { self.present(viewController, animated: true, completion: nil) }
I was getting this error while was presenting controller after the user opens the deeplink.
I know this isn't the best solution, but if you are in short time frame here is a quick fix - just wrap your code in asyncAfter:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.7, execute: { [weak self] in
navigationController.present(signInCoordinator.baseController, animated: animated, completion: completion)
})
It will give time for your presenting controller to call viewDidAppear.
For swift 3.0 and above
public static func getTopViewController() -> UIViewController?{
if var topController = UIApplication.shared.keyWindow?.rootViewController
{
while (topController.presentedViewController != nil)
{
topController = topController.presentedViewController!
}
return topController
}
return nil}
let storyboard = UIStoryboard(name: "test", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "teststoryboard") as UIViewController
UIApplication.shared.keyWindow?.rootViewController?.present(vc, animated: true, completion: nil)
This seemed to work to make sure it's the top most view.
I was getting an error
Warning: Attempt to present myapp.testController: 0x7fdd01703990 on myapp.testController: 0x7fdd01703690 whose view is not in the window hierarchy!
Hope this helps others with swift 3
I have tried so many approches! the only useful thing is:
if var topController = UIApplication.shared.keyWindow?.rootViewController
{
while (topController.presentedViewController != nil)
{
topController = topController.presentedViewController!
}
}
All implementation for topViewController here are not fully supporting cases when you have UINavigationController or UITabBarController, for those two you need a bit different handling:
For UITabBarController and UINavigationController you need a different implementation.
Here is code I'm using to get topMostViewController:
protocol TopUIViewController {
func topUIViewController() -> UIViewController?
}
extension UIWindow : TopUIViewController {
func topUIViewController() -> UIViewController? {
if let rootViewController = self.rootViewController {
return self.recursiveTopUIViewController(from: rootViewController)
}
return nil
}
private func recursiveTopUIViewController(from: UIViewController?) -> UIViewController? {
if let topVC = from?.topUIViewController() { return recursiveTopUIViewController(from: topVC) ?? from }
return from
}
}
extension UIViewController : TopUIViewController {
#objc open func topUIViewController() -> UIViewController? {
return self.presentedViewController
}
}
extension UINavigationController {
override open func topUIViewController() -> UIViewController? {
return self.visibleViewController
}
}
extension UITabBarController {
override open func topUIViewController() -> UIViewController? {
return self.selectedViewController ?? presentedViewController
}
}
The previous answers relate to the situation where the view controller that should present a view 1) has not been added yet to the view hierarchy, or 2) is not the top view controller.
Another possibility is that an alert should be presented while another alert is already presented, and not yet dismissed.
Swift Method, and supply a demo.
func topMostController() -> UIViewController {
var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!
while (topController.presentedViewController != nil) {
topController = topController.presentedViewController!
}
return topController
}
func demo() {
let vc = ViewController()
let nav = UINavigationController.init(rootViewController: vc)
topMostController().present(nav, animated: true, completion: nil)
}
Swift 5.1:
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let mainViewController = storyboard.instantiateViewController(withIdentifier: "ID")
let appDeleg = UIApplication.shared.delegate as! AppDelegate
let root = appDeleg.window?.rootViewController as! UINavigationController
root.pushViewController(mainViewController, animated: true)
Rather than finding top view controller, one can use
viewController.modalPresentationStyle = UIModalPresentationStyle.currentContext
Where viewController is the controller which you want to present
This is useful when there are different kinds of views in hierarchy like TabBar, NavBar, though others seems to be correct but more sort of hackish
The other presentation style can be found on apple doc

Resources