iOS - UISlider jumps on first touch
Asked Answered
P

2

20

I've added a UISlider to an iOS application I'm developing with Xcode 5.1 and I cannot get rid of the following annoying behavior. When I first touch the thumb, it jumps by a small amount.

Here's a quick example detailing the behavior.

When the slider thumb is initially positioned to the left of the center (e.g. at the minimum value), it jumps to the right. If I run the app in the iOS simulator, the thumb jumps upon first touch without any dragging. The thumb always jumps towards the center of the track. The only time it does not jump is when it is at the precise center of the track. This behavior appears even when the slider is not connected to anything.

Is there any way I can disable it?

Pyrochemical answered 5/4, 2014 at 19:27 Comment(10)
@ipinak It's obvious. No code is needed.Blastogenesis
Is this purely a Simulator phenomenon? Have you got a device handy that you can try this on? If it's just a Simulator thing, you should laugh and ignore it. The mouse cursor is not a big fat finger, and it may be that the Simulator is compensating in some way.Blastogenesis
No, it happens when I test on my iPad as well. I just mentioned the simulator to emphasize that the "jumping" isn't a result of my inadvertently moving my finger on the ipad screen.Pyrochemical
I can't believe I've never noticed this before (unless it's new iOS 7.1 behavior)!Blastogenesis
I tried it in iOS 6.1 simulator and no jump. Downloading the 7.0 simulator now...Blastogenesis
Jumps in the 7.0 simulator.Blastogenesis
Okay, this is known and has a solution: #20903817Blastogenesis
Fantastic! Thanks so much, not sure how I missed that other answer.Pyrochemical
Thanks for raising it again, though; I hadn't noticed it. And please file a bug with Apple! The more people complain to them, the more likely they are to listen.Blastogenesis
Sure thing. Glad to know I wasn't being crazy.Pyrochemical
S
4

Just came across this issue today. For some reason, you have to set a custom thumb image, and it works.

[self.yourSlider setThumbImage:[UIImage imageNamed:@"customThumb"] forState:UIControlStateNormal];
Salleysalli answered 29/6, 2015 at 23:10 Comment(0)
J
1

Meanwhile in 2019 and iOS 12 has that bug

An old question and still the relevant issue. So while accepted answer works correctly, it has one small and important issue - the thumb touch zone is getting smaller, while increasing touchable area in most scenario leads to new jumping (have tried overriding thumbRect(forBounds and beginTracking methods)

Let's fix it!

So the solution is not so complex but inconvenient a bit (need some image resources), still easier than re-implementing own Slider

The purpose of this class is to eliminte UIKit bug which is jumping thumb during tapping. The main hack is to replace original thumb with some image Other code is extendinig tapping area which is: 1) setup an image with some side alpha offset, i.e. a circle whithin larger rectangle

setThumbImage(thumb, for: .normal)

2) larger image means larger thumb and as the result track on min and max value is visible under image's alpha area. So the next step is to set min and max track image which is track on the left and rigth sides respectevly,

setMinimumTrackImage(anImage, for: .normal) setMaximumTrackImage(maxSliderImage, for: .normal)

but the image also has a some alpha zone which is needed to 'hide' track on the sides of thumb's (with min/max values where it's purely visible).

4) while we have larger thumb and track visually decreased (respectively to slider bounds width) need to increase visible part of the track by overriding trackRect(forBounds method. we won't have out of slider bounds track as part of min/max images are transparent 5) as one of goals is to increase thumb's touch zone need to override point(inside point:, with event:) method to make touchable zone on the sides

PS: don't forget to set in xcassets min and max slider tracks attributes: 1) Slicing horizontal and left/right insets 2) Streching techninque

change slicing attributes

class Slider: UISlider {
    /// The value is distance between an image edge and thumb,
    /// Assume that's an image scheme: [...o...] so 3 dots here is imageSideOffset, where 'o' is a thumb
    /// as we want to hide that zone
    private let imageSideOffset: CGFloat = 30

    override func awakeFromNib() {
        super.awakeFromNib()

        if let thumb = UIImage(named: "slider_thumb"),
            let minSliderImage = UIImage(named: "min_slider_track"),
            let maxSliderImage = UIImage(named: "max_slider_track") {
            setThumbImage(thumb, for: .normal)
            setMinimumTrackImage(minSliderImage, for: .normal)
            setMaximumTrackImage(maxSliderImage, for: .normal)
            clipsToBounds = true
        } else {
            assertionFailure("failed to load slider's images")
        }
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        return bounds.insetBy(dx: -imageSideOffset, dy: 0).contains(point)
    }

    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        let superRect = super.trackRect(forBounds: bounds)
        guard let _ = self.thumbImage(for: .normal) else {
            return superRect
        }
        return superRect.insetBy(dx: -imageSideOffset, dy: 0)
    }
}
Jumbuck answered 30/8, 2019 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.