Last active
June 12, 2024 02:40
-
-
Save vibrazy/79d407cf2eac2b0e65a61ab07f584105 to your computer and use it in GitHub Desktop.
Using OptionSet to simplify your view state. Unify your states into 1
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
// | |
// Created by Daniel Tavares on 07/05/2021. | |
// | |
import SwiftUI | |
// MARK: - OptionsSet | |
// Blog post https://dev.to/vibrazy/easy-swiftui-view-bindings-using-optionset-and-sourcery-4edb | |
protocol OptionsBinding {} | |
enum MyEnum: OptionsBinding { | |
case showBackground | |
case showBorder | |
case reduceMotionEnabled | |
case largeFont | |
case receiveEmailNotifications | |
} | |
extension MyEnum { | |
struct Options: OptionSet { | |
var rawValue: UInt | |
static let none = Self([]) | |
static let showBackground = Self(rawValue: 1 << 0) | |
static let showBorder = Self(rawValue: 1 << 1) | |
static let reduceMotionEnabled = Self(rawValue: 1 << 2) | |
static let largeFont = Self(rawValue: 1 << 3) | |
static let receiveEmailNotifications = Self(rawValue: 1 << 4) | |
} | |
} | |
extension Binding where Value: OptionSet, Value == Value.Element { | |
func bindedValue(_ options: Value) -> Bool { | |
return wrappedValue.contains(options) | |
} | |
func bind( | |
_ options: Value, | |
animate: Bool = false | |
) -> Binding<Bool> { | |
return .init { () -> Bool in | |
self.wrappedValue.contains(options) | |
} set: { newValue in | |
let body = { | |
if newValue { | |
self.wrappedValue.insert(options) | |
} else { | |
self.wrappedValue.remove(options) | |
} | |
} | |
guard animate else { | |
body() | |
return | |
} | |
withAnimation { | |
body() | |
} | |
} | |
} | |
} | |
struct LessStateVariablesView: View { | |
@State var viewOptions = MyEnum.Options.none | |
var body: some View { | |
ZStack { | |
if $viewOptions.bindedValue(.showBackground) { | |
Color.red.edgesIgnoringSafeArea(.all) | |
} | |
VStack { | |
Toggle(isOn: $viewOptions.bind(.showBackground)) { | |
Text(LocalizedStringKey("Show Background")) | |
} | |
Toggle(isOn: $viewOptions.bind(.showBorder)) { | |
Text(LocalizedStringKey("Show Border")) | |
} | |
Toggle(isOn: $viewOptions.bind(.reduceMotionEnabled)) { | |
Text(LocalizedStringKey("Reduce Motion Enabled")) | |
} | |
Toggle(isOn: $viewOptions.bind(.largeFont)) { | |
Text(LocalizedStringKey("Large Font")) | |
} | |
Toggle(isOn: $viewOptions.bind(.receiveEmailNotifications)) { | |
Text(LocalizedStringKey("Receive Notifications")) | |
} | |
} | |
.padding() | |
.font(.system(size: 22, weight: .light, design: .rounded)) | |
} | |
} | |
} | |
/* | |
https://github.com/krzysztofzablocki/Sourcery | |
Stencil Template | |
{% for type in types.enums where type.cases.count > 0 and type.based.OptionsBinding or type|annotated:"OptionsBinding" %} | |
extension {{ type.name }} { | |
struct Options: OptionSet { | |
var rawValue: UInt | |
static let none = Self([]) | |
{% for i in 0...type.cases.count %} | |
{% if not forloop.last %} | |
static let {{type.cases[i].name}} = Self(rawValue: 1 << {{i}}) | |
{% endif %} | |
{% endfor %} | |
} | |
} | |
{% endfor %} | |
extension Binding where Value: OptionSet, Value == Value.Element { | |
func bindValue(_ options: Value) -> Bool { | |
return wrappedValue.contains(options) | |
} | |
func bind( | |
_ options: Value, | |
animate: Bool = false | |
) -> Binding<Bool> { | |
return .init { () -> Bool in | |
self.wrappedValue.contains(options) | |
} set: { newValue in | |
let body = { | |
if newValue { | |
self.wrappedValue.insert(options) | |
} else { | |
self.wrappedValue.remove(options) | |
} | |
} | |
guard animate else { | |
body() | |
return | |
} | |
withAnimation { | |
body() | |
} | |
} | |
} | |
} | |
*/ |
Hi @vibrazy,
This would be very helpful for an app I'm working on -- would you be wiling to add a license so I can safely reuse it?
Thanks,
Julie
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Good shout thank you, updated it :)