A Swift checkbox for iOS and iPadOS, implemented as a UISwitch subclass. Existing UISwitch outlets and target/action connections remain usable. Version 3.1.0 requires iOS 17 or later and Swift tools 5.9 or later. The library and its example apps have no external dependencies.
The control draws SF Symbols or your own images, supports an optional indeterminate state, and uses standard .valueChanged events. Image colors, aspect ratio, accessibility activation, and optional selection haptics are handled by the control.
Add https://github.com/RiftValleySoftware/RVS_Checkbox through Xcode's package dependency UI, link the RVS_Checkbox product to your app, then:
import RVS_CheckboxFor direct-source integration, add Sources/RVS_Checkbox/RVS_Checkbox.swift to your app target. Copy its neighboring PrivacyInfo.xcprivacy into your app's resources, merging with an existing app manifest when necessary. A static .a library does not contain resource bundles; the Direct harness demonstrates copying the manifest separately. Swift Package Manager copies it into the package's resource bundle automatically.
let checkbox = RVS_Checkbox(frame: CGRect(x: 20, y: 20, width: 44, height: 44))
checkbox.accessibilityLabel = "Include archived items"
checkbox.tintColor = .systemBlue
checkbox.setOn(true, animated: false)
checkbox.addAction(UIAction { [weak checkbox] _ in
guard let checkbox else { return }
print("Include archived items:", checkbox.isOn)
}, for: .valueChanged)
view.addSubview(checkbox)In a storyboard, add a Switch, then set its custom class and module to RVS_Checkbox. A custom View may also be used. Connect .valueChanged to your handler. Configure onImage, offImage, clearImage, isThreeState, useOffImageForClear, and useHaptics in the Attributes Inspector or in code.
The inherited intrinsic size is the standard switch size. Give the control explicit width/height constraints for a square appearance and allow a comfortable touch target. onTintColor, thumbTintColor, and switch styles do not customize the checkbox; use tintColor or original-color images.
| Mode | Stored checkboxState |
isOn |
isOff |
isClear |
value |
|---|---|---|---|---|---|
| Two-state | .clear (off) |
false | true | true | -1 |
| Two-state | .on |
true | false | false | 1 |
| Three-state | .off |
false | true | false | -1 |
| Three-state | .clear (indeterminate) |
false | false | true | 0 |
| Three-state | .on |
true | false | false | 1 |
The initial state is .clear, unless a storyboard supplies an on state. Two-state mode normalizes .off assignments to .clear. Switching to three-state mode preserves the stored selection, so an off two-state checkbox becomes indeterminate. Set an explicit state after changing modes if that is not what you intend.
In three-state mode, user activation follows off → clear → on → clear → off. From a fresh clear state, the next state is on. nextState predicts the next activation without changing the control or its direction.
All programmatic setters (checkboxState, isOn, value, and the set… methods) are silent: they do not send control events or play haptics. State updates synchronously even when the visual change is animated. Animations honor Reduce Motion. Both value and setValue accept any integer: negative means off, positive means on, and zero means clear.
A touch released inside or accessibility activation advances the state, plays at most one haptic, and notifies .valueChanged and .primaryActionTriggered handlers once each. Attach a handler to one of these events to process a change once. Outside releases, cancelled touches, and disabled interaction do not change the selection. sendActions(for:) only sends notifications. On iOS 17.4 and later, performPrimaryAction() performs an activation, including the state change.
checkbox.isThreeState = true
checkbox.useOffImageForClear = false
checkbox.offImage = UIImage(named: "Unchecked")?.withRenderingMode(.alwaysTemplate)
checkbox.onImage = UIImage(named: "Checked")?.withRenderingMode(.alwaysTemplate)
checkbox.clearImage = UIImage(named: "Mixed")?.withRenderingMode(.alwaysTemplate)
checkbox.setClear()Both on and off images must exist before custom images are used. Assigning either automatically selects custom mode once the pair exists. Set isUsingSFSymbols = true to keep the images but display the built-in symbols instead; a later on/off image assignment reevaluates the mode.
useOffImageForClear defaults to true. Set it to false to distinguish indeterminate visually, using minus.square or your clear image. Two-state mode always uses the off image for clear. A missing custom clear image also falls back to off. Those fallbacks do not overwrite your preference, so adding a clear image later works regardless of property assignment order.
Template images use tintColor; original-color images retain their colors. Disabled controls are dimmed, with template images using the dynamic secondary label color. Images fit the full bounds without distortion, including during partial redraws. Empty images are ignored safely.
Use the control on the main actor, as with other UIKit controls. Set a localized accessibilityLabel describing the checkbox's purpose. VoiceOver activation uses the same state transition and events as touch activation. The selected and disabled accessibility traits follow the current state.
Default accessibility values are On, Off, and Mixed. Translate RVS_Checkbox.On, RVS_Checkbox.Off, and RVS_Checkbox.Mixed in your app's Localizable.strings, or assign a custom accessibilityValue. Set the override to nil to restore automatic state descriptions.
Physical haptics depend on device capability and system settings. Verify their feel on hardware.
Open RVS_Checkbox.xcworkspace and run either:
- RVS_Checkbox_TestHarness (Direct): links this checkout's Xcode static library.
- RVS_Checkbox_TestHarness (SPM): uses this checkout as a local Swift package.
Both harnesses exercise the code you are editing. They demonstrate storyboard and programmatic construction, image replacement, tint, enable/disable, animation, and two/three-state behavior. The console logs control events. See the manual verification checklist.
Use Product → Build Documentation on the library scheme for DocC, or Option-click a symbol / open Xcode's Quick Help inspector for its API contract. The checked-in docs/ website may describe an older release; the source comments and DocC catalog are the maintained documentation.
UISwitch inheritance, enum raw values, two-state storage, and the public checkbox API are preserved. The following corrections can affect code that depended on previous bugs:
isOffnow returns true for two-state clear/off.- Reading
nextStateno longer changes the next activation. - Direct property assignments no longer trigger haptics; user activation produces one feedback request.
valueaccepts all signed integers, matchingsetValue, instead of trapping outside -1...1.- Missing images and two-state mode no longer permanently force
useOffImageForClearto true. - UIKit's native switch subviews are hidden instead of deleted; caller-added subviews survive layout.
See CHANGELOG.md and PRIVACY.md. Distributed under the MIT License.