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
}
Related
I am on NSView Controller B where I came from A using
**let vc = RegisterViewController.registerViewController()
vc.title = "Sign up"
self.presentViewControllerAsModalWindow(vc)**
Now from B, I want to go to C while closing A as my B is opened in separate window.
I have wrote an extension for this, you can close any view controller using it:
extension NSApplication{
static func closeWindow(withVCType vcType: NSViewController.Type)
{
// Find the window of desired view controller by comparing class name
if let windowOfA = shared.windows.first(where: {
$0.contentViewController?.className == vcType.className()
}){
// Close it
windowOfA.close()
}
}
}
So in order to close let say view controller A, call it like this:
NSApplication.closeWindow(withVCType: A.self)
Another Thing that worked for was this solution.
if let window = NSApplication.shared.mainWindow {
let VC = ViewController.VC()
window.contentViewController = VC
if let windowController = window.windowController as? homeWindow {
}
}
self.view.window?.close()
Where the function VC() is.
class func VC() -> ViewController {
return NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "VC")) as! ViewController
}
There are 2 apps A & B.
App A has a url to open App B.
Soon as App B opens, it has to load 5 view controllers onto the navigation stack which is done by the following code:
let LandingVC = self.storyboard?.instantiateViewControllerWithIdentifier("LandingVC") as! LandingVC
let Dashboard = self.storyboard?.instantiateViewControllerWithIdentifier("Dashboard") as! Dashboard
let PlayerVC = self.storyboard?.instantiateViewControllerWithIdentifier("PlayerVC") as! PlayerVC
let PlayerDetailVC = self.storyboard?.instantiateViewControllerWithIdentifier("PlayerDetailVC") as! PlayerDetailVC
let ScoreReportVC = self.storyboard?.instantiateViewControllerWithIdentifier("ScoreReportVC") as! ScoreReportVC
let viewControllersList = [LandingVC, Dashboard, PlayerVC, PlayerDetailVC, ScoreReportVC]
self.navigationController?.setViewControllers(viewControllersList, animated: false)
From ScoreReportVC I need to be able to set variables on previous ViewControllers so that the user can navigate to previous screens even though they fired the application from another application.
Here is what I have tried: defined a protocol in previous view controllers that are behind ScoreReportVC that are on the stack and inside ScoreReportVC like so:
for viewcontroller in self.navigationController?.viewControllers {
if viewcontroller is PlayerDetailVC {
PlayerDetailVC.delegate = self
}
if viewcontroller is PlayerVC {
PlayerVC = self
}
if viewcontroller is Dashboard {
Dashboard.delegate = self
}
if viewcontroller is LandingVC {
LandingVC.delegate = self
}
}
But the delegates are not getting called. Any help how to correctly pass data to all the ViewControllers on the stack would be greatly appreciated.
For relatable classes we use Delegations. But the classes which are not relatable , for those, we use Notifications. In your case Notifications will be needed for implementation and to pass data from one VC to another VC.
A better approach would be to create/set the variables when the controllers are created.
EDIT: I was off base in my approach. I'm trying to pass data from one VC to another. The source VC is not controlled by a Tab Bar, so I can't reference the destination VC through it. I am attempting to reference the destination VC thorough the Storyboard, but the data is still nil on the destination VC. I'm using Core Data and wish to pass the ManagedObjectContext to another VC. Here is the code:
......
do {
try self.managedObjectContext.save()
appUserID = user.userID!
} catch {
fatalError("Error: \(error)")
}
}
}
self.passManagedObjectContext()
}
func passManagedObjectContext() {
let profileTableViewController = UIStoryboard(name: "Profile", bundle: nil).instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileTableViewController
profileTableViewController.managedObjectContext = self.managedObjectContext
}
I am passing data from the AppDelegate to other ViewControllers by referencing my TabBarViewControllers stack like so:
let tabBarController = window!.rootViewController as! UITabBarController
if let tabBarViewControllers = tabBarController.viewControllers {
let profileNavController = tabBarViewControllers[4] as! UINavigationController
let profileTableViewController = profileNavController.topViewController as! ProfileTableViewController
profileTableViewController.managedObjectContext = context
}
}
.....
END EDIT
I'm trying to do the same now but from one ViewController to another that is accessed through the tab bar (no segue). The code above does not recognize window!.rootViewController how can I reference the root UITabBarController?
UIViewController has a built-in optional property tabBarController, so inside your view controller you can access the tabBarController like so:
if let tabBarController = tabBarController {
//get the controller you need from
//tabBarController.viewControllers
//and do whatever you need
}
Here is the current list of properties available in the 'Getting Other Related View Controllers' section of Apple's UIViewController documentation:
I have a sign up flow with different view controllers. I take input from user in ViewController1, move to the next view controller (ViewController2), and pass the data to the last view controller (for e.g ViewController5).
The issue is I can pass my data to the next view controller but I can’t pass it to the last view controller, as in:
// Move to Second View of the flow.
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: “SecondViewController“) as! SecondViewController
secondViewController.dataText = dataText!
self.navigationController?.show(secondViewController, sender: nil)
// Pass the data to the last View of the flow.
let fifthViewController = self.storyboard?.instantiateViewController(withIdentifier: “FifthViewController“) as! FifthViewController
fifthViewController.dataText = emailText!
The dataText would pass to the SecondViewController but not the FifthViewController. How do I achieve this?
you can use class and create singleton object to store data and retrieve
in fifth view controller
class SingletonClass {
var sharedInstance: SingletonClass {
struct Static {
static let instance = SingletonClass()
}
return Static.instance
}
var dataText : String = ""
}
now you can store data in singleton object like this as below
let singleTon = SingletonClass()
singleTon.sharedInstance.dataText = "store data"
and use like this in your fifthViewController
let singleTon = SingletonClass()
print(singleTon.sharedInstance.dataText)
In Your Appdelegate.swift file create a instance of FifthViewController like this
'var fifthViewController: FifthViewController?'
in your view controller from where you want to pass data
'let appDelegate = UIApplication.shared.delegate as! AppDelegate'
'
appDelegate.fifthViewController = self.storyboard?.instantiateViewController(withIdentifier: “ FifthViewController“) as! FifthViewController
appDelegate.fifthViewController.dataText = emailText!
'
when ever you want to push FifthViewController use Appdelegte refrence of this controller like this
'self.navigationController?.pushViewController(appDelegate.fifthViewController!, animated: true)'
My mission
When app receive notification and user taps on the notification i want to redirect the user to the correct View. In my case, SingleApplicationViewController.
Current code
PushNotification.swift - A class with static functions to handle behaviors when receiving Push Notifications
The __getNavigationController returns a specific NavigationController based on a tab -and viewIndex from TabBarController.
internal static func __getNavigationController(tabIndex: Int, viewIndex: Int) -> UINavigationController {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let window:UIWindow? = (UIApplication.sharedApplication().delegate?.window)!
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewControllerWithIdentifier("MainEntry")
window?.rootViewController = viewController
let rootViewController = appDelegate.window!.rootViewController as! UITabBarController
rootViewController.selectedIndex = tabIndex
let nav = rootViewController.viewControllers![viewIndex] as! UINavigationController
return nav
}
The applicationClicked is being called when user click on notification and that method calls on __getApplication to fetch the application from the db with the objectId received in the push notification and then instantiate a GroupTableViewController to perform segue to the SingleApplicationViewController.
(TabbarController -> Navigation Controller -> GroupTableViewController -> SingleApplicationViewController)
What is a bit strange is when I set tabIndex to 0 and viewIndex to 1. The GroupView however is on second tab (tab 1) and the view controller should be the first (0). But when I set them to the corresponding numbers, I receive nil and the application crashes.
I read that you will force the view controller to load when doing _ = groupTableViewController.view and which it actually does. When this is being called, the viewDidLoad -function is being called.
/************** APPLICATION ***************/
static func applicationClicked(objectId: String) {
__getApplication(objectId) { (application, error) in
if application != nil && error == nil {
let nav = __getNavigationController(0, viewIndex: 1)
let groupTableViewController = nav.viewControllers.first as! GroupsTableViewController
_ = groupTableViewController.view
groupTableViewController.performSegueWithIdentifier("GroupTableToApplicationToDetailApplication", sender: application!)
} else {
// Hanlde error
}
}
}
GroupTableViewController.prepareForSegue()
Here I create a new instance of the ApplicationTableViewController, which is a middle step before getting to SingleApplicationViewController
} else if segue.identifier == "GroupTableToApplicationToDetailApplication" {
let navC = segue.destinationViewController as! UINavigationController
let controller = navC.topViewController as! ApplicationViewController
controller.performSegueWithIdentifier("ApplicationsToSingleApplicationSegue", sender: sender as! Application)
}
So, what's not working?
Well, the prepareForSegue in GroupTableViewController is not being called. I use the same code structure on my TimeLineViewController, and almost the exact same code, when getting another Push Notification and it works perfectly. In that case I use tabIndex 0 and viewIndex 0 to get the proper NavigationController.
Please, any thoughts and/or suggestions is more than welcome!
There is change in following method..
internal static func __getNavigationController(tabIndex: Int) -> UINavigationController {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let window:UIWindow? = (UIApplication.sharedApplication().delegate?.window)!
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewControllerWithIdentifier("MainEntry")
window?.rootViewController = viewController
let rootViewController = appDelegate.window!.rootViewController as! UITabBarController
rootViewController.selectedIndex = tabIndex
let nav = rootViewController.selectedViewController as! UINavigationController //This will return navigation controller..
//No need of viewIndex..
return nav
}
you have written
let nav = rootViewController.viewControllers![viewIndex] as! UINavigationController
change to rootViewController.selectedViewController give you UINavigationController.
Here you get navigavtion controller object..In your applicationClicked method nav object might be nil so it can not execute further performsegue code.
Check following method.
/************** APPLICATION ***************/
static func applicationClicked(objectId: String) {
__getApplication(objectId) { (application, error) in
if application != nil && error == nil {
let nav = __getNavigationController(0)//0 is your tab index..if you want 1 then replace it with 1
let groupTableViewController = nav.viewControllers.first as! GroupsTableViewController //Rootview controller of Nav Controller
groupTableViewController.performSegueWithIdentifier("GroupTableToApplicationToDetailApplication", sender: application!) //Perform seque from Root VC...
} else {
// Hanlde error
}
}
}