UINavigationController, UITabBarController, UITableViewController - swift

I currently have the application set up with a UINavigationController as the initial view, which has a UITableViewController as its root view controller. The app runs fine up until this point. I have the following code in AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let favLibrary = FavLibrary()
let navController = window!.rootViewController as! UINavigationController
let favController = navController.topViewController as! FLViewController
favController.favLibrary = favLibrary
return true
}
I am trying to implement a UITabBarController so that I can switch between two UITableViewControllers at the same level (Favorites and a Library) using the Tab Bar.
I embed each VC in its own Navigation Controller, then I embed the two Navigation controllers into one Tab Bar Controller.
Upon running the application, it crashes with the following error:
Could not cast value of type 'UITabBarController' (0x115f9e430) to 'UINavigationController' (0x115f971d0).
2018-09-27 15:49:43.811377-0700 appName [3675:954448] Could not cast value of type 'UITabBarController' (0x115f9e430) to 'UINavigationController' (0x115f971d0).
How can I correct the code in AppDelegate to retain functionality with the new arrangement of Tab Bar and Navigation Controllers?

If I understand your question correctly, I suppose you should access the tabBarController first, then retrieve the view controllers it contains, which should be a list of navigation controllers. Then you can get your view controller inside the selected navigation controller:
let tabBar = window!.rootViewController as! UITabBarController
let targetTabNav = tabBar.viewControllers![1] as! UINavigationController // change index to what you want
let targetVc = targetTabNav.viewControllers.first!
// Do what you want with the target Vc ...

Related

How to set property in app delegate for tabview controller

I've read several of the suggested posts that were to help with this problem but could not find something for this particular issue.
I need to set a property on my controller, the book is saying to do so in the app delegate. In the previous assignment using a navbar, this worked:
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
// Override point for customization after application launch.
// Create an ItemStore to hold list of items
let itemStore = ItemStore()
// Access the ItemsViewController and set its item store
let itemsController = window!.rootViewController as! ItemsViewController
itemsController.itemStore = itemStore
return true
}
However, creating a similar program I need to use tab bar but can't get it to work and keep running into the error stated above, on the let editcontroller = tabcontroller. - line
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Create an pindatabase
let pinDataBase = PinDatabase()
// Access the pairsViewController and set its database
let tabController = window!.rootViewController as! UITabBarController
// this edit controller line is where I am stuck
let editController = tabController.tabBar as! EditViewController
editController.pinDataBase = pinDataBase
return true
}
The error is:
fatal error: unexpectedly found nil while unwrapping an Optional value
The controller I'm trying to set the property on is not the root controller but the third tab if that helps.
Your issue is your attempt to cast the UITabBar to your EditViewController. You need to access the array of view controllers from the tab bar controller and access the one that is actually the EditViewController.
let editController = tabController.viewControllers[2] as! EditViewController
This will access the 3rd view controller and cast it as needed.

Pass data from tabBarController to uiviewcontroller embedded in navigationcontroller

I have a custom tab bar controller. each tab contains a viewcontroller embedded in a navigation controller. I get a value for pro_user from the database in appledelegate and set it there. then before CustomTabBarController gets launched (in appledelegate), i set it's "pro_user" property to true or false (this works and CustomTabBarController receives the value from appledelegate).
Now i'm trying to pass this same value to the ViewControllers (ViewController1 and ViewController2). each view controller also has a "pro_user" property.I'm doing this by creating the viewcontroller instances and then setting their pro_user property before embedding each viewcontroller in a navigationcontroller. but neither viewcontroller is actually receiving the value of pro_user which i'm setting in CustomTabBarController. I hope this is clear. How do i pass the value of pro_user from CustomTabBarController to each of the view controllers? programmatically (i'm not using storyboards)
class AppDelegate: UIResponder, UIApplicationDelegate {
var pro_user = true
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame:UIScreen.main.bounds)
window?.makeKeyAndVisible()
let customTabBarController = CustomTabBarController()
customTabBarontroller.pro_user = pro_user
self.window?.rootViewController = customTabBarController
return true
}
}
class CustomTabBarController:UITabBarController{
var pro_user : Bool?
override func viewDidLoad(){
super.viewDidLoad()
let viewController1 = ViewController1()
viewController1.pro_user = pro_user //doesn't work
let firstNavigationController = UINavigationController(rootViewController: viewController1)
let viewController2 = ViewController2()
viewController2.pro_user = pro_user //doesn't work
let secondNavigationController = UINavigationController(rootViewController:viewController2)
viewControllers=[firstNavigationController,secondNavigationController]
}
It looks like you're setting a global setting. If so you may want to consider using UserDefaults.
E.g. with a nice extension:
extension UserDefaults {
var isProUser: Bool {
get {
return bool(forKey: "isProUser")
}
set {
set(newValue, forKey: "isProUser")
}
}
}
Then anywhere in your app you can set it:
UserDefaults.standard.isProUser = true
And get it:
let isProUser = UserDefaults.standard.isProUser
The value is also saved between launches.

Swift - Access different View Controllers in App Delegate

In app delegate, with a simple app having only 2 screens:
first screen is a Table View Controller embedded in Navigation Controller
second screen is a View Controller which is used to add items to the first screen table via protocol/delegate/segue
And this is the code for didFinishLaunchingWithOptions in app delegate that I can reference the first screen as viewController[0]:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let navController = self.window?.rootViewController as! UINavigationController
let courseListController = navController.viewControllers[0] as! CourseListController
courseListController.managedObjectContext = self.managedObjectContext
return true
}
How can I reference the screens at above, below and next to the center sreen? Please suggest me a solution. Thank you!
It's important to keep in mind that the view controller objects for all of the "peripheral" view controllers in your story board won't actually exist until the segue to get to them is executed, so there's no way to get access to them directly from the app delegate. Instead, you need to push state to each child view controller as it's created, from whatever the source view controller is. Segues are the appropriate way to do this.
You will probably want to assign each of the segues from the central view controller a unique segue identifier in Interface Builder. Click on the segue, then enter it here:
In the central view controller, implement prepareForSegue(_:sender:), doing something like the following:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject) {
switch segue.identifier {
case "SegueIdentifier1":
guard let destination = segue.destinationViewController as? ViewController1 else {
return
}
// set up your view controller here
case "SegueIdentifier2":
guard let destination = segue.destinationViewController as? ViewController2 else {
return
}
// set up your view controller here
// add additional segues as required
default:
break // unknown segue
}
}
Go to each view Controller you want to reference and in the identity inspector, add some string to its StoryBoard ID.
next to reference it from the new ViewController (say, XViewController) to (say, YViewController)
do this :
var referencedViewController = self?.storyboard.
instantiateViewControllerWithIdentifier("referenceViewID") as! YViewController
self.presentViewController(referencedViewController,
animated: true, completion: nil)

Populate a Navigation Controller on First Launch

When my app is first launched I want to create some sample data that new users will see. I'd like them to start a level (maybe more) into the navigation controller, like so:
tableViewController0 -> tvc1 (user starts here)
Picture a notes app that has folders as its top level of navigation. You might want to show the user a few sample notes in a sample folder first, then let him/her go back later and create new folders.
My thought was that I'd run a method in application didFinishLaunchingWithOptions that would check for first launch (checking/setting a Bool in NSUserDefaults) and then, if we are in the first launch, create some sample data. Then I thought I could just create each view controller and set my UINavigationController's viewControllers property, but I get this error:
'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
(The cell definitely does have an identifier Cell in the storyboard and works if I don't create the data and view controllers beforehand.)
Some sample code from my AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// From Xcode's stock AppDelegate
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as! UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController
// Check for first launch, get back a sample object.
if isFirstLaunch == true {
let newObject = prepareFirstLaunch()
let tvc0 = TableViewController0()
tvc0.managedObjectContext = managedObjectContext
let tvc1 = TableViewController1()
tvc1.someObject = newObject
masterNavigationController.viewControllers = [tvc1, tvc0]
} else {
// this is moved from the stock AppDelegate down into this else statement.
let controller = masterNavigationController.topViewController as! TableViewController0
controller.managedObjectContext = self.managedObjectContext
}
return true
}
private func isFirstLaunch() -> Bool {
// return whether we're launching for the first time
}
private func prepareSampleObject() -> SomeObject {
/*
If we're launching for the first time
create someObject, create some other objects that are owned
by this object in CoreData, set up their relationships, etc.
*/
return someObject
}
Is there another way I can set this up so the user can jump right into a populated navigation stack rather than having to start at the top level?
You're using storyboards, which means you have an initial view controller.
Make this initial view controller a UINavigationController whose rootViewController is some SetupViewController where all of your checking logic occurs. Show a UIActivityIndicatorView in it, or whatever loading animation. Then, depending on what you've found, push either the dummy notes screen or the top-level folder screen.
In the storyboard, you will create two segues from the SetupViewController--one to the notes, one to the folder. Give each segue its own name. You call performSegueWithIdentifier in the code where you determine which screen is getting pushed.

Orientation issue when loading storyboard - iOS

I have created a view controller in didFinishLaunching method of AppDelegate and assigning it to window's root view controller.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds) // issue with allocating UIWindow
self.viewController = AKSidePanelController()
self.viewController!.centerPanel = UINavigationController(rootViewController: UIStoryboard.centerViewController()!)
window!.rootViewController = self.viewController
window!.makeKeyAndVisible()
return true
}
func shouldAutorotate() -> Bool {
return true
}
func supportedInterfaceOrientations() -> Int {
return UIInterfaceOrientation.Portrait.rawValue | UIInterfaceOrientation.LandscapeLeft.rawValue | UIInterfaceOrientation.LandscapeRight.rawValue
}
for some reasons, auto rotation is not working. i think the problem is because of allocating window in didfinishlaunching. When i try to launch directly from storyboard(by marking isInitialViewController = yes) without adding any code on didFinishLaunching, autorotation works. As you can see, i need to load "viewController" as rootviewcontroller. What i am doing wrong?
I am using Xcode 6.1
The problem is that i am loading View from storyboard as well as initialising view controller in didFinishLaunchingOption in App delegate. the solution is to avoid loading initial View from storyboard.
Set IsinitialViewController to no and load the view controller from appdelegate
This one helps me to fix the issue. Programmatically set the initial view controller using Storyboards
Note that you have to make "Main storyboard file base name" as empty in plist.