Created
November 10, 2021 13:50
-
-
Save berikv/ec3b184ab29e66253e74b22c1a50825a to your computer and use it in GitHub Desktop.
SwiftUI Bindings playground
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
// Drop this code in an xcode playground for iOS | |
// Open the canvas to see the view rendered | |
// Cmd+Enter to Run | |
import PlaygroundSupport | |
import SwiftUI | |
// Wrap your application state in observable objects | |
// Adhering to ObservableObject is needed for @ObservedObject, @StateObject and @EnvironmentObject | |
final class Clock: ObservableObject { | |
static let shared = Clock() | |
// @Published properties update the view when they change | |
@Published var hour: Int = 0 | |
@Published var minute: Int = 0 | |
@Published var second: Int = 0 | |
init() { | |
// Add an offset to see which clocks exist | |
let timeOffset = Double.random(in: 0 ..< 4) * 60 * 60 | |
Timer.scheduledTimer(withTimeInterval: 1.0/4, repeats: true, block: { _ in | |
let now = Date().advanced(by: timeOffset) | |
// Setting @Published properties will trigger the objectWillChange publisher | |
self.hour = Calendar.current.component(.hour, from: now) | |
self.minute = Calendar.current.component(.minute, from: now) | |
self.second = Calendar.current.component(.second, from: now) | |
}) | |
} | |
} | |
struct ModelOwnedClockView: View { | |
// @ObservedObject tracks changes on an object that is owned outside of the view | |
@ObservedObject var clock = Clock.shared | |
var body: some View { | |
Text("\(clock.hour):\(clock.minute):\(clock.second)") | |
} | |
} | |
struct PassedInClockView: View { | |
// @ObservedObject tracks changes on an object passed through init | |
@ObservedObject var clock: Clock | |
// Whenever a @State or other binding changes, the whole subtree is re-rendered. | |
// So it is not needed to observe value types: `var font: Font` would work fine here too. | |
// @Binding makes a value type mutable: Setting font in this view will also update the value in the parent view | |
@Binding var font: Font | |
var body: some View { | |
Text("\(clock.hour):\(clock.minute):\(clock.second)") | |
.font(font) | |
.onChange(of: clock.second) { newValue in | |
if newValue.isMultiple(of: 10) { | |
font = .system(size: 25) | |
} | |
} | |
} | |
} | |
struct EnvironmentClockView: View { | |
// @EnvironmentObject tracks changes in an object, provided by ancester views | |
@EnvironmentObject var clock: Clock | |
// @Environment(...) tracks changes in value types, provided by ancester views | |
@Environment(\.font) var font | |
var body: some View { | |
Text("\(clock.hour):\(clock.minute):\(clock.second)") | |
.font(font) | |
} | |
} | |
// A generic wrapper, to show how @Environment and @EnvironmentObject are passed through | |
struct WrapperView<Wrapped: View>: View { | |
private let subView: Wrapped | |
init(_ subView: Wrapped) { self.subView = subView } | |
var body: some View { subView.foregroundColor(.red) } | |
} | |
struct ParentView: View { | |
@StateObject var clock = Clock() | |
@State var font = Font.body | |
var body: some View { | |
VStack(spacing: 4) { | |
Text("The time is") | |
ModelOwnedClockView() | |
.font(font) // SwiftUI internally uses @Environment(...) to make "inheritance" work | |
PassedInClockView(clock: clock, font: $font) | |
WrapperView(EnvironmentClockView()) | |
.environmentObject(clock) | |
.environment(\.font, font) | |
Button("Toggle bold") { | |
if font == .body { | |
font = .body.bold() | |
} else { | |
font = .body | |
} | |
} | |
} | |
} | |
} | |
// Types required to support @Environment(\.font) | |
enum FontKey: EnvironmentKey { | |
static var defaultValue: Font { Font.body } | |
} | |
extension EnvironmentValues { | |
var font: Font { | |
get { self[FontKey.self] } | |
set { self[FontKey.self] = newValue } | |
} | |
} | |
PlaygroundPage.current.setLiveView(ParentView().frame(width: 200, height: 300)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment