我开始了解SwiftUI的@Binding和@State方法。或者至少我喜欢这样想。无论如何,有一些调试结果使我很困惑。让我解释一下。
此处的目标是控制ContentView上“浮动”视图的位置。这涉及到一条消息,该消息将@binding变量发送到ContentView中的@State。这行得通:您可以在调试器中看到它。预期的结果是一个浮动矩形,当按下齿轮按钮时,该矩形会在屏幕上改变位置。
可以将浮动视图传递给自己的@State来控制其浮动的“位置”(“ y”坐标的高低)。如果ViewPosition是通过硬编码传递的,则此方法有效。
现在,问题是,如果您看到调试器中传递的值,那么拼图会很好地起作用,但是事实是,浮空视图始终使用相同的值来处理。怎么会这样?
我们可以在所附的代码中看到效果,如果观察到替代情况,则在第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
没有错误,编译正确并且传递了变量。可以将经过硬编码的值传递给浮动视图,并且矩形会响应,但是如果以编程方式提供值,则不会。
答案 0 :(得分:0)
只需删除TestView中@State
中的@State var aposition
。基本上,@ State变量用于表示真相的来源,因此它们永远不会从较高的View传递值。
我可以写一个很长的关于绑定如何工作的解释,但是在带有SwiftUI的WWDC会话数据流中可以很好地解释。仅需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 )
}
}