i am trying to get the presenting viewController of a viewController's view
The idea is like that :
i have a
viewController = CategoriesViewController
and i am presenting its view inside anther
viewController = HomeViewController
by using
CategoriesViewController.view
so when i want to reach the
HomeViewController from CategoriesViewController
i do this
let vc = self.presentingViewController as? HomeViewController
but it is telling me that it is nil
i tried the
.parentViewController
and it is returning
CategoriesViewController
In case you want to change a variable in HomeVieController using CategoriesViewController you could create your own protocol. You can use protocols to communicate between different controllers.
protocol ChangeVariableProtocol {
func changeVar(variable: Int)
}
In the protocol itself you only declare methods.
In your CategoriesViewController you would create a delegate Varibale like this
var changeVarDelegate: ChangeVariableProtocl?
Whenever you want to change the variable in CategoriesViewController you call your protocol method.
changeVarDelegate?.changeVar(10)
In HomeViewController you need to implement this protocol and initialize the changeVarDelegate variable.
extension HomeViewController: ChangeVarProtocol {
func changeVar(var: variable) {
// Implement your own logic here
self.valueToChange = variable
}
And make sure that you initialize changeVarDelegate when you are instancing your CategoriesViewController.
Hope this helps!
Create a callback closure property both in CategoriesViewController and in its view.
Right before presenting the controller assign the closure to change the value of a property to the callback property in the controller.
In viewDidLoad hand the closure over to the view.
Call the closure in the view to be executed in the presenting controller.
Related
My app is using Tab Bar Controller which contains several View Controller in different tab. When user open the app, they will firstly enter FirstView. I would like to put some method in SecondView which refresh the FirstView. This is my FirstViewController.swift:
class FirstViewController: UIViewController, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
I have tried to put
FirstViewController().viewDidLoad()
in my SecondViewController.swift, but this is not working. Is there any better way to refresh the FirstView?
Try this way by make a static reference of firstViewController and then through this reference you can call any function
class ViewController: UIViewController {
static var firstVC : ViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("m on FirstViewController ")
ViewController.firstVC = self
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("m on SecondViewController ")
ViewController.firstVC?.viewDidLoad()
}
}
you can try this:-
instead of 0 pass the index of your FirstViewController
if let firstVC = self.tabBarController?.viewControllers?[0] as? FirstViewController {
firstVC.viewDidLoad()
}
You should not call viewDidLoad yourself. Its called only once when the view is loaded.
If you want to update the controllers view just before its displayed, you can use viewWillAppear for changing the layout or whatever you want to do.
The issue you're battling it how to tell another view controller that it needs to update its view. For this, you have two potential solutions because effectively, you're determining the best way to communicate between different objects.
Notifications are loosely decoupled and tend to be useful for one to many relationships. One object can fire off a notification and one or more objects can be listening for that notification. In your situation, a notification can be broadcast when a certain piece of state has changed in one view controller, and the other view controller can observe that notification so it can be notified when it should change.
Delegates are more closely coupled because they're one to one. They are often times implemented by creating a delegate property on an object that conforms to some protocol. Another object then assigns that delegate property to itself and implements the protocol so its implementation will be invoked whenever that function is called on the delegate. In your situation, each view controller could have a delegate property for some protocol(s). The tab bar controller can assign the delegate property to itself and handle the implementations of these functions. Therefore, whenever a change happens and a delegate is invoked, the tab bar controller can take can responsibility of telling which view controllers to update their view.
There are also of course other ways of handling your situation such as updating the view in viewWillAppear. This way, whenever a view controller appears on the screen, some code can execute that will update its view.
It ultimately depends on how you're storing application state and the design of your application.
I am currently setting up a Tab Bar Application for iOS.
Normally, I would use an overridden method like prepareforSeque for dependency injection when changing viewControllers, but that method is not called when the UITabBarController changes its active child ViewController. How do I correctly do dependency injection into UITabBarController child ViewControllers?
In the RootViewController's viewDidLoad you can iterate thru childViewControllers and find the various child controllers that you want and set the dependency to each of them. In this case the dependency will be available in viewDidLoad of the child view controllers. Tab bar instantiates the child view controller instances but does not load the view until its required.
Once the tab bar view controller is loaded you can use the delegate methods to inject updated dependencies and use it in viewDidAppear because viewDidLoad will not get called once its selected in the tab bar.
With some extra research, I have come up with an answer. Thanks to Will-m for the clue I needed. The current cavaet with this answer is that the first view loaded by the TabBarController will not get injected.
In order to inject data into ViewControllers from a UITabBarController, you need to do the following:
First, you need to set the RootViewController to be its own delegate when it loads.
You don't necessarily need the controller class to be its own delegate
unless you need to inject the required data from another class directly to the UITabBarController.
You also need to declare the delegate class as compliant to UITabBarControllerDelegate protocol.
// Declare UITabBarControllerDelegate protocol
class RootViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Set class delegate to self
self.delegate = self
}
}
You need to set the RootViewController's delegate because the
delegate's protocol contains an important method:
tabBarController(_:shouldSelectViewController:). This method is
called when the RootViewController changes its active tab.
The "viewController" parameter of tabBarController(_:shouldSelectViewController:) is the instance of the child ViewController which the TabBarController is switching to. Provided you have assigned a protocol to that ViewController (so that the compiler knows your variable is declared in the class), you can inject the variable into the child.
So add the function to the RootViewController class like this:
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) {
// Get your view controller using the correct protocol.
// Use guard to make sure the correct type of viewController has been provided.
guard let vc = viewController as? YourProtocol
else { fatalError("wrong view controller type") }
// Assign the protocol variable to whatever you want injected into the class instance.
vc.VariableInYourProtocol = InjectedVariable
}
That's it. If you need to support controllers of different protocols, I might write something up on using a switch statement to do that. That
s just not something I've needed to work with as of yet.
Also, as a note, this method is applicable for CoreData practices in which only one managedObjectContext instance is passed between active ViewControllers. This method is used rather than retrieving different instances of the context directly from the application delegate for each ViewController.
I have a button in mainviewcontroller, when that button is tapped I want to call a method on another view controller. Here below is an image showing my scenario, Green button click to call preview controller VC1 received method without navigation anything. I just need to call that method only!
Declare a method in .h file of VC1 and implement in .m file of VC1
Call that method from MainView Controller ,
Create a NSNotificationObserver in MainView Controller and implement it in .m file of MainView Controller ,
Now when method of VC1 is called and before it returns, fire a PostNotification from VC1 and pass the parameter which you want in MainView Controller.
#RobAu has given correct answer as you can use NSNotificationObserver and call from anywhere.
Other alternate which i have used is using protocol and is very simple.
Just declare a protocol in the child controller. while launching the view put the parent controller as delegate. now call the delegate method from where ever you want from child controller
Here is example which i used (In my case all the child controller was of same kind)
//ChildClass.h file of child class
#protocol updateIndex <NSObject>
-(void)updateMediaId:(NSString*)currentMediaId;
#end
#interface ChildClass : UIViewController
#property NSString *imageID;
#property id updateIndexDelegate;
#end
//ChildClass.m file for child class where you want to call the delegate method
(I called in viewDidAppear method)
-(void)viewDidAppear:(BOOL)animated{
[self.updateIndexDelegate updateMediaId:_imageID];
}
And in ParentClass.m file of parent class use the delegate to self like this
ChildClass *childObject = [[ChildClass alloc] init];
childObject.delegate = self;
and define the delegate method like this
-(void)updateMediaId:(NSString*)currentMediaId {
NSLog(#"%#",currentMediaId);
}
Enjoy coding
I have a method inside my viewController (MainViewController.m) that accepts a parameter (Let's say it changes the background color of the viewController based on the number):
-(void) methodThatDoesSomething:(int)indexNumber {}
Inside this viewController I have a UITableView, but the delegate of this table is another class (TableDelegateClass.m). So inside this class I have didSelectRowAtIndexPath.
How do I call the "methodThatDoesSomething" from didSelectRowAtIndexPath? If I do it like this:
MainViewController* mainView = [[MainViewController alloc] init];
[mainView methodThatDoesSomething:indexPath.row];
It doesn't work. As another instance of the class is created and it doesn't after the viewController that I'm currently using.
I can easily do it if the delegate of the table is the MainViewController (just with this code inside didSelectRowAtIndexPath) :
[self methodThatDoesSomething:indexPath.row]
but I want to organize better the code and have different classes, one for the table and another for the viewController containing the table.
One simple solution could be to define your own delegate protocol for -(void) methodThatDoesSomething:(int)indexNumber {} so you can set TableDelegateClass delegate to your viewController and then call [delegate methodThatDoesSomething:indexPath.row]; (after checking respondsToSelector).
You can also pass the viewController instance when you create TableDelegateClass but this is a higher coupling level than delegate.
One way to solve your problem is to create a property of type MainViewController in the delegate class and assign the controller instance to it.
But maybe this problem that you are having is an indicator that the tableView delegate should indeed be the MainViewController and not another class?
I have some variable lets say isMenuVisible = false; I want to set some function when this var is changed:
isMenuVisible: Bool!{
didSet{
callFunctionFromOtherViewController()
}
}
How is that possible? Do I need to create instance of VC in order to access that function? Or I need to make that function public?
To call a method/function from another VC (or from any other class), you have 2 choices:
Create a class method in this other view controller:
In your MyViewController.h
+(void)myClassMethod;
Wherever you need to use it
[MyViewController myClassMethod];
Create an instance method in this other view controller:
In your MyViewController.h
-(void)myClassMethod;
Wherever you need to use it
MyViewController *myViewControllerInstance = [[MyViewController alloc] init];
[myViewControllerInstance myClassMethod];
definitely no need to create instance for the viewController,
if it is a rootViewController you can get it like that :
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var vc = (appDelegate.window?.rootViewController as? MyViewController)!
if your view controller is parent, it is like that:
var myViewfromParent = self.parentViewController as? MyViewController
if you use storyboard with id, you can get it like that:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
hope it helps
If you're using viewController containment and you can easily reference the view controller you can simply create a public method in your .h file and call that method... however, with viewControllers it's often the case that a. you don't have / want universal access to the viewController instance in other classes and b. the viewController may not even exist when this method is changed. For these reasons I'd go with NSNotifications when triggering actions in viewControllers.
NSNotificationCenter addObserver in Swift
If this isn't acceptable and you can guarantee that this class will be linked in an architecturally sound way, I would create a delegate. That way you can set the delegate when you create the viewController and there's no mess when trying to get back to your viewController... I'd only recommend this if there's a genuine connection between the viewController and the class that contains the action (in this case the bool)
In objective c you'd do
MyViewController *myViewController = [[MyViewController alloc] init];
myViewController.delegate = theClassContainingTheBool; // could be self.. might not be... If you can't do this line of code, then you should use notifications
In swift
Delegates in swift?
How to define optional methods in Swift protocol?