http://imgur.com/2lAJmVd
In the picture provided above, shows three view controllers.
Let's call the view controllers A, B, and C in the order they're displayed.
View Controller A passes parsed jSON data by clicking on the table cell. This fills up the "total sales, discounts, etc." strings/labels inside of the View Controller B using a prepareForSegue method.
This data is based on a start time/end time and the default parameter when performing the segue gives information from 8 AM to 10 PM of the current day.
The parsing string looks like this
NSDate *currentDate = [NSDate date];
NSDateFormatter *dateformatter = [[NSDateFormatter alloc] init];
[dateformatter setDateFormat:#"YYYY-MM-dd"];
NSString *theDate = [dateformatter stringFromDate: currentDate];
NSString *salesStr = #"http://";
salesStr = [salesStr stringByAppendingString:host];
salesStr = [salesStr stringByAppendingString:#":8080/sales.php?password="];
salesStr = [salesStr stringByAppendingString:pass];
salesStr = [salesStr stringByAppendingString:#"&db="];
salesStr = [salesStr stringByAppendingString:db];
salesStr = [salesStr stringByAppendingString:#"&sdate="];
salesStr = [salesStr stringByAppendingString:theDate];
salesStr = [salesStr stringByAppendingString:#"%2008:00:00&edate="];
salesStr = [salesStr stringByAppendingString:theDate];
salesStr = [salesStr stringByAppendingString:#"%2022:00:00"];
Inside of the View Controller B, you can notice a button labeled "Start Time". This button initiates a push segue to View Controller C. In this new view controller, you are able to select a date and time and it is displayed in the UILabel above the date picker. The button below the date picker is an IBAction and uses
[self.navigationController popViewControllerAnimated:YES];
My question is: How am I able to select a date/time in View Controller C, press a button, and send that information located in the UILabel to View Controller B so it can be used to update the parsed information?
One way I could think of doing this is to have the button segue to View Controller B, and RE-PARSE the information based on the date/time selection, but that would just cause too much 'navigation controller stacking' and just doesn't seem efficient to me.
Any suggestions are appreciated.
Hacker's approach would work.
Apple's documentation suggests a slightly different technique. Define a simple protocol with a method that view controller C can use to communicate with view controller B.
Then give view controller C a delegate property that conforms to that protocol.
In your prepareForSegue method, set yourself as view controller C's delegate.
Then, in view controller C, when the user changes the date and clicks the button, invoke a delegate method to notify view controller B that the user changed the date value before popping yourself.
I would suggest adding a cancel button as well, so the user can discard their changes. In that case, you just would skip calling the delegate method before popping.
in the interface of your view controller C add a property
#property (nonatomic, strong) NSDate *selectedDate;
before pushing C from B do
[cViewController addObserver:self forKeyPath:#"selectedDate" options:0 context:NULL];
and implement
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
//do some stuff
}
in view controller B.
when selecting a new date do (in view controller V)
self.date = myPicker.date;
so view controller B gets notified when the new date is set
don't forget to remove the observer in implementation of B (e.g. viewWillAppear)...
[cViewController removeObserver: self....];
Related
I have a view controller that implements UITextViewDelegate, UITextFieldDelegate, and NSFetchedResultsControllerDelegate. It is a UIViewController, not a table VC. Nor is there a table in it.
When I make a change to the Core Data store in another view controller, most text fields and text views in the first VC update However, I have another text field that shows the date and is derived from `NSDateFormatter, based on a date attribute in Core Data. It is hidden when nil and when the value is no longer nil, the code to unhide it is not working.
There isn't any code in the VC that says the text views and text fields that DO update specifically are delegates of anything. They just update naturally.
Perhaps the delegate was set in storyboard, but I don't remember and don't know a way to check what delegates are set in storyboard.
Is there a special date delegate? Or how would I get this text field derived from NSDateFormatter to update?
EDIT:
This is where the date is displayed in a method called updateInterface called within viewWillAppear
//Here is an example of a label that does update when data changes.
self.nameLabel.text = self.item.name; //this sets name...It updates when name is changed.
//Here is the data label that is not changing from hidden to not hidden.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEEE MMMM d, YYYY 'at' h:mma"];
NSString *date = [dateFormatter stringFromDate:self.item.date];
if (self.item.date!=nil) {
self.dateLabel.text =date;
else {
self.dateLabel.hidden = YES;
}
I'm working on a project that has many view controllers. Suppose that they are:
A -> B -> C -> D -> E ->F ->G -> H. Each of them has a back and a next button to switch to another view and has many text fields.
I typed text into every textfield. From H view, I can go back to previous views by popviewcontroller and review typed data. but when I click on next button again, all of data on the view were lost. I need to back/next continuous without losing data. How can I do that?
Create a Singleton class.
Give in Singleton class a property like Form *form;
If you start your first ViewController create a new Form
[Singleton sharedInstance].form = [[Form alloc] init];
On leave first ViewController set property from TextField
[Singleton sharedInstance].form.name = textField.text
On leave second ViewController set property
[Singleton sharedInstance].form.mail = textField.text
In each ViewController in viewWillAppear method set stored text
self.textField.text = [Singleton sharedInstance].form.name
or
self.textField.text = [Singleton sharedInstance].form.mail
It's a simple example, but hope it helps to understand what is to do :)
What about using a NSMutableDictionary to keep the models for each view controller as a key value pair. And Each View Controller initialized with this NSMutableDictionary
- (id) initWithDataDictionary:(NSMutableDictionary *)aDataDictionary
{
self = [super init];
_myDataModel = (MyDataModel*)[aDictionary valueForKey:#"MyKeyName"];
if(_myDataModel == nil)
{
_myDataModel = [MyDataModel alloc] init];
aDataDictionary setValue:_myDataModel forKey:#"MyKeyName"];
}
return self;
}
- (void) viewDidLoad
{
[super viewDidLoad];
[self displayData];
}
- (void) displayData
{
self.text1.text = _myDataModel.name;
}
You may also able to keep a key-value pair for setting focus to a last focused textfield.
I see two options here:
If you use storyboards unwind segues are very good option.
Else you can create own delegate.
I'm struggling to get my data from a UIContainerView to another UIContainerView. For example: I have 2 containers, which are calculatorContainer and displayResultContainer. So when I press "=" button to calculate the result, I want it to show up in the displayResultContainer. I already tried different options with the segue method and parentViewController access, but still no luck.
There are two possibilities.
1.Using appDelegate. Use a property in app delegate to pass data between containers.
Put this in first container
MyAppdeleagte appDelegate=[[UIApplication sharedApplication]delegate];
appDelegate.dataToPass=dataToPass;
in the second container
MyAppdeleagte appDelegate=[[UIApplication sharedApplication]delegate];
dataToPass=appDelegate.dataToPass;
2.Using ParentViewController.
in first container
ParentViewController parent=(ParentViewController *)[self parentViewController];
parent.dataToPass=dataToPass;
in the second container
ParentViewController parent=(ParentViewController *)[self parentViewController];
data=parent.dataToPass;
Use delegates.
Steps:
From 1st UIContainerView, call a delegate method to the parent view controller.
The parentview controller then pass the value to the 2nd UIContainerView.
You have to declare this method in the second UIContainerView:
UIContainerView2
-(id)initWithsetresult:(Int)Result {
int showPreResult = result;
return self;
}
On button action:
antagonist = [[UIContainerView2 alloc]initWithsetresult: calculateResult];
Here is an easy method, but first you should know that this method works for forward flow not backward (in backward flow you will need a thing called delegate):
View 1 = calculator
View 2 = result display.
In view2 get a text field and an NSString in its .h file:
#property(nonatomic,strong) NSString *myResult; //to contain the result
#property(nonatomic,strong) IBOutlet UITextfield *myResultText; // to display the result
In the first View (view1) take inputs and add buttons like (addition +) etc.:
#property(nonatomic,strong) IBOutlet UIButton *addition;
Connect that Outlet(button) with the view controller. Then add a method for that button and connect it to that button:
-(int)addFunc;
Then take in user input from 2 text fields that you have already made in the 1st View (input1TextField and input2TextField):
Inside the addFunc write:
(int)addFunc{
int input1,input2,result;
input1 = self.input1TextField.text;
input2 = self.input2TextField.text;
result = input1+input2;
return result;
}
Now inside the IBaction method write:
-(IBAction)myIbaction{
[self performSegueWithIdentifier #"seguename", sender :self]; // set the segue name and fill it here.
SecondViewController * sec = [segue destinationViewController];
int result = [self addFunc];
sec.myResult = (NSString)result;
}
Now in the second View (view2) in viewDidLoad method write:
-(void)viewDidLoad{
self.myResultText = self.myResult;
}
And it will display your result.
Don't know if it helps or not but surely you get the picture that this is how you will be able to perform it.
Hope it helps.
I am reading some book and I stumbled this thing.
In my ViewController when a user clicks change date button following
code is called:
- (IBAction)changeDate:(id)sender {
DateViewController *vc = [[DateViewController alloc] init];
[vc setItem: item];
[[self navigationController] pushViewController:vc animated:YES];
}
item is a pointer to a custom class object, which has ivar of type NSDate *;
Now, inside DateViewController when user already picked new date and wants
to navigate to previous view, I have following code:
- (void)viewWillDisappear:(BOOL)animated
{
NSLog(#"%#", [datePicker date]);
item.dateCreated = [datePicker date]; // get selected date
}
This code works and when user goes back from above code change is reflected
in item data structure and user can see new date. However, if I change above code, to following code, it doesn't work anymore, any clues why?
(This does NOT work):
- (IBAction)changeDate:(id)sender {
DateViewController *vc = [[DateViewController alloc] init];
vc.userDate = currentItem.dateCreated;
[[self navigationController] pushViewController:vc animated:YES];
}
DateViewController:
- (void)viewWillDisappear:(BOOL)animated
{
NSLog(#"%#", [datePicker date]);
self.userDate = [datePicker date];
}
In the first case, item is a mutable instance, because you can change the date that it contains. In the second case you are supplying the NSDate itself, which is immutable.
So, in the first case, you pass a reference to item which can be edited and these edits are available later.
But, the second case doesn't edit the original date, it just stores the chosen date into a property on the view controller which is in the middle of being dismissed.
Generally it is better to make the communication clear, so the view controller would pass the chosen date back to the caller by delegation or provide a property (like in your second case) that can be queried once the selection is made. Your first option is effectively hiding the data exchange by sharing the instance item while the second view controller is on display.
Immutable means that the object itself (its contents) can not be changed. It does not prevent any reference to the object from being changed. If we use arrays (where there are mutable an immutable versions) to demonstrate:
NSArray *a = [NSArray array];
NSArray *b = a;
[b editSomething]; // illegal (not a true method name but just an example of something you might want to try)
b = nil; // just nils b, no affect on a at all
And
NSMutableArray *a = [NSArray array];
NSMutableArray *b = a;
[b addObject:#"String"]; // edits a, because a and b are the same object
b = nil; // just nils b, no affect on a at all
The NSArray is like the situation where you just pass the NSDate. The NSMutableArray is like the situation where you pass item (because you can change the contents).
I want to carry out the 'TotalLabel' Calculation, onto another ViewContoller known as 'AdultPayNowViewController'. I also want to carry out the 'x' value, which is a UITextField that the user inputs a quantity onto 'AdultPayNowViewController'
//AdultTicketCalculator.h
#interface StudentTicketCalculatorViewController : UIViewController {
IBOutlet UITextField *Quantity;
IBOutlet UILabel *Pricelabel;
IBOutlet UILabel *TotalLabel;
IBOutlet UIButton *CheckoutButton;
}
-(IBAction)calculate;
-(IBAction)clear;
#end
\\AdultTicketCalculator.m
-(IBAction)calculate {
float x = ([Quantity.text floatValue]);
float c = x*15;
TotalLabel.text = [[NSString alloc] initWithFormat:#"$%.2f", c];
}
-(IBAction)clear {
Quantity.text =#"";
Pricelabel.text =#"$15.00";
TotalLabel.text =#"";
First off, these values probably shouldn't even live in a controller. If they're really values, they ought to be a part of a model that is shared by or passed between controllers in the usual MVC manner.
But moving on, there are really only a few ways to pass values between controllers (or any object, for that matter). Most straight forward is, you create a custom initializer in AdultPayNowViewController called -initWithTotalLabel:andXValue: and created that controller within StudentTicketCalculatorViewController like so:
[[AdultPayNowViewController alloc] initWithTotalLabel:totalLabel andXValue:x];
Or from some shared parent like:
[[AdultPayNowViewController alloc] initWithTotalLabel:[studentController totalLabel] andXValue:[studientController x]];
Likewise, if you don't need those values for the adult controller's initialization, you could always make them properties and set them directly:
adultController = [[AdultPayNowViewController alloc] init];
[adultController setTotalLabel:[childController TotalLabel]];
[adiltController setXValue:[childController x]];
Another possibility would be to make the student controller a delegate of the adult controller, and make the adult controller ask its delegate for the values of totalLabel and x when it needs them.
Finally, you could pass student controller as a parameter or user data to a method or notification responsible for creating adult controller, and then use its properties to either set or initialize adult controller with the desired values. This is more or less the approved "Storyboard" way of doing things, I believe, with everything going down in -prepareForSegue:sender:.