I have two views, for each view I have associated a UIViewController class.
In view1 (named RechercherViewController) I have a simple UITextField in which the user enter something, and then click on a button, when this button is clicked, the user is redirected to view2 (named StationsSurLaCarteViewController) and I have to show him, in a UILabel, what he has entered in the previous view. My plan worked pretty good as I want, but for the first essai, I mean the first value is unchanged although the user returned and changed it, he find always(in the label in view2) what he has entered for the first time.
all declarations are right, and here is my code in the IBAction of the button in view1 :
RechercherViewController.m
-(IBAction)goToStationsSurLaCarteView {
TopStationAppDelegate *topStation=(TopStationAppDelegate *)[[UIApplication sharedApplication]delegate];
topStation.data=[typeCarburantTextField text];
stationsSurLaCarteViewController.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:stationsSurLaCarteViewController animated:YES];
}
and in the second view this code is in the viewDidLoad :
StationsSurLaCarte.m:
- (void)viewDidLoad {
TopStationAppDelegate *topStation=(TopStationAppDelegate *)[[UIApplication sharedApplication]delegate];
[label setText:topStation.data];
[super viewDidLoad];
}
I don't know but I have doubt if I have missed something which has to be released to have alwayes the new value entered by the user.
In goToStationsSurLaCarteView, since you are not re-creating stationsSurLaCarteViewController every time (using alloc+init), viewDidLoad will only be called the first time presentModalViewController is called.
One simple fix is to move the label setting code to viewWillAppear: (or viewDidAppear:) in StationsSurLaCarte.m:
-(void)viewWillAppear:(BOOL)animated
{
TopStationAppDelegate *topStation=(TopStationAppDelegate *)[[UIApplication sharedApplication]delegate];
[label setText:topStation.data];
}
Related
I have two views in my app and a plist file to store some values.
In the first view I've created a button called frequenciesButton that opens the second view and another button to restore the default values.
In the second view there is a pickerView and a "Done" button.
On the .m of the first view:
- (void)viewDidLoad {
[super viewDidLoad];
//
self.gameSettings = [[NSMutableDictionary alloc] initWithContentsOfFile:gameSettingsFilePath];
}
-(void)viewWillAppear:(BOOL)animated {
[self refreshView];
}
- (void)refreshView {
[self.frequenciesButton setTitle:[NSString stringWithFormat:#"%# hz and %# hz", [self.gameSettings objectForKey:#"freq-freq1"], [self.gameSettings objectForKey:#"freq-freq2"]] forState:UIControlStateNormal];
...
}
- (IBAction)setDefaultValues:(UIButton *)sender {
[self.gameSettings setValue:#880 forKey:#"freq-freq1"];
[self.gameSettings setValue:#1122 forKey:#"freq-freq2"];
...
[self.gameSettings writeToFile:gameSettingsFilePath atomically:YES];
[self refreshView];
}
When the first view is loaded, the button title is changed to the default values stored in the gameSettings dictionary. The method setTitle: works.
When I click on the frequenciesButton it opens the second view with the pickerView, I select the two new values for the freq-freq1 and freq-freq2 and it saves to the plist file on done button.
The problem is that the frequenciesButton title is not changed when the second view is dissmissed and the first view appears. The refreshView method is called but the button setTitle: does not work.
In this case, if I go back one screen, and return to this view, the button title is updated.
And when I click on defaultValuesButton, the frequenciesButton title changes. The method setTitle: also works.
Any ideas of what must be happening?
HaHA! I love that you added a link to your project.
SO!! The problem was that you have separate properties in each view to hold the data from the saved plist file, self.settings. This is fine, don't mesh them together. The requirement you had to do with this, when switching views, is to keep the ivar or properties updated as the data updates too :D
Here is how I fixed the problem:
- (void)viewWillAppear:(BOOL)animated {
self.settings = [NSMutableDictionary dictionaryWithContentsOfFile: filePath];
[self updateView];
}
I checked out the file and that was updated, but the dictionary in the TestViewController.h was not updated
I hope this was the problem :)
One problem there, not sure if it will fix it, is the fact that you have used ViewWillAppear incorrectly, you have this:
-(void)viewWillAppear:(BOOL)animated {
[self refreshView];
}
but it should be this:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self refreshView];
}
You need to invoke "[super viewWillAppear:animated];" or you will have side effects, fix that first and see what happens.
I created a method that sets the title of a button based on a value.
This method needs to be called when opening the viewController and maybe refreshed when the controller appears again.
So i created the method and I called that method in viewDidLoad and viewDidApper but it seems to be called only when I change page and turn back to the view controller.
Why?
My code is
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self controlloRichieste];
......
}
-(void)viewDidAppear:(BOOL)animated{
[self controlloRichieste];
}
-(void)controlloRichieste{
//Numero richieste di contatto
NSString *numeroRichieste = #"1";
if([numeroRichieste isEqual:#"0"]){
[_labelRequestNumber setTitle:#"Nessuna" forState:UIControlStateNormal];
} else {
_labelRequestNumber.titleLabel.text = numeroRichieste;
_labelRequestNumber.tintColor = [UIColor redColor];
}
//Fine Numero richieste di contatto
}
You can also move that code to viewWillAppear so that it gets called each time it appears.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self controlloRichieste];
}
I see the problem now, try the other way around
-(void)controlloRichieste{
//Numero richieste di contatto
NSString *numeroRichieste = #"1";
if([numeroRichieste isEqual:#"0"]){
[_labelRequestNumber setTitle:#"Nessuna" forState:UIControlStateNormal];
} else {
_labelRequestNumber.tintColor = [UIColor redColor];
[[_labelRequestNumber titleLabel]setText:numeroRichieste];
}
//Fine Numero richieste di contatto
}
Change set the button color, before you change its titleLabel's text
I created a demo PROJECT for you, hope it's helpful!
When you open view first time the viewDidLoad is called and the viewDidAppeare.
The viewDidAppeare is called every time when the view is opened, when you push or present other view controller and go back to the maine one viewDidAppeare is called.
You should call:
[super viewDidAppear:animated];
The viewDidLoad is called just when the view is loaded and after that when it's deallocated and it needs to be allocated again. So mostly when you push or present other view controller and go back to the maine one viewDidLoad is not called.
I am using a uipopover to present a mini number pad to the user when they enter a textfield on my main view controller.
when they enter numbers using the number pad, i save the entry into a nsstring property that I've named keypadvalue.
there is an unwind segue wired to a done button on the popover which fires the following code.
- (IBAction)doneWithKeyboard:(UIStoryboardSegue *)segue
{
NSLog(#"unwind");
if ([segue.sourceViewController isKindOfClass:[KeyPopupViewController class]])
{
KeyPopupViewController *popOver2 = segue.sourceViewController;
activeField.text =popOver2.keypadValue;
}
}
the activetextfield on my main view controller then gets updated to the kepadvalue, and this all works fine.
my problem now is that i want the activetextfield to update the same way if the user presses outside the uipopover, and it dismisses without firing the unwind segue.
i thought i might use the following to perform the update when the popover dismisses
-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
activeField.text = controller.keypadValue;
return YES;
}
unfortunately despite multiple attempts i can't get the property to return a value it is always null even though the method fires as expected.
how should i recover the property value from the popover using this or another method?
i am obviously doing something wrong
can anyone advise
thanks
It should help:
-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
[self.view endEditing:YES];
activeField.text = controller.keypadValue;
return YES;
}
edit 1
I am adding some code to indicate its state after I have tried to work with codeInOrange's answer so far which so far behaves like my code originally behaved, that is sample link shows up at first in the text field and can be altered by the user, but when the user returns to the VC, any new link text has been replaced by the original sample link. My reason for posting this additional code is to try to reconnect with codeInOrange's promising answer because I am misunderstanding the logical flow of his original suggestions and his later comments.
In the current Storyboard I am leaving the Text field and the Placeholder Text empty because the sample link seems to be adequately supplied by the viewDidLoad method below.
- (void)viewDidLoad
{
[super viewDidLoad];
self.urlNameInput.text = #"sample http";
// Do any additional setup after loading the view, typically from a nib.
self.urlNameInput.clearButtonMode = UITextFieldViewModeAlways;
self.urlNameInput.clearsOnBeginEditing = NO;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.urlNameInput) {
[textField resignFirstResponder];
[self processPbn];
}
return YES;
}
- (void)viewDidAppear:(BOOL)animated
{
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
// self.urlNameInput.text = appDelegate.stringForTextField;
appDelegate.stringForTextField = self.urlNameInput.text;
}
- (void) processPbn
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:self.urlNameInput.text] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[NSURLConnection sendAsynchronousRequest:theRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *connection, NSData *data, NSError *error)
{
// lots of detail code has been elided in this method
self.iboard = 0;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:toMatch options:NSRegularExpressionDotMatchesLineSeparators error:&error];
for (NSTextCheckingResult* board in [regex matchesInString:string options:NSRegularExpressionDotMatchesLineSeparators range:NSMakeRange(0, [string length])])
{
if (self.iboard>0) {
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
appDelegate.stringForTextField = self.urlNameInput.text;
}
}];
}
edit 1
edit 0
I do not want to preserve the text between application shutdowns and launches, so the answer using NSUserDefaults is not quite what I need.
Also, it appears from my trials that the solution suggested by Michael Dautermann which suggests either putting my intialization text in viewDidLoad or in the Xib or Storyboard, does not work because the text always returns to its initial value upon return to the VC (likely because the viewDidLoad method is triggered), so I think I do need to create an ivar in my AppDelegate.m as I asked in my original question, and not in my ViewController.m viewDidLoad, to get the desired result, apparently. Perhaps it would be easier to create a B00L ivar in AppDelegate.m which is a flag that tells whether original text or current text is desired. But I cannot figure out how to do that, either. So, please consider this edit in your answer.
edit 0
My AppDelegate.m contains the following code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
BDViewController *vc = [sb instantiateInitialViewController];
self.viewController = (id)vc;
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
In the VC I want an ivar, an NSString, to be set at launch so that it can be the example text in my UITextField. Later I want that UITextField to be adjusted to an new value when the user supplies valid text into a UITextField.
Currently in my VC.h, the text field is declared and is synthesized in VC.m as follows .
#property (nonatomic, strong) UITextField *urlNameInput;
#synthesize urlNameInput;
I have tried putting the following code into didFinishLaunchingWithOptions: but do not see the desired text when I run the app.
self.viewController.urlNameInput.text = #"example http";
How can I programmatically accomplish my goal of initializing the UITextField?
Put that "urlNameInput.text =" bit into your view controller's "viewDidLoad" method, instead of the "didFinishLaunchingWithOptions:" method (where your view controller is not likely yet instantiated.
Even better than that, just set the initial text in your storyboard or XIB file and then you can programmatically adjust it later on.
Ok I'm having a hard time understanding what you're trying to do but creating an NSString iVar on your app delegate (although there are many other solutions) will allow you to set the textfield text to whatever you want when that VC comes back on the screen.
In your AppDelegate.h
#property (strong, nonatomic) NSString *stringForTextField;
This way you can initialize your textfield text when the view is loaded (viewDidLoad)
self.urlNameInput.text = #"example http";
then whenever that text value needs to be changed (say for example in textFieldShouldReturn in the other view controller. I'm assuming you have another textfield based on your question)
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
appDelegate.stringForTextField = textField.text;
and in viewDidAppear in the VC with the textField set that value.
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
self.urlNameInput.text = appDelegate.stringForTextField;
probably not the best way to go about it, but it will work.
EDIT
Ok in viewDidAppear:
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if ([appDelegate.stringForTextField isEqualToString:#""]){
self.urlNameInput.text = #"example http";
} else {
self.urlNameInput.text = appDelegate.stringForTextField;
}
Now what this will do is if the user goes to another view controller, and comes back, the text field text will be what the user last entered, unless in another view controller, stringForTextField is updated to some new value. If this still does not work, look at your processPbn method to make sure the if clause is entered and that value is set. Otherwise it will always say "example http"
I'm not understanding why it's unimportant to persist the previous value across application launches, especially when it is beneficial to your users to have it persist only during the application lifecycle. codeInOrange's answer works by adding a property to the AppDelegate. The only thing I would add to his answer is a conditional if() . If you want to do it without any properties, you can still use the NSUserDefaults.
At the top of your ViewController.m file
#define SetHTTPString(string) [[NSUserDefaults standardUserDefaults]setObject:string forKey:#"HTTPString"] //no semicolon
#define GetHTTPString() [[NSUserDefaults standardUserDefaults]objectForKey:#"HTTPString"] //no semicolon
Then, in viewWillAppear...
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSString *httpString = GetHTTPString();
if (httpString) {
self.urlNameInput.text = httpString;
} else {
self.urlNameInput.text = #"Example http";
}
}
Next, in the method where the user enters text and "enters it"
...methodToEnterURL {
SetHTTPString(self.urlNameInput.text);
}
Finally, if you absolutely want to destroy the value in the NSUserDefaults, add this method to your AppDelegate's didEnterBackground method:
[[NSUserDefaults standardUserDefaults]setObject:#"Example http" forKey:#"HTTPString"];
This is a perfect usage for NSUserDefaults. When the user enters something just store it in NSUserDefaults. Check to see if the NSUserDefaults entry is blank on each launch, and if so just display the original string.
Save the text in NSUserDefaults with something like:
[[NSUserDefaults standardUserDefaults]setObject:#"yourNewString" forKey:#"userTextEntered"];
And then just check it on each launch:
if([[NSUserDefaults standardUserDefaults] objectForKey:#"userTextEntered"])
{
//display the user entered string
}
else
{
//display the string that you want to display prior to text being entered
}
However, this solution is only necessary if you want to preserve the text between application shutdowns and launches.
The code below assumes that the Storyboard contains the initial, default Text (of at least 3 characters length).
I really appreciated the help I got from others, especially from codeInOrange . I actually believe this is codeInOrange's solution, but I was never able to quite put his pieces together until I finally stumbled upon this one.
I hope this is really a valid answer, and apologize to all if I did not state my question clearly or if I mistook others' valid answers, especially codeInOrange.
//
// ViewController.m
// StickyPlaceholder
//
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize textInput;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (!appDelegate.stringForTextField)appDelegate.stringForTextField = self.textInput.text ;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (textField == self.textInput) {
[textField resignFirstResponder];
// next line is dummy processing
if (self.textInput.text.length>2)appDelegate.stringForTextField = self.textInput.text;
}
return YES;
}
- (void)viewDidAppear:(BOOL)animated
{
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
self.textInput.text = appDelegate.stringForTextField;
}
#end
I need to keep track of which text field is the firstResponder for my custom keyboard to work. In the code below, I have grossly oversimplified my program, but here is the gist of the problem:
#implementation SimplePickerViewController
#synthesize pickerKeyboard;
#synthesize textView;
#synthesize textView2;
#synthesize firstResponder;
-(void)viewDidLoad{
pickerKeyboard = [[PickerKeyboardViewController alloc] initWithNibName:#"PickerKeyboard" bundle:nil];
pickerKeyboard.delegate = self;
[self.textView setInputView:pickerKeyboard.view];
[self.textView setDelegate:self];
[self.textView2 setInputView:pickerKeyboard.view];
[self.textView2 setDelegate:self];
}
-(void)hideKeyboard{
[self.firstResponder resignFirstResponder];
self.firstResponder = nil; //without this line, the code doesn't work.
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
self.firstResponder = textView;
[self.pickerKeyboard.picker reloadAllComponents];
return YES;
}
If I remove the line setting the firstResponder to nil, the code ceases to function properly, but I am not sure why. (Without that line, I can select the first textView to bring up the keyboard, but after that I can never bring the keyboard back. Any ideas? Thanks!
I'm not sure that I understand why firstResponder needs to be kept track of for a custom keyboard to work. I use a custom keyboard without knowing what the first responder is.
Do you use:
textView.inputView = pickerKeyboard
How about the following, called on the view to resign the first responder:
[self.view endEditing:NO];
I have had a similar problem and I have just figured out the issue. Somewhere in some part of Apple's first responder code, they are using a selector named firstResponder. When you created the property firstResponder you inadvertently overrode that selector. That will cause Apple's code to fail. This, in my humble opinion, is a bug in Apple's framework, and the firstResponder method isn't documented anywhere. Name your property myFirstResponder or anything else and everything should work just fine.
See Why does the keyboard not show when a view is popped from the navigation stack?