Is it possible to handle touch events in the key UIWindow in the app Delegate or anywhere else?
Any help would be appreciated please.
There is a handy catch-all method in UIWindow called sendEvent: which sees every event near the start of the event-handling pipeline. If you want to do any non-standard additional event handling, this is a good place to put it. Something like this:
- (void)sendEvent:(UIEvent *)event {
if ([self eventIsNoteworthy:event]) [self extraEventHandling:event];
[super sendEvent:event]; // Apple says you must always call this!
}
Docs: UIWindow class reference | iOS event delivery docs
This blog post also mentions how to override hitTest:withEvent: to catch some events before they're bound to the target leaf subview in the view hierarchy. You can also override that method on your UIWindow object if you want.
You will have to subclass UIWindow with your own class and override sendEvent: method. But remember that the method gets other types of events - not only touches so you have to check for event type (event.type == UIEventTypeTouches). Also since you get set of touches you might want to check which ones just began, which ones ended, moved etc. To do that you have to iterate through allTouches and check the phase property of every UITouch.
#implementation TouchWindow
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeTouches) {
for(UITouch * t in [event allTouches]) {
if(t.phase == UITouchPhaseBegan) {
/*
Paste your code here.
Inform objects that some touch has occurred.
It's your choice if you want to perform method/selector directly,
use protocols/delegates, notification center or sth else.
*/
}
}
}
[super sendEvent:event];
}
#end
Of course TouchWindow is subclass of UIWindow
#interface TouchWindow : UIWindow
#end
And you will probably have to change that class in your .xib file in XCode
UIWindow is a subclass of UIView, so you simply subclass it and use it in you AppDelegate:
self.window = [[MyWindow alloc] initWithFrame:CGRectMake(0,0,320,480)]; // Sorry for hard-coded frame size!
and in MyWindow you override -hitTest:withEvent: and/or -pointInside:withEvent:
UIWindow is a subclass of UIResponder, which has APIs for handling touch events (e.g., touchesBegan:withEvent:). It is possible then for you to subclass UIWindow, override the touch event handling APIs, and manage the touch events yourself.
Related
I want to be able to recognise ALL touches in an interface, no matter what was touched.
I've tried:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
.. but this just recognises when the user taps on something that doesn't respond to taps (uiimages for instance)
The reason i need this ability is that I want to kick in a slide show if the user doesn't touch the screen for 5 minutes, so I want to reset the timer whenever they touch. It seems wrong to put this reset code in each UI event individually.
There are several possible solutions, but as said #omz - overriding the sendEvent: it is the best one.
#interface YourWindow : UIWindow {
NSDate timeOfLastTouch;
}
#end
#implementation YourWindow
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
if( touch.phase == UITouchPhaseEnded ){
timeOfLastTouch = [NSDate date];
}
}
#end
Do not forget replace UIWindow with YourWindow.
You could subclass UIWindow and override the sendEvent: method.
you could use a tap gesture
In your interface add the UIGestureRecognizerDelegate
#interface ViewController : UIViewController <UIGestureRecognizerDelegate> {
then in your viewDidLoad add this
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapMethod)];
tapped.delegate=self;
tapped.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapped];
then do your timer code in the tapped method
-(void)tapped {
//timer code
}
Make sure you UI elements have setUserInteractionEnabled:YES
You can subclass the UIWindow and override the sendEvent: method like this:
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeTouches) {
// You got a touch, do whatever you like
};
[super sendEvent:event]; // Let the window do the propagation of the event
}
As #Alladinian said in one of the comments, iOS Reference Documentation mentions that subclassing UIApplication is the right application and thus, seems preferred to subclassing UIWindow. cf. https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html :
You might decide to subclass UIApplication to override sendEvent: or
sendAction:to:from:forEvent: to implement custom event and action
dispatching.
Good morning,
I've creates a Subclass of UIScrollView and I now want to know when the user is scrolling in my subclass. For that I implemented it like the following:
ImageScroller.h
#interface UIImageScroller : UIScrollView <UIScrollViewDelegate> {
ImageScroller.m (within the #implementation)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"did scroll");
}
The problem is, that the method scrollViewDidScroll doesn't seem to get fired.
Is there any possibility to get it to work?
I also tried to set the delegate to it self, but it doesn't work.
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
self.directionalLockEnabled = YES;
[self setDelegate:self];
}
return self;
}
I added a ScrollView to my XIB-File and set the Class if it to my ImageScroller. Also I've set the Fileowner and I'm using the UIScrollViewDelegate in the .h-File of the ViewController as well as implementing the Method scrollViewDidScroll in the .m-file.
When I set the delegate of my ImageScroller in the code of the .m-file from the XIB like
[imageScroller setDelegate:imageScroller]
the scrollViewDidScroll is fired in my ImageScroller-Subclass, but the one in my ViewController isn't fired, but I need both.
Any solutions for that?
Thanks for your answers in advance.
I ran into the same problem creating a subclass of the UIScrollView but solved it this way. As mentioned above, you can set yourself (subclass instance) as the delegate however this is what I did.
In the sub class I overrode the following methods
//Override this to detect when the user is scrolling, this is also triggered during view
//layout. Here you can check your deceleration rate and determine when the scrolling has
//grinded to a halt.
-(void)setContentOffset:(CGPoint)contentOffset
//Override this to detect when the view has been resized (just a handy override)
-(void)setFrame:(CGRect)frame;
As far as the other UIScrollViewDelegate methods, the View Controller should be responsible for handling those in my opinion.
I think you can try setting self.delegate = self; to receive events.
If anyone is looking for a Swift answer, it's as simple as this:
override var contentOffset: CGPoint {
didSet {
if contentOffset != oldValue {
//same as scrollViewDidScroll
}
}
}
i try to inspect the low-level of the uiresponder,Because i try to get to the bottom of what happen when i press a button(uibutton), if there is any other methods that called when a button is clicked or only the IBaction that i had defined.
If you want to know nearly everything that happens on the Cocoa level, create a category for UIResponder like this:
#implementation UIResponder (Inspecting)
- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(#"%# respondsToSelector: %#", [self class], NSStringFromSelector(aSelector));
return [super respondsToSelector:aSelector];
}
#end
It will print just everything that is invoked on UIResponder subclasses in runtime.
There are several ways you could get more information about that button, and the button press. First of all, if you just need the button, any time you create a method for an interface builder object, you follow this format:
-(IBAction)someMethod:(id)sender
Well, that (id)sender is actually your button. So, you can do pretty much anything you want with it, just give it a cast to stop the compiler from whining at you:
UIButton *myButton = (UIButton *)sender;
If you wanted to be super careful to make sure it actually IS a button before you cast it as such, (id basically means it could be anything) you can check any object's class with the isMemberOfClass method:
if ([sender isMemberOfClass:[UIButton class]]) {
UIButton *myButton = (UIButton *)sender;
}
If you need to know which button it is, you could, for instance, give each button a unique tag, and then access that tag like so:
int theTag = [sender tag];
From within your method.
If you need to do more, like know more about the touch itself, you can create your own subclass of UIButton, and then within interface builder, set the class of the button subclass you created to your custom subclass. Then, within that button, override (implement) any of the
UIResponder methods that deal with event handling, such as:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *aTouch = [touches anyObject];
[super touchesBegan:touches withEvent:event];
NSLog(#"Yay! A touch was down, at x: %f and y:%f", [touch locationInView].x, [touch locationInView].y);
}
For event handling, you can access the UIEvent object supplied, in this case as event, but most often, you're interested in the touches.
In my application I have a lot of little UIImageViews and a view off to the side. What I need is that view off to the side to be able to detect which image view was tapped and give me all the information about it (much like with (id)sender) as well as its coordinates. What is the best way to do this?
This is the internal class from my project. I think you get the idea.
As an option you can create protocol for the owner (or delegate).
You can obtain coords using
-[UITouch locationInView: someView]
Here is the code:
#interface _FaceSelectView : UIImageView {
#protected
VIFaceSelectVC* _owner;
}
-(id) initWithOwner:(FaceSelectVC*) owner;
#end
#implementation _FaceSelectView
-(id) initWithOwner:(FaceSelectVC*) owner {
if( self = [super init] ) {
_owner = owner;
self.userInteractionEnabled = YES;
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[_owner _touchesBeganIn: self withTouch: (UITouch*)[touches anyObject]];
}
-(void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event {
[_owner _touchesEndedIn: self withTouch: (UITouch*)[touches anyObject]];
}
-(void) touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event {
[_owner _touchesMovedIn: self withTouch: (UITouch*)[touches anyObject]];
}
#end
I would subclass UIImageView and add a new property for the target view you would like to message. Then reenable userInteractionEnabled and add a action to touchesUpInside.
In the action method of the custom subclass call a method on the target view in which you also give the object of the custom subview. Basically you use delegation and pass in the delegate call all parameters you need.
Add tag to UIImageView like myUIImageView.tag = intNumber;
and in side touchesBegan or you can call any common method for all your UIImageView and use tag to identify which view is tapped.
I am subclassing UIWindow and creating an implementation of sendEvent: as follows:
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
// Send event to UIScrollView here
}
How can I send the event to a UIScrollView. I tried calling the touchesBegan:, etc. messages, but that did not do anything.