MkMapView setRegion animation prevents touch events on Annotation Views - iphone

We have a MKmapView with a bunch of Image Annotation where each Image annotation responds to touch
by overriding these methods of AnnotationView subclass:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Our map region is updated using
[MkMapView setRegion:animated:]
whenever the new location is received and is far enough from the old location to make a difference.
What I noticed is that if we set animated flag to YES the touches on our annotation are rarely detected(probably due to the fact that main thread is busy animating between two map regions.
When we set animated flag to NO, everything is fine, but map transition may(or may not) become jerky.
The question I have is whether this is an expected behavior of animated flag of [MkMapView setRegion:animated] function or whether there is a workaround for this issue.
Thanks in advance

Typically, the mapview being animated has its userInteractionEnabled property set to no during the animation. If you need to change this behavior, you should subclass the view and override the appropriate methods.
Alternatively, you could place a transparent view over the mapview for the duration of the animation to capture specific types of actions, such as respond to a double-tap to stop the change in region.

Related

why are touches (set) are passed instead of a touch in touchesBegan?

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
why is 'touches' not just 'touch' being passed for touchesBegan/Moved/Ended?
What's the meaning of them?
That's because user may do a multiple taps (quickly) in which case you'll receive a set of multiple NSTouch objects.
A demonstration of this is the zooming effect when you double tap on a scroll (or image) view.
Because you can receive multiple touches, but they are disabled by default. In order to receive multiple touch events you must set the multipleTouchEnabled property of the corresponding view instance to YES and touch with 2 or more fingers.

touchesBegan stops working when view is moved outside its superview

I'm using [aSubview touchesBegan] to move aSubview's position around on a screen in relation to its superview. Its superview is not much larger than the subview itself. This is quite straightforward to do as the following snippet shows:
UITouch* touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:[self superview]];
self.center = touchPoint;
However once a aSubview is moved, as soon as any portion of it falls outside the bounds of its superview, touches in that section no longer register. In other words, touchesBegan no longer fire. I want touches in aSubview to register no matter where it's moved in relation to its superview.
Any thoughts?
Howard
mcpunky's answer is almost good, except you can NOT make pointInside function to always return YES. This way the view will intercept all touches.
Instead, one needs to do more fine check:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return CGRectContainsPoint(self.subviewOutsideMe.frame, point) || CGRectContainsPoint(self.bounds, point);
}
I just had this problem with subviews not receiving input because the superview was simply not sending touch events for subviews that are outside it's bounds. Also, it was crucial to keep the bounds of the superview as they were and moving subviews up the hierarchy also wasn't feasible. What did the job for me was overriding superview's
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
to always return YES. Result: pretty much nothing changed in superview, nor the subviews, but input is received outside superview's boundaries.
I've talked to an Apple engineer about this. touchesBegan won't work in the portion of a subview that's not contained w/in its superview because the system clips each subview on the way down the hierarchy as it tries to determine which subview's touchesBegan gets called.
In order to resolve the issue, I removed the intermediate wrapper views that were causing the clipping problem and hoisted the subviews up one level. This necessitated a minor change in logic but ultimately proved to be a cleaner solution -- and more importantly, one that worked.
I'm unsure if this is the correct way, but it's certainly one way.
Listen to the touchesBegan even in your parent object.
If you get an event, pass it onto the childs view by calling its touchesBegan yourself.

Why does touchesBegan not fire when the number of touches changes?

For some reason my touchesBegan method doesn't seem to be responding correctly. If I touch the screen with two fingers, then lift one up and put it down again, touchesBegan gets called correctly. If I touch the screen with one finger, then add a second finger, touchesBegan does not get called like it should. Is there some flag that I need to check? Below is a sample that illustrates my problem:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touch");
}
Any ideas what's going on? I'm testing on an iPad with iOS 4.2 if it matters.
Yes, you need to set the multipleTouchEnabled property on your view.
When set to NO, the receiver receives
only the first touch event in a
multi-touch sequence. The default
value of this property is NO.

How to determine click location coordinates in iphone app view class

i'm trying to make a connect four iphone app. I have made my own custom UIView class, which is where the game is played. However, I am trying to use the touchesMoved, touchesEnded, etc. methods to retrieve data based on where the user clicked (so i know which column they are trying to put a piece into). How can i get this information from the viewController class to my UIview class?
I would just use - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event and potentially override - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event to ignore touches in views that I may have overlaid. It does depend on your view hierarchy really.

Implement custom zooming for a UIScrollView

I have a UIScrollView with the requirement that, when zooming, the contentSize.height should remain the same. Zooming in from 200x100 should result in a new contentSize of 400x100 instead of 400x200, for instance. I'd like to do my own drawing while the user is zooming.
I don't think I can use the normal zooming behaviour of UIScrollView to achieve this, so I'm trying to roll my own. (I could just let it do its thing and then redraw my contents when -scrollViewDidEndZooming:withView:atScale: gets called, but that wouldn't be very pretty).
Currently I am subclassing UIScrollView and trying to do my own zooming when two fingers are on the screen:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] != 2) {
[super touchesMoved:touches withEvent:event];
} else {
// do my own stuff
}
}
I thought that by overriding touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent: and touchesCancelled:withEvent: in this way should work, but it doesn't.
An earlier failed attempt was to place a transparent view on top of the scrollview and send touches that I'm not interested in to the scrollview :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] != 2) {
[self.theScrollView touchesMoved:touches withEvent:event];
} else {
// do my own stuff
}
}
Any help would be appreciated.
Thanks,
Thomas
You probably won't be able to maintain decent performance during zooming if you attempt to redraw your content on every frame of a pinch-zooming event. I'd recommend taking the approach of letting the UIScrollView zoom a scaled version of your drawing in or out, then redraw the content to be sharp at the end of the zoom in the -scrollViewDidEndZooming:withView:atScale: delegate method. This is what I do in my application, and it ends up working very well.
There are some tricks to resizing your content view properly at the end of a zoom, which I describe in this answer. Basically, you need to intercept the setting of a transform to your content view so that you can set it to a scale factor of 1 when you redraw your content. You'll also need to keep track of that scale factor, because the UIScrollView doesn't, and use that scale factor to adjust the transform that UIScrollView tries to apply to your content view with subsequent zoom operations.
You could use a modification of this technique to force a redraw of your content during the pinch-zooming, but in my tests this ended up being far too jerky to provide a good user experience.
I'm not sure what you mean by this:
I thought that by overriding
touchesBegan:withEvent:,
touchesMoved:withEvent:,
touchesEnded:withEvent: and
touchesCancelled:withEvent: in this
way should work, but it doesn't.
Do you not get the events? You should receive the events, but I think there is a logic error in your if statement that may have been preventing this from working.
if ([touches count] != 2)
This is a problem, because the likelihood of the two touches happening precisely the same time is low. You'll want to be able to handle when touches happen independently, as well as when a user holds a finger stationary, and moves the other one. In these scenarios (which are common) you may only get one touch in that NSSet, even though the other is still valid.
The solution to handling touches properly is to remember some things about which touches came in and which touches left. Remember, the address of the UITouch does not change for the life of the touch, so you can safely compare addresses to ensure you are still dealing with the same touch as before, and track it's lifecycle.
If you are not getting the touch events, then that is a different problem altogether, you may need to turn set multiTouchEnabled:YES
I'm trying to do the same thing as this and I really want to be able to redraw while it's zooming. Fixing it up at the end in scrollViewDidEndZooming:withView:atScale is not really good enough.
The way I do it is pass a dummy view in viewForZoomingInScrollView: and read the height of this dummy view and set the frame on the actual content view to whatever I want. Because the frame changes, it means that drawRect gets called everytime. It seems fine on the simulator, I'm just drawing a few lines. But I don't actually own a device though, so I can't test it properly.
Also in the code you've got, you have touchesBegan:withEvent: but then you are forwarding to super touchesMoved:withEvent: instead of touchesBegan:withEvent: