swift - 自定义视图不会使用通过绑定提供的状态变量更新,但调试手表会显示更改
问题描述
我开始了解 SwiftUI 的 @Binding 和 @State 方式。或者至少我喜欢这样想。无论如何,有一些调试结果让我很困惑。让我解释。
这里的目标是控制 ContentView 上“浮动”视图的位置。这涉及将@binding 变量发送到ContentView 中的@State 的消息。这有效:您可以在调试器中看到它。预期的结果是一个浮动矩形,当按下齿轮按钮时它会改变屏幕中的位置。
浮动视图可以通过它自己的@State 来控制它浮动的“位置”(“y”坐标中的高或低)。如果 ViewPosition 是硬编码的,则此方法有效。
现在,问题在于,如果您观察在调试器中传递的值,拼图工作得很好,但事实是 floatong 视图总是使用相同的值来处理。怎么会这样?
我们可以在附加代码中看到效果,如果监视替代情况,则在第 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
没有错误,编译良好并且变量通过。可以将硬编码的值传递给浮动视图,并且矩形会响应,但如果以编程方式提供值则不会。
解决方案
只需删除TestView 中的 in @State
。@State var aposition
基本上,@State 变量旨在表示事实来源,因此它们永远不会从更高的视图传递值。
我可以写一个关于绑定如何工作的长篇解释,但它在 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 )
}
}
推荐阅读
- android - Sqlite Ionic:IOS设备找不到数据库
- python - 来自 Python 的 Arduino 串行数据处理
- python - Python Matplotlib:着色 3D 散点图
- sails.js - Waterline:创建或更新填充记录
- django - 如何在 django 中获取知道 ID 号的字段名称
- python - 我想对字典键中的整数进行排序
- c# - 将 Unity Admob 横幅定位在带有显示屏切口的设备底部
- azure - AKS 是否/是否支持跨更新域传播 pod?
- jboss - Jboss 模块文件夹下的 .jar.index 文件是什么?
- r - 使用 R 的条件滞后计算