自定义视图将不使用通过绑定提供的状态变量更新,但是调试监视将显示更改

时间:2019-07-13 19:54:40

标签: swift swiftui

我开始了解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

没有错误,编译正确并且传递了变量。可以将经过硬编码的值传递给浮动视图,并且矩形会响应,但是如果以编程方式提供值,则不会。

1 个答案:

答案 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 )
    }
}