Facing some difficulties implementing draggable View. using monotouch, but I dont' think it is language-specific...
the problem details:
I have a View that handles TouchesBegan, TouchesMove and reposition itself (by changing Frame property). That work perfect!
once I'm adding ScrollView with disabled scrolling and paging, any touches made on the scrollview are not getting to the View that has to move. Setting UserInteraction to NO fixes moving problem, but none of the controls hosted inside the scroller responds any touch.
Any idea on how to have ScrollView and allow it's superview to receive all the touches when there's no scrolling events available?
You have to pass the touch to the subViews, this category for UIScrollView will do the job:
#implementation UIScrollView (FixedApi)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
NSLog(#"touch view = %#", [[touch view].class description]);
if ([[[touch view].class description] isEqualToString:#"UITableView"]) {
//You have touched a UITableView
} else if ([[[touch view].class description] isEqualToString:#"UIView"]) {
//You have touched a UIView
} else {
//Ignore the category and return the touch to the UIScrollView.
[self.superview touchesBegan:touches withEvent:event]; // or 1 nextResponder, depends
[super touchesBegan:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ( !self.dragging ) [self.nextResponder.nextResponder touchesEnded:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
#end
You can drag the subview using PanGesture Action...
-(void)panGRAction {
if (panGR.state == UIGestureRecognizerStateBegan) {
UITouch *touch = [[event allTouches] anyObject];
NSLog(#"touch view = %#", [[touch view].class description]);
if ([[[touch view].class description] isEqualToString:#"UITableView"]) {
//You have touched a UITableView
} else if ([[[touch view].class description] isEqualToString:#"UIView"]) {
//You have touched a UIView
} else {
//Ignore the category and return the touch to the UIScrollView.
[self.superview touchesBegan:touches withEvent:event]; // or 1 nextResponder, depends
[super touchesBegan:touches withEvent:event];
}
} else if (panGR.state == UIGestureRecognizerStateEnded) {
if ( !self.dragging ) [self.nextResponder.nextResponder touchesEnded:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
}
Related
I'm trying to subclass UIView and design a transparent view. This view will sit on top of many other views and it's only task is to capture and record user touches (tap and pan). I have tried many different methods, explained in different questions asked by other users with no luck. This is what I have done so far in my implementation file:
#import "touchLayer.h"
#implementation touchLayer
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) [self commonInit];
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) [self commonInit];
return self;
}
- (void)commonInit
{
self.userInteractionEnabled = YES;
self.alpha = 0.0;
}
- (id)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
id hitView = [super hitTest:point withEvent:event];
if (hitView == self)
{
UITouch *touch = [[event allTouches] anyObject];
if (touch.phase == UITouchPhaseBegan) NSLog(#"touchesBegan");
else if (touch.phase == UITouchPhaseMoved) NSLog(#"touchesMoved");
else if (touch.phase == UITouchPhaseEnded) NSLog(#"touchesEnded");
return nil;
}
else
return hitView;
}
#end
Now this code works just fine, and I see see the touches in the lower layers, but I cannot differentiate between touchBegan, touchMoved, and touchEnded. [[event allTouches] anyObject] returns nil. Do you have any idea how I can capture tap and pan on a UIView without blocking the touches? Thanks a lot.
After investigating, actually i can't find solution to detect touch using hitTest method with a touchLayer. But your question is about capturing and recording user touches, so i have another for this issue.
My solution is
Subclass UIWindow
Replace window of UIAppDelegate with a new one which is created with your window class.
Override sendEvent method of UIWindow, capture and record user touches in this method.
This is my subclass of UIWindow to detect touch. I tried and it work.
#implementation DetectTouchWindow
- (void)sendEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
switch ([touch phase]) {
case UITouchPhaseBegan:
NSLog(#"Touch Began");
break;
case UITouchPhaseMoved:
NSLog(#"Touch Move");
break;
case UITouchPhaseEnded:
NSLog(#"Touch End");
break;
case UITouchPhaseCancelled:
NSLog(#"Touch Cancelled");
break;
default:
break;
}
[super sendEvent:event];
}
#end
For more detail and easier, i created a demo repo to check it. You can take a look at this link https://github.com/trungducc/stackoverflow/tree/recording-touch-events
Hope this helps ;)
I've an UITextView into an UITableViewCell, but I can't scroll the text. When I try to scroll the textView, the tableView catch the touches.
There is any way to fix this?
PS: Subclass of UITableViewController
I'm thinking you can detect the object being touched and if it is a UITextView, then temporarily disable scrolling with UITableView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
//detect if touch is on UITextView
if ([touch.view isKindOfClass: UITextView.class]) {
yourTableView.scrollEnabled = NO;
}
}
Don't forget to re-enable scrolling of the UITableView afterwards in touchesEnded
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
yourTableView.scrollEnabled = YES;
}
I put UIButton inside UITableViewCell in UITableView that is behind UIScrollView. I subclassed UIScrollView to forward touches to UITableView.
So method from UITableViewDelegate didSelectRow is calling properly. The problem is that UIButton inside table cell is not receiving TouchUpInside actions.
How can I solve this problem without deleting ScrollView over TableView?
EDIT:
I resolved this issue by detecting which view will receive touch. Here's the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *hitView = [self hitTest:[(UITouch*)[[touches allObjects] objectAtIndex:0] locationInView:self] withEvent:event];
if ([hitView isKindOfClass:[UIButton class]]) {
[(UIButton*)hitView sendActionsForControlEvents:UIControlEventTouchUpInside];
}
[super touchesBegan:touches withEvent:event];
}
If you want to enable actions for bouth objects - UIScrollView and UIButton you should to implement custom hit test mechanism for ScrollView.
In your UIScrollView subclass override - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method to make views behind ScrollView available for getting events.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return __touchesEnabled;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_touchesEnabled = NO;
UIWindow *window = [UIApplication sharedApplication].delegate.window;
for(UITouch *touch in touches) {
CGPoint point = [touch locationInView:self];
point = [window convertPoint:point fromView:self];
UIView *view = [window hitTest:point withEvent:event];
[view touchesBegan:touches withEvent:event];
}
_touchesEnabled = YES;
}
It works for me
Since you have added your scroll view over the UIButton, all the touch actions will not be passed to the button.
[yourScrollView setUserInteractionEnabled:NO];
This may solve your problem.
I have been trying to figure out how to forward the touches from a UIScrollView to its superview. The reason is for sort of a cursor to follow the touch. Anyway, the scrollview is populated with UIButtons. My code for forwarding the touch is to use delegation in a scrollview subclass:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateBegan:)]) {
[[self tfDelegate]tfDelegateBegan:[touches anyObject]];
}
NSLog(#"touches began");
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateMoved:)]) {
[[self tfDelegate]tfDelegateMoved:[touches anyObject]];
}
NSLog(#"touches moved");
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesEnded:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateEnded:)]) {
[[self tfDelegate]tfDelegateEnded:[touches anyObject]];
}
NSLog(#"touches ended");
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesCancelled:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateCancelled:)]) {
[[self tfDelegate]tfDelegateCancelled:[touches anyObject]];
}
NSLog(#"touches cancelled");
}
However, I have learned that UIScrollviews operate via UIGestureRecognizers, so these methods aren't even called by default. I realize that the gesture recognizers are exposed in iOS 5 but I need to support 4.0 as well. I did this instead:
NSArray *a = [[theScrollview gestureRecognizers]retain];
for (UIGestureRecognizer *rec in a) {
if ([rec isKindOfClass:[UIPanGestureRecognizer class]]) {
NSLog(#"this is the pan gesture");
rec.cancelsTouchesInView = NO;
}
}
This allows the gesture to work and the touches methods to be called simultaneously. The issue is, now if you try to scroll while touching a button, the button can be pressed while scrolling. Normally, the scroll cancels the button and the only time a button can be pressed is if the scrollview is not scrolling.
This is the desired functionality. Any suggestions on how I might achieve this?
Maybe try controlling the button action using a flag that would prevent the event from firing if the scroll view is scrolling.
BOOL isScrolling = NO;
- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {
isScrolling = YES;
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
isScrolling = NO;
}
- (void) didTapButton {
if(isScrolling)
return;
//Do Button Stuff
}
What's the Xcode code for this pseudocode?
if ([Any UIPickerView in my ViewController isTouched]) {
[AnyUIView setHidden:NO];
}
if ([Any UIPickerView in my ViewController is__NOT__Touched__Anymore__]) {
[AnyUIView setHidden:YES];
}
Tried it with the -(void)touchesBeganmethod, it detects the touches but I was not able to make it object-specific. Thanks
P.S. I want to display a hint on the display while the UIPickerViewis touched.
This is just from the top of my head...... but you should be able to get the idea...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
if ([touch.view isKindOfClass: UIPickerView.class]) {
//your touch was in a uipickerview ... do whatever you have to do
}
}
..and do the same with touchesEnded:withEvent: