how do I tell which UITextField just triggered the textFieldShouldReturn callback? - iphone

I have mulitple UITextFields in a view.
I'm assuming the place to capture the vlue of a UITextField once the user comes out of it is to implement the delegate method "textFieldShouldReturn".
Question - In "textFieldShouldReturn" however, how do I tell which of the UITextField's triggered this?
For example assuming at this stage I now need to update my data model with the value of what the UITextField now shows, so need to update the correct field in the model with aligns with that particular UITextField.
PS If there's a better approach, or a way to kind of "binding" approach I'm missing I'd be interested

...or you can skip all the tags and make your UITextViews instance vars and do:
- (void)viewDidLoad {
myTextView1 = [[UITextView alloc] init];
myTextView2 = [[UITextView alloc] init];
myTextView3 = [[UITextView alloc] init];
myTextView4 = [[UITextView alloc] init];
......
}
- (void)textFieldShouldReturn:(UITextField *)textField {
BOOL shouldReturn = NO;
if (textField == myTextView1)
{
shouldReturn = YES;
}
...and so on...
}
... release the instance vars in the dealloc...
I kinda prefer this way, but the other answer will work too.

Related

Programmatically instantiating a Delegate to a component

I have an app which has a controller which already handles UITextFieldDelegate for textfields, now I want to add a second controller + UITextField programmatically which is separately from the main controller but is called from it.
So I have a MainController which handles input (that is working), then next to that, I defined this:
#interface TestUIDelegate : UIViewController <UITextFieldDelegate>{
TestUIDelegate has a property textField.
TestUIDelegate has a method:
- (void)initGUI:(UIView *)myView;
which initialises a textField like thus:
- (void)initGUI:(UIView *)myView {
self.view = myView;
textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 200, 300, 40)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.font = [UIFont systemFontOfSize:15];
textField.placeholder = #"enter text";
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyDone;
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.delegate = self;
[self.view addSubview:textField];
}
which also works; the field shows up and is editable, keyboard comes up and you can type stuff.
So far so good, but now I want to Done button to work so the keyboard is dismissed, so I added:
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
NSLog(#"Here?");
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
NSLog(#"Or here?");
}
To my TestUIDelegate (actually I added all delegate methods from TextUIFieldDelegate).
I instantiate the TestUIDelegate from the didFinishLaunchingWithOptions in the MainController like this:
TestUIDelegate *controller = [[TestUIDelegate alloc] init];
[controller initGUI:self.view];
TextField still shows up the delegate methods are never called, none of them. Now I imagine i'm doing something very weird and I have looked through all answers on SO and in Google slightly relating to this, but they all do it by implementing the delegates in the MainController which is exactly what I really do not want. And I need it programmatically; not (at all) with the gui builder.
Can anyone enlighten me please? I don't think i'm going to figure this out on my own.
You need to add textFieldShouldReturn to get the keyboard to dismiss:
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
TextFieldDidEndEditing will then be called as well, but you need to resignFirstResponder before that happens.
I found out what the problem was (but not why...); I was using
[controller initGUI:self.view];
changing that to:
[controller initGUI:self.window.rootViewController.view];
makes it work. I don't know why.
For people who are interesting to do this same thing (like I said; I couldn't find even one example on the web), here is the simplified project; https://dl.dropboxusercontent.com/u/6134596/TestDelegate.zip

(id)sender = nil?

I have the following code that updates the value of a textfield with the current time. My question is: Why do you send nil as the sender at the bottom part of the code? [self showCurrentTime:nil];
CurrentTimeViewController.m
- (IBAction)showCurrentTime:(id)sender
{
NSDate *now = [NSDate date];
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setTimeStyle:NSDateFormatterShortStyle];
}
[timeLabel setText:[formatter stringFromDate:now]];
}
...
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"CurrentTimeViewController will appear");
[super viewWillAppear:animated];
[self showCurrentTime:nil];
}
Because normally when action handlers are called, they pass the object that initiated the call to the method, like a UIButton, or UISegmentedControl. But, when you want to call the method from your code, and not as the result of an action, you cannot pass a human as sender, so you pass nil.
Also, the - (IBAction) indicates that this method can be connected to an event via the Interface Builder by dragging a Touch Up Inside (or touch down outside/etc) event from a button (or any other control that has some sort of event) to the File's Owner and selecting thumbTapped:.
For example:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = 1001;
[button addTarget:self action:#selector(thumbTapped:) forControlEvents:UIControlEventTouchUpInside];
When the touch is released (and the touch is still inside the button), will call thumbTapped:, passing the button object as the sender
- (IBAction)thumbTapped:(id)sender {
if ([sender isKindOfClass:[UIButton class]] && ((UIButton *)sender).tag == 1001) {
iPhoneImagePreviewViewController *previewVC = [[iPhoneImagePreviewViewController alloc] initWithNibName:#"iPhoneImagePreviewViewController" bundle:nil];
[self.navigationController pushViewController:previewVC animated:YES];
[previewVC release];
} else {
[[[[UIAlertView alloc] initWithTitle:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDisplayName"]
message:#"This method was called from somewhere in user code, not as the result of an action!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease] show];
}
}
IBAction methods, in their most common form, take a single sender argument. When invoked by the system, they pass the control that triggered the action as the sender parameter. If you're going to call the method directly, you'll need to provide a sender. Since this method isn't being invoked by a control that's just been interacted with by the user, nil is used.
I actually think that calling actions directly isn't a good pattern to follow. Having a method with the tag IBAction implies "This method is invoked in response to user action" and that's an important bit of context to be able to depend on when working within that method. If code is going to call the method directly that idea's been violated.
Usually I think it's better to do something like this:
- (void)updateTime:(NSDate *)date {
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setTimeStyle:NSDateFormatterShortStyle];
}
[timeLabel setText:[formatter stringFromDate:date]];
}
- (IBAction)showCurrentTime:(id)sender {
[self updateTime:[NSDate date]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self updateTime:[NSDate date]];
}
It's because you need to send something, since the signature of the method you're calling takes an argument.
The :(id)sender is commonly seen in button press actions. When you connect a button in a nib to a method in your view controller, it will check to see if it can take an argument. If it does, the "sender" will point to the instance of the UIButton that was created in the nib.
This is also true for programmatic button creation, and many other cases where you send in selectors.
The specifcation for the showCurrentTime function has 1 argument named sender. If you called the function without sending the ID of an object the call would be invalid.
Nil is used instead of NULL in objective-c and it is purely being sent to satisfy the specification of the function you are calling.
As the function does not actually use the argument within the body it does not actually matter what object you send to the function.

Having Trouble With UITextField and NSString

I have the following code for UITextField. The problem I'm having is that the text does not persist. For example, when I present a modal view, then dismiss it, the UITextField does not have the text anymore in it. I want the text to remain there until I dimiss that view with the text field.
I am displaying the UITextField like this:
UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
nameTextField.delegate = self;
[nameTextField addTarget:self action:#selector(editingEnded) forControlEvents:UIControlEventEditingDidEnd];
[nameTextField setEnabled: YES];
self.myTextField = nameTextField;
[nameTextField release];
Then I have:
- (void)editingEnded
{
NSString *tempRoutineName = self.myTextField.text;
self.routineName = tempRoutineName;
[tempRoutineName release];
}
Instead of editingEnded, Implement the UITextFieldDelegate protocol. Go to the textFieldDidEndEditing method and reassign the value of text in it.
Like,
-(void) textFieldDidEndEditing:(UITextField *)textField{
if (textField.tag ==0){
self.myTextField = textField;
// myTextField is a property
}
Now in the in the viewDidLoad method or the viewWillAppear method, go ahead and assign this value back to the textField.
If necessary use [tableView reloadData] if this is used in a tableView or use [self reloadInputViews] (if necessary).
Then again, its all logical. Nothing too complex in code.
Use UITextFieldDelegate method that is textFieldDidEndEditing, which gets called when editing ends
In my opinion:
NSString *tempRoutineName = self.myTextField.text;
self.routineName = tempRoutineName;
[tempRoutineName release];
You haven't own tempRoutineName to release. Comment out the release and check.

Can't handle didSelectObject:atIndexPath: after search in TTTableViewController

Greetings, I got some strange problem in my code...
My code is based on Model Search (TTCatalog) three20 example. I successfully handling didSelectObject:atIndexPath: when row touched before search, but this event not even firing when something is found (ex. filtered 2 results of 30).
I working with remote data, fetched once, then like in Model Search.
Thanks in advance.
Got it! I've just were making huge mistake:
I've used this construction:
#implementation RegionSelectionViewController
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)loadView {
[super loadView];
TTTableViewController* searchController = [[[TTTableViewController alloc] init] autorelease];
searchController.dataSource = [[[RegionDataSource alloc] initWithDuration:1.5] autorelease];
self.searchViewController = searchController;
self.tableView.tableHeaderView = _searchController.searchBar;
}
instead of this:
#implementation RegionSelectionViewController
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)loadView {
[super loadView];
RegionSelectionViewController* searchController =
[[[RegionSelectionViewController alloc] init] autorelease];
searchController.delegate = _delegate;
self.searchViewController = searchController;
self.tableView.tableHeaderView = _searchController.searchBar;
}
Now it's working as it should!
Looks like there are no option to overload delegate of TTTableViewController...
One more time, thanks!

Update UILabel from applicationDidBecomeActive?

I want to update UILabel by clicking a reload button. Additionally, I want to update the label in background, because it is fetching the new data via XML from my website. Of course it would be nice to auto-update the label when the application is opened. And there is my problem:
I was able to make it work well when user were clicking the button manually. But I don't understand how to do the same by calling my method via "applicationDidBecomeActive". I tried to do it the same way, but it obviously doesn't work because my label is returned nil.
I suppose there is a problem of my understanding and the solution should be quite easy. Thanks for your input! Note: I am a beginner with Objective-C and have sometimes problems with "easy" things. ;-)
Below is a summary of the important code parts:
AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[MyViewController alloc] reloadButtonAction];
}
MyViewController
#synthesize label
- (void)reloadButtonAction {
[self performSelectorInBackground:#selector(updateData) withObject:nil];
}
- (void)updateData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Parse the XML File and save the data via NSUserDefaults
[[XMLParser alloc] parseXMLFileAtURL];
// Update the labels
[self performSelectorOnMainThread:#selector(updateLabels) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)updateLabels {
NSUserDefaults *variable = [NSUserDefaults standardUserDefaults];
myLabel.text = [variable stringForKey:#"myLabelText"];
// myLabel is nil when calling all of this via AppDelegate
// so no changes to the myLabel are done in that case
// but: it works perfectly when called via button selector (see below)
NSLog(#"%#",myLabel.text);
}
-(void)viewDidLoad {
// Reload button in the center
UIButton *reloadButton = [UIButton buttonWithType:UIBarButtonSystemItemRefresh];
reloadButton.frame = CGRectMake(145,75,30,30);
[reloadButton setTitle:#"" forState:UIControlStateNormal];
[reloadButton addTarget:self action:#selector(reloadButtonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:reloadButton];
}
First:
[[MyViewController alloc] reloadButtonAction];
Doesn't make sense. You allocate memory, without initializing an object. And then you want to call a method on it. Doesn't work
Use an instance for it:
[myViewControllerInstance reloadButtonAction];
In your app delegate you should have an reference to your rootcontroller instance if that is the object contains the reload method, use that instance.
Note:
Alloc only reserves space in the memory for an object which size the size of MyViewController instance. An init method will fill it.