在 SwiftUI 中使用分段式选择器在两个页面之间滑动

Posted

技术标签:

【中文标题】在 SwiftUI 中使用分段式选择器在两个页面之间滑动【英文标题】:Swipe between two pages with Segmented-style Picker in SwiftUI 【发布时间】:2020-01-10 21:37:14 【问题描述】:

我有一个 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 来实现它。

【问题讨论】:

【参考方案1】:

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

Demo(只是使用了stub view,动画和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))
    

【讨论】:

【参考方案2】:

您可以将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))
    

【讨论】:

【参考方案3】:

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

以下是让我的工作发生的变化:

已将填充添加到 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))
    

【讨论】:

以上是关于在 SwiftUI 中使用分段式选择器在两个页面之间滑动的主要内容,如果未能解决你的问题,请参考以下文章

使用选择器在页面中插入内容

SwiftUI 选择器在 ObservedObject 公共变量更新期间滚动时跳转

基本的 SwiftUI 选择器在屏幕变化时发出严重警告和奇怪的动作

使用选择器在页面中插入内容

无法使用 SwiftUI 使选取器居中

没有找到选择器的节点,但选择器在 HTML 页面上