在 SwiftUI 中使用 Segmented-style Picker 在两个页面之间滑动

Swipe between two pages with Segmented-style Picker in SwiftUI

我有一个 Picker.pickerStyle(SegmentedPickerStyle()) 使其成为分段控件。我想让页面之间平滑滑动,而不是使用条件语句替换视图。

这是我到目前为止制作的 gif:

这是目前的代码(由 if 控制,而不是在不同页面之间切换):

struct AuthView: View {

    @State private var authPath = 0

    /* ... */

    var body: some View {
        VStack {
            Picker(selection: $authPath, label: Text("Authentication Path")) {
                Text("Log In").tag(0)
                Text("Sign Up").tag(1)
            }
            .pickerStyle(SegmentedPickerStyle())

            Spacer()

            if authPath == 0 {
                LogInView(/* ... */)
            } else {
                SignUpView(/* ... */)
            }

            Spacer()
        }
        .padding()
        .background(Color("Color.Background").edgesIgnoringSafeArea(.all))
    }
}

我想要类似于 UIPageViewController 的东西。如果有 SwiftUI 版本或好的替代版本,那将非常有帮助。

但是,如果我确实需要借助 UIViewRepresentable 来使用 UIKit,我不知道如何使用 SwiftUI 来实现它。

您可以将 LoginViewSignupView 包裹在某个容器中以使其具有动画效果。

                 var body: some View {
        VStack {

            Picker(selection: $authPath, label: Text("Authentication Path")) {
              //  withAnimation(Animation.easeInOut.speed(0.5)){
                Text("Log In").tag(0)
                    Text("Sign Up").tag(1)}
                //}
            .pickerStyle(SegmentedPickerStyle())

            Spacer()


            if authPath == 0 {
               NavigationView{
                Text("1")
                }.animation(.default).transition(.move(edge: .leading))
                //(/* ... */)
            } else {
                NavigationView{
                Text("2")
                }.animation(.default).transition(.move(edge: .leading))

             //(/* ... */)

                }
            Spacer()
        }
        .padding()
        .background(Color("Color.Background").edgesIgnoringSafeArea(.all))
    }

这是可能的方法(请注意,为了测试它需要使用模拟器,因为预览无法正确处理 .move 转换)

Demo(只是用stub views,动画和transition的参数可以配置,但是思路还是一样的)

注意:转换视图的背景应该是不透明的(这里使用 Color.white),否则转换看起来...不太好。

struct TestTwoViewMoveIn: View {
    @State private var authPath: Int? = nil

    /* ... */

    var body: some View {
        VStack {
            Picker(selection: Binding<Int>(
                get: { self.authPath ?? 0 },
                set: { tag in
                    withAnimation { // needed explicit for transitions
                        self.authPath = tag
                    }
                }),
                   label: Text("Authentication Path")) {
                Text("Log In").tag(0)
                Text("Sign Up").tag(1)
            }
            .pickerStyle(SegmentedPickerStyle())

            Spacer()

            ZStack {
                Rectangle().fill(Color.clear)
                if nil == authPath {
                    LogInView(/* ... */)
                        .background(Color.white) // << set your background 
                }

                if authPath == 0 {
                    LogInView(/* ... */)
                        .background(Color.white) // << set your background 
                        .transition(.move(edge: .leading))
                }

                if authPath == 1 {
                    SignUpView(/* ... */)
                        .background(Color.white) // << set your background 
                        .transition(.move(edge: .trailing))
                }
            }

            Spacer()
        }
        .padding()
        .background(Color("Color.Background").edgesIgnoringSafeArea(.all))
    }
}

这些其他答案为我指明了正确的方向,但代码似乎有点冗长或未按预期运行。

以下是使我的工作正常进行的更改:

  • 填充已添加到 Picker
  • 已删除 VStack 末尾的填充。
  • if-else 改为 2 ifs.
  • LogInViewSignInView 添加了 animationtransitionpadding 修饰符。

原文:

struct AuthView: View {

    @State private var authPath = 0

    /* ... */

    var body: some View {
        VStack {
            Picker(selection: $authPath, label: Text("Authentication Path")) {
                Text("Log In").tag(0)
                Text("Sign Up").tag(1)
            }
            .pickerStyle(SegmentedPickerStyle())

            Spacer()

            if authPath == 0 {
                LogInView(/* ... */)
            } else {
                SignUpView(/* ... */)
            }

            Spacer()
        }
        .padding()
        .background(Color("Color.Background").edgesIgnoringSafeArea(.all))
    }
}

新:

struct AuthView: View {

    @State private var authPath = 0

    /* ... */

    var body: some View {
        VStack {
            Picker(selection: $authPath, label: Text("Authentication Path")) {
                Text("Log In").tag(0)
                Text("Sign Up").tag(1)
            }
            .pickerStyle(SegmentedPickerStyle())
            .padding()

            Spacer()

            if authPath == 0 {
                LogInView(/* ... */)
                    .animation(.default)
                    .transition(.move(edge: .leading))
                    .padding()
            }
            if authPath == 1 {
                SignUpView(/* ... */)
                    .animation(.default)
                    .transition(.move(edge: .trailing))
                    .padding()
            }

            Spacer()
        }
        .background(Color("Color.Background").edgesIgnoringSafeArea(.all))
    }
}