In TabBarViewController, how can I access my children directly? - ios

I want to access a child called SubscriptionsViewController (3rd tab)
This is what I'm doing, but it doesn't work.
var subscriptionsViewController: SubscriptionsViewController? {
get {
let viewControllers = self.childViewControllers
for viewController in viewControllers {
if let vc = viewController as? SubscriptionsViewController {
return vc
}
}
return nil
}
}

Assuming you have an instance of tab bar controller, you can do it as follows:
var subscriptionsViewController: SubscriptionsViewController? {
get {
let viewControllers = tabController.viewControllers //assuming you have a property tabBarController
for viewController in viewControllers {
if viewController is SubscriptionsViewController {
return vc
}
}
return nil
}
}

You can access a child of your tab bar controller with the following :
self.tabBarController.viewControllers[2]

Related

Get another tab's viewcontroller variable in current tab bar - iOS - Swift

I have a tabBarController with below hierarchy
TabBarController
Tab 1 -> Contains Navigation Controller(NavController1) -> ViewController1 -has--> ContainerView --contains--> DisplayedViewControllerTab1 (this is my tab1 view controller displayed)
Variable dataForVC1 is in DisplayedViewControllerTab1
When user taps Tab3 (DisplayedViewControllerTab3), I'm trying to get value of dataForVC1 to pass to tab3 viewController
So far I've tried this
In in TabBarController - didSelect method
var data: ModelData?
if let navController = tabBarController.viewControllers?[0] as? NavController1,
let childNavVC = navController.children.first as? ViewController1 {
//Get container view
let conView = childNavVC.containerView. //This is outlet
//Looking for something like this - struck here
if let displayedVC1 = "Container view's VC as? DisplayedViewControllerTab1 {
data = displayedVC1.dataForVC1
}
}
Kindly advice how to achieve this
You don't need to hustle around with container views. In your ViewController1 simply add references to the DisplayedViewControllerTab1 or other vc's you need and then access them directly.
Your VC code would look something like this.
class ViewController1: UIViewController {
//...
var displayedVC1: DisplayedViewControllerTab1?
//...
}
And then your code now:
var data: ModelData?
if let navController = tabBarController.viewControllers?[0] as? NavController1,
let childNavVC = navController.children.first as? ViewController1 {
data = childNavVC.displayedVc1
}
P.S. For convenience, if you have your own TabBarController class you can add all the references you need when the controller is created or simply have computed properties, so you don't need to go deep in the hierarchy all the time.
In your TabbarViewController class:
var myViewController1: MyViewControllerOne? {
return (viewControllers[0] as? NavController)?.children.first as? MyViewControllerOne
}
var myViewController2: MyViewControllerTwo? {
return (viewControllers[1] as? NavController)?.children.first as? MyViewControllerTwo
}

What is the Use of Returning the Same function itself in Swift

Well I see some syntax in the following function which returns the topMostViewController. This function is defined in AppDelegate
func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
//***A topViewController which is Returning itself
//***This is where I got Confusion
return topViewController(controller: navigationController.visibleViewController)
} else if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
} else if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
And it's used as
if (self.topViewController() as? SomeViewController) != nil {
if orientation.isPortrait {
return .portrait
} else {
return .landscape
}
}
I understood that the code is trying to set orientation based on the currently visible View Controller but I don't understand what is the necessity of returning the same function itself in topViewController. Also I see some syntax like
extension UIApplication {
/// The top most view controller
static var topMostViewController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
}
}
extension UIViewController {
/// The visible view controller from a given view controller
var visibleViewController: UIViewController? {
if let navigationController = self as? UINavigationController {
// *** Here it's returning Same variable i.e visibleViewController
// *** a function could call itself recursively. But how can a Variable calls itself recursively?
return navigationController.topViewController?.visibleViewController
} else if let tabBarController = self as? UITabBarController {
return tabBarController.selectedViewController?.visibleViewController
} else if let presentedViewController = presentedViewController {
return presentedViewController.visibleViewController
} else {
return self
}
}
}
Edited
That is called recursion. There is a condition in the recursion that cause t end the cycle :
Not in the navigationController, because it has another visible controller
Not in the tabBarController, because it has another visible controller
Not presenting another controller, because the presented one is visible
if one of these appears -> we go down one level and call this function again until none of these true.
It is a recursive function. The topViewController function calls itself to find the top most controller which is visible. The function will exit when controller?.presentedViewController returns nil (Which means that the value held by controller is the top most visible controller). You can also achieve the same without a recursive function as mentioned here: How to find topmost view controller on iOS, but it looks much more cleaner than the looping implementation.

Swift Deeplink to already open App's already open viewcontroller?

