How can I respond to touch events on a UITextField? - ios

I want to respond to touch events (specifically UIControlEventTouchUpInside and UIControlEventTouchUpOutside) on a UITextField, but the only event that comes through is UIControlEventTouchCancel.
I can't use UIControlEventEditingDidBegin, because it only occurs when field gains focus, which is not what I'm trying to detect. I want to know when the user lifts their finger, and whether it was inside or outside the text field, regardless of current focus state.
Does anyone know a way to accomplish this?
To be clear I've tried using addTarget:action:forControlEvents and it does not work. The touch up event is prevented by the cancel. I want a way to detect the touch up before the UITextField consumes it and produces the Cancel event.
Also, I'd like to avoid creating any extra views if possible.

Well a round about way of doing it would be to create a UIButton with the fill set to clear to cover all of the space, and a UITextField on top of it. If the user touches in the text field, the text field should send an event, and if the user touches outside of the text field, the UIButton will send the event.

Good answer here:
Change default touch events for UITextField
Use the text field's delegate and do what you want to do in textFieldShouldBeginEditing:
You can return NO to prevent the default behavior.

You can create category for the UiView and override the touchesBegan meathod as follows.
It is working fine for me.And it is centralize solution for this problem.
#import "UIView+Keyboard.h"
#implementation UIView(Keyboard)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.window endEditing:true];
[super touchesBegan:touches withEvent:event];
}
#end

I have faced this problem just today. My goal was to change textfield's background color on touch down and revert it back on touch up/cancel.
It is weird that touch down is sent to target from UITextField, but touch up inside, touch up outside and even touch cancel are not.
I found the solution. It is simple. Just create a subclass of UITextField and override touchesEnded: and touchesCancelled:.
#interface TextField : UITextField
#end
#implementation CuteTextField
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesEnded:touches withEvent:event];
self.backgroundColor=GetDefaultBackgroundColor();
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesCancelled:touches withEvent:event];
self.backgroundColor=GetDefaultBackgroundColor();
}
#end

Add a target for your desired event programatically, or, use IB.
UITextField * touchyTextField = [[UITextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 21.0)];
[touchyTextField addTarget:self
action:#selector(yourDesiredMethod)
forControlEvents:UIControlEventTouchUpInside];

Related

Dismiss Keyboard in one of multiple views

I have a 3 views in one screen with different components such as tables and buttons, when I try to dismiss the keyboard with the code below it doesn't dismiss when I click on the view that contains the textView( and that’s what I need! ) . However when I click on one of the other views it dismisses.
The following image shows the views that I have and the last view inside the scrollview is the one that contains the textView.
How can I get the keyboard to dismiss despite the view that is taped.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
if(touch.phase == UITouchPhaseBegan) {
[aTextField resignFirstResponder];
}
}
also this code fails to dismiss:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[[self view] endEditing:TRUE];
}
You need to add tap gesture in the views which you want to click:
For you are using two UIViews with name Your_view1 and Your_view2, and textfields are txt1 and txt2. The code is below:
Your_view1:
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapGesture:)];
singleTapGesture.numberOfTapsRequired = 1;
[Your_view1 addGestureRecognizer:singleTapGesture];
Your_view2:
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapGesture:)];
singleTapGesture.numberOfTapsRequired = 1;
[Your_view2 addGestureRecognizer:singleTapGesture];
And the delegate is:
-(void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTapGesture{
[txt1 resignFirstResponder];
[txt2 resignFirstResponder];
}
If your textView is editable , tapping on that, will make it first responder. That’s why keyboard is not dismissing. If you don’t need edit textView, set it’s editable attribute to false
You should take a look at Apple document for UIScrollView.
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement.
That's why touchesBegan doesn't work when you click inside your scroll view.
A easy way to fix it, add a UITapGestureRecognizer for your viewController.view.
#property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer;
- (void)viewDidLoad {
[super viewDidLoad];
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self.aTextField action:#selector(resignFirstResponder)];
[self.view addGestureRecognizer:self.tapGestureRecognizer];
}
managing keyboard get most of our development time luckly we have an alternative solution with this library
No need to write multiple line of code in every viewcontroller simply add one line of code in your didFinishLaunchingWithOptions
IQKeyboardManager.sharedManager().enable = true
This will save time
Hope this will help you

Detecting a touch anywhere on the screen

I am wanting to know when a user has touched anywhere on the screen of my app.
I have looked into using -(UIResponder *)nextResponder but unfortunately this will not work, as I am also reloaded a table automatically, so this gets trigged when that occurs.
I have also tried a gesture recognizer, with the following code. But this will only recognise touches on the view. Where as I have many buttons the user will be using to operate the app. I would like to avoid adding a gesture recogniser or code for this in every button and segment control I have on the screen
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnView:)];
[self.mainView addGestureRecognizer:tap];
- (void)tapOnView:(UITapGestureRecognizer *)sender
{
//do something
}
I have also tried -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event , but this has the same issue as the gesture recognizer.
I was wondering if there is any way I could achieve this task. I was hoping that I may be able to recognise the type of event from within the nextResponder, and then I could detect if it is button for example.
EDIT: The reason I am working on this is that my app needs to stay active and the screen cannot be locked (so I have disabled screen locking). To avoid excessive use of power, I need to dim the screen, but then return the brightness back to the original level once the app is touched. I need this feature to only occur on 1 of my viewcontrollers.
As mentioned by Ian MacDonald, using hitTest:: is a great solution to detect user interaction on an app wide scale, including when buttons, textfields, etc, are selected.
My solution was to subclass UIWindow and implement the hitTest method.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// do your stuff here
// return nil if you want to prevent interaction with UI elements
return [super hitTest:point withEvent:event];
}
You could attach your UITapGestureRecognizer to your [[UIApplication sharedApplication] keyWindow].
Alternatively, you could override hitTest: of your root UIView.
Is there a particular task you are hoping to accomplish? There may be a better way than assigning an "anywhere" gesture.
Edit: Use hitTest:.
#interface PassthroughView : UIView
#property (readonly) id target;
#property (readonly) SEL selector;
#end
#implementation PassthroughView
- (void)setTarget:(id)target selector:(SEL)selector {
_target = target;
_selector = selector;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
[_target performSelector:_selector];
return nil;
}
#end
#implementation YourUIViewController {
PassthroughView *anytouchView;
}
- (void)viewDidLoad {
// Add this at the end so it's above all other views.
anytouchView = [[PassthroughView alloc] initWithFrame:self.view.bounds];
[anytouchView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[anytouchView setTarget:self selector:#selector(undim)];
[anytouchView setHidden:YES];
[self.view addSubview:anytouchView];
}
- (void)undim {
[anytouchView setHidden:YES];
}
- (void)dim {
[anytouchView setHidden:NO];
}
#end
Your edit adds more clarity to your question.
The reason I am working on this is that my app needs to stay active
and the screen cannot be locked (so I have disabled screen locking).
To avoid excessive use of power, I need to dim the screen, but then
return the brightness back to the original level once the app is
touched.
Since you are controlling the screen brightness, you can add one transparent view controller before dimming screen on top of your root controller which does only one job, listen to tap using Tap gesture. And on tap you can dismiss the view controller and adjust brightness to previous state.
By doing so you dont have to worry about buttons being clicked as they will be below the transparent view controller. Since its a whole new view controller sitting on top of stack you dont have to modify your existing code as well.
Ok I have had a similar problem before.
As I remember I subclassed the UIWindow for full screen detection and made it First responder.
Than I overridden the touch to handle from subclasses.
You can also use code to identify the control that is been touched.
#import <QuartzCore/QuartzCore.h>
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setMultipleTouchEnabled:YES];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// Enumerate over all the touches
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
// Get a single touch and it's location
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.view];
...
}];
}
To disable the locking of screen I used below code:
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
I used following functions to dim or increase the screen brightness
[[UIScreen mainScreen] setBrightness:0.0f]; //and
[[UIScreen mainScreen] setBrightness:1.0f];

hitTest to dismiss keyboard causing weird behaviour

