This question already exists:
How to pass a value to Appdelegate from ViewController [closed]
Closed 3 years ago.
I am trying to get the value in ViewController from AppDelegate, but I am not able to do so.
I have only one ViewController. I tried to make the value as a constant or variable. None of them works.
I am not sure if this is the correct approach, but I tried to use rootViewController to access.
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
var test:String = "test"
// let test = "test"
}
}
in AppDelegate
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let viewController = self.window?.rootViewController as? ViewController
print("from_viewC \(String(describing: viewController?.test)))")
}
You have to declare the variable outside of viewWillAppear to make it accessible from outside:
class ViewController: UIViewController {
var test = "test"
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// whatever is in here...
}
}
Set windows root view controller like this:
self.window.rootViewController = ViewController()
Than you can access your value as you are doing above. Also you have to make test variable a class member before you can compile.
Why did you try to access rootViewController's variable inside didFinishLaunchingWithOptions? What is your exact requirement?
DidFinishLaunchingWithOptions is the point where your rootViewController will get allocated!
You can try inside didEnterBackground, didEnterForeground, didBecomeActive delegate functions in appDelegate to access the rootViewController's variable!
Related
I've been trying for days to get UISplitViewController to call the delegate. I tried setting it in the MasterViewController, I tried setting it in the DetailViewController, I tried having the SVC as an embedded view controller in a container inside another view controller, and use the prepareForSegue call to set the delegate. I tried instantiating it from the Storyboard in the AppDelegate and setting the delegate there. The delegate is never ever called. I'm going crazy.
I'm on the latest non-beta Swift (3.1), on iOS 10.3.1.
I've used the Debugger to check the delegate is in fact set, and remains in memory, however, the methods are never called.
I appreciate any help you can provide me with.
This is my current AppDelegate but I've tried countless ways as I said:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Realm.Configuration.defaultConfiguration.deleteRealmIfMigrationNeeded = true
if let svc = self.window?.rootViewController as? UISplitViewController {
svc.delegate = svc.viewControllers.last as! UISplitViewControllerDelegate
}
return true
}
Edit 1:
Some of you didn't understand what delegate I'm trying to implement. To clarify, I'm trying to control the delegate of UISplitViewController, in other words, the UISplitViewControllerDelegate. The reason I'm trying to do this is to be able to control the collapsing of views. In the default configuration of the UISplitViewController, on an iPhone on portrait mode, it defaults to the detailViewController. This behavior is clearly wrong for most apps (and mine). I intend to first show the masterViewController that lists the content I have for display, and trigger the detailViewController only when an item has been selected.
I would also accept an answer that gave me some alternative way to have that behavior (prefer masterViewController on portrait unless my detailViewController has content set.
I know you can get the described behavior by not setting a detailViewController in the Storyboard, and using the showDetail segue when selecting an item, but my problem with that is I can't have a default view when no item has been selected, causing a grey square being shown (not very pretty) when in landscape on an iPad or iPhone Plus.
Please try below code it's work for me.
In AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let splitViewController = self.window!.rootViewController as! UISplitViewController
let navViewController = splitViewController.viewControllers.first as? UINavigationController
let masterVC = navViewController?.topViewController as? MasterViewController
let detailViewController = splitViewController.viewControllers.last as? DetailsViewController
masterVC?.delegate = detailViewController
return true
}
In MasterViewController
protocol MasterViewControllerDelegate : class{
func passingData(strName : String)
}
class MasterViewController: UITableViewController {
weak var delegate : MasterViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
//Calling Delegate
delegate?.passingData(strName: "Any Message")
if let detailViewController = self.delegate as? DetailsViewController {
splitViewController?.showDetailViewController(detailViewController, sender: nil)
}
}
}
In DetailViewController
class DetailsViewController: UIViewController, MasterViewControllerDelegate{
//Other IBOutlet and Methods
//Delegate method of MasterViewControllerDelegate
func passingData(strName : String)
print(strName) //OUTPUT: "Any Message"
}
}
I hope it also work for you.
I know how to create singleton class in swift. The best and easy way to create singleton class is the following:
class Singleton {
static let sharedInstance = Singleton()
}
But I don't need singleton for any normal class. I need to create singleton for a viewcontroller class. So I'm using this code create singleton
class AViewController:UIViewController {
static let sharedInstance = AViewController()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
it gives me error near AViewController()
Missing argument for parameter 'coder' in call
Looks like it want me to initialize with init(coder: NSCoder). But what parameter or value should I pass through the coder?
If you really wanted to have singleton for a view controller corresponding to some scene, you'd probably do something like:
class SecondViewController: UIViewController {
static let shared = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Foo")
}
In this example, the storyboard was Main.storyboard and the storyboard identifier for the scene in question was Foo. Obviously, replace those values for whatever was appropriate in your case.
Then your other view controller that was invoking this could do something like:
#IBAction func didTapButton(_ sender: Any) {
let controller = SecondViewController.shared
show(controller, sender: self)
}
I wouldn't recommend singletons for view controllers. View controllers (and their views) should be created when needed and be allowed to be deallocated when they're dismissed. And you're losing many storyboard benefits (by which you see the logical flow between scenes with segues between them). And, if you use this view controller in different contexts, you're inviting problems stemming from the view controller hierarchy falling out of sync with the view hierarchy. I really would discourage you from using singletons for view controllers.
But if you were going to do it, you could do something like that...
Try to do:
AppDelegate:
Add a reference to the ViewController, so you can access it globally, like so:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private var viewController: ViewController?
func getViewController() -> ViewController {
if viewController == nil {
// make sure that the name of the storyboard is "Main"
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// make sure that you named the viewcontroller in storyboard (Storyboard ID), it is the identifier
viewController = storyboard.instantiateViewController(withIdentifier: "ViewControllerStoryboardID") as! ViewController
}
return viewController!
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// ....
}
AnotherViewController (Usage):
Now you can access it via "AppDelegate", like so:
class AnotherViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = appDelegate.getViewController()
}
// ...
}
Hope this helped.
I would recommend something like:
I know how to create singleton class in swift. The best and easy way to create singleton class is the following:
class Singleton {
static let sharedInstance = Singleton()
}
But I don't need singleton for any normal class. I need to create singleton for a viewcontroller class. So I'm using this code create singleton
class AViewController:UIViewController {
static let sharedInstance = AViewController()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
it gives me error near AViewController()
Missing argument for parameter 'coder' in call
Looks like it want me to initialize with init(coder: NSCoder). But what parameter or value should I pass through the coder?
If you really wanted to have singleton for a view controller corresponding to some scene, you'd probably do something like:
class SecondViewController: UIViewController {
static let shared = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Foo")
}
In this example, the storyboard was Main.storyboard and the storyboard identifier for the scene in question was Foo. Obviously, replace those values for whatever was appropriate in your case.
Then your other view controller that was invoking this could do something like:
#IBAction func didTapButton(_ sender: Any) {
let controller = SecondViewController.shared
show(controller, sender: self)
}
I wouldn't recommend singletons for view controllers. View controllers (and their views) should be created when needed and be allowed to be deallocated when they're dismissed. And you're losing many storyboard benefits (by which you see the logical flow between scenes with segues between them). And, if you use this view controller in different contexts, you're inviting problems stemming from the view controller hierarchy falling out of sync with the view hierarchy. I really would discourage you from using singletons for view controllers.
But if you were going to do it, you could do something like that...
Try to do:
AppDelegate:
Add a reference to the ViewController, so you can access it globally, like so:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private var viewController: ViewController?
func getViewController() -> ViewController {
if viewController == nil {
// make sure that the name of the storyboard is "Main"
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// make sure that you named the viewcontroller in storyboard (Storyboard ID), it is the identifier
viewController = storyboard.instantiateViewController(withIdentifier: "ViewControllerStoryboardID") as! ViewController
}
return viewController!
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// ....
}
AnotherViewController (Usage):
Now you can access it via "AppDelegate", like so:
class AnotherViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = appDelegate.getViewController()
}
// ...
}
Hope this helped.
I would recommend something like:
I wanted to use EZSwipeController as my rootViewController, I also have a iCarousel in my Storyboard. Before I added these lines of code (to make the EZSwipeController as my rootViewController):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window!.rootViewController = MyRootViewController()
window!.makeKeyAndVisible()
return true
}
Everything is working fine, but after I added these lines of code to my AppDelegate xCode complains about unwrapping a nil value for my iCarousel variable. Is it because I'm initializing my MyRootViewController in AppDelegate and it isn't linking up with what I have in my Storyboard? If so how am I able to fix this?
Edit
The line of code that is causing the crash is in my viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
// crashes here says that myCarousel is nil even though it is linked from the Storyboard
myCarousel.delegate = self
myCarousel.dataSource = self
}
I
From my appdelegate I need to execute a method that is from a viewcontroller.
Please, can anybody tell me how to easily have the possibility to call whatever method I need from my appdelegate?
A lot of questions regarding this argument but none of these are useful/complete/right, so please avoid to post URL to other topics: I already checked all questions but I really can't find any precise answer regarding doing this in Swift. :)
It depends on how you've arranged your view controllers but here's an example from a simple iPhone master/detail project.
let root : UINavigationController = self.window!.rootViewController! as UINavigationController
let master : MasterViewController = root.topViewController as MasterViewController
println("\(master.description)")
The way I did it was to search for the view controller I want, starting on AppDelegate's var window: UIWindow? and then going deeper until I find it. I originally tried to implement NSNotification but this is not recommended on swift (I think computed property is a good replace for that, but it don't work in this case).
This is how I did for my tab based application with a NavigationController on top of my ViewController:
if let rootViewController = self.window?.rootViewController as? UITabBarController
if let viewControllers = rootViewController.viewControllers {
for navigationController in viewControllers {
if let yourViewController = navigationController.topViewController as? YourCustomViewController {
if yourViewController.hasSomeFlag {
yourViewController.theMethod()
}
break
}
}
}
}
You can do it with notifications, just add observer to your viewcontroller, post a notification from your app delegate, this observer will catch it and run a function you specify.
this tutorial should help get you started: https://www.codefellows.org/blog/how-to-implement-an-nsnotification-observer-in-swift
Try this
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Override point for customization after application launch.
return true
}
func customMethod()
{
}
}
Call custom method from ViewController
class ViewController: UIViewController {
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
appDelegate.customMethod()
}