UIButton touch is delayed when in UIScrollView

IosUiscrollviewUibuttonTouchescancelled

Ios Problem Overview


I'm running into a small issue in my app.

I essentially have a series of UIButtons added as subviews in a UIScrollView which is part of a nib. Every time I tap on a button there is a noticeable delay before the button is highlighted. I essentially have to hold it for about half a second before the button dims and appears selected.

I'm assuming this is because the UIScrollView needs to determine if the touch is a scroll or if it's a touch that is meant for a subview.

Anyways, I'm a little unsure on how to proceed. I simply want the button to appear selected as soon as I tap it.

Any help is appreciated!

Edit:

I've tried setting delaysContentTouches to NO but scrolling becomes almost impossible since a majority of my scrollView is filled with UIButtons.

Ios Solutions


Solution 1 - Ios

Jeff's solution wasn't quite working for me, but this similar one does: http://charlesharley.com/2013/programming/uibutton-in-uitableviewcell-has-no-highlight-state

In addition to overriding touchesShouldCancelInContentView in your scroll view subclass, you still need to set delaysContentTouches to false. Lastly, you need to return true rather than false for your buttons. Here's a modified example from the above link. As commenters suggested, it checks for any subclass of UIControl rather than UIButton specifically so that this behavior applies to any type of control.

Objective-C:

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.delaysContentTouches = false;
    }
    
    return self;
}

- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
    if ([view isKindOfClass:UIControl.class]) {
        return true;
    }
    
    return [super touchesShouldCancelInContentView:view];
}

Swift 4:

override func touchesShouldCancel(in view: UIView) -> Bool {
    if view is UIControl {
        return true
    }
    return super.touchesShouldCancel(in: view)
}

Solution 2 - Ios

Ok I've solved this by subclassing UIScrollView and overriding touchesShouldCancelInContentView

Now my UIButton that was tagged as 99 highlights properly and my scrollview is scrolling!

myCustomScrollView.h:

@interface myCustomScrollView : UIScrollView  {

}

@end

and myCustomScrollView.m:

@implementation myCustomScrollView

    - (BOOL)touchesShouldCancelInContentView:(UIView *)view
    {
    	NSLog(@"touchesShouldCancelInContentView");
    	
    	if (view.tag == 99)
    		return NO;
    	else 
    		return YES;
    }

Solution 3 - Ios

Try to set UIScrollView delaysContentTouches property to NO.

Solution 4 - Ios

Storyboard solution: Select the scroll view, open the "Attributes Inspector" and uncheck "Delays Content Touches"

enter image description here

Solution 5 - Ios

In Swift 3:

import UIKit

class ScrollViewWithButtons: UIScrollView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        myInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        myInit()
    }
    
    private func myInit() {
        self.delaysContentTouches = false
    }

    override func touchesShouldCancel(in view: UIView) -> Bool {
        if view is UIButton {
            return true
        }
        return super.touchesShouldCancel(in: view)
    }
}

You can then use this ScrollViewWithButtons in IB or in code.

Solution 6 - Ios

Swift 3 :

scrollView.delaysContentTouches = false

Solution 7 - Ios

None of the existing solutions worked for me. Maybe my situation is more unique.

I have many UIButtons within a UIScrollView. When a UIButton is pressed a new UIViewController is presented to the user. If a button is pressed and held long enough, the button will show its depressed state. My client was complaining that if you tap too quickly, no depressed state is shown.

My solution: Inside the UIButtons' tap method, where I load the new UIViewController and present it on screen, I use

[self performSelector:@selector(loadNextScreenWithOptions:) 
           withObject:options 
           afterDelay:0.]

This schedules the loading of the next UIViewController on the next event loop. Allowing time for the UIButton to redraw. The UIButton now shows its depressed state before loading the next UIViewController.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeffView Question on Stackoverflow
Solution 1 - Iosjlong64View Answer on Stackoverflow
Solution 2 - IosJeffView Answer on Stackoverflow
Solution 3 - IosVladimirView Answer on Stackoverflow
Solution 4 - IosJoséView Answer on Stackoverflow
Solution 5 - IosSimon ReggianiView Answer on Stackoverflow
Solution 6 - IosOubaida AlQuraanView Answer on Stackoverflow
Solution 7 - IosBrad GView Answer on Stackoverflow