Skip to content

Instantly share code, notes, and snippets.

@christianselig
Last active July 3, 2024 21:30
Show Gist options
  • Save christianselig/f8f63cfc893e8926c1829379f1d1ca94 to your computer and use it in GitHub Desktop.
Save christianselig/f8f63cfc893e8926c1829379f1d1ca94 to your computer and use it in GitHub Desktop.
How to accomplish a long-pressable button in SwiftUI with two different techniques. The first just uses SwiftUI but due to the simultaneous gesture requirement you also have to ensure both don't fire concurrently. The second uses UIKit and wraps UIButton and UILongPressGestureRecognizer which natively handles this behavior.
import SwiftUI
// PURE SwiftUI way with State tracking to prevent double events
struct ContentView: View {
@State private var ignoreTapEvent = false
var body: some View {
Button {
guard !ignoreTapEvent else {
ignoreTapEvent = false
return
}
print("Tapped")
} label: {
Text("Tap me!")
}
.simultaneousGesture(LongPressGesture(minimumDuration: 1.0)
.onEnded { didEnd in
ignoreTapEvent = true
print("Long pressed!")
}
)
}
}
// UIKit-assisted way by wrapping UIButton and using UILongPressGestureRecognizer to have system properly handle concurrent button gestures
struct ContentView: View {
var body: some View {
LongPressButton(useCircularShape: true, systemIconName: "list.bullet.clipboard.fill") {
print("Tap")
} onLongPress: {
print("Long press")
}
}
}
struct LongPressButton: View {
let useCircularShape: Bool
let systemIconName: String
let onTap: () -> Void
let onLongPress: () -> Void
var body: some View {
_LongPressButton(useCircularShape: useCircularShape, systemIconName: systemIconName, onTap: onTap, onLongPress: onLongPress)
.fixedSize()
}
}
struct _LongPressButton: UIViewRepresentable {
let useCircularShape: Bool
let systemIconName: String
let onTap: () -> Void
let onLongPress: () -> Void
func makeUIView(context: Context) -> some UIView {
var config = UIButton.Configuration.borderless()
config.image = UIImage(systemName: systemIconName)
config.cornerStyle = useCircularShape ? .capsule : UIButton.Configuration.borderless().cornerStyle
let button = UIButton(configuration: config, primaryAction: .init() { action in
onTap()
})
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.longPressed(gestureRecognizer:)))
button.addGestureRecognizer(longPressGestureRecognizer)
return button
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(button: self)
}
class Coordinator: NSObject, UIGestureRecognizerDelegate {
let button: _LongPressButton
init(button: _LongPressButton) {
self.button = button
}
@objc func longPressed(gestureRecognizer: UILongPressGestureRecognizer) {
guard gestureRecognizer.state == .began else { return }
button.onLongPress()
}
}
}
@marcusziade
Copy link

kewl 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment