Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Toggled - Simpler way to deal with hardcoded ViewModifers values in SwiftUI
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