I've googled on how to dismiss keyboard when touching a blank area in UITableView in iOS, and there're several ways to solve this. Like using delegate, UITapGestureRecognizer, - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event and - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event.
I decide to take hitTest by subclassing the corresponding UIView class and override this method like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *result = [super hitTest:point withEvent:event];
[self endEditing:YES];
return result;
}
This really works, will dismiss the virtual keyboard when I touch / scroll / swipe / pinch ... somewhere else, but another problem shows up.
The keyboard is active or shown when I touch one UITextField object, then I touch the same UITextField object, here is the problem, the keyboard try to dismiss but not completely, in the middle of somewhere it begin to show up, doing this kind of weird animation. Most of the case in our APPs, the keyboard should stay still when we touch the same UITextField object. Is there a nice and simple way to solve this problem?
Solved:
Finally, I figure it out myself. Thanks to #Wain, thanks to #Wain's hint. I do check the result before I invoke [self endEditing:YES];. Here is the modified code:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *result = [super hitTest:point withEvent:event];
// You can change the following condition to meet your own needs
if (![result isMemberOfClass:[UITextField class]] && ![result isMemberOfClass:[UITextView class]]) {
[self endEditing:YES];
}
return result;
}
As per #iBCode answer, if I select same text field multiple times then keyboard automatically hide and show immediately, to avoid that I have added one more condition and added code below in swift languagge
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let resultView = super.hitTest(point, with: event) {
if resultView.isMember(of: UITextField.self) ||
resultView.isKind(of: UITextField.self) ||
resultView.isMember(of: UITextView.self) ||
resultView.isKind(of: UITextView.self) {
return resultView
}
if !resultView.isMember(of: UITextField.self) && !resultView.isMember(of: UITextView.self) {
endEditing(true)
}
return resultView
}
return nil
}
Check the class of the result so that you can limit when you end editing. If the hit target was an editable class (like a text field, or a switch) then don't end the editing session.

Problems getting touch event on UIImageView in iOS 5

I have problems detecting touch events on my UIImageView. I have sub classed UIImageView and overrided canBecomeFirstResponder, touchedBegan and touchesEnded:
#implementation ImageViewNext
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.userInteractionEnabled = YES;
}
return self;
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches Began event");
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches Ended");
}
I have ensured that all the parent views have User Interaction Enabled.
But I don't get my events?
I have tried to find the solution here but without any luck. But in my search I found that some people use "UIGestureRecognizer" to get the same effect. So as a side question, what is the "right" solution use the override method or use UIGestureRecognizer? I don't like the solution where a normal button is given an image, so please keep those post away :)
Are you sure your initWithFrame: is called? UIViews have two designated initializers, initWithFrame: and initWithCoder, depending on how they are created.
I don't know why you're not getting the touch events, but to answer the other part of your question using a UIGestureRecognizer will significantly simplify your touch handling for any of the gestures for which there's a recognizer available. It's much simpler to use and you'll need less code.
Furthermore, if in the future you need to negotiate between different touch events (is it a tap of a drag type questions) then all that logic is handled trivially.

how to detect touchesbegan touchesended for UIBarButtonItem?

I am using an UIBarButtonItem in order to trigger events. I use the integrated InterfaceBuider in xcode4 to create my UIBarButtonItem and then I connected the button to a method in my view controller. The method looks like this:
-(IBAction)NoteOnOff:(id)sender
{
UIButton *button = (UIButton*)sender;
/* now perform action */
}
Now I want to also detect fingerdown/fingerup because I want to trigger noteon/noteoff types of event for a midi synth kind of app.
1- Is there a way to detect if sender has been pressed down or up in the above method?
2- I tried to subclass UIBarButtonItem and implement touchesBegan and touchesEnded this way:
#interface myUIBarButtonItem : UIBarButtonItem {
}
#end
#implementation myUIBarButtonItem
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touchesBegan");
NSLog(#"touches=%#,event=%#",touches,event);
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touchesEnded");
NSLog(#"touches=%#,event=%#",touches,event);
}
#end
Then I changed the class of the UIBarButtonItem to myUIBarButtonItem in the interface editor but no luck. Is that the correct way of using my customized class in the interface editor?
3- I read somewhere that UIBarButtonItem does not inherit from UIResponder so they can't intercept touchesbegan/touchesended events. If that is the case then what is the proper way to be able to detect touches down and touches up events? I am mostly a C/C++ programmer and my knowledge is pretty limited about objective C and the iphone environement. I only know how to use the UI editor and I do not know how create custom UIs and use them without this editor yet.
Bottom line is: what is the simplest way to detect touchdown/touchup with the lest latency possible?
Any pointers to tutorial or docs is also welcome.
Thanks,
Baba
You can do it like this (no need to subclass UIBarButtonItem):
[button addTarget:self action:#selector(touchUp:)
forControlEvents:UIControlEventTouchUpInside];
[button addTarget:self action:#selector(touchDown:)
forControlEvents:UIControlEventTouchDown];
- (void) touchUp:(id)sender {
}
- (void) touchDown:(id)sender {
}

Resources