I have an app that accepts a Deeplink URL and opens a viewcontroller with variables from the link and it works well if the App is opened/run for the first time by the user using the Deeplink.
However, if the App is already open/or in the background and has that view controller open... it then opens the same viewcontroller back up again so then I have two. I do not want to open the viewcontroller an additional time.
Is there some way I can identify that viewcontroller that is already open and pass the variables from the Deeplink to it?
or do I need to close it in some way and re-open it?
I am open to suggestions.... thanks in advance.
Try using UIApplication.shared.keyWindow?.rootViewController and testing what class it is. For example:
if let vc = UIApplication.shared.keyWindow?.rootViewController {
if vc is SomeViewController {
// Do something.
}
}
You can find the visible view controller with the following method
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!
}
return getVisibleViewController(presented)
}
return nil
}
you can then switch on the presented view
if let presentedView = getVisibleViewController(window?.rootViewController) {
switch presentedView {
//code
default:
//code
}
}
and of course in the switch present a view controller if it is not the one that you want to be open.
No need to close a viewcontroller before opening it!

How to compare UIViewController in Swift 3?

I am trying to compare to UIViewController in Swift 3 but there is some error
extension UINavigationController
{
func myPopToViewController(viewController:UIViewController, animated:Bool) -> UIViewController? {
var arrViewControllers:[UIViewController] = []
arrViewControllers = self.viewControllers
for vc:UIViewController in arrViewControllers {
if(vc.isKind(of: viewController) ) // This Line gives me error
{
return (self.navigationController?.popToViewController(vc, animated: animated)?.last)!
}
}
return nil
}
}
/Users/varunnaharia/Documents/Projects/appname/appname/Public/UINavigationController+Extra.swift:18:30: Cannot convert value of type 'UIViewController' to expected argument type 'AnyClass' (aka 'AnyObject.Type')
and if try to use
if(vc is viewController)
It gives
/Users/varunnaharia/Documents/Projects/appname/appname/Public/UINavigationController+Extra.swift:18:22: Use of undeclared type 'viewController'
I am calling it through this
self.navigationController?.popOrPopToViewController(viewController: MyUIViewController(), animated: false)
for viewsController in arrViewControllers
{
if(viewsController.isKind(of: YourControllerClassName.self)){
}
}
Swift 4
Hope it will work for you
extension UINavigationController {
func myPopToViewController(viewController:UIViewController, animated:Bool) {
var arrViewControllers:[UIViewController] = []
arrViewControllers = self.viewControllers
for vc:UIViewController in arrViewControllers {
if(vc.isKind(of: viewController.classForCoder)){
(self.popToViewController(vc, animated: animated))
}
}
}
}
In swift, we use is instead of isKind(of:).
is is used to check the type of the object.
So you can use,
if(vc is UIViewController)
But I think here you are trying to match the 2 references of UIViewController.
So, you need to use === instead of is. This operator is used to match 2 references of same type.
if(vc === viewController)
If you want to compare to a particular view controller you have to compare their refererences.
Try this...
if(vc === viewController) )
{
return (self.navigationController?.popToViewController(vc, animated: animated)?.last)!
}
i just modify the answer of Mr. #BangOperator for move to particular View controller.
extension UINavigationController {
func popTo(controllerToPop:UIViewController) {
//1. get all View Controllers from Navigation Controller
let controllersArray = self.viewControllers
//2. check whether that view controller is exist in the Navigation Controller
let objContain: Bool = controllersArray.contains(where: { $0 == controllerToPop })
//3. if true then move to that particular controller
if objContain {
self.popToViewController(controllerToPop, animated: true)
}
}
}

How to remove special controller from navigationController's viewControllers?

I want to write a class method with two params to delete viewController from navigation controller, but I don't know how to do with it.
My code is below, I tested, not success:
class func removeVC(_ fromNav:UINavigationController, _ controller:UIViewController) {
let controllers:NSArray = fromNav.viewControllers as NSArray
for item in controllers {
if (item as AnyObject).isMember(of:controller) { // There is not pass by Xcode
// remove item out of fromNav.viewControllers
}
}
}
How to judge the controller's class equals to the param controller in swift?
Try like this
Method 1)
class func removeVC(fromNav: UINavigationController, controller: UIViewController) {
let controllers = fromNav.viewControllers
for item in controllers {
if item == controller {
fromNav.viewControllers.remove(at: fromNav.viewControllers.index(of: item)!)
}
}
}
Usage: UtilSwift.navRemoveVC(self.navigationController!, self)
Method 2):
class func navRemoveVC(_ fromNav:UINavigationController, withControllerClass:AnyClass) {
let controllers = fromNav.viewControllers
for item in controllers {
if (item as AnyObject).isKind(of: withControllerClass.self) {
fromNav.viewControllers.remove(at: fromNav.viewControllers.index(of: item)!)
}
}
}
Usage: UtilSwift.navRemoveVC(self.navigationController!, withControllerClass:UserRegisterViewController.self)
you can try this:
if (item as AnyObject).isKind(of: UIViewController.self){
// remove item out of fromNav.viewControllers
}

Resources