Hey does anyone know how to do something like:
[(NSString) becomeFirstResponder];
(where NSString goes thats where the textfields name is).
Any help appreciated.
Thanks
[(NSString) becomeFirstResponder]; will cause exception.
Use
[textField becomeFirstResponder]; to make your textfield as first responder.
where textField is the instance of your UITextField. Make sure that you have assigned IBoutlet for that.
If the corresponding view controller has instance variables or declared properties pointing to the text fields (e.g. IBOutlet), you could try:
UIViewController *containerViewController;
NSString *fieldName = #"someFieldName";
id field = [containerViewController valueForKey:fieldName];
if ([field isKindOfClass:[UITextField class]])
[(UITextField *)field becomeFirstResponder];
Bear in mind that Cocoa Touch has a tag mechanism that can be used to get a specific subview from an entire view hierarchy regardless of IB outlets, instance variables, or declared properties. Tags are integers that can be set via Interface Builder or programmatically, and usually there’s a constant for each integer representing a tag. For instance:
static const NSInteger kFirstNameTextFieldTag = 42;
UIView *containerView;
field = [containerView viewWithTag:kFirstNameTextFieldTag];
if ([field isKindOfClass:[UITextField class]])
[(UITextField *)field becomeFirstResponder];
You could extend this to feature a dictionary mapping field names (strings) to their corresponding tags, and then use the tag to get a particular field.
Related
I am using the approach described in this stackoverflow posting to retrieve values from a textfield. My problem is that the tableview is presented modally and I have a save button that validates the input and stores it.
The problem is that the textFieldDidEndEditing method is not called when the user clicks an UIBarButtonItem (= the save button, which closes the modal view).
In this event (when the user wants to save the input) I would like to validate it. But the values are stored in properties in the textFieldDidEndEditing. Due to the fact that this method is not called, I cannot validate the input values correctly.
Does anyone have a hint or solution on this?
Thanks in advance!
You should assign unique tag numbers to your text fields, then keep track on which is currently active (i.e. use a int iVar to store the active text fields tag value) in the textFieldDidBeginEditing delegate and when the user clicks the save, you should get the last textfield by it's tag value and then it's text value so you can validate it.
Okay, here we go:
Thanks to #Lefteris and his idea with storing the current index. Due to the fact that I cannot store the index into the tag attribute I decided to store the active indexPath and additionally the active textField. (I know, a reference to the UITextField would have been enough but I needed it for other stuff)
First I have added these two properties:
#property (nonatomic, strong) NSIndexPath *activeIndexPath;
#property (nonatomic, strong) UITextField *activeTextField;
Then I implemented textFieldDidBeginEditing: and textFieldDidEndEditing: of UITextFieldDelegate.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
NSIndexPath *indexPath = (NSIndexPath*)[self.tableView indexPathForCell:(UITableViewCell*)[[textField superview] superview]];
self.activeTextField = textField;
self.activeIndexPath = indexPath;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
NSString *input = textField.text;
//assuming values from input textfield into corresponding properties
[self assumeInput:input withIndexPath:self.activeIndexPath];
self.activeTextField = nil;
self.activeTextField = nil;
}
In textFieldDidEndEditing: I am storing the values into my properties (such as self.firstName, self.lastName, and so on...) by using the method [self assumeInput:input withIndexPath:self.activeIndexPath];.
In my saveAction-Method I am storing the value from the currently active TextField.
- (IBAction)saveButtonClicked:(UIBarButtonItem *)sender
{
//assuming input from active field (didEndEditing _not_ called right now!)
[self assumeInput:self.activeTextField.text withIndexPath:self.activeIndexPath];
//test output
NSLog(#"firstName: %#", self.firstName);
NSLog(#"lastName: %#", self.lastName);
NSLog(#"email: %#", self.email);
...
}
... and that's it!
Hope it helps! Thanks to #Lefteris for his input.
Best,
Chris
I put 10 UITextField inside UITableViewCell and put tags on every UITextField.
My question is, can I access the tags of those UITextFields in
- (void)textFieldDidBeginEditing:(UITextField *)textField?
My question is, can I access the tags of those UITextFields in
-(void)textFieldDidBeginEditing:(UITextField *)textField?
Sure. Just use:
textField.tag
The method textFieldDidBeginEditing: is called for a specific field, and that field will be specified in the textField parameter. If you want to access a different field, then you'll need to get a pointer to the field you want. You could do something like:
UIView *parent = [textField superview];
UITextField *someOtherField = [parent viewWithTag:theTagOfTheFieldYouWant];
I need a way of determining the UITextField that is currently selected in a view. Is this possible without passing a reference or tag?
To be more specific I need to be able to tell which UITextField is selected so that I can hide the keyboard. The reason I need this is because I want to create a UIToolbar to add to all the UITextField's as an input accessory. On this UIToolbar I will add a 'Done' button, when pressed this should hide the keyboard for the currently selected UITextField.
I assume you mean you want to know which UITextField is the first responder (which is the text field that gets input from the keyboard).
There is no public API for this (though there is a private API). You can track which text field is the first responder manually using the textFieldDidBeginEditing: method of each text field's delegate, or you can use a little trickery to find the first responder at any time.
Here's the trick. The UIApplication object knows which object is the first responder, and can send a message to it. So you write a category like this on UIResponder:
UIResponder+firstResponderHack.h
#import <UIKit/UIKit.h>
#interface UIResponder (firstResponderHack)
+ (UIResponder *)firstResponderByHack;
#end
UIResponder+firstResponderHack.m
#import "UIResponder+firstResponderHack.h"
#interface FirstResponderFinder : NSObject
#property (strong, nonatomic) UIResponder *firstResponder;
#end
#implementation FirstResponderFinder
#synthesize firstResponder = _firstResponder;
#end
#implementation UIResponder (firstResponderHack)
- (void)putFirstResponderIntoFinder:(FirstResponderFinder *)finder {
if (self.isFirstResponder)
finder.firstResponder = self;
}
+ (UIResponder *)firstResponderByHack {
FirstResponderFinder *finder = [FirstResponderFinder new];
// Sending an action to nil sends it to the first responder.
[[UIApplication sharedApplication] sendAction:#selector(putFirstResponderIntoFinder:) to:nil from:finder forEvent:nil];
return finder.firstResponder;
}
#end
Then you can find the first responder, and check whether it's a UITextField, like this:
UIResponder *firstResponder = [UIResponder firstResponderByHack];
if (firstResponder && [firstResponder isKindOfClass:[UITextField class]]) {
UITextField *textField = (UITextField *)firstResponder;
// do something with textField
}
There is an easy way to dismiss the keyboard without having to track the currently active control, or iterating through all the available controls, or using a UITextFieldDelegate.
[self.view endEditing:YES]
From the docs:
endEditing:
Causes the view (or one of its embedded text fields) to
resign the first responder status.
- (BOOL)endEditing:(BOOL)force
Parameters
force
Specify YES to force the first responder to resign, regardless of whether it wants to do
so.
Return Value
YES if the view resigned the first responder status or NO if it did not.
Discussion
This method looks at the current view and its subview
hierarchy for the text field that is currently the first responder. If
it finds one, it asks that text field to resign as first responder. If
the force parameter is set to YES, the text field is never even asked;
it is forced to resign.
There is a delegate method:
- (void)textFieldDidBeginEditing:(UITextField *)textField
Apple Docs:
This method notifies the delegate that the specified text field just
became the first responder. You can use this method to update your
delegate’s state information. For example, you might use this method
to show overlay views that should be visible while editing.
There is also a property:
#property(nonatomic, readonly, getter=isEditing) BOOL editing
Apple Docs:
A Boolean value indicating whether the text field is currently in edit
mode. (read-only)
Just make an ivar for the UITextView in your header file:
UITextField *editingField;
#property (nonatomic, copy) UITextField *editingField;
Then,
- (void)textFieldDidBeginEditing:(UITextField *)textField;
{
editingField = textField;
// Whatever else you want to do
}
I'm thinking that you need to diff the textFields without reference.
So, the recommended why is using ObjectiveC runtime.
It's pretty straight forward.
Firstly:
#import <objc/runtime.h>
Then, define a char for its address:
static char UITextFieldViewIdentifier;
Then set the identifier with something like this:
objc_setValue(textField, &UITextFieldViewIdentifier, #"Identifier") //typing on a phone, not so sure about the expression
In the delegate method:
NSString *identifier = objc_getObject(textField, &UITextFieldViewIdentifier)
Just call this line where you want to dismiss the keyboard:
[self.view endEditing:YES];
I am new to interface builder and I would like to have a screen which contains a 3x3 grid of a UIView each of which contain a UIImageView, and 4 UILabels.
My logic is probably flawed but the way I am trying to achieve this was to:
Create a UIView Nib file MyUIView.nib and layout in IB with an imageView and 4 labels
Create a UIView subclass called MyUIView.m which contains 1 IBOutlet UIImageView and 4 IBOutlet UILabels. Link the MyUIView.nib and MyUIView.m as files owner and connect the outlets.
Then create another nib MyGridViewController.nib which has 9 MyUIView in it laid out in the 3x3 grid.
Create a UIViewController which has 9 IBOutlet MyUIView and connect them via Interface Builder.
Is it possible to load a nib into another nib graphically from within InterfaceBuilder, if so how do I do it? Do I drag a "standard" UIView onto the canvas and then change the class to be a MyUIView?
Or do I need to do this all programmatically within the MyGridViewController.m with something like:
for (int i=0; i<9; i++)
{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"MyUIView" owner:self options:nil];
[myViewArray addObject:[ nibViews objectAtIndex: 1]];
}
The only other way I have gotten this to work was to have a single Nib and put 9 UIImageViews and 36 UILabels but this obviously is a pain when I want to change something as I need to update each one of the 3x3 "cells". I thought it would be easier to change it in one file and all 9 would be updated.
You cannot do it in Interface Builder.
What I would probably do is to make a custom view, say MyGridItem, that loads MyUIView.nib as a subview when it awakes, and then use 9 of them in your MyGridView.nib.
Be careful with awakeFromNib, as it can be called twice if the view is involved in the loading of two different nibs (eg, if MyGridItem is the owner when loading MyGridView.nib, then MyGridItem awakeFromNib will be called once when it is loaded as part of loading MyUIView.nib, and once when it loads the MyGridView.nib.
Also, since you're loading the nib 9 times, you may want to cache the nib once using
NSNib* theNib = [[NSNib alloc] initWithNibNamed:#"MyGridItem" bundle:nil];
load it with:
if ( [theNib instantiateNibWithOwner:self topLevelObjects:NULL] ) {
You then may want to deallocate it after you've loaded all nine subviews.
"Yes you (almost) can."
I do it in my projects using Interface Builder.
The only flaw is that you see a white area to represent the 'nested nibs' in Interface Builder. Let's say that, in the mean time (I main waiting for Apple to add this feature in XCode), the solution I present here is acceptable.
First read this: https://blog.compeople.eu/apps/?p=142
Then, if you do ARC, follow these instructions and grab my UIVIew+Util category included here.
For ARC, you will have to allow this 'self' assignation. (https://blog.compeople.eu/apps/?p=142 state that it's not needed, but it is. If you do not, you will get some 'messages send to deallocated instance')
To achieve this in an ARC project, add the '-fno-objc-arc' flag compiler setting on your file.
Then do NO-ARC coding in this file (like dealloc setting nils, calling super dealloc, etc..)
Also, client nib's viewcontroller should use strong property to hold the instance returned by awakeFromNib. In the case of my sample code, the customView is referenced like this:
#property (strong, nonatomic) IBOutlet CustomView* customView;
I finally added some other improvements to properties handling and nib loading using copyUIPropertiesTo: and loadNibNamed defined in my UIView+Util category.
So awakeAfterUsingCoder: code is now
#import "UIView+Util.h"
...
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder
{
// are we loading an empty “placeholder” or the real thing?
BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);
if (theThingThatGotLoadedWasJustAPlaceholder)
{
CustomView* customView = (id) [CustomView loadInstanceFromNib];
// copy all UI properties from self to new view!
// if not, property that were set using Interface buider are lost!
[self copyUIPropertiesTo:customView];
[self release];
// need retain to avoid deallocation
self = [customView retain];
}
return self;
}
The UIView+Util category code is
#interface UIView (Util)
+(UIView*) loadInstanceFromNib;
-(void) copyUIPropertiesTo:(UIView *)view;
#end
along with its implementation
#import "UIView+Util.h"
#import "Log.h"
#implementation UIView (Util)
+(UIView*) loadInstanceFromNib
{
UIView *result = nil;
NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner: nil options: nil];
for (id anObject in elements)
{
if ([anObject isKindOfClass:[self class]])
{
result = anObject;
break;
}
}
return result;
}
-(void) copyUIPropertiesTo:(UIView *)view
{
// reflection did not work to get those lists, so I hardcoded them
// any suggestions are welcome here
NSArray *properties =
[NSArray arrayWithObjects: #"frame",#"bounds", #"center", #"transform", #"contentScaleFactor", #"multipleTouchEnabled", #"exclusiveTouch", #"autoresizesSubviews", #"autoresizingMask", #"clipsToBounds", #"backgroundColor", #"alpha", #"opaque", #"clearsContextBeforeDrawing", #"hidden", #"contentMode", #"contentStretch", nil];
// some getters have 'is' prefix
NSArray *getters =
[NSArray arrayWithObjects: #"frame", #"bounds", #"center", #"transform", #"contentScaleFactor", #"isMultipleTouchEnabled", #"isExclusiveTouch", #"autoresizesSubviews", #"autoresizingMask", #"clipsToBounds", #"backgroundColor", #"alpha", #"isOpaque", #"clearsContextBeforeDrawing", #"isHidden", #"contentMode", #"contentStretch", nil];
for (int i=0; i<[properties count]; i++)
{
NSString * propertyName = [properties objectAtIndex:i];
NSString * getter = [getters objectAtIndex:i];
SEL getPropertySelector = NSSelectorFromString(getter);
NSString *setterSelectorName =
[propertyName stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[propertyName substringToIndex:1] capitalizedString]];
setterSelectorName = [NSString stringWithFormat:#"set%#:", setterSelectorName];
SEL setPropertySelector = NSSelectorFromString(setterSelectorName);
if ([self respondsToSelector:getPropertySelector] && [view respondsToSelector:setPropertySelector])
{
NSObject * propertyValue = [self valueForKey:propertyName];
[view setValue:propertyValue forKey:propertyName];
}
}
}
Tadaaaa :-)
Credits goes to https://stackoverflow.com/users/45018/yang for initial solution. I just improved it.
If you create your view with the 1 image view and 4 labels in a nib (I'll call this view the "grid cell"), you can create another nib for your grid and drag in 9 instances of your grid cell. The 9 instances will only show up as placeholders (blank views) in IB, but they'll work when you run the app.
This article tells you how to do it. Check out the section called "A reusable subview". And since you're making a grid with 9 identical cells, it might make sense to use AQGridView -- see the section "A reusable AQGridViewCell".
AFAIK, there's no way to load a NIB within a NIB. What I would do, in your case, is add the UILabels programmatically in MyUIView.
Here's a slightly more dynamic way to do something like this.
I didn't really want to be pulling in the nibs and sifting through their objects wherever I wanted to use them in code, seemed messy. Also, I wanted to be able to add them in interface builder.
1) Create your custom subclass of UIView (lets call it myView)
2) Create your nib and call it myViewNIB
Add these two methods (would be smarter to have them in a superclass UIView and subclass that)
- (UINib *)nib {
NSBundle *classBundle = [NSBundle bundleForClass:[self class]];
return [UINib nibWithNibName:[self nibName] bundle:classBundle];
}
- (NSString *)nibName {
NSString *className = NSStringFromClass([self class]);
return [NSString stringWithFormat:#"%#NIB", className];
}
The so-called magic is that last line there where it returns a nibName by appending NIB to the current class.
Then for your init method, which is initWithCoder since you want to use this in interface builder (you could make it more robust so it can be used programmatically too by also setting up initWithFrame):
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Initialization code
NSArray *nibObjects = [[self nib] instantiateWithOwner:nil options:nil];
UIView *view = [nibObjects objectAtIndex:0];
[self addSubview:view];
}
return self;
}
So this requires that myViewNIB's first (and probably only) member is a view. What this won't give you is a way to set things like labels programmatically inside the nib, at least not easily. Without looping through the views and looking for tags I'm not sure how else you'd do that.
Anyway. At this point you can drag out a new UIView in IB, and set the class to myView. myView will automatically search for our associated myViewNIB.xib and add its view as a subview.
Seems there should be a better way to do this.
What I've done, is:
1) For the owning view, I put in the subview as an empty view with the class name of the custom view as a placeholder. This will create the custom view, but none of its subviews or connections will be made as these are defined in the custom view's nib.
2) I then manually load a new copy of the custom view using its nib and replace the placeholder custom view in the parent.
Use:
NSView *newView = (Code to load the view using a nib.)
newView.frame = origView.frame;
NSView *superView = origView.superview;
[superView replaceSubview:origView with:newView];
Ugh!
I have picker controls on a single view with two outlets for each. When any of them are selected the same method is called. How should I distinguish between which picker control has called the delegate?
How can I change the frame size of the pickers to fit in one view?
Thanks,
Amit
Set the view to a UILable and set the font size or adjustsFontSizeToFitWidth:
UILabel *myView;
myView = [[[UILabel alloc] initWithFrame:myFrameRect] autorelease];
myView.adjustsFontSizeToFitWidth = YES;
return myView;
Two pickers in a view sounds crazy ugly. Would it really not work for you to have a picker with two components (ie, columns)?
Nevertheless, the first parameter in every method defined by UIPickerViewDelegate is the picker that called you. If you have wired an IBOutlet to each UIPickerView, then you should be able to compare this parameter to those outlets to figure out which one has called you. Something like this:
IBOutlet UIPickerView *fooPicker;
IBOutlet UIPickerView *barPicker;
// ...
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (pickerView == fooPicker)
return [NSString stringWithFormat #"Foo row %d", row];
else
return [NSString stringWithFormat #"Bar row %d", row];
}
It might be cleaner to connect to different objects as your delegates, but with two pickers in one view, "clean" probably isn't an option for you anyways.
I've had great success with putting two pickers right on top of one another and then using a UISegmentedControl to select which is visible, by setting the .hidden properties of each.
Instead of having two picker, you can make it as one.
You can have the picker controller in a view . Once the user touch a button rise up the view and make them to select from the picker and remove the view.This will reduce the space in the Main viewcontroller.