I've a UIScrollView that contains an UIImageView.
The UIScrollView lets zooming and panning through the UIImageView.
The problem is that I would like know the finger movement every time, and I'm trying to catch the event with touchesMoved method.
But it's not working, despite touchesBegan and touchesEnded that are called correctly.
In fact, touchesMoved is called if the finger movement is really small, and the UIScrollView doesn't started panning. At the moment that UIScrollView starts getting moved, the event stops being called.
Somebody know what's the problem and how to fix it?
I've thought that maybe the UIImageView inside is catching the event or something like that.
Actually, that's really quite a problem, as the UIScrollView does "eat" TouchesMoved events (even if propagates several first ones).
So, I've just come up with the approach to get the events directly from UIWindow. That's for sure not the best approach in the application structure sense, but in some custom situation (which was exactly what I needed) is fine.
(Examples are in MonoTouch C#).
Create your custom UIWindow (I don't show here how to replace standard UIWindow with MyWindow due to my custom logic (using MvvmCross framework), but that's pretty easy and usually done in appDelegate initialization logic - you will find that in google/stack overflow):
public class MyWindow : UIWindow
{
public MyWindow(RectangleF bounds) : base(bounds)
{
}
public override void SendEvent(UIEvent evt)
{
if (evt.Type == UIEventType.Touches) {
var el = (UITouch)evt.AllTouches.AnyObject;
if (el.Phase == UITouchPhase.Began)
{
if(OnTouchBegan != null)
OnTouchBegan(el.View, new TouchCommandArgs(evt.AllTouches, evt));
}
if (el.Phase == UITouchPhase.Moved)
{
if(OnTouchMoved != null)
OnTouchMoved(el.View, new TouchCommandArgs(evt.AllTouches, evt));
}
if (el.Phase == UITouchPhase.Ended)
{
if(OnTouchEnd != null)
OnTouchEnd(el.View, new TouchCommandArgs(evt.AllTouches, evt));
}
if (el.Phase == UITouchPhase.Cancelled)
{
if(OnTouchCancel != null)
OnTouchCancel(el.View, new TouchCommandArgs(evt.AllTouches, evt));
}
} else
MvxTrace.Trace (evt.Type == null ? "-" : evt.ToString ());
base.SendEvent(evt);
}
public event TouchCommand OnTouchBegan;
public event TouchCommand OnTouchEnd;
public event TouchCommand OnTouchCancel;
public event TouchCommand OnTouchMoved;
}
public class TouchCommandArgs : EventArgs
{
public NSSet Touches { get; set; }
public UIEvent Evt { get; set; }
public TouchCommandArgs(NSSet touches, UIEvent evt)
{
Touches = touches;
Evt = evt;
}
}
and in the place to handle the events subscribe for the custom events handlers:
var window = (MyWindow) UIApplication.SharedApplication.KeyWindow;
window.OnTouchBegan += view_OnTouchBegan;
window.OnTouchMoved += view_OnTouchMoved;
window.OnTouchCancel += view_OnTouchCancel;
window.OnTouchEnd += view_OnTouchEnd;
handlers like (that's all by your own):
void view_OnTouchBegan(object sender, TouchCommandArgs args)
{
// do your logic
}
In that case the events will be handling simultaneously (in both places: yours and UIScrollView), so additionally if you want, you can cancel the window's event propagation only if you want to prevent any other handlers to apply except yours (you can add "Handled" flag to your handlers, for instance).
Try to use the UIScrollViewDelegate to trace the offset change or zoom scale changes.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewDidZoom:(UIScrollView *)scrollView;
Or check the zoomScale
value change of UIScrollView
between touchesBegan: & (touchesMoved: or touchesCancelled:) events
.
Create a subclass of UIScrollView class and override the touchesBegan: and other touch methods as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// If not dragging, send event to next responder
if (!self.dragging)
{
[self.nextResponder touchesBegan: touches withEvent:event];
}
else
{
[super touchesEnded: touches withEvent: event];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// If not dragging, send event to next responder
if(!self.dragging)
{
[self.nextResponder touchesBegan: touches withEvent:event];
}
else
{
[super touchesEnded: touches withEvent: event];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// If not dragging, send event to next responder
if (!self.dragging)
{
[self.nextResponder touchesBegan: touches withEvent:event];
}
else
{
[super touchesEnded: touches withEvent: event];
}
}
The scrollview property canCancelContentTouches = NO
allows the touch events to be passed up the responder chain.
Please check this to recognize touch events(began,moved,ended) using pan gesture of the scrollview,
Class CustomScrollView:UIScrollView
{
CustomScrollView()
{
(this.GestureRecognizers[1] as UIPanGestureRecognizer).AddTarget(() => this.PanHandle(this.GestureRecognizers[1] as UIPanGestureRecognizer));
}
private void PanHandle(UIPanGestureRecognizer gestureRecognizer)
{
var touchLocation=gestureRecognizer.LocationInView(this)
if (gestureRecognizer.State == UIGestureRecognizerState.Began)
{
//Touches began
}
else if (gestureRecognizer.State == UIGestureRecognizerState.Changed)
{
//Touches Moved
}
else if (gestureRecognizer.State == UIGestureRecognizerState.Ended)
{
//Touches ended
}
}
}
Regards,
Ashwin