Skip to content

Instantly share code, notes, and snippets.

@mattyoung
Last active September 25, 2023 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattyoung/b76aa5447429a7ee7a88d63258c934a5 to your computer and use it in GitHub Desktop.
Save mattyoung/b76aa5447429a7ee7a88d63258c934a5 to your computer and use it in GitHub Desktop.
import SwiftUI
import ComposableArchitecture
struct CounterFeature: Reducer {
struct State: Equatable {
var count = 99
var numberFactAlert: String?
}
enum Action: Equatable {
case decrementButtonTapped
case incrementButtonTapped
}
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .decrementButtonTapped:
state.count -= 1
return .none
case .incrementButtonTapped:
state.count += 1
return .none
}
}
}
struct SymbolButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.symbolVariant(.circle.fill)
.foregroundStyle(.white)
.padding(15)
.background(
RoundedRectangle(cornerRadius: 15, style: .continuous)
.fill(.tint)
)
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
}
}
extension ButtonStyle where Self == SymbolButtonStyle {
static var symbolButtonStyle: Self {
SymbolButtonStyle()
}
}
// MARK - CounterView
struct CounterView: View {
let store: StoreOf<CounterFeature>
let accentColor = Color.pink
let counterFont = Font.custom("Digital-7Monoitalic", fixedSize: 45).monospacedDigit()
let clockFont = Font.custom("Digital-7Monoitalic", fixedSize: 215).monospacedDigit()
var body: some View {
WithViewStore(self.store, observe: { $0 }) { store in
VStack(spacing: 5) {
TimelineView(.periodic(from: .now, by: 0.2)) { context in
// this does 24-hour style. Who figured this out?
// https://forums.swift.org/t/new-date-formatstyle-anyway-to-do-24-hour/52994/34
Text(context.date,
format: Date.VerbatimFormatStyle(format: "\(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits):\(second: .twoDigits)",
timeZone: .current,
calendar: .current)
)
.font(clockFont)
.lineLimit(1)
.padding(.horizontal, 20)
.minimumScaleFactor(0.2)
.foregroundColor(accentColor)
.contentTransition(.numericText(value: context.date.timeIntervalSince1970.rounded()))
.animation(.spring, value: context.date.timeIntervalSince1970)
}
HStack(spacing: 15) {
Button {
store.send(.decrementButtonTapped, animation: .spring)
} label: {
Image(systemName: "minus")
.font(counterFont)
}
.buttonStyle(.symbolButtonStyle)
.buttonRepeatBehavior(.enabled)
.sensoryFeedback(.decrease, trigger: store.count)
counterDisplay(store: store)
Button {
store.send(.incrementButtonTapped, animation: .spring)
} label: {
Image(systemName: "plus")
.font(counterFont)
}
.buttonStyle(.symbolButtonStyle)
.buttonRepeatBehavior(.enabled)
.sensoryFeedback(.increase, trigger: store.count)
}
.padding(.horizontal)
}
}
}
func counterDisplay(store: ViewStore<CounterFeature.State, CounterFeature.Action>) -> some View {
// Use this view for sizing
Text("0,000")
.font(counterFont)
.lineLimit(1)
.foregroundColor(.gray)
.opacity(0.25)
.overlay {
Text(store.count, format: .number)
.font(counterFont)
.foregroundColor(accentColor)
.frame(maxWidth: .infinity, alignment: .trailing)
.contentTransition(.numericText(value: Double(store.count)))
}
.padding(.init(top: 15, leading: 20, bottom: 15, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 20, style: .continuous)
.strokeBorder(accentColor, lineWidth: 2)
}
}
}
#Preview {
CounterView(
store: Store(initialState: CounterFeature.State()) {
CounterFeature()
}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment