getting the MasterViewController from the DetailViewController in a UISplitViewController app - ios

I have a standard SplitViewController app in Swift in Xcode 6.1.1. It would be very convenient for my DetailViewController have a pointer to my MasterViewController. (I don't need to send notifications, but I need to pull the current statemachine from my MVC)
My thought was that I would use my DetailViewController viewDidLoad method to grab the MasterViewController from the SplitViewController.
In my DetailViewController:viewDidLoad I have
(snip)
let tVC = self.splitViewController?.viewControllers[0] as? UIViewController
let tNav = self.splitViewController?.viewControllers[0] as? UINavigationController
let tMVC = self.splitViewController?.viewControllers[0] as? MasterViewController
When I inspect these, my tVC and tNav variables were successfully set, but my tMVC was nil. I thought that the splitViewController.viewController[0] would give me my MasterViewController directly, but I'm getting a UINavigationController instead.
Am I wrong/is there a better way to do this?

Found it.
let tNav = self.splitViewController?.viewControllers[0] as? UINavigationController
if let tMVC = tNav?.topViewController as? MasterViewController {
masterViewController = tMVC
stateMachine = tMVC.stateMachine
}

Related

Adding a ViewController in front of window!.rootViewController of type UITabBarController Swift 4

I have inherited a storyboard that launched directly to a UITabBarController. In the AppDelegate.swift file in the didFinishLaunchingWithOptions method there is the following code:
//let loginScreen = window!.rootViewController
let tabController = window!.rootViewController as! UITabBarController
let navController = tabController.viewControllers![0] as! UINavigationController
let salaryController = navController.topViewController as! SalaryTableViewController
let nav2Controller = tabController.viewControllers![1] as! UINavigationController
let employeeController = nav2Controller.topViewController as! EmployeeTableViewController
I am trying to change the code so the login screen is the rootViewController but when I remove the rootViewController from the tabController assignment I get an error that casting a UITableViewController to UITabBarController always fails. Does anyone know why casting a UITableViewController to a UITabBarController always fails unless the UITableViewController is the rootViewController? Is there a way to change the launch screen to a different controller without changing the rootViewController assignment? Here is the storyboard for visual:
enter image description here
UITabBarController is a subclass of UIViewController. You cannot cast superclass to subclass. (Viceversa is possible)
To implement your idea, you should remove initialing your root view controller from storyboard and do it programmatically. Because now, the Xcode think your initial view controller is UITabBarController and the only way is that you instantiate your class programmatically and then make it root view controller.
Why don't you present your login VC as a modal view controller? That way you didn't need to change anything on the view hierarchy.

Error Application tried to present modally an active controller Swift

In have a UITabBarController with several UIViewControllers. Inside one of the controllers, when a certain condition is met, I want to instantiate another UIViewController which is a child of the same UITabBarController.
I keep getting this error "Application tried to present modally an active controller", but I don't understand how is scheduleNavController already active. I looked up several answers on SO, but I still don't understand what is my mistake and how can I solve it?
The flow of the app is like this: WelcomeViewController,LoginViewController,UITabBarController and the children of the UITabBarController.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabController = storyboard.instantiateViewController(withIdentifier: "CentralTabBarControllerID") as! UITabBarController
if let viewControllers = tabBarController?.viewControllers {
let scheduleNavController = viewControllers[1] as! UINavigationController
let scheduleVC = scheduleNavController.childViewControllers[0] as! Schedule
tabController.present(scheduleNavController, animated: true, completion: {
scheduleVC.segmentedControlIndexReceivedFromClaimDetail = self.segmentedControlIndex
})
}
Thanks to Andreas Oetjen suggestion to use the selectedIndex of the UITabBarController I have come up with another solution. However, I still don't know exactly how I should fix my code from the original question to make it work.
//select index of the UIViewController we want to switch to
// get the UINavigationController for the tab we want to switch to
// get UIViewController to which we want to pass data
self.tabBarController?.selectedIndex = 1
let scheduleNavController = tabBarController?.viewControllers?[1] as! UINavigationController
let scheduleVC = scheduleNavController.childViewControllers[0] as! Schedule
scheduleVC.segmentedControlIndexReceivedFromClaimDetail = self.segmentedControlIndex
//remove the the current UIViewController from which we switch to another controller above.
let claimDetailNav = tabBarController?.viewControllers?[0] as! UINavigationController
let claimDetailVC = claimDetailNav.childViewControllers[1] as! ClaimDetail
claimDetailVC.removeFromParentViewController()

Passing data between tabs of UITabBar

Storyboard screenshot with description
I've used this line of code in the UIViewController class of 2nd tab to access the tabBarViewController. But it crashes.
let friendView = self.tabBarController?.viewControllers![2] as! AccountTableViewController
Note: AccountTableViewController is the name of UITableViewController class of 3rd tab.
Thanks for your help.
Change your code like this
let navController = self.tabBarController?.viewControllers![2] as! UINavigationController
for vc in (self.navigationController?.viewControllers)! {
if (vc.isKindOfClass(AccountTableViewController.classForCoder())) {
let friendView = vc as! AccountTableViewController
}
}

Cannot convert a viewController object to viewController type in Swift

I want to pass data from one view controller to another. And I am using the following code in the prepareForSegue to do so...
let detailVC: DetailVC = segue.destinationViewController as! DetailVC
detailVC.alertDict = sender as! NSDictionary
In the first line as you can see there is a ! after the as. Thats because I have to use it to force downcast, and if I do not include the ! then I get an error: UIViewController is not convertable to DetailVC. But DetailVC is a inheritance of UIViewController class, so why can it not convert? Here is the code for the class..
class DetailVC: UIViewController, MFMailComposeViewControllerDelegate, MFMessageComposeViewControllerDelegate {/*code*/}
Think of it like this.
All DetailVCs are UIViewControllers, but not all UIViewControllers are DetailVCs.
Therefore, while you can freely convert a DetailVC to a UIViewController such as:
let detailVC = DetailVC()
let vc : UIViewController = detailVC as UIViewController
You cannot do the reverse, as the view controller might not be a DetailVC.
Therefore you can use as! in order to force the cast to the DetailVC.
let vc : UIViewController = DetailVC()
let detailVC = vc as! DetailVC
If it's a DetailVC, then it'll convert no problem; if it isn't, it will crash.
If you're not 100% sure of the type before you cast, you should always use as? in order to check the type before converting. For example:
let vc : UIViewController = DetailVC()
if let detailVC = vc as? DetailVC {
// do something
}
Or, as user965972 says, you can go one step further by using a guard statement to prevent any further execution if the casting fails. For example:
let vc : UIViewController = DetailVC()
guard let detailVC = vc as? DetailVC else {
// uh oh, casting failed. maybe do some error handling.
return
}
// freely use detailVC from the this point onwards, with the correct type.
The method you use depends entirely on if you want to handle errors and whether further execution depends on the casting being successful.
I assume you are getting a runtime error.
Make sure your view controller is set to the correct class in interface builder. If you first set the class in IB and then define the class in your code, IB will not find the class. You will notice this by looking at the "module" field under the "class" field in IB. It should not be "None", but something like the name of your project.

TabBarController instance Swift 2.0

I am trying to Compile my app again now that Swift 2 is out and the thing is I am having an error with TabBarController instances.
I am declaring the instances in vars in order to use methods from anothers ViewControllers.
Here it's my code:
let barViewControllers = self.tabBarController?.viewControllers
let listViewController = barViewControllers![2].viewControllers![0] as! dbViewController //The [2] is because it's the third TabBar and the [0] it's because It's embebed in a NavigationController.
let calendarViewController = barViewControllers![1] as! CalendarViewController
In the second line Im having the following error:
UIViewController does not have a member named "viewControllers"
Anybody could help me?
Thanks
You are trying to access the property viewControllers of the type UIViewController, which it doesn't have. viewControllers is a property on a UITabBarController, but viewControllers returns an array of UIViewController.
Cast viewControllers to an array of UITabBarController (or only the item you extract) to access it's viewController property.
Like this:
let barViewControllers = self.tabBarController?.viewControllers as! [UITabBarController]
Or this:
let listViewController = (barViewControllers![2] as! UITabBarController).viewControllers![0] as! dbViewController

Resources