SwiftUI 视图未更新到环境对象
SwiftUI View Not Updating to Environment Object
我在按下 MenuView 中的按钮时让 ContentBodyView 正确更新时遇到问题。
当我打印出 @Published currentPage var 时,它会在按下按钮时发生变化,但似乎没有被传送到 ContentBodyView 中。我错过了什么吗?
我根据本教程编写了代码:https://blckbirds.com/post/how-to-navigate-between-views-in-swiftui-by-using-an-environmentobject/
我尝试在 ContentBodyView 上执行 .onReceive 事件,但视图仍然没有改变。
App.swift:
@main
struct App: App {
@StateObject var viewRouter = ViewRouter()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(viewRouter)
}
}
}
内容视图:
struct ContentView: View {
// MARK: - PROPERTIES
// for future use...
@State var width = UIScreen.main.bounds.width - 90
// to hide view...
@State var x = -UIScreen.main.bounds.width + 90
@EnvironmentObject var viewRouter: ViewRouter
// MARK: - BODY
var body: some View {
VStack {
ZStack(alignment: Alignment(horizontal: .leading, vertical: .top)) {
ContentBodyView(x: $x)
.environmentObject(ViewRouter())
MenuView(x: $x)
.shadow(color: Color.black.opacity(x != 0 ? 0.1 : 0), radius: 5, x: 5, y: 0)
.offset(x: x)
.background(Color.black.opacity(x == 0 ? 0.5 : 0).ignoresSafeArea(.all, edges: .vertical).onTapGesture {
// hiding the view when back is pressed...
withAnimation {
x = -width
}
}) //: background
.environmentObject(ViewRouter())
} //: ZSTACK
// adding gesture or drag feature...
.gesture(DragGesture().onChanged({ (value) in
withAnimation {
if value.translation.width > 0 {
// disabling over drag...
if x < 0 {
x = -width + value.translation.width
}
} else {
if x != -width {
x = value.translation.width
}
}
}
}).onEnded({ (value) in
withAnimation {
// checking if half the value of menu is dragged means setting x to 0...
if -x < width / 2 {
x = 0
} else {
x = -width
}
}
})) //: GESTURE
} //: VSTACK
}
}
菜单视图:
struct MenuView: View {
// MARK: - PROPERTIES
var edges = UIApplication.shared.windows.first?.safeAreaInsets
// for future use...
@State var width = UIScreen.main.bounds.width - 90
// to hide view...
@Binding var x: CGFloat
@EnvironmentObject var viewRouter: ViewRouter
// MARK: - BODY
var body: some View {
HStack(spacing: 0) {
VStack(alignment: .leading) {
HStack{
Button(action: {
withAnimation {
x = -width
}
}) {
Image(systemName: "xmark")
.resizable()
.frame(width: 18, height: 18)
.padding()
.padding(.top, 25)
.foregroundColor(Color.black)
}
Spacer()
}
ForEach(menuData) { item in
Button(action: {
withAnimation {
if (item.router == "shareables") {
viewRouter.currentPage = .shareables
} else {
viewRouter.currentPage = .home
}
x = -width
}
}) {
Text("\(item.label)")
} //: BUTTON
} //: FOREACH
} //: VSTACK
.padding(.horizontal,20)
// since vertical edges are ignored....
.padding(.top,edges!.top == 0 ? 15 : edges?.top)
.padding(.bottom,edges!.bottom == 0 ? 15 : edges?.bottom)
// default width...
.frame(width: UIScreen.main.bounds.width - 90)
.background(Color.white)
.ignoresSafeArea(.all, edges: .vertical)
Spacer(minLength: 0)
} //: HSTACK
}
}
ContentBodyView:
struct ContentBodyView: View {
// MARK: - PROPERTIES
@EnvironmentObject var viewRouter: ViewRouter
@Binding var x : CGFloat
// MARK: - BODY
var body: some View{
VStack {
switch viewRouter.currentPage {
case .home:
NavigationBarView(x: $x, title: "Home")
Spacer()
HomeView()
.transition(.scale)
case .shareables:
NavigationBarView(x: $x, title: "Shareables")
Spacer()
ShareablesView()
.transition(.scale)
} //: SWITCH
} //: VSTACK
// for drag gesture...
.contentShape(Rectangle())
.background(Color.white)
}
}
ViewRouter:
final class ViewRouter: ObservableObject {
@Published var currentPage: Page = .home
}
enum Page {
case home
case shareables
}
尝试在所有视图中使用相同的 ViewRouter
实例。
以下代码创建 ViewRouter
的新实例:
ContentBodyView(x: $x)
.environmentObject(ViewRouter())
MenuView(x: $x)
...
.environmentObject(ViewRouter())
将它们替换为:
@EnvironmentObject var viewRouter: ViewRouter
...
.environmentObject(viewRouter)
但实际上您通常只需要为每个环境注入一次 .environmentObject
,因此所有这些调用可能都是不必要的。
在 ContentView
中注入一次 ViewRouter
应该就足够了(如果你没有使用 sheet
等):
@StateObject var viewRouter = ViewRouter()
...
ContentView().environmentObject(viewRouter)
我在按下 MenuView 中的按钮时让 ContentBodyView 正确更新时遇到问题。
当我打印出 @Published currentPage var 时,它会在按下按钮时发生变化,但似乎没有被传送到 ContentBodyView 中。我错过了什么吗?
我根据本教程编写了代码:https://blckbirds.com/post/how-to-navigate-between-views-in-swiftui-by-using-an-environmentobject/
我尝试在 ContentBodyView 上执行 .onReceive 事件,但视图仍然没有改变。
App.swift:
@main
struct App: App {
@StateObject var viewRouter = ViewRouter()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(viewRouter)
}
}
}
内容视图:
struct ContentView: View {
// MARK: - PROPERTIES
// for future use...
@State var width = UIScreen.main.bounds.width - 90
// to hide view...
@State var x = -UIScreen.main.bounds.width + 90
@EnvironmentObject var viewRouter: ViewRouter
// MARK: - BODY
var body: some View {
VStack {
ZStack(alignment: Alignment(horizontal: .leading, vertical: .top)) {
ContentBodyView(x: $x)
.environmentObject(ViewRouter())
MenuView(x: $x)
.shadow(color: Color.black.opacity(x != 0 ? 0.1 : 0), radius: 5, x: 5, y: 0)
.offset(x: x)
.background(Color.black.opacity(x == 0 ? 0.5 : 0).ignoresSafeArea(.all, edges: .vertical).onTapGesture {
// hiding the view when back is pressed...
withAnimation {
x = -width
}
}) //: background
.environmentObject(ViewRouter())
} //: ZSTACK
// adding gesture or drag feature...
.gesture(DragGesture().onChanged({ (value) in
withAnimation {
if value.translation.width > 0 {
// disabling over drag...
if x < 0 {
x = -width + value.translation.width
}
} else {
if x != -width {
x = value.translation.width
}
}
}
}).onEnded({ (value) in
withAnimation {
// checking if half the value of menu is dragged means setting x to 0...
if -x < width / 2 {
x = 0
} else {
x = -width
}
}
})) //: GESTURE
} //: VSTACK
}
}
菜单视图:
struct MenuView: View {
// MARK: - PROPERTIES
var edges = UIApplication.shared.windows.first?.safeAreaInsets
// for future use...
@State var width = UIScreen.main.bounds.width - 90
// to hide view...
@Binding var x: CGFloat
@EnvironmentObject var viewRouter: ViewRouter
// MARK: - BODY
var body: some View {
HStack(spacing: 0) {
VStack(alignment: .leading) {
HStack{
Button(action: {
withAnimation {
x = -width
}
}) {
Image(systemName: "xmark")
.resizable()
.frame(width: 18, height: 18)
.padding()
.padding(.top, 25)
.foregroundColor(Color.black)
}
Spacer()
}
ForEach(menuData) { item in
Button(action: {
withAnimation {
if (item.router == "shareables") {
viewRouter.currentPage = .shareables
} else {
viewRouter.currentPage = .home
}
x = -width
}
}) {
Text("\(item.label)")
} //: BUTTON
} //: FOREACH
} //: VSTACK
.padding(.horizontal,20)
// since vertical edges are ignored....
.padding(.top,edges!.top == 0 ? 15 : edges?.top)
.padding(.bottom,edges!.bottom == 0 ? 15 : edges?.bottom)
// default width...
.frame(width: UIScreen.main.bounds.width - 90)
.background(Color.white)
.ignoresSafeArea(.all, edges: .vertical)
Spacer(minLength: 0)
} //: HSTACK
}
}
ContentBodyView:
struct ContentBodyView: View {
// MARK: - PROPERTIES
@EnvironmentObject var viewRouter: ViewRouter
@Binding var x : CGFloat
// MARK: - BODY
var body: some View{
VStack {
switch viewRouter.currentPage {
case .home:
NavigationBarView(x: $x, title: "Home")
Spacer()
HomeView()
.transition(.scale)
case .shareables:
NavigationBarView(x: $x, title: "Shareables")
Spacer()
ShareablesView()
.transition(.scale)
} //: SWITCH
} //: VSTACK
// for drag gesture...
.contentShape(Rectangle())
.background(Color.white)
}
}
ViewRouter:
final class ViewRouter: ObservableObject {
@Published var currentPage: Page = .home
}
enum Page {
case home
case shareables
}
尝试在所有视图中使用相同的 ViewRouter
实例。
以下代码创建 ViewRouter
的新实例:
ContentBodyView(x: $x)
.environmentObject(ViewRouter())
MenuView(x: $x)
...
.environmentObject(ViewRouter())
将它们替换为:
@EnvironmentObject var viewRouter: ViewRouter
...
.environmentObject(viewRouter)
但实际上您通常只需要为每个环境注入一次 .environmentObject
,因此所有这些调用可能都是不必要的。
在 ContentView
中注入一次 ViewRouter
应该就足够了(如果你没有使用 sheet
等):
@StateObject var viewRouter = ViewRouter()
...
ContentView().environmentObject(viewRouter)