Wierd issues when performing selector "selectAll" on UITextField - ios

I am facing the the wierdest bug (ether in my app or in IOS 7.1) ever.
After many hours I managed to create a simple app that demonstrate the problem.
Two UITextField - Dragged and dropped from interface builder and wired to t1, t2.
the ViewController:
#implementation ViewController
#synthesize t1;
#synthesize t2;
#pragma mark - UITextFieldDelegate
-(void)textFieldDidBeginEditing:(UITextField *)iTextField {
NSLog(#"textFieldDidBeginEditing");
[iTextField performSelector:#selector(selectAll:) withObject:iTextField afterDelay:0.0];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
t1.delegate = self;
t2.delegate = self;
}
#end
When tapping on t1 and t2 at the same time, both textFields become first responder in an endless loop!
When omitting ether the PerformSelector statement or the textField:shouldChangeCharactersInRange: implementation, problem is gone.
Can someone explain why does it happen?

Edit: Also set the exclusiveTouch property of each UITextField to: YES to prevent them from editing at the same time.
- (void)viewDidLoad
{
[super viewDidLoad];
t1.exclusiveTouch = YES;
t2.exclusiveTouch = YES;
t1.delegate = self;
t2.delegate = self;
}
- (void)textFieldDidBeginEditing:(UITextField *)iTextField
{
[iTextField performSelector:#selector(selectAll:) withObject:nil afterDelay:0.0];
}
Or more simply without using the exclusiveTouch properties:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)iTextField
{
if (iTextField == t1 && t2.isFirstResponder == NO)
{
return YES;
}
else if (iTextField == t2 && t1.isFirstResponder == NO)
{
return YES;
}
return NO;
}

I try to use the selectedTextRange property to instead of selectAll, it makes the endless loop's problem gone.
func textFieldDidBeginEditing(_ textField: UITextField) {
DispatchQueue.main.async {
let begin = textField.beginningOfDocument
let end = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: begin, to: end)
}
}

Related

How to use keyboard in OpenGL es app

I have an OpenGL ES app which uses keyboard. I can make the keyboard pop-up on screen when screen is touched. If I am correct, each time I press a key,
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
should be called. But it doesn't. The app was initially a pure OpenGL Mac game, which I am trying to make an iOS version of, so I am not using storyboard. I prefer to do everything programmatically if possible. Here is my code for ViewController.h:
#import <GLKit/GLKit.h>
#import "KeyboardView.h"
#interface ViewController : GLKViewController {
KeyboardView* keyBoard;
}
#end
relevant parts of ViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(#"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
CGRect viewRect = CGRectMake(0, 0, 100, 100);
keyBoard = [[KeyboardView alloc] initWithFrame:viewRect];
[self setupGL];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view addSubview:keyBoard];
[keyBoard becomeFirstResponder];
}
KeyboardView.h:
#import <UIKit/UIKit.h>
#interface KeyboardView : UIView <UIKeyInput, UITextFieldDelegate> {
UITextField *field;
}
KeyboardView.m:
#import "KeyboardView.h"
#implementation KeyboardView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
field = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 10)];
}
return self;
}
- (void)insertText:(NSString *)text {
}
- (void)deleteBackward {
}
- (BOOL)hasText {
return YES;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSLog(#"text: %#", textField.text);
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if ([newString length] < 1) {
return YES;
} else
{
textField.text = [newString length] > 1 ? [newString substringToIndex:1] : newString;
[textField resignFirstResponder];
return NO;
}
}
#end
I need to be able to get each character entered by user while keyboard is active. I most confess, I am a little bit confused. I am not sure if my approach is correct, so I really appreciate your help.
The keyboard text input isn't captured through the UITextField's
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
It is indeed captured through UIKeyInput's
- (void)insertText:(NSString *)text;
I am using the soft keyboard to capture text on my GLKView (OpenGLES) app, and I don't even need any UITextField at all
For reference:
UIKeyInput Reference Doc
EDIT:
You need to call your keyboardview's becomeFirstResponder to show your keyboard and call resignFirstResponder to hide it
You also need to override canBecomeFirstResponder and return TRUE

Navigation using return button in apple keyboard

I have more than one text field in my view controller how can i navigate from one to another using the return button in the IOS keyboard.
Use UITextFieldDelegate delegate and the textFieldShouldReturn: method. Inside you can get the tag of the textfield passed in argument and direct to another one based on this information.
To have the delegate working you have to set delegate property of sender (textfield) to be the receiver (e.g. your view controller)
myTextField.delegate = self;
or do it in the storyboard (here is some storyboard hints). Your view controller then needs to specify this delegate as follows (in the "h" file):
#interface MyViewController:UIViewController<UITextFieldDelegate>
#end
and then in the "m" file
-(BOOL)textFieldShouldReturn:(UITextField*)textfield{
if([textfield tag] == 1)
{
//pass focus to next textfield
[self.nextTextField becomeFirstResponder];
} else {
//remove focus from current textfield
[textfield resignFirstResponder];//
}
return YES;//YES if textfield should implement its default behaviour
}
-(BOOL) textFieldShouldReturn: (UITextField *) textField
{
[textField resignFirstResponder];
if(textField == _your1stTextField)
[_your2ndTextField becomeFirstResponder];
return YES;
}
try this...
set delegates to all of your text fields and then..
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == textField1)
{
[textField2 becomeFirstResponder];
}
else if (textField == textField2)
{
[textField3 becomeFirstResponder];
}
else if textField == textField3)
{
[textField4 becomeFirstResponder];
}
.
.
.
[textField resignFirstResponder];
return YES;
}
I think this solution is more dynamic as it works for any number of textFields as long as your superview doesn't have a view with a tag == lastTextField.tag + 1
In viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.textField1.delegate = self;
self.textField2.delegate = self;
...
self.lastTextField.delegate = self;
self.textField1.tag = 1;
self.textField2.tag = 2;
...
self.lastTextField.tag = n;
}
Then implement the textFieldDelegate:
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder)
[nextResponder becomeFirstResponder];
else
[textField resignFirstResponder];
return NO;
}
You need to declare at .h
.m set
yourTextfieldRef1.delegate=self;
yourTextfieldRef2.delegate=self;
yourTextfieldRef3.delegate=self;
yourTextfieldRef4.delegate=self;
yourTextfieldRef5.delegate=self;
.
.
.
set
yourTextFieldRef1.tag=1;
yourTextFieldRef2.tag=2;
yourTextFieldRef3.tag=3;
yourTextFieldRef4.tag=4;
yourTextFieldRef5.tag=5;
.
.
.
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
NSInteger nextTag = textField.tag + 1;
// Try to find next responder
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
// Found next responder, so set it.
[nextResponder becomeFirstResponder];
} else {
// Not found, so remove keyboard.
[textField resignFirstResponder];
}
return YES;
}
Hope it helps you...!

resignFirstResponder with UIControlEventEditingChanged causes app to crash

I set UITextFieldDelegate in my .h and in my .m have #property (strong, nonatomic) IBOutlet UITextField *myPhoneNumber;
In my viewDidLoad method I have
[self.myPhoneNumber setDelegate:self];
[self.myPhoneNumber addTarget:self
action:#selector(editingChanged:)
forControlEvents:UIControlEventEditingChanged];
My editingChanged: method listens to make sure the text input is >9 characters and then enables a button
- (IBAction)editingChanged:(id)sender {
if ([self.myPhoneNumber.text length] <= 9) {
self.myButton.enabled = NO;
}
else {
self.myButton.enabled = YES;
}
}
This works exactly how I want it to but when I press return on the keyboard to hide it this crashes the app with the error:
2014-07-15 08:08:27.089 sample-chat[47702:90b] -[MyViewController editingChanged]: unrecognized selector sent to instance 0x10ba7420
2014-07-15 08:08:27.093 sample-chat[47702:90b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyViewController editingChanged]: unrecognized selector sent to instance 0x10ba7420'
My method for returning the keyboard is as follows
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
You specified the return type of editingChanged: as IBAction. This is only required for linking up this method in Interface Builder, which would be redundant since you manually added the UIControlEventEditingChanged listener in viewDidLoad.
Either use
- (void)editingChanged:(id)sender
OR
use the method as is, but link it to the UIControlEventEditingChanged in Interface Builder.
Try this Way #mostly
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger length = editingTextField.text.length - range.length + string.length;
if (length > 0) {
yourButton.enabled = YES;
} else {
yourButton.enabled = NO;
}
return YES;
}
oky u can also try do this by using NSNotificationCenter, for example u want t check each character that entered to text field then u can try this
for example
//add the observer
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(editingChanged:) name:UITextFieldTextDidChangeNotification object:nil];
}
//remove the observer
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
}
- (void)editingChanged:(id)sender
{
if ([self.myPhoneNumber.text length] <= 9) {
self.myButton.enabled = NO;
}
else {
self.myButton.enabled = YES;
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
I think there is no need to use a custom selector like you done. It is better to use the text field delegate methods to manage this.
Set the delegate [self.myPhoneNumber setDelegate:self];
And implement the delegate methods like :
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
self.myButton.enabled = NO;
if (textField == _myPhoneNumber)
{
if (textField.text.length > 9)
{
self.myButton.enabled = YES;
}
}
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
[self.view endEditing:YES];
return YES;
}

How to hide the View After clear the textfield data in iOS

I want to hide the View after clear the Text field data.But My view is not hiding.Please give me the solution i am using this code
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
partialSearchView.hidden = YES;
isCheckType = YES;
return YES;
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
partialSearchView.hidden = NO;
isCheckType = NO;
return YES;
}
you should use the setter method: ...
[self.partialSearchView setHidden:YES];
- (BOOL)textFieldShouldClear:(UITextField *)textField
The text field calls this method in response to the user pressing the built-in clear button. (This button is not shown by default but can be enabled by changing the value in the clearButtonMode property of the text field.) This method is also called when editing begins and the clearsOnBeginEditing property of the text field is set to YES.
Implementation of this method by the delegate is optional. If it is not present, the text is cleared as if this method had returned YES.
you need actually implementation
- (BOOL)textFieldShouldClear:(UITextField *)textField {
partialSearchView.hidden = YES;
isCheckType = YES;
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (isCheckType )
return YES;
partialSearchView.hidden = NO;
isCheckType = NO;
if (textField.text.length >=8) {
return NO; // return NO to not change text
} else {
return YES;
}
}

Enable copy and paste on UITextField without making it editable

I want the text in a UITextField (or ideally, a UILabel) to be non-editable, but at the same time give the user the ability to copy it to paste elsewhere.
My final solution was the following:
I created a subclass of UILabel (UITextField should work the same) that displays a UIMenuController after being tapped. CopyableLabel.m looks like this:
#implementation CopyableLabel
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if(action == #selector(copy:)) {
return YES;
}
else {
return [super canPerformAction:action withSender:sender];
}
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)becomeFirstResponder {
if([super becomeFirstResponder]) {
self.highlighted = YES;
return YES;
}
return NO;
}
- (void)copy:(id)sender {
UIPasteboard *board = [UIPasteboard generalPasteboard];
[board setString:self.text];
self.highlighted = NO;
[self resignFirstResponder];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if([self isFirstResponder]) {
self.highlighted = NO;
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setMenuVisible:NO animated:YES];
[menu update];
[self resignFirstResponder];
}
else if([self becomeFirstResponder]) {
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setTargetRect:self.bounds inView:self];
[menu setMenuVisible:YES animated:YES];
}
}
#end
This question is pretty old and I'm surprised nobody has posted a solution without subclassing. The idea presented in #mrueg's answer is correct, but you shouldn't need to subclass anything. I just came across this problem and solved it like this:
In my view controller:
- (void)viewDidLoad {
self.textField.delegate = self;
self.textField.text = #"Copyable, non-editable string.";
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)copyTextFieldContent:(id)sender {
UIPasteboard* pb = [UIPasteboard generalPasteboard];
pb.string = self.textField.text;
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// UIKit changes the first responder after this method, so we need to show the copy menu after this method returns.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self becomeFirstResponder];
UIMenuController* menuController = [UIMenuController sharedMenuController];
UIMenuItem* copyItem = [[UIMenuItem alloc] initWithTitle:#"Copy"
action:#selector(copyTextFieldContent:)];
menuController.menuItems = #[copyItem];
CGRect selectionRect = textField.frame;
[menuController setTargetRect:selectionRect inView:self.view];
[menuController setMenuVisible:YES animated:YES];
});
return NO;
}
If you want to make this work for a UILabel, it should work the same way with just adding a tap gesture recognizer instead of using the delegate method.
This will do everything you need. Will be copyable. But not editable, and won't show a keyboard or a cursor.
class ViewController: UIViewController {
#IBOutlet weak var copyableUneditableTextfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
copyableUneditableTextfield.delegate = self
copyableUneditableTextfield.inputView = UIView() //prevents keyboard
copyableUneditableTextfield.tintColor = .clear //prevents cursor
copyableUneditableTextfield.text = "Some Text You Want User To Copy But Not Edit"
}
}
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return false //prevents editing
}
}
Try UITextView instead (I suspect it would work like a UILabel for you). I tested this with its editable property set to NO, and double-tapping-to-copy worked for me.
Another solution is keeping the UITextField enabled but programmatically preventing it from being edited. This is done with the following delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
return NO;
}
I'm not aware of possible limitations though, currently suits my needs.
The following code saved me.
textField.addTarget(target, action: "textFieldEditingDidEndAction:", forControlEvents: [.EditingDidEnd])
It seems Paste is a single and complete edit event.

Resources