I want to put something like this in a method for UITextField & UITextView.
- (void)changeKeyboardType:(UIKeyboardType)keyboardType {
paymentTextView.keyboardType = UIKeyboardTypeAlphabet;
[paymentTextView resignFirstResponder];
[paymentTextView becomeFirstResponder];
}
How do I do this? I know I can create categories for both UITextField & UITextView but is it possible to do it in one shot?
By one shot, I mean add it to both classes with one protocol instead of making two categories, one for UITextView & one for UITextField. I've heard a protocol is similar to a Ruby module, but in a Ruby module, I can implement the method. In a protocol, it only seems that I can declare the method but not implement it. Can I also implement the method in the protocol, and then include this protocol in UITextField & UITextView?
How to add a method to an existing protocol in Cocoa? is close but not quite.
What about something like this?
// UIView+UITextInputTraits.h
#interface UIView (UITextInputTraits)
- (void)changeKeyboardType:(UIKeyboardType)keyboardType;
#end
// UIView+Additions.m
#import "UIView+UITextInputTraits.h"
#implementation UIView (UITextInputTraits)
- (void)changeKeyboardType:(UIKeyboardType)keyboardType {
if ([self conformsToProtocol:#protocol(UITextInputTraits)]) {
id<UITextInputTraits> textInput = (id<UITextInputTraits>)self;
if (textInput.keyboardType != keyboardType) {
[self resignFirstResponder];
textInput.keyboardType = keyboardType;
[self becomeFirstResponder];
}
}
}
#end
For each of these, you can create a category.
Interface file:
#interface UITextField (ChangeKeyboard)
- (void)changeKeyboardType:(UIKeyboardType)keyboardType;
#end
Implementation file:
#implementation UITextField (ChangeKeyboard)
- (void)changeKeyboardType:(UIKeyboardType)keyboardType {
self.keyboardType = keyboardType;
[self resignFirstResponder];
[self becomeFirstResponder];
}
#end
That would be the way to add these, but I haven't tested the functionality.
Like #Josh said, method swizzling isn't what you are looking for. However what I actually had in mind (My bad for not researching more into it before submitting an answer) is to add method at runtime on UITextView and UITextField. While this needs a bit more code to implement, it can give you the sort of one-shot you are looking for (You create a method and add it to both UITextView & UITextField at run-time)
Here's a blog post about it:
http://theocacao.com/document.page/327
http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html
Related
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'm working on an app which has a tableView with a textField in the right side of its each cell(there are more than 20 cells).
i've created custom cell's for each row except for the last one.
In the last row there is only a button.
Now i want to call resignFirstResponder on the button's click.
What should i do Please help?
You will have to keep track of which textfield in which cell has the first responder and resign it like this.
[myCellTextField resignFirstResponder];
You probably want to keep track of the text field with the keyboard. Implement the <UITextFieldDelegate> protocol in your controller, and set the controller as each of the text fields' delegates. Write the textFieldDidBeginEditing: method like so, setting an instance variable called currentTextField:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
currentTextField = [textField retain];
}
Then, in your action for the button run [currentTextField resignFirstResponder].
Aopsfan's answer is probably the best solution so far. However, to add to it (as I cannot post comments), do remember to deallocate the object:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (currentTextField != nil) {
[currentTextField release];
}
currentTextField = [textField retain];
}
Better still use #property's and #synthesize so the runtime can do the memory management for you.
[ViewController].h
#property (nonatomic, retain) UITextField* currentTextField;
[ViewController].m
#synthesize currentTextField = _currentTextField;
- (void)viewDidLoad|Appear {
self.currentTextField = nil;
}
- (void) dealloc {
[_currentTextField release], _currentTextField = nil;
...
[super dealloc];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.currentTextField = textField;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.currentTextField) {
[self.currentTextField resignFirstResponder];
}
}
I think this link will help you:
Objective C: ResignFirstResponder with a button
Provide some code so that, it will be easier to help u.
Hope this helps you. :)
I've done some looking around but I haven't been able to find anything that clearly explains how I could simultaneously scroll two un-editable UITextViews. I think I may need to use either scrollRangeToVisible, or setContentOffset, though I could not get either of them to work.
Does anyone have any examples/samples, or documentation regarding this that they could point me towards?
EDIT: To clarify, I would like to be able to scroll one UITextView, and have the changes as a result of the scrolling reflected on a second UITextView as well.
Thanks!
Use the UIScrollViewDelegate methods to get information about scroll actions of the first scroll view and then scroll the second programmatically like that:
- (void) scrollViewDidScroll:(UIScrollView *)view1 {
scrollView2.contentOffset = view1.contentOffset;
}
React in
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
and set the other's scrollView setContentVisible according to scrollView.contentOffset.
Just be aware that some methods of UIScrollView will invoke scrollViewDidScroll even if called programmatically. This applies to scrollRangeToVisible and will end up in a loop unless you take action to prevent this loop. I don't think that setContentOffset or setting scrollView2.contentOffset = CGPointMake(..,..) does call scrollViewDidScroll. However, I would not sign this in blood. Be prepared to see a loop and take actions to avoid it. (such as boolean instance variable set before calling setContentOffset and re-set in scrollViewDidScroll followed by return;)
Just continuing with previous answers, to give some more information, I generated this code:
In the interface (.h):
#import <UIKit/UIKit.h>
#interface DoubleTextViewController : UIViewController <UITextViewDelegate>
#property (strong, nonatomic) IBOutlet UITextView *textView1;
#property (strong, nonatomic) IBOutlet UITextView *textView1;
#end
In your implementation (.m):
Use this viewDidLoad function after defining the corresponding properties and global variables.
#import "DoubleTextViewController.h"
#define TEXT_VIEW_1_TAG 1001
#define TEXT_VIEW_2_TAG 1002
#interface DoubleTextViewController () {
BOOL isScrolling;
}
#end
#implementation DoubleTextViewController
#synthesize textView1, textView2;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view..
isScrolling = NO;
[self.textView1 setTag:TEXT_VIEW_1_TAG];
[self.textView2 setTag:TEXT_VIEW_2_TAG];
[self.textView1 setDelegate:self];
[self.textView2 setDelegate:self];
}
And add this function for simultaneous scroll.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (isScrolling) {
return;
}
isScrolling = YES;
if (scrollView.tag == TEXT_VIEW_1_TAG) {
[self.textView2 setContentOffset:scrollView.contentOffset animated:NO];
} else if (scrollView.tag == TEXT_VIEW_2_TAG) {
[self.textView1 setContentOffset:scrollView.contentOffset animated:NO];
}
isScrolling = NO;
}
As proposed by Hermann Klecker, the isScrolling variable stops scrolling loops and makes the user experience nicer. Using the code proposed by Fabian Kreiser makes the scroll stops as soon as the user leaves the finger, making it strange.
Just ran into UX problem to save UITextField input value.I've got 6 UItextfield entries which saves individual value in sqlite db.
Right now each field has separate save button.So six ones quite look messy and silly.
I just want to know if there is any method to save entry after editing ends.
To be more concise...
I want to save data in UITextField after user ends editing.Just needs 'Saving Logic' for problem
You probably want to use an object which implements UITextFieldDelegate protocol. It defines –textFieldShouldEndEditing: and –textFieldDidEndEditing: methods which are called just before and once text editing ends.
Your delegate should be declared like:
#interface ATextFieldDelegate : NSObject<UITextFieldDelegate>
{
}
#end
And implements the methods:
#implementation ATextFieldDelegate
- (BOOL) textFieldShouldBeginEditing:(UITextField *)textField
{
// Test if the textField.text is valid for storage
// Return YES if it is, NO if not
}
- (void) textFieldDidEndEditing:(UITextField *)textField
{
// Store textField.text into your SQLite database
}
#end
And you should set your UITextField's delegate:
UITextField *myTextField; // could be an IBOutlet
ATextFieldDelegate *myTextFieldDelegate; // must be initialized somewhere
myTextField.delegate = myTextFieldDelegate;
I am curious about conforming a class to UITextFieldDelegate, in the past I have always added it to enable access to methods defined within the protocol. However this last time I forgot to add it, only realising later that it was missing. My question is why does it work with or without, I thought it was needed to correctly access the protocol methods?
#interface MyController : UIViewController <UITextFieldDelegate> {
UITextField *text_001;
}
#property(nonatomic, retain) IBOutlet UITextField *text_001;
#end
OR:
#interface MyController : UIViewController {
UITextField *text_001;
}
#property(nonatomic, retain) IBOutlet UITextField *text_001;
#end
WITH:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSLog(#"Return: ... ");
[textField resignFirstResponder];
return YES;
}
Cheers Gary
Delegate declarations are really just there as compiler hints; obviously, you still have to implement the underlying methods. However, the main purpose is to let the compiler double check you when assigning them. If you try to manually (in code, as opposed to IB) assign a delegate which wasn't declared as such, you'll frequently get a compiler warning.
Since Objective-C uses duck-typing for most stuff (if it walks like a duck and quacks like a duck; if it responds to -textFieldShouldReturn:, etc), you're pretty safe.