Skip to content

Instantly share code, notes, and snippets.

@FradSer
Created November 28, 2021 08:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FradSer/3ea2366945bf60993823f13d39e554a6 to your computer and use it in GitHub Desktop.
Save FradSer/3ea2366945bf60993823f13d39e554a6 to your computer and use it in GitHub Desktop.
A iOS flashlight like button in SwiftUI.
//
// LongPressButton.swift
// Pachino
//
// Created by Frad LEE on 6/28/21.
//
import SwiftUI
// MARK: - LongPressButton
/// A iOS flashlight like button.
///
/// # References
///
/// - [Building Fluid Interfaces. How to create natural gestures and…](https://medium.com/@nathangitter/building-fluid-interfaces-ios-swift-9732bb934bf5)
/// - [Composing SwiftUI Gestures](https://developer.apple.com/documentation/swiftui/composing-swiftui-gestures)
struct LongPressButton<Label: View>: View {
// MARK: Lifecycle
public init(@ViewBuilder label: () -> Label, action: @escaping () -> Void) {
self.action = action
self.label = label()
}
// MARK: Internal
@State var viewState: Bool = false
var action: () -> Void
var label: Label
// var alertMessage: String
// var alertDestructiveButton: String
var body: some View {
let minimumLongPressDuration = 0.3
let longPress =
LongPressGesture(
minimumDuration: minimumLongPressDuration
)
.sequenced(
before: DragGesture(minimumDistance: 0)
)
.updating($pressState) { value, state, _ in
switch value {
case .first:
state = .activated
case .second:
state = .confirmed
}
}
.onEnded { value in
guard case .second = value else { return }
self.viewState.toggle()
self.showingAlert = true
action()
}
Button(action: {}, label: {
label
.symbolRenderingMode(.hierarchical)
.scaleEffect(pressState.isPressing ? 1.4 : 1)
.animation(.easeOut(duration: minimumLongPressDuration),
value: pressState.isPressing)
.gesture(longPress)
})
// .alert(alertMessage, isPresented: $showingAlert) {
// Button("Cancel", role: .cancel) {}
// Button(alertDestructiveButton, role: .destructive) {}
// }
}
// MARK: Private
@GestureState private var pressState = PressState.idle
@State private var showingAlert = false
}
// MARK: - PressState
private enum PressState {
/// **Default state**.
/// The button is ready to be activiated.
case idle
/// The button with enough duration long pressing.
case activated
/// The button has recently switched on/off.
case confirmed
// MARK: Internal
var isPressing: Bool {
switch self {
case .activated:
return true
case .confirmed, .idle:
return false
}
}
}
// MARK: - LongPressButton_Previews
struct LongPressButton_Previews: PreviewProvider {
static var previews: some View {
LongPressButton(label: {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 64))
}, action: {})
.frame(width: 64, height: 64, alignment: .center)
.foregroundColor(.red)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment