Implemented the RecodingButtonStyle button style in the Recording package target.
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import SwiftUI
|
||||
|
||||
/// The button style for the controls of a ``RecordingView``.
|
||||
///
|
||||
/// The style renders the button's label over a padded, circular red background, shrinks it slightly while pressed, and dims it while disabled.
|
||||
struct RecordingButtonStyle: ButtonStyle {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// Whether the button allows user interaction.
|
||||
@Environment(\.isEnabled) private var isEnabled
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Creates the styled body of the button.
|
||||
///
|
||||
/// - Parameter configuration: The properties of the button being styled.
|
||||
func makeBody(
|
||||
configuration: Configuration
|
||||
) -> some View {
|
||||
configuration.label
|
||||
.frame(
|
||||
width: Constant.Size.button,
|
||||
height: Constant.Size.button
|
||||
)
|
||||
.padding(Constant.Padding.button)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(.red)
|
||||
)
|
||||
.opacity(opacity)
|
||||
.scaleEffect(scaleEffect(configuration.isPressed))
|
||||
.animation(
|
||||
.easeOut(duration: Constant.Animation.duration),
|
||||
value: configuration.isPressed
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension RecordingButtonStyle {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The opacity of the button: dimmed while disabled, fully opaque otherwise.
|
||||
var opacity: Double {
|
||||
isEnabled
|
||||
? Constant.Opacity.enabled
|
||||
: Constant.Opacity.disabled
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Provides the scale of the button for a press state.
|
||||
///
|
||||
/// - Parameter isPressed: Whether the button is currently pressed.
|
||||
/// - Returns: A slightly shrunken scale while pressed, the resting scale otherwise.
|
||||
func scaleEffect(
|
||||
_ isPressed: Bool
|
||||
) -> CGFloat {
|
||||
isPressed
|
||||
? Constant.Scale.pressed
|
||||
: Constant.Scale.idle
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Styles
|
||||
|
||||
extension ButtonStyle where Self == RecordingButtonStyle {
|
||||
|
||||
/// The button style for the controls of a ``RecordingView``.
|
||||
static var recording: RecordingButtonStyle {
|
||||
.init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
/// The constant values used across the button style.
|
||||
private enum Constant {
|
||||
/// The animation constants.
|
||||
enum Animation {
|
||||
/// The duration of the press animation.
|
||||
static let duration: TimeInterval = 0.15
|
||||
}
|
||||
|
||||
/// The opacity constants.
|
||||
enum Opacity {
|
||||
/// The opacity of a disabled button.
|
||||
static let disabled: Double = 0.4
|
||||
/// The opacity of an enabled button.
|
||||
static let enabled: Double = 1
|
||||
}
|
||||
|
||||
/// The padding constants.
|
||||
enum Padding {
|
||||
/// The padding between a button's label and its background.
|
||||
static let button: CGFloat = 8
|
||||
}
|
||||
|
||||
/// The scale constants.
|
||||
enum Scale {
|
||||
/// The scale of a button at rest.
|
||||
static let idle: CGFloat = 1
|
||||
/// The scale of a button while pressed.
|
||||
static let pressed: CGFloat = 0.85
|
||||
}
|
||||
|
||||
/// The size constants.
|
||||
enum Size {
|
||||
/// The width and height of a button's label.
|
||||
static let button: CGFloat = 24
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#Preview(
|
||||
"Recording button style"
|
||||
) {
|
||||
@Previewable @State var isDisabled: Bool = false
|
||||
|
||||
Button {} label: {
|
||||
Image(.Icon.record)
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.recording)
|
||||
.disabled(isDisabled)
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
Toggle(
|
||||
"Button disabled",
|
||||
isOn: $isDisabled
|
||||
)
|
||||
.fontWeight(.semibold)
|
||||
.dynamicTypeSize(.large)
|
||||
.padding()
|
||||
.tint(.red)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user