coc*_*oco 20 environment-variables swiftui
该应用程序有一个model存储用户当前对亮/暗模式的偏好,用户可以通过单击按钮来更改:
class DataModel: ObservableObject {
@Published var mode: ColorScheme = .light
Run Code Online (Sandbox Code Playgroud)
ContentViewbody跟踪模型,并在模型更改时调整 colorScheme:
struct ContentView: View {
@StateObject private var dataModel = DataModel()
var body: some View {
NavigationStack(path: $path) { ...
}
.environmentObject(dataModel)
.environment(\.colorScheme, dataModel.mode)
Run Code Online (Sandbox Code Playgroud)
从 Xcode 版本 14.0 beta 5 开始,这会产生一个紫色警告:Publishing changes from within view updates is not allowed, this will cause undefined behavior.还有其他方法可以做到这一点吗?或者是测试版中的一个小问题?谢谢!
mar*_*rkb 16
Xcode 14.1 Beta 3(最终)修复了“不允许从视图更新中发布更改,这将导致未定义的行为”
完全披露 - 我不完全确定为什么会发生这种情况,但这是我发现似乎有效的两个解决方案。
// -- main view
@main
struct MyApp: App {
@StateObject private var vm = ViewModel()
var body: some Scene {
WindowGroup {
ViewOne()
.environmentObject(vm)
}
}
}
// -- initial view
struct ViewOne: View {
@EnvironmentObject private var vm: ViewModel
var body: some View {
Button {
vm.isPresented.toggle()
} label: {
Text("Open sheet")
}
.sheet(isPresented: $vm.isPresented) {
SheetView()
}
}
}
// -- sheet view
struct SheetView: View {
@EnvironmentObject private var vm: ViewModel
var body: some View {
Button {
vm.isPresented.toggle()
} label: {
Text("Close sheet")
}
}
}
// -- view model
class ViewModel: ObservableObject {
@Published var isPresented: Bool = false
}
Run Code Online (Sandbox Code Playgroud)
注意:根据我的测试和下面的示例,我仍然会出现错误。但如果我有一个更复杂/嵌套的应用程序,那么错误就会消失。
将 a 添加.buttonStyle()到执行初始切换的按钮。
因此,在aContentView中Button() {}添加.buttonStyle(.plain),它将消除紫色错误:
struct ViewOne: View {
@EnvironmentObject private var vm: ViewModel
var body: some View {
Button {
vm.isPresented.toggle()
} label: {
Text("Open sheet")
}
.buttonStyle(.plain) // <-- here
.sheet(isPresented: $vm.isPresented) {
SheetView()
}
}
}
Run Code Online (Sandbox Code Playgroud)
^ 这可能更像是一种黑客而不是解决方案,因为它会从修改器输出一个新视图,这可能是导致它不在较大视图上输出错误的原因。
这要归功于 Alex Nagy(又名Rebeloper)
正如亚历克斯所解释的:
.. 在 SwiftUI 3 和 SwiftUI 4 中,数据处理方式发生了变化。SwiftUI 如何处理,更具体地说是
@Published变量..
因此,解决方案是让布尔触发器成为@State视图中的变量,而不是@PublishedViewModel 中的变量。但正如亚历克斯指出的那样,如果你的视图中有很多状态,或者无法深层链接等,它可能会让你的视图变得混乱。
但是,由于这是 SwiftUI 4 希望这些操作的方式,因此我们按如下方式运行代码:
// -- main view
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ViewOne()
}
}
}
// -- initial view
struct ViewOne: View {
@State private var isPresented = false
var body: some View {
Button {
isPresented.toggle()
} label: {
Text("Open sheet")
}
.sheet(isPresented: $isPresented) {
SheetView(isPresented: $isPresented)
// SheetView() <-- if using dismiss() in >= iOS 15
}
}
}
// -- sheet view
struct SheetView: View {
// I'm showing a @Binding here for < iOS 15
// but you can use the dismiss() option if you
// target higher
// @Environment(\.dismiss) private var dismiss
@Binding var isPresented: Bool
var body: some View {
Button {
isPresented.toggle()
// dismiss()
} label: {
Text("Close sheet")
}
}
}
Run Code Online (Sandbox Code Playgroud)
@Published和@State继续观看视频,如果您仍然需要使用该@Published变量,因为它可能与应用程序的其他区域相关联,您可以使用 a.onChange和 a.onReceive来链接这两个变量:
struct ViewOne: View {
@EnvironmentObject private var vm: ViewModel
@State private var isPresented = false
var body: some View {
Button {
vm.isPresented.toggle()
} label: {
Text("Open sheet")
}
.sheet(isPresented: $isPresented) {
SheetView(isPresented: $isPresented)
}
.onReceive(vm.$isPresented) { newValue in
isPresented = newValue
}
.onChange(of: isPresented) { newValue in
vm.isPresented = newValue
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您必须为每个sheet或触发它,那么您的代码可能会变得非常混乱fullScreenCover。
因此,为了让您更轻松地实现它,您可以创建一个 ViewModifier,Alex 也展示了它的工作原理:
extension View {
func sync(_ published: Binding<Bool>, with binding: Binding<Bool>) -> some View {
self
.onChange(of: published.wrappedValue) { newValue in
binding.wrappedValue = newValue
}
.onChange(of: binding.wrappedValue) { newValue in
published.wrappedValue = newValue
}
}
}
Run Code Online (Sandbox Code Playgroud)
并在视图上使用:
struct ViewOne: View {
@EnvironmentObject private var vm: ViewModel
@State private var isPresented = false
var body: some View {
Button {
vm.isPresented.toggle()
} label: {
Text("Open sheet")
}
.sheet(isPresented: $isPresented) {
SheetView(isPresented: $isPresented)
}
.sync($vm.isPresented, with: $isPresented)
// .onReceive(vm.$isPresented) { newValue in
// isPresented = newValue
// }
// .onChange(of: isPresented) { newValue in
// vm.isPresented = newValue
// }
}
}
Run Code Online (Sandbox Code Playgroud)
^ 以此表示的任何内容都是我的假设,而不是真正的技术理解 - 我不是技术知识渊博的人:/
| 归档时间: |
|
| 查看次数: |
8620 次 |
| 最近记录: |