As I segue from SearchViewController to MatchCenterViewController, I want to set the bool value of _didAddNewItem in MatchCenterViewController to YES. I'm attempting to accomplish this like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowMatchCenterSegue"]) {
MatchCenterViewController *controller = (MatchCenterViewController *) segue.destinationViewController;
_didAddNewItem = YES;
NSLog(#"we're about to set controller values before segueing to MC");
// Send over the matching item criteria
controller.itemPriority = self.itemPriority;
if (_didAddNewItem == YES) {
NSLog(#"Did add new item is YES");
}
[self.tabBarController setSelectedIndex:1];
}
}
The destination view controller is tab 1, so I want to set the bool before it switches to that tab. It is logging out "Did add new item is YES", which tells me that the value of the bool is indeed YES just before segueing.
However, upon switching to the destination, when the viewDidAppear of the destination controller runs, it logs out "don't refresh".
- (void)viewDidAppear:(BOOL)animated
{
if (_didAddNewItem == YES) {
NSLog(#"well then lets refresh the MC");
}
else if (_didAddNewItem == NO) {
NSLog(#"dont refresh");
}
}
I set the bool property in the headers of both originating and destination VC like so:
#property (assign) BOOL didAddNewItem;
Very confused as to what I'm missing here.
You are confusing access to a property with access to the automatically synthesised iVar.
When you say
#property BOOL something;
and don't use an #synthesize statement the compiler will automatically create an iVar BOOL _something to store the property. You can then access this property using self.something or _something. You should get into the habit of always using self.something (or object.something in the case of another object reference) except in initialiser methods.
In your code you are setting the local iVar _didAddNewItem when what you want is to set the property on the destination controller -
controller.didAddNewItem = YES;
Related
I have made a segue passing a string which tells the next view controller which instance to parse the CoreData for. The problem is, I am using some code that calls init methods and the sent string is not initialized when it is called. However, the segue is working when I display the string in the destination view controller's viewDidLoad
- (id)init
{
self = [super init];
if (self)
{
[self initFakeData];
}
return self;
}
When that initFakeData method is called it sets up a graph and needs the exercise to hold a valid value
- (void)initFakeData
{
NSString *myExercise=exercise; //returns nil
if (myExercise==nil)
{
myExercise=#"Default";
}
}
Meanwhile...
-(void)viewDidLoad{
NSString *myExercise=exercise; //returns value
}
exercise is a property that is initialized by the previous view controller in a tableview
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([segue.identifier isEqualToString:#"showGraph"]) {
JBLineChartViewController *destViewController = segue.destinationViewController;
NSString *myExericse=[NSString stringWithFormat:#"%#", [[_exercises valueForKey:#"exercise"]objectAtIndex:indexPath.row]];
NSLog(#"%#",myExericse);
destViewController.exercise = myExericse;
}
}
The behaviour is correct because during init the exercise in JBLineChartViewController was not set. If you need the exercise attribute in the init method to set certain behaviour that have to be before viewDidLoad, my suggestion is to not use segue but do a designated initWithExercise and push the controller in code. Maybe like this:
- (IBAction)chartButtonPressed:(id)sender {
JBLineChartViewController *vc = [[ShopViewController alloc]initWithExercise:#"EXERCISE_STRING_HERE"];
[self showViewController:vc sender:self];
}
The new view controller is allocated and initialized before prepareForSegue is called. Anything you need to do with CoreData should be done in viewDidLoad. Or you can do it later, e.g. in viewWillAppear or viewDidAppear.
I'd like to pass some object from one view (number 2) to view number 1. View 1 triggers view 2 and before that in "prepareForSeque" I'm passing "self" to the second view and store it in variable "delegate". After some time I'd like to pass back new created object to view 1 and I'm trying to achieve it but I got an error that method is not visible for this interface. How to pass created object to the mother view triggering method?
When I declare #property someObject and synthetize it, it works ok using delegate. Is there another option or am I forced to use delegate?
Code:
- (IBAction)saveAndClose:(id)sender {
KwejkModel *mod = [[KwejkModel alloc] init];
((ViewController *)self.delegate).model = mod;
}
It works ok, but is there another option triggering method not using the property? Like this:
[((ViewController *)self.delegate) someMethod];
but here is an error.
Here is my code:
VIEW 1
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"addItemSeque"]) {
ScrollViewViewController *destViewController = segue.destinationViewController;
destViewController.delegate = self;
}
}
-(void) addNewPosition:(KwejkModel *)newKwejk
{
//for testing only this
[modelArray count];
}
VIEW 2:
- (IBAction)saveAndClose:(id)sender {
KwejkModel *mod = [[KwejkModel alloc] init];
// ((ViewController *)self.delegate).model = mod;
//it crashes here with error:-[NSPlaceholderString initWithString:]: nil argument' but it sees method from VIEW 1
[self.delegate addNewPosition:mod];
}
Try this link: Passing Data between View Controllers
Take a look at the first answer under Passing Data Back.
I'm new to Objective-C and have a question. Did the search multiple times but I couldn't find what I was looking for.
I'm using storyboard for this app. On the homescreen you've got some buttons with labels above them. Those labels should tell a number. When pushing the button you go to a new viewController where you have input that (after 'save') goes back to the homescreen and updates the label with the correct number. All that works great for one button and I'm very happy about it.
The problems are:
1. Since I have multiple buttons with labels, I want to use the same viewController to give input over and over again. I tried connecting every button to slide to the viewController under the identifier "AddData", but Xcode doesn't allow the same identifiers twice or more in storyboard. So I would need something else for this. Any idea?
2. Currently I use the following code to bring back the data to the homescreen:
homeScreenViewController
- (IBAction)unwindToHomeScreen:(UIStoryboardSegue *)segue;
{
inputDataViewController *source = [segue sourceViewController];
self.logoOneLabel.text = source.endTotalNumber;
}
inputDataViewController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) {
return;
} else {
if (endTotalLabelNumber > 0) {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
} else if (endTotalLabelNumber == 0 && totalLabelNumber == 0){
self.endTotalNumber = 0;
} else {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
}
}
}
This works great for the one button, but how to use this with multiple? I heard about Delegates to use the same viewController multiple time and get data back to different places, but I just don't get it. Any help?
You shouldn't need delegates.
What you will need is a property on the view controller that handles input to it knows which button it is handling input for.
When you segue to the input controller, set this property, based on which button was pushed. When you unwind back, fetch this property to know which label to modify.
For example, in your input view controller's .h file, add a property like this:
#property (nonatomic,assign) NSInteger handlingTag;
Or something, whatever name makes sense to you.
Now you need to implement your home screen view controller's prepareForSegue:sender:.
Use the sender argument to determine which button was pushed, and based on that, set the input view controller's new handlingTag property based on the button in a way that you will know what to do with it when we unwind.
Now in the unwind method:
switch (source.handlingTag)
Create a switch structure based on the source's handlingTag property, and set the appropriate label based on this value.
As Jeff points out in the comments, it'd be a really good idea to define an NS_ENUM to use here for the property rather than an NSInteger. The NS_ENUM would allow you to name the values you're using.
There is a few different way to implement what you need. But i think most common its a delegate.
This is how your inputDataViewController looks like:
#import <UIKit/UIKit.h>
#protocol inputDataDelegate;
#interface inputDataViewController : UIViewController
#property (weak) id<inputDataDelegate> delegate;
#property (strong, nonatomic) NSNumber *buttonTag;
#end
#protocol inputDataDelegate <NSObject>
-(void) inputDataViewControllerDismissed:(id)data;
#end
Then in #implementation, you should in "save" button action, message to you delegate method :
[self inputDataViewControllerDismissed:#{#"buttonTag":buttonTag,#"endTotalNumber":endTotalNumber}
Next in homeScreenViewController connect delegate :
#interface homeScreenViewController : UIViewController<inputDataDelegate>
After that in #implementation:
-(void)inputDataViewControllerDismissed:(id)data
{
// if you use modal
[self dismissViewControllerAnimated:YES completion:nil];
// or if you use push
//[self.navigationController popViewControllerAnimated:YES];
switch (data[#"buttonTag"]) {
case 1:
self.lableWtiTagOne = data[#"endTotalNumber"];
break;
case 2:
self.lableWtiTagTwo = data[#"endTotalNumber"];
break;
// number of cases depend how many buttons you have
}
Also, most important, thing didn't forget send self to our delegate:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"inputDataController"])
{
inputDataViewController *inputCtrl = [segue destinationViewController];
inputCtrl.delegate = self;
inputCtrl.buttonTag = sender.tag
}
}
Hi I'm currently developing an app where you have a viewController with a UISwitch that i have got working with NSLog. My question is if you can change stuff in other viewControllers by creating a BOOL and then referring to that in the if statement? Heres some code:
UISwitch *mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(251, 111, 0, 0)];
[mySwitch addTarget:self action:#selector(changeSwitch:) forControlEvents:UIControlEventValueChanged];
[mySwitch setOnTintColor:UIColorFromRGB(0xac1f2d)];
[self.view addSubview:mySwitch];
- (void)changeSwitch:(id)sender{
if([sender isOn]){
NSLog(#"Switch is ON");
} else{
NSLog(#"Switch is OFF");
}
If you want to change something in another ViewController, you can do this by creating a reference to the other ViewController.
If you add public properties to the 'other' ViewController, you can change them in your if statement.
Say you have another VC, OtherViewController.
Create a BOOL property in it like:
#property(nonatomic) BOOL flag;
Implement the setter for the property in .m file of OtherViewController:
-(void)setFlag:(BOOL)flag {
_flag = flag;
if(_flag) { //if flag is true do something
//...........
}
else { //do anything else
//........
}
}
Now create a reference of OtherViewController instance in your first view controller, containing the switch. Say its referred by a property named other from the current view controller. Then you can trigger the required action on OtherViewController based on the switch's value from your present view controller like this:
- (void)changeSwitch:(UISwitch*)sender{
self.other.flag = sender.on;
}
This will call the setter of flag property in OtherViewController and would according to what you have written there.
Of course, the instance of OtherViewController should not be deallocated in between.
To expand on Stefan answer:
you would need to add property to the ViewController you want to change
so in ViewControllerA you would import the ViewControllerB header:
#import "ViewControllerB.h"
So that you can change that property in the method
- (void)changeSwitch:(id)sender{
ViewControllerB *viewcontroller = [[ViewControllerB alloc]init]];
if([sender isOn]){
viewcontroller.log = #"Switch is ON";
} else{
viewcontroller.log = #"Switch is OFF";
}
}
I'm interpreting your question to be asking whether you can change the state of another view controller using a switch on your existing view controller. The answer is yes.
You have a conditional to decide the state of the switch already. To make the connection you want to add a property to the view controller you are about to push. When you instantiate the new view controller, set the state of its property after checking if([mySwitch isOn]) (create an IBOutlet so you can check it at any time rather than responding to an IBAction), then push it. If you're going to get there via segue, you would check the state of the switch in prepareForSegue: instead.
Here's how you set the property in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"uniqueSegueIdentifierFromIB"])
{
// Get a reference to the DestinationViewController
DestinationViewController *destinationVC = [segue destinationViewController];
// DestinationViewController has public #property (nonatomic) BOOL configurationOption;
if([mySwitch isOn])
{
[destinationVC setConfigurationOption:YES];
}
else
{
[destinationVC setConfigurationOption:NO];
}
}
}
Check the state of the new view controller's property in viewDidLoad: to decide how you want to configure it.
Let's say that I have view controller Origin and Destination. I would like to declare something like:
//origin.m file
-(void)pushNextView {
self.conditional = YES;
[self performSegueWithIdentifier:#"toDestination" sender:self];
}
Where I have set my conditional as:
//origin .h file
#propery BOOL conditional;
Now in my Destination view controller I'd like to set a conditional based on the property that I've set in my origin:
// destination .m file
#import "OriginViewController.h"
OriginViewController *origin = [OriginViewController alloc] init];
if (origin.conditional == YES){
self.navigationItem.hidesbackbutton = YES;
}else{
// Do Nothing
}
for some reason this conditional statement does not work. Does this have to do anything with storyboards?
With the setup you seem to have it would be most easy to do this. You could directly access destination.hidesbackbutton when executing the segue:
//in origin.m
-(void) prepareForSegue.... {
if ([segue.identifier isEqualToString: #"identifierString"]) {
DestinationVC *destination = (DestinationVC*)[segue destinationViewController];
destination.hidesbackbutton = self.conditional; //you can set the .hidesbackbutton property here directly, no need for another property, if your setup is just as simple as in the given example
}
}
Like this, the destination doesn't check the origin's state and then set it's state, instead the origin just sets the destination's state.
You can do it both ways, but this way is more common.
Of course hidesbackbutton has to be a public property declared in DestinationVC .h file.
And as already mentioned, it should really be hidesBackbutton Or hidesBackButton.
(This all assumes that the class of your DestinationViewController is called DestinationVC)
Two things: hidesbackbutton looks like there's some camelCase missing. This should at least give a warning, doesn't it?
Also from an architectural point of view, I would not ask for the condition. Pass the state to the destination view controller and implement a BOOL variable there.
Draft:
// origin.m
- (void)prepareForSegue...
{
if([segue.identifier isEqualToString:"yourIdentifier"]) {
[(XYZViewController *)segue.destinationViewController setConditional:YES];
}
}
you should pass the state from your main controller to destination controller where you should handle this View state like and you have to define
//destination.h file
#propery BOOL conditional;
so when you push the controller from main you can set the destination controller view state in
- (void)viewDidLoad
{
[super viewDidLoad];
if (origin.conditional == YES) {
self.navigationItem.hidesBackButton = YES;
} else {
}
}