10
votes

I have a (vertical) UISlider inside a UIScrollview. I'd like to be able to change the value of the slider, and, without lifting my finger, scroll the scrollview left or right.

Desired behavior:

Touch down inside vertical UISlider, followed by a finger drag left or right causes the scrollview to scroll

Actual behavior:

Touch down inside vertical UISlider, followed by a finger drag left or right causes no movement in UIScrollview. A touch down outside the UISlider followed by a drag will scroll the scrollview as expected

UIView has a property called exclusiveTouch which seems as if it might be related to my problem. I tried setting it to NO, with no luck.

So, how can is set up my UISliders so that the scrollview beneath them will respond to touches which originate inside the UISliders?

5
+1 for desired/expected result vs. actual result. More questions should do this.David Rönnqvist

5 Answers

5
votes

Have you tried subclassing UIScrollView and implementing - (BOOL)touchesShouldCancelInContentView:(UIView *)view? According to the Apple documentation:

// called before scrolling begins if touches have already been delivered to a subview of the scroll view. if it returns NO the touches will continue to be delivered to the subview and scrolling will not occur
// not called if canCancelContentTouches is NO. default returns YES if view isn't a UIControl

If you simply return NO if the view is your UISlider, this may do what you want, assuming your UIScrollView only scrolls horizontally. If this doesn't work, you likely will have to do custom touch handling (ie. overriding touchesBegan:withEvent:, touchesChanged:withEvent:, etc.) for both your UIScrollView and your UISlider.

2
votes

What you are seeing is the intended behavior.

Each touch event only gets handled by one control. What exclusiveTouch does is actually to prevent other touch events from being delivered to other views.

To do what are trying to do you would have to do some of the touch handling yourself. Passing the event to both your views. You could do either do it by implementing all the touchesBegan:, touchesMoved: etc. methods and pass the events to both views. You can read more about that approach in the UIResponder documentation. Another approach is to do the event handling in a UIGestureRecognizer on the scroll view that hit tests the slider and updates the value of the slider using the y-delta. You can read more about gesture recognizers and event handling in the section about Gesture Recognizers in the Event Handling Guide for iOS.


Side note

Go to the Settings app and toggle a switch half way (for example the Airplane mode toggle) and then drag down. Nothing will happen. The rest of the OS behaves the same way. Are you sure that this is the interaction that you really want to do? Apps that behave differently often feel weird and unfamiliar.

1
votes

Your question confused me a bit. You are saying a vertical slider - but dragging left and right?

If you wish to scroll the scrollview when dragging the UISlider, the proper way to do so is

[mySlider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged];

and

- (void) sliderMoved:(UISlider*) slider {
    myScrollView.contentOffset.x = slider.value * (myScrollView.contentSize.width - myScrollView.bounds.size.width);
}

Hope this is what you want.

1
votes

You need to set delaysContentTouches as NO and prevent for UISlider objects to scroll, Check below code.

mySlider.delaysContentTouches = NO;

- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
    if ([view isKindOfClass:[UISlider class]])
    {
        UITouch *touchEvent = [[event allTouches] anyObject];
        CGPoint locationEvent = [touchEvent locationInView:view];
        CGRect thumbRect;
        UISlider *mySlide = (UISlider*) view;
        CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds];
        thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value];
        if (CGRectContainsPoint(thumbRect, locationEvent))
            return YES;
    }
    return NO;
}
0
votes

I think you can get some reference from this example in this example it is shown that how to cancel any touch or any gesture recognizers and apply them to other views.

May this lead you to the solution of your problem and if it will just let me know about it

Happy Codding :)