我经常读到,我可以使用环境变量注入数据,而不是通过带有参数的视图层次结构向上传递数据。但我遇到了以下不对称(问题在最后)...
我创建了一个包含分页 TabView 的视图,该视图包含两个“并排”视图,因此我可以在它们之间滑动,或者通过按下(红色)按钮以编程方式选择另一个视图。我通过将选择作为参数传递来完成此操作,并且它按预期工作。我使用随机背景颜色来验证这两个视图没有被重绘,而只是“滑入 iPhone 显示屏”。
struct SwipeTabView: View {
@State var selectedTab = 1
var body: some View {
VStack {
Text("Main View")
// A paged tabview containing two vies side by side.
// Swipe within this main view to view the "off-screen" other view, or
// click the red button to select it dynamically.
TabView(selection: $selectedTab) {
tab(tabID: 1, selectedTab: $selectedTab)
.tag(1)
tab(tabID: 2, selectedTab: $selectedTab)
.tag(2)
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
.background(Color.rndCol())
}
.background(Color.rndCol())
}
}
struct tab: View {
var tabID: Int
@Binding var selectedTab: Int
var body: some View {
VStack {
Text("Tab \(tabID) ").foregroundColor(.black)
Button(action: {
withAnimation{
selectedTab = tabID == 1 ? 2 : 1
}
}, label: {
ZStack {
circle
label
}
})
}
}
var circle: some View {
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.red)
}
var label: some View {
Text("\(tabID == 1 ? ">>" : "<<")").font(.title).foregroundColor(.black)
}
}
extension Color {
static func rndCol() -> Color {
Color(
red: .random(in: 0.5...1),
green: .random(in: 0.5...1),
blue: .random(in: 0.5...1)
)
}
}
然后我修改了代码,将选择作为注入的环境变量传递,但现在每次我通过滑动或按下按钮切换显示内容时,视图都会完全重绘。我可以判断这是这种情况,因为视图的背景颜色会发生变化。
@Observable class envTab {
var selectedTab = 1
}
struct SwipeTabEnvView: View {
@State var varEnvTab = envTab()
var body: some View {
SwipeTabView()
.environment(varEnvTab)
}
}
struct SwipeTabView: View {
@Environment(envTab.self) var viewEnvTab
var body: some View {
VStack {
Text("Main View")
TabView(selection: Bindable(viewEnvTab).selectedTab) {
tab(tabID: 1)
.tag(1)
tab(tabID: 2)
.tag(2)
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
.background(Color.rndCol())
}
.background(Color.rndCol())
}
}
struct tab: View {
var tabID: Int
@Environment(envTab.self) var viewEnvTab
var body: some View {
VStack {
Text("Tab \(tabID) ").foregroundColor(.black)
Button(action: {
withAnimation{
viewEnvTab.selectedTab = tabID == 1 ? 2 : 1
}
}, label: {
ZStack {
circle
label
}
})
}
}
...
}
如果重新绘制完整的视图,这会影响性能,因此应该避免。
我担心这不仅仅是 TabViews 的一个怪癖,而是注入的一个更普遍的方面。我曾认为注入数据而不是将数据作为参数传递是一种有效的代码结构化方法,以实现陡峭的层次结构,但看到这种行为后,我现在想知道注入方法是否不等同于传递参数,是否应该避免?
顺便说一句:停止selectedTab
@ObservationIgnored
重绘行为但也会阻止以编程方式切换显示的选项卡。
这只是的一个怪癖
TabView
。SwiftUI 无法识别状态selectedTab
是 的依赖项SwipeTabView
,因此当其发生变化时不会调用SwipeTabView.body
。例如
Picker
,没有这个问题。尝试用 替换选项卡视图Picker
,当您更改选择器选择时,背景颜色会发生变化,这是预期的行为。毕竟,
selectedTab
是的依赖项SwipeTabView
,因此在更改时应SwipeTabView.body
调用selectedTab
。这与环境无关。要获得预期的行为,您需要做的就是让 SwiftUI 正确识别出这
selectedTab
是一个依赖项。使用@Observable
可以实现这一点,因为 SwiftUI 以不同的方式处理这些对象。它不必在环境中:selectedTab
另一种方法是简单地调用in的 getterSwipeTabView.body
。