Skip to content

Instantly share code, notes, and snippets.

@yannxou
Last active February 23, 2023 16:50
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 yannxou/18ead574db14cab93f04212d8310ebee to your computer and use it in GitHub Desktop.
Save yannxou/18ead574db14cab93f04212d8310ebee to your computer and use it in GitHub Desktop.
SwiftUI: Synchronize bindings
import SwiftUI
extension View {
/// Synchronizes the bindings so changes in any of them are propagated to the other.
///
/// The following example shows how to synchronize a Published property in a view model with a Binding used for presenting a sheet:
/// ```
/// class ViewModel: ObservableObject {
/// @Published var isPresented = false
/// }
///
/// struct ContentView: View {
/// @ObservedObject var viewModel: ViewModel
/// @State private var isPresented = false
///
/// var body: some View {
/// Button {
/// viewModel.isPresented.toggle()
/// } label: {
/// Text("Show detail")
/// }
/// .sheet(isPresented: $isPresented) {
/// DetailView()
/// }
/// .sync($viewModel.isPresented, with: $isPresented)
/// }
/// }
///
/// struct DetailView: View {
/// @Environment(\.dismiss) private var dismiss
///
/// var body: some View {
/// Button {
/// dismiss()
/// } label: {
/// Text("Dismiss")
/// }
/// }
/// }
/// ```
/// - Parameters:
/// - binding1: First binding
/// - binding2: Second binding
/// - Returns: A view that automatically syncs the two values.
func sync<T: Equatable>(_ binding1: Binding<T>, with binding2: Binding<T>) -> some View {
self
.onChange(of: binding1.wrappedValue) {
binding2.wrappedValue = $0
}
.onChange(of: binding2.wrappedValue) {
binding1.wrappedValue = $0
}
}
/// Synchronizes a binding with a @FocusState property wrapper so changes in any of them are propagated to the other.
///
/// The following example shows how to synchronize a Published property in a view model with a view's FocusState property:
/// ```
/// class ViewModel: ObservableObject {
/// @Published var hasFocus: Bool = false
/// }
///
/// struct ContentView: View {
/// @ObservedObject private var viewModel = ViewModel()
/// @FocusState private var hasFocus: Bool
///
/// var body: some View {
/// Form {
/// TextField("Text", text: $viewModel.textField)
/// .focused($hasFocus)
/// }
/// .sync($viewModel.hasFocus, with: _hasFocus)
/// }
/// }
/// ```
/// - Parameters:
/// - binding: A binding
/// - focusState: A `@FocusState` property wrapper
/// - Returns: A view that automatically syncs the two values.
func sync<T: Equatable>(_ binding: Binding<T>, with focusState: FocusState<T>) -> some View {
self
.onChange(of: binding.wrappedValue) {
focusState.wrappedValue = $0
}
.onChange(of: focusState.wrappedValue) {
binding.wrappedValue = $0
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment