UISlider
iOS development
Swift
user interface
drag detection

How to detect the end of UISlider drag?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UISlider can send updates continuously while the thumb moves, but many actions should happen only after the user lets go. The usual solution is to listen for the slider's touch-end control events rather than trying to infer the end of dragging from repeated value changes.

Listen for the End of Touch Tracking

The most direct approach is to register separate handlers for live changes and drag completion.

swift
1import UIKit
2
3final class VolumeViewController: UIViewController {
4    private let slider = UISlider()
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8
9        slider.minimumValue = 0
10        slider.maximumValue = 100
11        slider.isContinuous = true
12
13        slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged)
14        slider.addTarget(
15            self,
16            action: #selector(sliderDidEndDragging(_:)),
17            for: [.touchUpInside, .touchUpOutside, .touchCancel]
18        )
19    }
20
21    @objc private func sliderChanged(_ sender: UISlider) {
22        print("Preview value: \(sender.value)")
23    }
24
25    @objc private func sliderDidEndDragging(_ sender: UISlider) {
26        print("Commit final value: \(sender.value)")
27    }
28}

This pattern is clear and reliable:

  • '.valueChanged updates the UI while the user drags'
  • '.touchUpInside, .touchUpOutside, and .touchCancel indicate that interaction has ended'

Including .touchCancel is important because the touch can be interrupted by the system.

If You Only Care About the Final Value

If you do not need live updates at all, set isContinuous to false. In that mode, UISlider sends .valueChanged only when the interaction ends.

swift
1slider.isContinuous = false
2slider.addTarget(self, action: #selector(sliderFinished(_:)), for: .valueChanged)
3
4@objc private func sliderFinished(_ sender: UISlider) {
5    print("Final slider value: \(sender.value)")
6}

This is often the simplest solution for expensive operations such as network requests, media seeking, or database writes. You get one callback when the user is done instead of many callbacks during the drag.

Choose the Right Interaction Model

Use continuous updates when the interface benefits from instant feedback, such as:

  • changing a label
  • previewing brightness
  • updating a progress readout

Use end-of-drag handling when each update is expensive or should be treated as a final commit, such as:

  • sending analytics
  • saving settings
  • seeking media playback
  • launching backend requests

Separating preview behavior from commit behavior often produces a smoother UI and simpler code.

Why Not Check isTracking Repeatedly?

You could inspect slider.isTracking inside a .valueChanged handler, but that usually makes the code harder to reason about. Touch-end events already describe exactly what you care about, so they are usually the better abstraction.

In other words, let the control events do the work for you. UIKit has already solved the edge cases around normal completion, release outside the control, and cancellation.

Interface Builder Version

If your slider is connected through a storyboard, the same idea applies. You can wire:

  • one action to Value Changed
  • another action to Touch Up Inside
  • another action to Touch Up Outside
  • optionally another action to Touch Cancel

The programmatic and Interface Builder approaches are equivalent. The difference is only where you declare the connections.

Common Pitfalls

  • Handling only .touchUpInside and missing cases where the user lifts outside the slider.
  • Forgetting .touchCancel, which can happen during interruptions.
  • Keeping isContinuous enabled when the code really needs only one final callback.
  • Doing expensive work on every .valueChanged event and creating laggy interaction.
  • Trying to infer drag completion indirectly instead of using the control events that already exist.

Summary

  • Use .touchUpInside, .touchUpOutside, and .touchCancel to detect the end of a UISlider drag.
  • Keep .valueChanged for live preview behavior while dragging.
  • Set isContinuous = false when you only want the final value.
  • Separate preview logic from commit logic for clearer code and better performance.
  • Prefer UIKit control events over manual drag-state inference.

Course illustration
Course illustration

All Rights Reserved.