Created
May 10, 2021 10:56
-
-
Save vibrazy/16e0408b5be7be4df508e92e0cf9448b to your computer and use it in GitHub Desktop.
Toggled - Simpler way to deal with hardcoded ViewModifers values in SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ToggledExampleView_Previews: PreviewProvider { | |
static var previews: some View { | |
DebuggingToggledExampleView() | |
} | |
} | |
// MARK: - Toggled Source | |
protocol ToggleInterface { | |
associatedtype ValueType | |
var values: (on: ValueType, off: ValueType) { get set } | |
func value(state: Bool) -> ValueType | |
} | |
struct Toggled<T>: ToggleInterface { | |
typealias ValueType = T | |
var values: (on: T, off: T) | |
func value(state: Bool) -> T { | |
guard state else { | |
return values.off | |
} | |
return values.on | |
} | |
} | |
extension Binding where Value: ToggleInterface { | |
var onValue: Binding<Value.ValueType> { | |
return Binding<Value.ValueType> { | |
wrappedValue.values.on | |
} set: { newValue in | |
wrappedValue.values.on = newValue | |
} | |
} | |
var offValue: Binding<Value.ValueType> { | |
return Binding<Value.ValueType> { | |
wrappedValue.values.off | |
} set: { newValue in | |
wrappedValue.values.off = newValue | |
} | |
} | |
} | |
struct DebuggingToggledExampleView: View { | |
@State var isShowingMenu = false | |
@State var backgroundColor = Toggled<Color>(values: (on: .red, off: .blue)) | |
@State var font = Toggled<Font>(values: (on: .title, off: .callout)) | |
@State var stackBackgroundColor = Toggled<Color>(values: (on: .yellow, off: .green)) | |
@State var stackPadding = Toggled<CGFloat>(values: (on: 20, off: 0)) | |
@State var rotation = Toggled<Angle>( | |
values: ( | |
on: .init(degrees: -45), | |
off: .zero | |
) | |
) | |
var body: some View { | |
ZStack { | |
backgroundColor.value(state: isShowingMenu) | |
.edgesIgnoringSafeArea(.all) | |
VStack { | |
VStack { | |
Text("Hello from Toggled") | |
.fixedSize() | |
.font(font.value(state: isShowingMenu)) | |
Button(action: { | |
withAnimation { | |
isShowingMenu.toggle() | |
} | |
}, label: { | |
Text("Toggle Me") | |
}) | |
} | |
.padding(stackPadding.value(state: isShowingMenu)) | |
.background(stackBackgroundColor.value(state: isShowingMenu)) | |
.rotationEffect(rotation.value(state: isShowingMenu)) | |
.zIndex(10) | |
Spacer() | |
} | |
VStack { | |
DebugToggledViewColor(title: "$backgroundColor", toggled: $backgroundColor) | |
DebugToggledViewColor(title: "$stackBackgroundColor", toggled: $stackBackgroundColor) | |
DebugToggledViewRangeValue(title: "$stackPadding", toggled: $stackPadding, range: 0...100) | |
DebugToggledViewAngleRangeValue(title: "$rotation", toggled: $rotation, range: -360...360) | |
} | |
.font(.footnote) | |
.padding() | |
.background(Color.white.opacity(0.2)) | |
.padding(.horizontal, 50) | |
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) | |
} | |
} | |
} | |
struct DebugToggledViewAngleRangeValue<R>: View where R : BinaryFloatingPoint, R.Stride : BinaryFloatingPoint { | |
var title: String | |
var toggled: Binding<Toggled<Angle>> | |
var range: ClosedRange<R> | |
var body: some View { | |
VStack(spacing: 0) { | |
Text(title) | |
HStack { | |
VStack { | |
Text("On") | |
Slider(value: Binding(get: { | |
R(toggled.wrappedValue.values.on.degrees) | |
}, set: { newValue in | |
toggled.wrappedValue.values.on = Angle(degrees: Double(newValue)) | |
}), in: range) | |
} | |
VStack { | |
Text("Off") | |
Slider(value: Binding(get: { | |
R(toggled.wrappedValue.values.off.degrees) | |
}, set: { newValue in | |
toggled.wrappedValue.values.off = Angle(degrees: Double(newValue)) | |
}), in: range) | |
} | |
} | |
} | |
} | |
} | |
struct DebugToggledViewRangeValue<T>: View where T : BinaryFloatingPoint, T.Stride : BinaryFloatingPoint { | |
var title: String | |
var toggled: Binding<Toggled<T>> | |
var range: ClosedRange<T> | |
var body: some View { | |
VStack(spacing: 0) { | |
Text(title) | |
HStack { | |
VStack { | |
Text("On") | |
Slider(value: toggled.onValue, in: range) | |
} | |
VStack { | |
Text("Off") | |
Slider(value: toggled.offValue, in: range) | |
} | |
} | |
} | |
} | |
} | |
struct DebugToggledViewColor: View { | |
var title: String | |
var toggled: Binding<Toggled<Color>> | |
var body: some View { | |
VStack(spacing: 0) { | |
Text(title) | |
HStack { | |
ColorPicker("On", selection: toggled.onValue) | |
Spacer() | |
ColorPicker("Off", selection: toggled.offValue) | |
} | |
} | |
} | |
} | |
extension CGSize { | |
static let identity: Self = .zero | |
} | |
extension Angle { | |
static let identity: Self = .zero | |
} | |
extension Double { | |
static let identity: Self = 1.0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment