I'm using a UIDatePicker in a modal view, to select a date. I found that if the view is closed while the date picker is still rolling, I got a EXC_BAD_ACCESS.
How can I detect of the date picker is rolling when the user tab a button?
I had two ideas:
1) detect if the value has changed with this:
[myDatePicker addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventValueChanged];
But I would need to be able to detect if the value WILL change.
2) check if the date is valid, but when the date picker is rolling, the previous date is returned, and of course is valid.
Any ideas?
In order to avoid this crash, you don't need to try to predict when the picker will try to send an action method call to your class, instead, you simply remove the picker's event action in your ViewController's dealloc routine.
EDIT: oops, as was pointed out in a comment, the UIDatePicker doesn't have a delegate. But you can assign it action events. So instead, any actions set pointing to the object that is being dealloc'd should be removed.
- (void dealloc
{
[myPicker removeTarget:self action:... forControlEvents:...];
[myPicker release];
[super dealloc];
}
I know that I am quite late, but I have just created a custom UIDatePicker subclass with a delegate:
This is the delegate:
// In TDDatePickerDelegate.h
#import <Foundation/Foundation.h>
#import "TDDatePicker.h"
#class TDDatePicker;
#protocol TDDatePickerDelegate
#required
- (void)datePickerChanged:(TDDatePicker *)datePicker newDate:(NSDate *)newDate;
#end
And this is the subclass
// In TDDatePicker.h
#import <UIKit/UIKit.h>
#import "TDDatePickerDelegate.h"
#interface TDDatePicker : UIDatePicker
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated;
- (void)dateChanged;
#property (nonatomic, assign) IBOutlet id <TDDatePickerDelegate> delegate;
#end
// In TDDatePicker.m
#import "TDDatePicker.h"
#implementation TDDatePicker
#synthesize delegate;
- (id)init {
self = [super init];
if (self) {
[self addTarget:self action:#selector(dateChanged) forControlEvents:UIControlEventValueChanged];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addTarget:self action:#selector(dateChanged) forControlEvents:UIControlEventValueChanged];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self addTarget:self action:#selector(dateChanged) forControlEvents:UIControlEventValueChanged];
}
return self;
}
- (void)dateChanged {
[delegate datePickerChanged:self newDate:self.date];
}
- (void)dealloc {
[self removeTarget:self action:#selector(dateChanged) forControlEvents:UIControlEventValueChanged];
}
#end
Objective-C:
[datePicker addTarget:self action:#selector(isRolled) forControlEvents:UIControlEventValueChanged];
- (void)isRolled {
NSLog(#"Date Picker has been rolled.");
}
Swift:
datePicker.addTarget(self, action: "isRolled", forControlEvents: .ValueChanged)
func isRolled() {
print("Date Picker has been rolled.")
}
Officially, you can't retrieve the state of the picker in these terms with just Apple's official UIDatePicker methods.
If there are solutions out there, however, I'd be curious to see them.
There is a trick to detect this but there is no delegate method/ property to detect if its scrolling or not
take a property as isScrolling
set isScrolling to true in func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? or equivalent method
set isScrolling to true in func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
enable the close button for modal view only if picker has reached to a finite state that is scrolling is disabled
hope this helps
Related
I have a custom UITableViewCell which has a text field inside it. I have created it using IB and have a custom class with it.
Now, my issue is that I want to setup the text field so that during text entry if the user clicks outside the text field (without hitting the return/done key on the keypad), the field resigns first responder. I understand, that do that I need to handle the Touch Up Inside Event. However my tableview class never receives this event even though I have done the connections. I am assuming that its because its not subclass of UIcontrol, which I cant make it as it needs to be UITableViewCel.
So whats the solution?? How do I receive these events??
Header File:
#import <UIKit/UIKit.h>
#interface MMSingleTextFieldCell : UITableViewCell <UITextFieldDelegate>
// Properties
#property (weak, nonatomic) IBOutlet UITextField *singleTextField;
// Methods
- (IBAction)eventTouchUpOutside:(id)sender;
- (IBAction)eventTouchUpInside:(id)sender;
#end
Class File:
#import "MMSingleTextFieldCell.h"
#implementation MMSingleTextFieldCell
#synthesize singleTextField;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (IBAction)eventTouchUpOutside:(id)sender {
[singleTextField resignFirstResponder];
}
- (IBAction)eventTouchUpInside:(id)sender {
[singleTextField resignFirstResponder];
}
I have just recently open sourced a project on Github that should make this all relatively easy to do. It includes a class that can be easily inserted into a cell and a sample project demonstrating its capabilities.
If you look in RootTableViewController's viewDidLoadMethod you will see that I am adding a gesture recognizer:
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissKeyboard)];
_tapGestureRecognizer.delegate = self;
_tapGestureRecognizer.cancelsTouchesInView = NO;
[self.tableView addGestureRecognizer:_tapGestureRecognizer];
Add the dismiss keyboard method:
- (void)dismissKeyboard {
[_textField resignFirstResponder];
}
Add a gesture recognizer callback (in RootTableViewController):
//see: http://stackoverflow.com/questions/7195661/why-is-uigesturerecognizer-being-called-on-my-textfield-clear-button
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if([touch.view isKindOfClass:[UITextField class]] ||
[touch.view isKindOfClass:[UIButton class]])
{
return NO;
}
return YES;
}
Of course, this means you have to make RootTableViewController adhere to the UIGestureRecognizerDelegate protocol (in the header file):
#interface RootTableViewController : UITableViewController<SATextFieldDelegate, UIGestureRecognizerDelegate>
If you want the user to scroll the table view and dismiss the keyboard implement the following table view delegate callback:
- (void)scrollViewWillBeginDragging:(UIScrollView *)activeScrollView {
if (_textField.isFirstResponder) {
[self dismissKeyboard];
}
}
I believe this is the function you want.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if (_textField.isFirstResponder) {
[self dismissKeyboard];
}
}
Try this:
1) Implement the UIGestureRecognizerDelegate protocol
2) In viewDidLoad for example, create the following
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(hideKeyboard:)];
tap.delegate = self;
[self.view addGestureRecognizer:tap];
3) Now, implement the method from protocol from 1
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
// Use this for allow some control to receive his natural tap event. You can use tap.cancelsTouchesInView = NO; in the 2 step instead of this, try both of then and choose on your own.
if (touch.view == <some control>) {
//NSLog(#"NO");
return NO;
}
//NSLog(#"YES");
return YES;
}
4) finally, implement the callback for tap
-(void) hideKeyboard:(id)sender{
if (<your edit text>.isEditing) {
[<your edit text> resignFirstResponder];
}
}
I hope this will help, or at least point you to the right direction
I have already researched this topic to death and found people posting the exact same question on a number of websites including right here in stackoverflow.
I have tried all of the suggestions but have been unable to get the UIDatePicker to actually display. It doesn't seem to matter what approach I take. I've tried using the inheritance model where I subclass the UITextBox control and override it's default methods in order to display the UIDatePicker and then I make sure that in StoryBoard I set the class of my UITextView control to be that custom class. I've tried programmatically generating the UIDatePicker and also tried dragging it onto the view in StoryBoard. When I try the programmatic approach nothing is displayed and when I try dragging it onto the StoryBoard it ALWAYS displays. When I set it's attribute to "hidden" it hides but then I can't get it to show even when I try to add code to the textboxDidBeginEditing method that should unhide it.
I've made sure to set the UITextView's inputView property equal to my UIDatePicker control.
Nothing works! I don't understand why Apple didn't just make the UIDatePicker one of the default options in the drop down list in the Attributes Inspector for the UITextView control. That would have made this so much easier.
Below is some of the code from my implementation class file.
#import "AddTasksViewController.h"
#implementation AddTasksViewController
#synthesize dueDate;
#synthesize description;
#synthesize shortTitle;
#synthesize datePicker;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.dueDate.inputView = datePicker;
}
return self;
}
- (IBAction)dueDateDidBeginEditing:(UITextView *)textField
{
[textField resignFirstResponder];
[datePicker setFrame:CGRectMake(0,200,320,120)];
[datePicker addTarget:self action:#selector(done) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:datePicker];
}
Here is what my header file looks like...
#import <UIKit/UIKit.h>
#interface AddTasksViewController : UIViewController
#property (nonatomic, copy) IBOutlet UITextView *dueDate;
#property (nonatomic, copy) IBOutlet UITextView *description;
#property (nonatomic, copy) IBOutlet UITextView *shortTitle;
#property (nonatomic, retain) IBOutlet UIDatePicker *datePicker;
- (IBAction)doneEditing:(id)sender;
- (IBAction)dateChanged:(id)sender;
- (IBAction)dueDateDidBeginEditing:(UITextView *)textField;
#end
As I've already mentioned, the other approach that I took (i.e. subclassing UITextView) didn't work either. I copied the code provided here exactly as it was show. I then added the custom classes called "DateField" to my project (both the header and implementation files) and then replaced the UITextBox declaration for the dueDate with DateField in both my AddTaskViewController.h and AddTaskViewController.m files and made sure that the StoryBoard references were updated to by selecting DateField for the control's class in the Identity Inspector.
No matter what I do I cannot get the UIDatePicker to display upon clicking on the UITextView. When I set a breakpoint it does hit the dueDateDidBeginEditing method so I know that something is being triggered. The problem is that I don't understand why a subView is not being showing with the UIDatePicker.
Any help would be greatly appreciated as this seemingly straightforward task seems to be taking much, much longer than it should. I could do this sort of stuff with my eyes closed and hands tied behind my back in Java and C# yet everything is so unnecessarily complicated when it comes to Objective-C. The syntax really irritates me.
So many square brackets!!! Arghhhh!!!
Showing a UIDatePicker for the inputView of a UITextField is relatively hard compared to showing a keyboard, but relatively easy compared to the trouble you've had.
One of the first problems I noticed with your code, and it's a very common problem with those new to iOS/Mac development, is that you are attempting to set the property of an object that doesn't exist yet. Putting "self.dueDate.inputView = datePicker;" in your initWithNibName:bundle: will not work because the view has not yet loaded, and dueDate is part of your view. In objective-c an object is instantiated with alloc & init. Init is the first real method call to any object. At this point in your view controller's life the view has not been created yet. The method call where self.dueDate.inputView = datePicker; belongs is in the viewDidLoad method. It is called exactly when it sounds like it's called, and your dueDate property will be properly loaded at that point. There is no need to use a custom subclass of UITextField to display a date picking view for for your text field. Here is a very basic example of a custom input view class:
ExampleBasicDateInputView.h:
#import <UIKit/UIKit.h>
#interface ExampleBasicDateInputView : UIView
#property (strong,nonatomic) UIDatePicker *datePicker;
#end
ExampleBasicDateInputView.m:
#import "ExampleBasicDateInputView.h"
#implementation ExampleBasicDateInputView{
UITextField *_inputField; // ivar to store the textfield currently being edited
}
#synthesize datePicker = _datePicker;
// TARGET METHODS
-(void)pickerValueChanged:(UIDatePicker *)picker{
_inputField.text = self.datePicker.date.description; // set text to date description
}
-(void)viewDoubleTapped:(UITapGestureRecognizer *)tapGR{
[_inputField resignFirstResponder]; // upon double-tap dismiss picker
}
-(void)textFieldBeganEditing:(NSNotification *)note{
_inputField = note.object; // set ivar to current first responder
}
-(void)textFieldEndedEditing:(NSNotification *)note{
_inputField = nil; // the first responder ended editing CRITICAL:avoids retain cycle
}
// INITI METHODS
-(void)initializationCodeMethod{
_datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, 0, 320, 0)];// All pickers have preset height
self.bounds = _datePicker.frame; // Make our view same size as picker
[self addSubview:_datePicker];
[_datePicker addTarget:self action:#selector(pickerValueChanged:) forControlEvents:UIControlEventValueChanged]; // register to be notified when the value changes
// As an example we'll use a tap gesture recognizer to dismiss on a double-tap
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewDoubleTapped:)];
tapGR.numberOfTapsRequired = 2; // Limit to double-taps
[self addGestureRecognizer:tapGR];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(textFieldBeganEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil]; // Ask to be informed when any textfield begins editing
[center addObserver:self selector:#selector(textFieldEndedEditing:) name:UITextFieldTextDidEndEditingNotification object:nil]; // Ask to be informed when any textfield ends editing
}
-(id)init{
if ((self = [super init])){
[self initializationCodeMethod];
}
return self;
}
-(id)initWithFrame:(CGRect)frame{
if ((self = [super initWithFrame:frame])){
[self initializationCodeMethod];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
[self initializationCodeMethod];
}
return self;
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidEndEditingNotification object:nil];
}
#end
Then in view did load you would use this class like this:
ExampleBasicDateInputView *dateEntryView = [[ExampleBasicDateInputView alloc] init];
self.dueDate.inputView = datePickerView;
And as you see in the .h file we've exposed the date picker instance as a property so you can set the style. etc. like so:
dateEntryView.datePicker.datePickerMode = UIDatePickerModeDate;
Obviously this is a very basic example, but I think it shows what can be done.
I had the same issue using Storyboard. Here is how I resolved it:
Overview:
I alloc and initialize my UIDatePicker property in viewDidLoad:. I then set the inputView property of my textfield to my datepicker property.
Here is my code:
In TripViewController.h:
#property (nonatomic,strong) IBOutlet UIDatePicker *datePicker;
In TripViewController.m:
#synthesize datePicker;
...
-(void)viewDidLoad {
...
self.datePicker = [[UIDatePicker alloc]init];
[self.datePicker addTarget:self action:#selector(dateChanged) forControlEvents:UIControlEventValueChanged];
self.dateField.inputView = self.datePicker;
}
I then implement my method, dateChanged so that whenever the date picker wheel stops moving, my textfield is updated.
- (void)dateChanged
{
NSDate *date = self.datePicker.date;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
[dateFormat setDateStyle:NSDateFormatterMediumStyle];
self.dateField.text = [dateFormat stringFromDate:date];
}
add UITextField's delegate in .h file to call this function when touched. then use the text field's tag value to determine what you want to do when the field is selected.
-(BOOL) textFieldShouldBeginEditing:(UITextField *) textField
{
activeTextField = textField;
if (textField.tag == 1)
{
self.datePicker.datePickerMode = UIDatePickerModeDate;
self.isDatePicker = TRUE;
self.tempTextField = textField;
[self showPicker];
return NO;
}
else if (textField.tag == 2)
{
self.datePicker.datePickerMode = UIDatePickerModeTime;
self.isDatePicker = TRUE;
self.tempTextField = textField;
[self showPicker];
return NO;
}
else if (textField.tag == 3)
{
self.isDatePicker = FALSE;
self.tempTextField = textField;
[self showPicker];
return NO;
}
else
{
self.tempTextField = textField;
return YES;
}
}
It is very easy. Defines delegate TextField (UITextFieldDelegate) in the .h in the viewDidLoad .m
myTextField.setDelegate = self;
In the same viewDidLoad associates the datePicker to myTextField.
myTextField.inputView = myDatePicker;
You can also do anime, but this is the easiest way.
I am adding a uipickerview as the subview of the main view. To dismiss the pickerview on tapping the backgroud view, i am adding a UITapGestureRecognizer to the main view.
I am using the following code to add the GestureRecognizer for main view
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
gestureRecognizer.numberOfTapsRequired=1;
gestureRecognizer.numberOfTouchesRequired=1;
gestureRecognizer.delegate = self;
[self.view addGestureRecognizer:gestureRecognizer];
[gestureRecognizer release];
In the handleSingleTap method i am dismissing the pickerview.
But the problem is handleSingleTap is also called when I tap inside the pickerview. To avoid it i used the following delegate method of UIGestureRecognizer
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
/*
*If the tap is inside a button return NO, to ensure the button click is detected.
*/
if ([touch.view isKindOfClass:[UIButton class]]){
return FALSE;
}else if([touch.view isKindOfClass:[UIPickerView class]]) {
return FALSE;
}
return TRUE;
}
It is working for button,But is not working for UIPickerView. Can anyone help me with this?
I have coded up a solution to your particular requirement.
first, i implemented your code as you have described and observed the same problem you reported - spurious tap events being sent to tap handler, when you tapped on anything, including a UIButton.
this told me that the UITapGestureRecogniser was "stealing" the touches that should have gone to the UIButton, so i decided the simplest, most pragmatic solution was to use that feature to my advantage, and so i assigned a UITapGestureRecogniser to both the pickerview and the button also. the taps for the pickerview we just discard, the others we parse and pass on to the button's tap handler.
note - for expedience i assigned the pickerview's datasource and delegate in the xib. you will need to do that also, or set it in code.
header
//
// ViewController.h
// stackExchangeDemo
//
// Created by unsynchronized on 18/01/12.
// released to public domain via http://stackoverflow.com/a/8908028/830899
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
<UIPickerViewDelegate, UIPickerViewDataSource>
{
UIButton *btn1;
UIPickerView *picker1;
}
#property (retain, nonatomic) IBOutlet UIButton *btn1;
#property (retain, nonatomic) IBOutlet UIPickerView *picker1;
#end
implementation
//
// ViewController.m
// stackExchangeDemo
//
// Created by unsynchronized on 18/01/12.
// released to public domain via http://stackoverflow.com/a/8908028/830899
//
#import "ViewController.h"
#implementation ViewController
#synthesize btn1;
#synthesize picker1;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
-(void) handleSingleTap:(UITapGestureRecognizer *) tapper {
if (tapper.state == UIGestureRecognizerStateEnded) {
NSLog(#"%#",NSStringFromSelector(_cmd));
}
}
- (IBAction)handleButtonTap:(id)sender {
NSLog(#"%#",NSStringFromSelector(_cmd));
}
-(void) handleButtonTapGesture:(UITapGestureRecognizer *) tapper {
// call the buttons event handler
UIControlEvents eventsToHandle = UIControlEventTouchUpInside;
if (tapper.state == UIGestureRecognizerStateEnded) {
UIButton *btn = (UIButton *) tapper.view;
for (NSString *selName in [btn actionsForTarget:self forControlEvent:eventsToHandle]) {
SEL action = NSSelectorFromString(selName);
if (action) {
[self performSelector:action withObject:btn1];
break;
}
};
}
}
-(void) handleDummyTap:(UITapGestureRecognizer *) tapper {
// silently ignore the tap event for this view.
}
-(void) setupTap:(UIView *) view action:(SEL)action {
// assign custom tap event handler for given view.
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:action];
[view addGestureRecognizer:gestureRecognizer];
[gestureRecognizer release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setupTap:self.view action:#selector(handleSingleTap:)];
[self setupTap:picker1 action:#selector(handleDummyTap:)];
[self setupTap:btn1 action:#selector(handleButtonTapGesture:)];
}
- (void)viewDidUnload
{
[self setBtn1:nil];
[self setPicker1:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#pragma mark #protocol UIPickerViewDataSource<NSObject>
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return 1;
}
#pragma mark #protocol UIPickerViewDelegate<NSObject>
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [#"so long and thanks for all the fish".copy autorelease ];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
NSLog(#"%#",NSStringFromSelector(_cmd));
}
- (void)dealloc {
[btn1 release];
[picker1 release];
[super dealloc];
}
#end
It is possible that the view touched (touch.view) is one of the subviews of the pickerview. I'd try testing:
[[pickerview subviews] containsObject: touch.view];
I implemented the following in Swift:
override func viewDidLoad()
{
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
func handleTapGesture(sender: AnyObject)
{
let subview = view?.hitTest(sender.locationInView(view), withEvent: nil)
if(!(subview?.isDescendantOfView(timePicker) ?? false))
{//might want to add a condition to make sure it's not your button ^^
showTimePicker(false)//method which handles showing/hiding my picker
}
}
I simply added an invisible UIControl instance behind the UIPickerView, which covers all the window, and gets all the touches behind UIPickerView. If it is touched, then both the UIPickerView and the UIControl is dismissed. (SelectButton and CancelButton are accessory buttons to UIPickerView.)
#property (strong, nonatomic) UIControl *touchRecognizer;
- (IBAction)showPicker:(id)sender {
self.touchRecognizer = [[UIControl alloc]initWithFrame:self.view.window.bounds];
[self.view.window addSubview:self.touchRecognizer];
[self.touchRecognizer addTarget:self action:#selector(touchedOutsidePicker:) forControlEvents:UIControlEventTouchUpInside];
[self.textField becomeFirstResponder];
}
- (IBAction)touchedOutsidePicker:(id)sender {
[self.touchRecognizer removeFromSuperview];
self.touchRecognizer = nil;
[self.textField resignFirstResponder];
}
-(void)selectButtonPressed:(id)sender{
[self.touchRecognizer removeFromSuperview];
self.touchRecognizer = nil;
[self.textField resignFirstResponder];
}
-(void)cancelButtonPressed:(id)sender{
[self.touchRecognizer removeFromSuperview];
self.touchRecognizer = nil;
[self.textField resignFirstResponder];
}
I am trying to use textFieldShouldBeginEditing to disable the keyboard from showing up for a custom UITextField. I'm implementing all the UITextFieldDelegate methods. However, for some reason, textFieldShouldBeginEditing actually never gets called.
The following delegate methods ALWAYS get called:
– textFieldDidBeginEditing:
– textFieldShouldEndEditing:
– textFieldDidEndEditing:
The view is structured in the following way:
UIViewController which holds a scrollview. Depending on the state of the view, this ScrollView will contain a UIView with a list of custom UITextFields.
I'm running iOS 4.3.5 (8L1) on this device.
Any ideas?
Edit; added some code snippets:
UIViewController has the following interface
#interface AViewController: UIViewController<UITextFieldDelegate>
Once the UIViewController loads, I connect all UITextFields to the view using
aSubView.aTextField.delegate = self;
(Simplified) delegate implementations located in AViewController
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return YES;
}
Custom UITextField code
Simplified implementation file --
#import "PVEntryTextField.h"
#import "EntryViewController.h"
#implementation PVEntryTextField
#synthesize isPasswordField, state, singleTap;
- (id)initWithCoder:(NSCoder *)inCoder
{
if (self = [super initWithCoder:inCoder])
{
self.font = [UIFont fontWithName:#"Helvetica-Bold" size:19];
self.textColor = [UIColor colorWithRed:51.0/255.0
green:51.0/255.0
blue:51.0/255.0
alpha:1.0];
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
}
return self;
}
- (CGRect)textRectForBounds:(CGRect)bounds
{
return CGRectMake(bounds.origin.x + 16, bounds.origin.y,
bounds.size.width - 16*2 - 10, bounds.size.height);
}
- (CGRect) editingRectForBounds:(CGRect)bounds
{
return [self textRectForBounds:bounds];
}
- (BOOL) canBecomeFirstResponder
{
return YES;
}
- (void) updateState:(int) newState
{
state = newState;
}
- (void)dealloc
{
[super dealloc];
}
#end
Is it posible that textFieldShouldBeginEditing is called by the default implementation of the method canBecomeFirstResponder?
Try implementing the method by [super canBecomeFirstResponder] or just removing it.
Have you set the UITextField delegate to "self"?
For anyone that comes here and didn't find a solution. My problem was that I created the textField in IB and then alloc one in my viewDidLoad. When I removed the instantiation, the delegate worked correctly as it was tied to the correct TF.
//I REMOVED these two lines because I created the textfield in IB
_nameTextField = [[UITextField alloc] init];
_priceTextField = [[UITextField alloc] init];
[_nameTextField setDelegate:self];
[_priceTextField setDelegate:self];
I also ran into the issue of not having textFieldShouldEndEditing or textFieldShouldReturn called. This occurred after updating my app's Storyboard.
The delegate methods were getting called when the UITextFields where part of the ViewController subclass.
But, when they were moved into a Scroll View within the ViewController, the methods were no longer called.
Setting their app delegates to self in ViewDidLoad fixed this problem.
self.emailField.delegate = self;
self.passwordField.delegate = self;
In Swift-3 following method required "_" before textField so that this delegate method will call. In my case it helps me.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
}
Dont know if is a typo error on copying the code here but the method name is incorrect:
CORRECT - textFieldShouldBeginEditing
YOURS - textFieldShoulBeginEditing (missing "d")
I am using a UITEXTVIEW. I am trying to send a resignFirstResponder when the done button is pressed. I am not sure how to trigger the method containing the resignFirstResponder line.
I have set the UITextView's delegate to the File's Owner in Interface Builder. This is my viewcontroller's header and implementation file:
#import <Foundation/Foundation.h>
#import "Question.h"
#interface CommentQuestionViewController : UIViewController {
Question *question;
IBOutlet UILabel *questionTitle;
IBOutlet UITextView *inputAnswer; //this is the textview
}
#property (nonatomic, retain) UILabel *questionTitle;
#property (nonatomic, retain) Question *question;
#property (nonatomic, retain) UITextView *inputAnswer;
- (void) addButton:(id)sender isLast:(BOOL)last;
- (void) setQuestionId:(NSString*)quId withTitle:(NSString*)quTitle number:(NSString*)quNum section:(NSString*)sId questionType:(NSString*)qt;
#end
#import "CommentQuestionViewController.h"
#implementation CommentQuestionViewController
#synthesize questionTitle, question, inputAnswer;
- (void) addButton:(id)delegate isLast:(BOOL)last{
UIBarButtonItem *anotherButton;
anotherButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStylePlain target:delegate action:#selector(finishQuestionnaire:)];
self.navigationItem.rightBarButtonItem = anotherButton;
[anotherButton release];
}
-(void)viewDidLoad{
//self.questionTitle.text = question.qTitle;
[[self questionTitle] setText:[question qTitle]];
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:#"Question %#", [question qNumber]];
//self.title = str;
[self setTitle:str];
[str release];
}
-(void) setQuestionId:(NSString*)quId withTitle:(NSString*)quTitle number:(NSString*)quNum section:(NSString*)sId questionType:(NSString*)qt{
question = [[Question alloc]init];
[question setQId:quId];
[question setQTitle:quTitle];
[question setQNumber:quNum];
[question setSectionId:sId];
[question setQType:qt];
}
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void) textViewDidEndEditing:(UITextView*)textView{
[textView resignFirstResponder];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[inputAnswer release];
[questionTitle release];
[question release];
[super dealloc];
}
You can try this:
–(BOOL)textView:(UITextView*)textView shouldChangeCharactersInRange:(NSRange)range replacementText:(NSString*)text {
if ([text isEqualToString:#"\n"]) {
[textView resignFirstResponder];
return NO;
}
else
return YES;
}
Of course, you won't be able to type actual carriage returns if you do this.
After looking at your source code, the link that I had posted is pointless. I thought you were talking about the done key on the keyboard. But now I see that you were talking about an instance UIButton on the navigation bar, right?
Your addButton:isLast: method is looking great so far, but I'd change it a bit so it adheres to the Apple HIG:
- (void)addButton:(id)delegate isLast:(BOOL)last
{
UIBarButtonItem *anotherButton;
anotherButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:delegate
action:#selector(finishQuestionnaire:)];
self.navigationItem.rightBarButtonItem = anotherButton;
[anotherButton release];
}
It's pretty much the same, except this one creates a button with the appropriate style.
Anyway, I don't see how the button is being added since addButton:isLast: is not being called in viewDidLoad:. Add the following line in your viewDidLoad:.
[self addButton:self isLast:NO];
last is redundant in your addButton:isLast: since it's not even being use so I just pass whatever (NO).
You're almost done, but if you press that button, your application would crash since finishQuestionnaire: is not implemented on self (CommentQuestionViewController). And since you want to dismiss the keyboard when the user presses your done button, that's where we will also resign your text view as first responder. Just add the following method:
- (void)finishQuestionnaire:(id)sender
{
[inputAnswer resignFirstResponder];
}
It's BOOL that's it..
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:#"\n"]) {
[textView resignFirstResponder];
return NO;
}
return YES;
}
If that doesn't work please check the following things..
Whether your textView is connected to the textView property defined in your class.. (You can see this in your xib, with an arrow icon).
2.Check whether the textView delegate is written in your .h file.
#interface YourViewController : UIViewController <UITextViewDelegate>
3.Check whether the textView's delegate is assigned.
//set this in the viewDidLoad method
self.textView.delegate = self;
Check for new line character by this method.
-(NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet
Then check the length of the text, if it is one character length, then new line character is entered, not pasted.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
BOOL hasNewLineCharacterTyped = (text.length == 1) && [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound;
if (hasNewLineCharacterTyped) {
[textView resignFirstResponder];
return NO;
}
return YES;
}