How to show Form Sheet from AppDelegate? - ios

The AppDelegate checks whether the user is logged in or not, and if not, shows the formSheet view from wherever the user is in the application.
How do I do this?

You can use the window property like rootViewController in appdelegate class
Try this:-
if let rootVC = self.window?.rootViewController as? YourViewController {
//Add the code here to present signupviewcontroller as form sheet
}
if your rootVC is UINavigationController than use visibleViewController property of UINavigationController
if let nsvigationVC = self.window?.rootViewController as? UINavigationController {
if yourVC = nsvigationVC.visibleViewController {
//Add the code here to present signupviewcontroller as form sheet
let sb = UIStoryboard(name: “Main”, bundle: nil) //get signupviewcontroller from storyboard
let signupVC = sb.instantiateViewControllerWithIdentifier("signupviewcontroller") as! signupviewcontroller
signupVC.modalPresentationStyle = UIModalPresentationStyle.FormSheet
signupVC.preferredContentSize = CGSize(width: 365, height: 500) // set content size as per your requirement
yourVC.presentViewController(signupVC, animated: true, completion: nil)
}
}

Related

Properly redirect to a view controller inside tab bar's navigation controller when push notification is clicked

I have a tabBar. Each of its tabs' ViewControllers have been embedded inside their respective NavigationControllers.
Hierarchy:
Tabbarcontroller --> NavigationController --> VC1 --> VC2 -->...VCn (for tab1)
I have to redirect to one of the viewcontrollers say VC2 when i click on push notifications. The code i am using is as follows.
let navigationController = UINavigationController()
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigationController.viewControllers = [chatViewController]
window?.rootViewController = navigationController
}
By using this, I am redirected to the respective ViewController. However, i am not able to get back to the tabBar. So is it possible to redirect in such a way that it allows me the get back to the tab bar, thus providing the same hierarchy as in the UI?
EDIT:
The image below shows my layout.
I would like to accomplish the following:
When the user taps the push notification, the app should be directed to VC-B. *It should not create new object for VC-B and add to the navigation stack if VC-B is already on top of navigation stack.
If the app had been terminated and the user taps on the notification, it should open VC-B.
For determining if the app had been terminated, I set a flag as:
func applicationWillTerminate(_ application: UIApplication) {
UserDefaults.standard.set(true, forKey: UserDefaultsKey.isTerminated)
}
This flag is set false at the end of didFinishLaunchingWithOptions function.
For redirection, I check this flag to determine if the app had been terminated:
func performRedirectionToSuitableViewController(userInfo: [AnyHashable: Any]) {
let isTerminated = UserDefaults.standard.object(forKey: UserDefaultsKey.isTerminated) as! Bool
if isTerminated {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let tab = storyboard.instantiateViewController(withIdentifier: "tabBar") as! UITabBarController
tab.selectedIndex = 0
let nav = tab.viewControllers![0] as! UINavigationController
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
nav.pushViewController(chatViewController, animated: true)
} else {
if let tab = window?.rootViewController?.presentedViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
if let nav = tab.selectedViewController as? UINavigationController {
nav.pushViewController(chatViewController, animated: true)
}
}
}
}
With this code, my first requirement is partially fulfilled. Need to determine if the viewcontroller is at the top of the navigation stack.
And, in the case of terminated app, clicking the push notification opens the tab bar, with the default selection index being selected.
I have spent several days trying to fix this. But cannot get it work.
I suppose you have to do something like this:
let tabBar: UITabBarController // get your tab bar
tabBar.selectedIndex = 0 // eg. zero. To be sure that you are on correct tab
if let navigation = tabBar.viewControllers?[tabBar.selectedIndex] as? UINavigationController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigation.pushViewController(chatViewController, animated: true)
}
}
Well, you are setting current window.rootViewController, which should be that TabBarViewController I'd say. That's why you cannot get back to the TabBar
What you should do is
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController, let tabBar = storyboard.instantiateViewController(withIdentifier: "tabBar") as? UITabBarController {
let navigationController = UINavigationController(rootViewController: chatViewController)
navigationController.viewControllers = [chatViewController]
tabBar.viewControllers = [navigationController]
window?.rootViewController = tabBar
}
To get the current navigation controller from the tabbar controller. You can use this function
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
//UIAlertController
if presented.isKind(of: UIAlertController.self) {
let alertController = presented as! UIAlertController
return alertController.presentingViewController
}
return getVisibleViewController(presented)
}
return nil
}
Using the function you can navigate your viewcontroller like below.
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let yourView = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourView") as! NewnoteViewController
let navi = self.getVisibleViewController(self.window!.rootViewController) as! UINavigationController
navi.pushViewController(yourView, animated: true)
You can create a method in your RootViewController that will redirect user to a specific view after receiving a push notification. Here's what I did in my previous project.
class RootViewController: UIViewController {
private var currentView: UIViewController
init() {
self.currentView = ViewController()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
addChildViewController(currentView) // 1
currentView.view.frame = view.bounds // 2
view.addSubview(currentView.view) // 3
currentView.didMove(toParentViewController: self) // 4
}
func showDetailsScreen() {
if let updateDetailView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UpdateDetailsNotif") as? UpdateDetailsViewController{
let navController = UINavigationController(rootViewController: updateDetailView)
addChildViewController(navController)
navController.view.frame = view.bounds
view.addSubview(navController.view)
navController.didMove(toParentViewController: self)
currentView.willMove(toParentViewController: nil)
currentView.view.removeFromSuperview()
currentView.removeFromParentViewController()
currentView = navController
}
}
}
Then you can call that method on your AppDelegate like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
AppDelegate.shared.rootViewController.showDetailsScreen()
completionHandler()
}
}

