how to detect touchesbegan touchesended for UIBarButtonItem? - ios

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 {
}

Related

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];

how to make an object move by touching it in iOS

I am a newbie in Objective-C and trying to make a simple app in which when you touch the object it will move randomly for a while and stops. then you need to touch it again so it will move again and stop after a while.
I have searched for touch method and some tutorials, but the problem is I don't know how to start. It is like I need a function to move the object and one to touch it, but I don't know how to connect them and use them.
here is a tutorial which helped me a lot to get a view of functionality and it actually function in opposite way of my app. but still I can not start programming on my own.
http://xcodenoobies.blogspot.se/2010/11/under-construction.html
Any help would be appreciated, regarding how to program my logic and how to find the right methods and how to find the type of variables I need.
Step 1: Put this code in your ViewDidLoad Method, in which i have created some UIImageView and add it to View Randomly
[self.view setTag:1];
for(int i=0;i<4;i++)
{
int x = arc4random()%300;
int y = arc4random()%400;
#warning set Image here
UIImageView *imgview = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"someImage.png"]];
[imgview setFrame:CGRectMake(x, y, 25, 25)];
[imgview setUserInteractionEnabled:YES];
[self.view addSubview:imgview];
}
Step 2 : Define touchBegan Method to handle touch and move objects around the view, we have set Tag = 1 for ViewController ,because we dont want to move our mainview, only subviews will be moved
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if([[touch view] tag] != 1)
{
[UIView animateWithDuration:0.25f animations:^{
int x = arc4random()%300;
int y = arc4random()%400;
[[touch view] setCenter:CGPointMake(x, y)];
}];
}
}
What you need is to add a gesture recognizer to the view you want to be able to touch:
// In a view controller or in a UIView subclass
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self addGestureRecognizer:tapGestureRecognizer];
// Or [self.view addGestureRecognizer:tapGestureRecognizer];
- (void)handleTap:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded) {
// Animate the view, move it around for a while etc.
// For animating a view use animateWithDuration:animations:
}
}
If I get you correctly I would say that the easiest way to achieve what you request is to create a UIButton in Interface Builder and connect it to an IBAction which moves it to a random spot.. You can then add a custom graphic to the button..
Create a public method in your ViewController with return type IBAction
Create a button in IB and connect its "Touch Up Inside" outlet to your IBAction
I your IBAction method, generate a random x and y coordinate within the screens bounds and animate the movement to this point.
I will/can not go into details on the specific code since it would take way to much space.
Note that your question is very open and vague which is not considered good style on StackOverflow. Also, you might wan't to save stuff like animations until you are a bit more experienced with iOS and Objective-C
-V
Actually Apple made a demo for this.
http://developer.apple.com/library/ios/#samplecode/MoveMe/Introduction/Intro.html
You can try to modify this code to your needs. And the actual functions you where looking for where:
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
If this is the answer you where looking for please click "answered" so this question can be considered as closed :-).
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
is a method called when user touches the view. if you override this in mainview (big view) you will have to find if touched point is where the object is with some helper method as described in your link. else you can override your object's class & implement the method so you dont have to explicitly find if touched point is on the object or not.
for your requirement. i'd say override the uiimageview & inside that put the touchesbegan implementation it will just work fine.
.h file
#interface StarView : UIImageView
#end
.m file
#implementation StarView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// your code in the link with the related methods
Destination = CGPointMake(arc4random() % 320, arc4random() % 480);
// calculate steps based on speed specified and distance between the current location of the sprite and the new destination
xamt = ((Destination.x - self.center.x) / speed);
yamt = ((Destination.y - self.center.y) / speed);
// ask timer to execute moveBall repeatedly each 0.2 seconds. Execute the timer.
mainTimer = [NSTimer scheduledTimerWithTimeInterval:(0.02) target:self selector:#selector(moveBall) userInfo:nil repeats: NO];
}
dont forget to copy the moveBall method after this.
In your mainview just make an instance of StarView & add to mainview

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 can I respond to touch events on a UITextField?

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];

How to hide number pad using background Tap?

I study iOS SDK using this book and in 4 Chapter, when it tells you how to hide number pad with background tap I have a problem. I do that author says but nothing happened. How I can do this?
What book's author says:
1)Create new method backgroundTap in ViewController.h
- (IBAction)touchBackground:(id)sender;
2)Add method in ViewController.m
-(void)touchBackground:(id)sender{
[nameField resignFirstResponder];
[numberField resignFirstResponder];}
3)Change class identity of View object in Interface Builder(as I understand in Xcode 4 its Control) from UIView to UIControl
4)Connect TouchDown method in events list with File's Owner and check method backgroundTap.
This is my code samples
ViewController.h
ViewController.m
P.S. I'm sorry for my English, I translated all information from the book to english because I have russian translate.
I think you might make this simpler by just implementing a method like this:
- (IBAction)backgroundTouched:(id)sender {
[self.view endEditing:YES];
}
make the background view UIControl instead of UIView in interface builder and hook the touches up event to this method.
Have a look at this small example project. I have done exactly this in the secondViewController where touching the background dismissed the keyboard. You can also see how I have changed the background to a UIControl in secondViewController.xib
Something you might want to try, without changing the background view, is:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[nameField resignFirstResponder];
[numberField resignFirstResponder];
}
You may have done Step 4 ("Connect TouchDown method in events list with File's Owner and check method backgroundTap") incorrectly. Include an NSLog in your touchBackground method to check whether the method is being called.
-(void)touchBackground:(id)sender{
[nameField resignFirstResponder];
[numberField resignFirstResponder];
NSLog(#"touchBackground was called");
}
If the message ("touchBackground was called") doesn't show up in your console, then you known touchBackground is not being called.

Resources