我开始了解 SwiftUI 的 @Binding 和 @State 方式。或者至少我喜欢这么认为。不管怎样,有一些调试结果让我很困惑。让我解释一下。
这里的目标是控制 ContentView 上“ float ” View 的位置。这涉及到一条消息,将 @binding 变量发送到 ContentView 中的 @State。这是有效的:您可以在调试器中看到它。预期结果是一个 float 矩形,当按下齿轮按钮时,该矩形会改变屏幕中的位置。
float View 可以传递它自己的@State来控制它 float 的“位置”(“y”坐标的高或低)。如果 ViewPosition 是硬编码传递的,则此方法有效。
现在的问题是,如果您观察在调试器中传递的值,则拼图效果很好,但事实是 floatong View 始终使用相同的值来处理。怎么会这样?
我们可以在所附代码中看到效果,如果监视替代情况,则在第 120 和 133 行设置断点;如果监视默认情况,则在第 76 行设置断点。
代码被剪切粘贴到选项卡式 swiftui 应用程序的新项目中。
我尝试了为两个不同的 ContentView 选项提供的两种粗略方法(重命名以更改执行分支)。观察调试器中的变量非常重要,以便享受完整的拼图体验,因为 .high 和 .low 值已顺利通过,但矩形仍保持静止。
//
// ContentView.swift
// TestAppUno
//
import SwiftUI
struct MenuButton1: View {
@Binding var menuButtonAction: Bool
var title: String = "--"
var body: some View {
Button(action: {self.menuButtonAction.toggle()}) {
Image(systemName:"gear")
.resizable()
.imageScale(.large)
.aspectRatio(1, contentMode: .fit)
.frame(minWidth: 50, maxWidth: 50, minHeight: 50, maxHeight: 50, alignment: .topLeading)
}
.background(Color.white.opacity(0))
.cornerRadius(5)
.padding(.vertical, 10)
.position(x: 30, y: 95)
}
}
struct MenuButton2: View {
@Binding var menuButtonAction: ViewPosition
var title: String = "--"
var body: some View {
Button(action: {self.toggler()}) {
Image(systemName:"gear")
.resizable()
.imageScale(.large)
.aspectRatio(1, contentMode: .fit)
.frame(minWidth: 50, maxWidth: 50, minHeight: 50, maxHeight: 50, alignment: .topLeading)
}
.background(Color.white.opacity(0))
.cornerRadius(5)
//.border(Color.black, width: 1)
.padding(.vertical, 10)
.position(x: 30, y: 95)
}
func toggler()->ViewPosition {
if (self.menuButtonAction == ViewPosition.high) { self.menuButtonAction = ViewPosition.low; return ViewPosition.low } else { self.menuButtonAction = ViewPosition.high; return ViewPosition.low }
}
}
struct ContentView: View {
@State private var selection = 0
@State var moveCard = false
@State var vpos = ViewPosition.low
var body: some View {
TabbedView(selection: $selection){
ZStack() {
MenuButton2(menuButtonAction: $vpos)
//if(self.moveCard){self.vpos = ViewPosition.low} else {self.vpos = ViewPosition.low }
// Correct answer, change 1 of 3
//TestView(aposition: $vpos) { // <-- OK
TestView(aposition:self.vpos) {
VStack(alignment: HorizontalAlignment.center, spacing: 1.0){
Text("See here")
.font(.headline)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
}
}
.tabItemLabel(Image("first"))
.tag(0)
Text("Nothing here")
.tabItemLabel(Image("second"))
.tag(1)
}
}
}
// Correct answer, change 2 of 3
struct ContentView1: View { // <-- Remove this block
@State private var selection = 0
@State var moveCard = false
@State var cardpos = ViewPosition.low
var body: some View {
TabbedView(selection: $selection){
ZStack() {
MenuButton1(menuButtonAction: $moveCard)
if(self.moveCard){
TestView(aposition:ViewPosition.low) {
VStack(alignment: HorizontalAlignment.center, spacing: 1.0){
Text("See here")
.font(.headline)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
}
}else{
TestView(aposition:ViewPosition.high) {
VStack(alignment: HorizontalAlignment.center, spacing: 1.0){
Text("See here")
.font(.headline)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
}
}
}
.tabItemLabel(Image("first"))
.tag(0)
Text("Nothing here")
.tabItemLabel(Image("second"))
.tag(1)
}
}
}
struct TestView<Content: View> : View {
@State var aposition : ViewPosition
//proposed solution #1
//var aposition : ViewPosition
//proposed solution #2 -> Correct
//@Binding var aposition : ViewPosition // <- Correct answer, change 3 of 3
var content: () -> Content
var body: some View {
print("Position: " + String( format: "%.3f", Double(self.aposition.rawValue)))
return Group {
self.content()
}
.frame(height: UIScreen.main.bounds.height/2)
.frame(width: UIScreen.main.bounds.width)
.background(Color.red)
.cornerRadius(10.0)
.shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)
.offset(y: self.aposition.rawValue )
}
}
enum ViewPosition: CGFloat {
case high = 50
case low = 500
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
没有错误,编译良好并且变量已传递。可以对 float View 进行硬编码传递值,并且矩形会做出响应,但如果以编程方式提供值则不会。
最佳答案
只需删除 TestView 中的 @State var aposition
中的 @State
即可。基本上,@State 变量旨在表示事实来源,因此它们永远不会从更高的 View 传递值。
我可以写一篇关于绑定(bind)如何工作的很长的解释,但它在 WWDC session Data Flow with SwiftUI 中得到了完美的解释。只需 37 分钟,最终将为您节省大量时间。它明确区分了何时需要使用:@State、@Binding、@BindingObject 和 @EnvironmentObject。
struct TestView<Content: View> : View {
var aposition : ViewPosition
var content: () -> Content
var body: some View {
print("Position: " + String( format: "%.3f", Double(self.aposition.rawValue)))
return Group {
self.content()
}
.frame(height: UIScreen.main.bounds.height/2)
.frame(width: UIScreen.main.bounds.width)
.background(Color.red)
.cornerRadius(10.0)
.shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)
.offset(y: self.aposition.rawValue )
}
}
关于swift - 自定义 View 不会使用通过绑定(bind)提供的状态变量更新,但调试监视会显示更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57022388/