Saving VC on exit (IOS)

I have the code to save VC on exit.
It works but I use SWRevealViewController but the navigation controller in the application is not displayed
How can I correct it?
ViewController.swift
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "View")
ViewController2.swift
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setInteger(1, forKey: "View")
AppDelegate.swift
let viewCount = UserDefaults.standard.integer(forKey: "View")
var VC = UIViewController()
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
print(viewCount)
if viewCount == 0 {
VC = storyboard.instantiateViewController(withIdentifier: "First") as! ViewController
} else if viewCount == 1 {
VC = storyboard.instantiateViewController(withIdentifier: "Second") as! ViewController2
}
self.window?.makeKeyAndVisible()
self.window?.rootViewController = VC
return true
Error
My StoryBoard
So I think the issue is that you are setting the root view controller of the window to be a regular view controller. What you need to do is set this to be a navigation controller with your embedded view controllers. I'm not sure of your exact setup with SWRevealViewController but I guess you need something like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let viewCount = UserDefaults.standard.integer(forKey: "View")
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewController(withIdentifier: "First") as! ViewController
let frontNavigationController = UINavigationController(rootViewController: VC)
let VC2 = storyboard.instantiateViewController(withIdentifier: "TableView") as! TableView
let rearNavigationController = UINavigationController(rootViewController: VC2)
let revealVC = SWRevealViewController(rearViewController: rearNavigationController, frontViewController: frontNavigationController)
revealVC.delegate = self
self.window?.rootViewController = nil
self.window?.rootViewController = revealVC
self.window?.makeKeyAndVisible()
return true
}
If this isn't working, or what you are looking to achieve, please ask in comments.
EDIT:
So, if you want to display the TableView menu on the left, you have to set the storyboard id of it to be TableView (obvs set this in the storyboard). And then the code above should work.
I'm not entirely sure why you are using UserDefaults to save the VC. But you can now use the TableView class to navigate between VCs. If you want to save the last visited VC and display this on application finished launching, you would need this:
var string = ""
switch viewCount {
case 0:
string = "First"
case 1:
string = "Second"
case 2:
string = "Third"
case 3:
string = "Fourth"
case 4:
string = "Fifth"
default:
string = "First"
}
let VC = storyboard.instantiateViewController(withIdentifier: string)
let frontNavigationController = UINavigationController(rootViewController: VC)
Just make sure you have set the storyboard id of each VC to be "First", "Second", "Third" and so on. This would replace the previous declarations of let VC and let frontNavigationController in my above answer.
If in your storyboard your initial ViewController is UINavigationViewController then you can access this navigation controller in AppDelegate's didFinishLaunchingWithOptions as follows:
let navigationController = application.windows[0].rootViewController as! UINavigationController
Also, there is no need to track/save your view controllers in UserDefaults, you can always access the list of ViewControllers like this
AppDelegate
let childViewControllers = window?.rootViewController?.childViewControllers
print("childVC: \(String(describing: childViewControllers))")
In Any other ViewController
let childViewControllers = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers
print("vcs : \(String(describing: childViewControllers))")
And of course, you can compare childViewControllers list
if let childViewControllers = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers {
for viewController in childViewControllers {
if viewController is FirstViewController {
print("firstViewController found...")
}
if viewController is SecondViewController {
print("secondViewController found...")
}
}
}
In case if you're setting SWRevealViewController for side-menu using storyboard then the basic setup should be like
For more detail on SWRevealViewController have a look at their GitHub documentation, check sample project and mentioned tutorials here for deep understanding. Hope it helps.

Dissmiss from UITabBarController

I guess it can be duplicated, but I looking everywhere and didn't find a solution for me.
So about my question. I have something like this
open this image to see more
In my AppDelegate I have func
func logIn() {
let userExist = UserDefaults.standard.string(forKey: "auth_key_user")
if userExist != nil && userExist != "" {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let whereToGo = storyboard.instantiateViewController(withIdentifier: "AllPostsOfUserTabBarController") as! AllPostsOfUserTabBarController
window?.rootViewController = whereToGo
}
}
If user exist it lead me to first view controller inside tab bar controller. There I have navigation button with action to logout.
I need log out(send data to the server) and then go to my first view controller with text field and button where I can again log in.
How do I need to implement it?
Try this code after logout-
DispatchQueue.main.sync() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateInitialViewController()
appDelegate.window?.rootViewController = loginVC
}
Hope it helps!
Put this code in function of your logout button. It will throw you back to your root controller.
func logoutBtnClicked()
{
DispatchQueue.main.sync()
{
(send your data to server)
self.navigationController?.popToRootViewController(animated: true)
}
}
Try this in appdelegate
While Login
self.window?.rootViewController = whereToGo
While Logout
func setupLoginViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC: UIViewController? = storyboard.instantiateViewController(withIdentifier: "loginVCID") as? UIViewController
let navController: UINavigationController? = UINavigationController(rootViewController: loginVC!) as UINavigationController
window?.rootViewController = navController
}

Swift 3 - Close current ViewController and Open new ViewController

I've gone through a tad amount of SO posts about this but none of them clearly state how to get this done in Swift 3.
Simply put, I need to close the current view that is displayed and open a new view controller. Such that the only view controller open would be the 2nd VC
This is the code I tried using, but when I try this the view controller gets closed without the rest of the code getting executed
//Close current VC
self.dismiss(animated: true, completion: nil)
//Open the new VC
let mainStoryboard: UIStoryboard = UIStoryboard(name:"Main",bundle:Bundle.main)
let secondViewController: SecondViewController = mainStoryboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.present(secondViewController, animated: true, completion: nil)
Can someone help me out? I just need a simple transition from 1st VC to 2nd VC and close the 1st VC.
The problem is, that "self" can not present the new ViewController since it is the ViewController that is getting dismissed.
You could try calling self.presentingViewController?.present(secondViewController, animated: true, completion: nil)
After spend full day i got a solution on Apple' developer website (on this link)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(identifier: "second_view_controller")
secondVC.modalPresentationStyle = .fullScreen
secondVC.modalTransitionStyle = .crossDissolve
present(secondVC, animated: true, completion: nil)
If you presented that view controller from some view controller or it is the viewcontroller from navigation or tab viewcontroller, then try to get last visible view controller, code is in this link
Get the current displaying UIViewController on the screen in AppDelegate.m
or else use this code (I have copied from that link)
public extension UIWindow {
public var visibleViewController: UIViewController? {
return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
}
public static func getVisibleViewControllerFrom(_ vc: UIViewController?) -> UIViewController? {
if let nc = vc as? UINavigationController {
return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
} else if let tc = vc as? UITabBarController {
return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
} else {
if let pvc = vc?.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(pvc)
} else {
return vc
}
}
}
}
Then present your viewcontroller from that visibleViewController -
//Close current VC
self.dismiss(animated: true, completion: nil)
//Open the new VC
let mainStoryboard: UIStoryboard = UIStoryboard(name:"Main",bundle:Bundle.main)
let secondViewController: SecondViewController = mainStoryboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
window?.visibleViewController?.present(secondViewController, animated: true, completion: nil)
If your view controller is the root view controller, then set it to root view controller
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.window?.rootViewController = secondViewController
}

Navigation and tab bar missing when presenting view controller

I am implementing home screen shortcuts using 3D Touch, and it's working well, however the way I currently have it means that when the shortcut takes the user to a specific view controller, the tab bar and navigation bar is missing.
This is my code:
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
var handled = false
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
let rootViewController = window!.rootViewController
switch shortcutType {
case .Favourites:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootController = storyboard.instantiateViewControllerWithIdentifier("favourites") as! FavouritesTableViewController
rootController.parkPassed = DataManager.sharedInstance.getParkByName(NSUserDefaults.standardUserDefaults().stringForKey("currentPark")!)
self.window?.rootViewController = rootController
self.window?.makeKeyAndVisible()
handled = true
}
return handled
}
Can anyone suggest what I need to change in the code?
This is the starboard layout (FavouritesTableViewController is indicated):
EDIT:
Here is my updated code:
#available(iOS 9.0, *)
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
var handled = false
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
switch shortcutType {
case .Favourites:
print("favourites")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootController = storyboard.instantiateViewControllerWithIdentifier("favourites") as! FavouritesViewController
rootController.parkPassed = DataManager.sharedInstance.getParkByName(NSUserDefaults.standardUserDefaults().stringForKey("currentPark")!)
let root = UIApplication.sharedApplication().delegate as! AppDelegate
if let navCont = root.window?.rootViewController?.navigationController {
navCont.presentViewController(rootController, animated: true, completion: nil)
} else {
root.window?.rootViewController?.presentViewController(rootController, animated: true, completion: nil)
}
root.window?.makeKeyAndVisible()
handled = true
}
}
return handled
}
Try this:
Get the delegate from app delegate:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
GEt the controller you would like to to present from storyboard:
Controller *cont=//get the reference from storyboard either using storyboard ID
and then make your rootview present the other controller:
[appDelegate.window.rootViewController presentViewController:cont animated:YES completion:^{
DLog(#"presented your view ON TOP of the tab bar controller");
}];
Swift:
var appDelegate: AppDelegate = UIApplication.sharedApplication().delegate()
var cont: Controller = //get the reference form storyboard
appDelegate.window.rootViewController.presentViewController(cont, animated: true, completion: { DLog("presented your view ON TOP of the tab bar controller")
})
you can move the presenting stuff on main thread,if you like!!!!
So I got the solution I hope that will work for you.
First you have to set your Storyboard instance.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
after that you need to set where you want to start the navigation.
let mynVC = storyboard.instantiateViewControllerWithIdentifier("root") as! UINavigationController
right now you can set the viewcontroller that you want to appear
let playVC = storyboard.instantiateViewControllerWithIdentifier("playVC")
So and now you can start the workflow but note that you have to do this in a completion like this
self.window?.rootViewController?.presentViewController(rootVC, animated: true, completion: { () -> Void in
rootVC.pushViewController(playVC, animated: true)
})
So your rootViewController will present you "rootVC" and after that your playVC.
I hope It will help you guys:)
The problem is, you are setting the view controller(named:rootController) to be the window?.rootViewController, other than presenting the rootController by the window?.rootViewController. Just change
self.window?.rootViewController = rootController
to
self.window?.rootViewController.presentViewController(rootController, animated: true, completion: nil)
if you use it like this you will be replaced rootview to this viewcontroller. So this will be a new solo page and its normal to dont have navigationController or tabbarController. Because you have only this new page in view hierarchy.
If you want to present this from rootview, you may try
let root=UIApplication.sharedApplication().delegate as! AppDelegate
if let navCont=root.window?.rootViewController?.navigationController
{
navCont.presentViewController(viewControllerToPresent: UIViewController, animated: true, completion: nil)
}
else{
root.window?.rootViewController?.presentViewController(viewControllerToPresent: UIViewController, animated: true, completion: nil)
}

Resources