Stop UIScrollView scrolling during touch - ios

Is there a way to stop a UIScrollView from scrolling while a touch is still held down? Setting the content offset using the scrollViewDidScroll: delegate method is excessive and can cause some issues (view moves and resets quickly) with the GPU, so I was hoping for a cleaner solution, if there is one.

You can simply turn it out by using its property scrollEnabled in viewDidLoad..
self.myScrollView.scrollEnabled = NO;

I would try:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[scrollView setScrollingEnabled:NO];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[scrollView setScrollingEnabled:NO];
}
That will turn it off for a press and you can adjust detecting how far the "Move" was from the original touch to see if you want to reenable scrolling or not

Related

Passing touches from UIImageView to superview

I have four UIImageViews placed on a circular UIView. Dragging the UIView rotates the view. But when I drag the UIImageView, the rotation does not occur. The UIImageView is eating the touches.
I subclassed the UIImageView and tried this:
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self.superview touchesMoved:touches withEvent:event];
[super touchesMoved:touches withEvent:event];
}
That doesn't seem to solve the problem. I tried disabling touches on the UIImageView - [imageView setUserInteraction:NO]. That doesn't solve the problem either. I know there are similar questions been asked. But none seem to solve my problem.
Thanks.
Try setting userInteractionEnabled to NO on your UIImageViews, it should prevent them from consuming the touches.

Detecting if user has (a) finger(s) in the area of a UIScrollView in iOS 7

I'm trying to track if the user has a finger (or fingers) in the area of a UIScrollView. I overrode UIScrollView's - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event, - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event and - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event to track the number of touches that are "down" and "up".
This seems to work perfectly otherwise, but when the screen is panned, it seems that UIScrollView doesn't call these methods.
Which is the correct way to detect if the user is touching UIScrollView or not?
These properties of UIScrollView may be the best choice instead of messing with the
- (void)touchesBegan - (void)touchesCancelled - (void)touchesEnded methods
if (yourScrollView.isTracking || yourScrollView.isZooming || yourScrollView.isDragging)
{
// User interacting
}

Detect when a view is tapped on

I have multiple Views inside a ViewController and I want to know when a view is tapped on and I'm not sure how to go about it
In order to check whether certain view inside another view was touched you can use hitTest.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
In your custom implementation of touchesBegan check every touch in touches set. The point for hitTest method can be obtained using
- (CGPoint)locationInView:(UIView *)view;
method, where the view is your superView (the one that contains other views).
EDIT: Here's a fast custom implementation:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint locationPoint = [[touches anyObject] locationInView:self];
UIView* viewYouWishToObtain = [self hitTest:locationPoint withEvent:event];
}
I hope this was helpful, Paul
Sources : Detect if certain UIView was touched amongst other UIViews

UIButton not firing touchesbegan

I have a bunch of regular UIButtons, with a target when pressed. But I also want it to work when swiped, and activate touchesbegan or touchesmoved/touchesended. I need it because I want create something like a swipable word puzzle, which will also receive touchup presses. But the swipe series only works if the touches begins on self.view , but not on the button themselves. It will just be pressed and no touch events are registered.
UIButton* Butt = [UIButton buttonWithType:UIButtonTypeCustom];
Butt.frame= CGRectMake(0, -500, 63 , 63);
[Butt addTarget:self action:#selector(clickWord:)forControlEvents:UIControlEventTouchUpInside];
Butt.userInteractionEnabled= YES;
[aSubview addSubview:Butt];
Can anyone help? I know that putting the UIButtons in the subview might be troublesome, but I really wish to do that to keep the elements in a structured way, and not everything is directly on self.view.
I tried to subclass the buttons but it didn't help
Subclassing code:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
// This is where the touch is passed on
}
- (void) toouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
[self.nextResponder touchesMoved:touches withEvent:event];
// This is where the touch is passed on
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self.nextResponder touchesEnded:touches withEvent:event];
// This is where the touch is passed on
}
If you're programming a game, maybe it would be worth it to use a framework like Cocos2D instead of the standard UI Elements?
But back to topic: If you create a screensized invisible UIView on top of your buttons to check for swipes and, if it's no swipe, switch "user interaction enabled" to off, maybe it works?
Otherwise create a matrix where you check where the user touched and what he did on this view and use this data in the background. Then you would also need no buttons anymore...
Hope it helps a bit.
Edit: It feels odd to modify the standard UI Elements in a way where they don't do anything close to what they're developed for. Maybe subclassing UIControl itself will be what you want? It won't be very easy, but probably the solution which works best for you?
Looks like touch are responding but when it gets to the rect of uibutton, they will be intercepted.

Horizontal movement of a UITableView?

I'm trying to distinguish between horizontal swiping / panning and
vertical scrolling in a UITableView. The behavior I'm looking to
imitate (in a sense) is that of the Twitter iPad app, that has
multiple UITableView that can be moved horizontally on the screen. If
I slide my finger left or right on one of these UITableView, the view
itself moves horizontally. If I swipe vertically, the view scrolls as
expected.
I'm having trouble figuring out the correct way to implement this
behavior. I've seen some tutorials on this which involve adding touch
event handlers in the UITableViewCells, and overriding hitTest in the
UITableViewto appropriately route events depending on which direction
the gesture is moving. I've implemented some of these techniques, but
none of them work particularly well.
Does anyone know the correct way to implement this sort of behavior?
Conditionally performing actions on a UITableViewdependent on the
direction of the user's finger movement?
Thanks.
I've been struggling with a similar problem for days, and I've went through several potential solutions. I've found the best way and also the simplest solution to be subclassing UIGestureRecognizer to handle horizontal movement and attach it to your UITableViews.
The way it works is that it intercepts any touch events before they go to the UITableView (also UIScrollView). The UITableView, being a subclass of UIScrollView, has a custom UIPanGestureRecognizer built in which detects dragging and scrolls it's view accordingly. By adding your own subclass of UIGestureRecognizer, you can get the touches before the UIScrollView's gesture recognizer does. If your recognizer sees that the user is dragging horizontally, it should change it's state in an overridden touchesMoved: method to UIGestureRecognizerStateBegan. Otherwise, it sets it's state to UIGestureRecognizerCancelled, which lets the underlying UIScrollView handle the touches instead.
Here's what my UIGestureRecognizer subclass looks like:
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface TableViewCellPanGestureRecognizer : UIGestureRecognizer
{
CGPoint startTouchPoint;
CGPoint currentTouchPoint;
BOOL isPanningHorizontally;
}
- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
#end
#implementation TableViewCellPanGestureRecognizer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
startTouchPoint = [[touches anyObject] locationInView:nil];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
currentTouchPoint = [[touches anyObject] locationInView:nil];
if ( !isPanningHorizontally ) {
float touchSlope = fabsf((currentTouchPoint.y - startTouchPoint.y) / (currentTouchPoint.x - startTouchPoint.x));
if ( touchSlope < 1 ) {
self.state = UIGestureRecognizerStateBegan;
isPanningHorizontally = YES;
[self.view touchesCancelled:touches withEvent:event];
} else {
self.state = UIGestureRecognizerStateCancelled;
[self.view touchesCancelled:touches withEvent:event];
}
} else {
self.state = UIGestureRecognizerStateChanged;
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
self.state = UIGestureRecognizerStateCancelled;
[self.view touchesCancelled:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
self.state = UIGestureRecognizerStateCancelled;
}
-(void)reset
{
[super reset];
startTouchPoint = CGPointZero;
currentTouchPoint = CGPointZero;
isPanningHorizontally = NO;
}
#end
Then I have a subclassed UITableView that attaches the recognizer to itself and implements an action method to trigger horizontal movement of individual rows:
In my UITableView init:
horizontalPanGesture = [[TableViewCellPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleHorizontalDrag:)];
[self addGestureRecognizer:horizontalPanGesture];
[horizontalPanGesture release];
And the action method:
-(void)handleHorizontalDrag:(UIGestureRecognizer *)gesture
{
UIGestureRecognizerState state = gesture.state;
// Set the touched cell
if (!touchedCell){
NSIndexPath *indexPathAtHitPoint = [self indexPathForRowAtPoint:[gesture locationInView:self]];
id cell = [self cellForRowAtIndexPath:indexPathAtHitPoint];
touchedCell = cell;
startTouchPoint = [gesture locationInView:touchedCell];
}
if ( state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged ) {
// move your views horizontally
} else if ( state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled ) {
touchedCell = nil;
}
}
The above gets the current cell being touched within the table view, and then applies horizontal movements to it as the user drags left or right. However, if my gesture recognizer determines that the touches are meant to scroll vertically, it just cancels itself and the following touches are sent on to the UITableView to initiate vertical scrolling automatically.
This setup seems to be much simpler than overriding hitTest and doing all sorts of touch event trickery within the UITableView itself. It simply makes an immediate determination about the direction of the touch movement. You'll want to read up on UIGestureRecognizers - specifically about how it should be subclassed. You need to make sure to forward on certain touch events like touchesCancelled to the UITableView, as the UITableView's built in panGestureRecognizer won't be handling these events as it normally does. Obviously, you'll want to move entire table views and not individual cells, but it should be pretty straightforward.
This solution, as simple as it is, took me awhile to get exactly right for my exact needs. I am pretty new to IOS development, so I had to spend a lot of time reading about and tinkering with gesture recognizers and scroll views to figure this out.
I have never done this myself, but as a UITableView is a subclass of UIScrollView, the delegates of UITableView are also UIScrollViewDelegates. So in your UITableViewController subclass, you should be able to use UIScrollView delegates, and intercept the scrolls - making sure to also call the super method.
If you want the UITableViews to be placed "side by side" and when you swipe horizontally you expect them to all move together horizontally at the same time, (like a photo gallery with UITableViews instead of images) you can do the following:
Use a UIScrollView and add the UITableViews as the UIScrollView's subviews. You should set the scrollview's contentSize like this:
CGRect bounds = scrollView.bounds;
scrollView.contentSize=CGSizeMake(bounds.size.width * kNumOfTableViews, bounds.size.height);
so that the UIScrollview scrolls horizontally and not vertically.
You may also want to use
scrollView.pagingEnabled=YES;
depending on the desirable behaviour.
The UITableviews will respond the normal way if you slide your finger vertically and you will be able to change between UITableViews by sliding your finger horizontally.
For more details about how to do this efficiently, you can look at the WWDC 2010 video Session 104 - Designing Apps with Scroll Views and check out the source code from here: http://developer.apple.com/library/ios/#samplecode/PhotoScroller/ . This session describes how to slide between images. Instead of images you will use UITableViews
However, if you want each UITableView to be able to move horizontally independently and maybe overlap with another one as in the twitter app for iPad, this solution will not work for you, at least not out of the box.

Resources