I am having trouble linking up a switch. I understand how to connect the switch as an outlet and have it have actions change the boolean state of a value, but I need the switch to perform an action in a different view controller.
Here's the situation: I have a main table view controller, called View Controller A. I have a second view controller, lets call it view controller B, that controls a menu sidebar (on a regular view controller, not a table view) triggered by a bar item. I want to be able to open up the menu, hit a switch in the sidebar, and have something change in the main table view that is controlled by view controller A.
Is there any way that I can accomplish this? I seem to have no way of accessing or changing the IBOutlets in View Controller A from B. Is there a way that I can have the action in B linked with the switch change the boolean state of a value, and have an action waiting in controller A that will respond to a change in boolean? I am not sure how to solve this problem. Help is appreciated!
You should use delegation pattern. You'll have an action waiting in controller A, but instead of responding to value changed in B the action will be triggered by B when appropriate
ViewControllerB.h
// Create delegate protocol and property
#protocol ViewControllerBDelegate <NSObject>
- (void)switchPressed:(BOOL)switchStatus;
#end
#interface ViewControllerB : NSObject
#property (nonatomic,weak) id<ViewControllerBDelegate> delegate;
#end
ViewControllerB.m
// When switch is tapped, call delegate method if it is implemented by delegate object
- (IBAction)flip: (id) sender {
UISwitch *onoff = (UISwitch *) sender;
if ([self.delegate respondsToSelector:#selector(switchPressed:)]) {
[self.delegate switchPressed:onoff.on];
}
}
ViewControllerA.h
// Conform to ViewControllerB protocol
#import ViewControllerB.h
#interface ViewControllerA : NSObject,ViewControllerBDelegate
ViewControllerA.m
// Set self (VC A) as VC B's delegate
- (void)ViewDidLoadOrSomeOtherFunction {
ViewControllerB *vcB = [[ViewControllerB alloc] init];
vcB setDelegate = self;
}
// Implement delegate method
- (void)switchPressed:(BOOL)switchStatus {
if (switchStatus) {
// Make changes on VC A
}
}
Related
I have an UIViewController A with push segue to UIViewController B, in some case i need to repeat this for example:
ViewController A -> ViewController B -> ViewController A -> ViewController B
Also i need to pass data from ViewController B -> ViewController A
I don't want to create 4 ViewControllers, so how can I recycle code in this case?
You simple push and pop the same view controllers.
If you want B to communicate with A then setup a protocol at A that communitactes with the B. (or who ever implements the protocol)
B code
#protocol MyProtocol <NSObject>
-(void)callFromB:(id)data;
#end
#property (weak) id <MyProtocol> delegate;
implementation:
if ([self.delegate respondsToSelector:#selector(callFromB:)]){
[self.delegate callFromB:data];
}
A code
B.delegate = self;
-(void)callFromB:(id)data {
...
}
You should not recycle them. By that I mean you should not recycle the concrete instances of those view controllers.
Certainly there might her some technical implementation allowing that but it is (contrary to table view cells) not necessary.
You are trying to optimise prematurely. Just create new view controller instances as you need. Any iPhone supporting iOS6 and later will handle that without any problem.
Passing the data can be done by a convenient method for example
-(void)configureWithData:(WhateverDataType *)paramData
In prepare for segue, you would obtain a reference to the target view controller and pass the data like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CustomViewController *nextViewController = [segue destinationViewController];
[nextViewController configureWithData:someData];
}
I have 2 table view controllers where I on button click on first controller, I am pushing to another view controller.
On the second table view controller, I have a list of items which is checkmarked upon selection. I want to return the selected value to the first view controller.
Also, please let me know how can I use that value in first view controller.
I cant use seagues and the second view controller is generic so I cannot set any of the variables of first view controller in it.
One option would be to create a delegate for that second view controller
In the .h you could declare
#protocol SecondControllerDelegate <NSObject>
- (void)selectionDidFinish:(NSArray*)objects;
#end
#interface SecondViewController : UIViewController
#property(nonatomic, weak) id<SecondControllerDelegate> delegate;
Then in your .m in view will disappear :
- (void)viewWillDisappear:(BOOL)animated{
[self.delegate selectionDidFinish:yourArray];
}
And in your first view controller you'd have to implement this protocol :
in .h :
#interface FirstViewController : UIViewController <SecondControllerDelegate>
and in .m
- (void)selectionDidFinish:(NSArray*)objects{
//Do whatever you want with selected objects
}
and of course when you create your second view controller you'd have to do :
SecondViewController *sVC = [[SecondViewController alloc] init];
sVC.delegate = self;
[self.navigationController pushViewController:sVC animated:YES];
A possible solution is to create a mutable object (NSMutableArray, NSMutableDictionary) in the parent controller and pass it to the child, so the child can modify it and when you are back to the parent - the mutable object will contain the modified values.
Another approach, which requires a little more knowledge is to use a protocol. The basic idea is to create a protocol and a delegate, so the child can "notify" the parent that it chose some values. You can check these examples:
http://www.theappcodeblog.com/2011/04/15/passing-data-between-views-tutorial-using-a-protocol-delegate-in-your-iphone-app/
http://iosdevelopertips.com/objective-c/the-basics-of-protocols-and-delegates.html
Please tell me if you need more information.
You can create delegates. Checkout the following link:
http://www.hardcodedstudios.com/home/ryan-newsome/simpledelegatetutorialforiosdevelopment
I have a picker view in a view controller A (as inputView of a textfield).
To enable the user to select a new value (one which is not a row of the pickerview yet), there is a button which presents another view controller B modally, where the user can create a new value. Upon closing, I want the textfield and its inputView pickerView to be updated with the new value.
My pickerview is backed by an NSArray from CoreData. Unfortunately the pickerview isn't updated, when I dismiss the View controller B, although the new value is updated in core data.
How can I achieve that?
One of the good solution is to implement the delegate pattern (a common pattern in Cocoa) :
In ViewControllerB.h declare a ViewControllerBDelegate protocol.
Then in your ViewControllerB interface add a delegate as ivar.
//ViewControllerB.h
#class ViewControllerB;
#protocol ViewControllerBDelegate <NSObject>
#required
- (void)viewControllerB:(ViewControllerB *)controller didChangeValueTo:(NSString *)value;
#end
#interface ViewControllerB : UIViewController
#property (weak, nonatomic) id<ViewControllerB> delegate;
[...]
Then when the value has changed (or when the user validate the change) send the event to the delegate like this :
if ([self.delegate respondsToSelector:#selector(viewControllerB:didChangeValueTo:)])
{
[self.delegate viewControllerB:self didChangeToValue:newValue];
}
In ViewControllerA just do
ViewControllerB *viewController = [...]; //initialization
[viewController setDelegate:self];
and add the method :
- (void)viewControllerB:(ViewControllerB *)controller didChangeValueTo:(NSString *)value
{
[...];//your stuff here
}
In FirstViewController I have a tableview. A button is clicked to push SecondViewController where an item is typed in and a button is pressed to add the item.
In SecondViewController.h file there is:
#protocol SecondViewControllerDelegate <NSObject>
-(void)itemAdded:(NSString *)item;
#property (nonatomic, weak)id <SecondViewControllerDelegate> delegate;
In `SecondViewController.m
- (IBAction)myButton:(id)sender {
[self.delegate itemAdded:#"someText"];
}
In FirstViewController.h
#interface SecondViewController: UITableViewController <UIAlertViewDelegate, SecondViewControllerDelegate>
In FirstViewController.m
-(void)itemAdded: (NSString *) item{
[self.items addObject: item];
}
Everything is working fine except that the [self.delegate itemAdded:#"someText"]; doesn't call the itemAdded function in the FirstViewController can anyone help?
The problem is likely that the delegate of the second view controller is nil. Where ever you create the second view controller, presumably somewhere in the first view controller, you need to set secondViewController.delegate = self so that when the second view controller needs to call back to the delegate, it's pointing to the first controller instead of nil.
Also, you have declared the SecondViewController class as a SecondViewControllerDelegate, but this is incorrect. A second view controller wouldn't be the delegate of itself, rather a FirstViewController will be. You need to move to the interface of FirstViewController, then the compiler won't complain when you try to set a FirstViewController as the delegate to the SecondViewController.
I have a VC named Dashboard (D) which can open a VC named Login (L) and a VC named Register (R). Login can open VC Register too.
I try to use storyboard as often as possible, so I have created with it three Segues, D to L, D to R, L to R
So, in case of D -> L -> R and in case of D -> R, when I close R, I have to close L if it necessary and inform D which he can begin to load the user infos (launch function in nutshell).
So, I would like get the sender of Segue in destination vc, knowing that I put it in sender entrie of performSegueWithIdentifier like that :
[self performSegueWithIdentifier:#"SegueToFbRegister" sender:self];
I'd do this by having R send a notification when the registration/login is done, and having D listen to it then pop everything and load your data.
If however you insist on getting a reference to the sender, you can add this property on your destination VC and set it in the source VC's prepareForSegue:sender:
This sounds like a great place to use Delegates. In your RegisterViewController.h define a protocol like this
#protocol RegisterViewDelegate <NSObject>
- (void)tellRegisterDelegateSomething:(NSObject*)something;
#end
Then on your class keep a pointer to your delegate
#interface RegisterViewController : UIViewController
#property (weak, nonatomic) id <RegisterViewDelegate> delegate;
#end
Now tell the presenting view controllers that they implement the new protocol you just created. This is done in the .h files of the other viewcontrollers that present this view.
In LoginViewController.h
#interface LoginViewController : UIViewController <RegisterViewDelegate>
#end
In DashboardViewController.h
#interface DashboardViewController : UIViewController <RegisterViewDelegate>
#end
In the .m files of the above classes, implement the protocol's method
- (void)tellRegisterDelegateSomething:(NSObject*)something
{
}
Now you need to assign the delegate when you perform your segue from either presenting view controller like this.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"SegueToFbRegister"])
{
RegisterViewController* destination = [segue destinationViewController];
destination.delegate = self;
}
}
Now you can call the presenting view controller (delegate) and have it do something with any information you need to send back like this (this would be called in your RegisterViewController.m).
if ([self.delegate respondsToSelector:#selector(tellRegisterDelegateSomething:)])
{
// Tell the delegate something.
[self.delegate tellRegisterDelegateSomething:something];
}
The instance where you need to pass back through two controller you follow the same basic pattern.
#protocol LoginViewDelegate <NSObject>
- (void)tellLoginDelegateSomething:(NSObject*)something;
#end
Then on your class keep a pointer to your delegate
#interface LoginViewController : UIViewController
#property (weak, nonatomic) id <LoginViewDelegate> delegate;
#end
Now tell the Dashboard view controller that it implements the protocol. This is done in the .h files of the Dashboard viewcontrollers that present this view.
In DashboardViewController.h
#interface DashboardViewController : UIViewController <RegisterViewDelegate, LoginViewDelegate>
#end
In the .m files of the DashboardViewController implement the protocol's method
Follow the above pattern of setting the delegate on the viewcontroller when you perform the segue. Now when the delegate method is called in the LoginViewController, you call the delegate in the DashboardViewController as well.
in LoginViewController.m
- (void)tellRegisterDelegateSomething:(NSObject*)something
{
if ([self.delegate respondsToSelector:#selector(tellLoginDelegateSomething:)])
{
// Tell the delegate something.
[self.delegate tellLoginDelegateSomething:something];
}
}
Now you are all connected so you can pass data back through both controllers (or just one) and do something with it. You will know which scenario you are in because different delegate methods will be called in the DashboardViewController based on which viewcontroller was visible.
Hope this helps.
Create a delegate for R and make D and L to implement the delegate methods.Use prepareForSegue:sender to assign the delegate of R.When you finish task in R use your delegate to perform the rquired action.
Another way would be to use an unwind segue.
Place the following code in you Dashboard (D) view controller.
#IBAction func loadUserInfoAfterRegistration(segue: UIStoryboardSegue) {
}
In Interface Builder, do the following steps for the Register (R) view controller:
Select the button that will be pressed on the completion of registration.
Ctrl + drag to the exit symbol on top of the view.
Select loadUserInfoAfterRegistrationWithSegue: from the list displayed.
Using this approach, the Register (R) view controller will always navigate to the Dashboard (D) view controller, regardless of what is between them. The view controllers between them will not have to be touched. The loading of the user data in the Dashboard (D) view controller can also be customized in the method declared above.
You can get it easily by using the type of the parent controller such as
let temp = self.navigationController?.viewControllers
if (temp != nil){
if let _parent = temp![temp!.count-2] as? UIControllerClass {
//do what you want here with the _parent
}
}
You have to subtract 2 because last one is the current view that you want to get its parent.