【问题标题】:SwiftUI in iOS14 Keyboard Avoidance Issues and IgnoresSafeArea Modifier IssuesiOS14 中的 SwiftUI 键盘避免问题和 IgnoresSafeArea 修饰符问题
【发布时间】:2021-01-06 03:07:17
【问题描述】:

iOS13 发现 TextField 没有任何类型的键盘回避处理。因此,我们创建了如何运作良好的键盘回避机制。我们升级到 iOS14,这导致 TextFields 内置了键盘回避功能。但是,键盘回避功能似乎没有按预期工作。

问题 1 我们遇到的第一个问题是在屏幕中心及其周围的 TextFields 无法正常工作。鉴于此代码:

struct ContentView: View {
    
    @State var text:String = ""
    
    var body: some View {
        
        TextField("Testing", text: $text)

    }
}

在 iPhone 8 Plus 上,文本字段向上移动。我们认为这不应该发生,因为 TextField 不会被键盘隐藏,因此它应该保留在同一个位置。

问题 1: 这是一个错误,是否应该向 Apple 报告?我们认为这是一个错误/问题。

我们发现使用以下方法:

struct ContentView: View {
    
    @State var text:String = ""
    
    var body: some View {
        
        VStack {
            Spacer()
            TextField("Testing", text: $text)
        }
        
    }
}

TextField 将移动到键盘上方。这是预期的行为。我们还通过以下代码发现了这一点:

struct ContentView: View {
    
    @State var text:String = ""
    
    var body: some View {
        
        VStack {
            TextField("Testing", text: $text)
            Spacer()
        }
        
    }
}

TextField 不会被移动,因为它永远不会被 TextField 覆盖。再一次,这是我们所期望的行为。但是,屏幕中心及其周围的任何 TextField 似乎键盘避让会将 TextField 移动到不应该出现的场景中。

问题 2 我们的应用程序在某些屏幕的中心及其周围保留了 TextField,因此上面发现的问题只会增加糟糕的用户体验,因此我们希望“关闭”提供给我们的这种键盘避免功能。我们希望使用ignoresSafeArea 修饰符,如下所示:

struct ContentView: View {
    
    @State var text:String = ""
    
    var body: some View {

        if #available(iOS 14.0, *) {
            
            VStack {
                TextField("Testing", text: $text)
            }
            .ignoresSafeArea(.keyboard, edges: .bottom)

            
        } else {
            // Fallback on earlier versions
            // Our iOS13 Code
        }
                
    }
}

观察到的结果是修饰符根本不起作用。 TextField 仍然向上移动。但是,当使用这样的东西时:

struct ContentView: View {
    
    @State var text:String = ""
    
    var body: some View {

        if #available(iOS 14.0, *) {
            
            VStack {
                Spacer()
                TextField("Testing", text: $text)
            }
            .ignoresSafeArea(.keyboard, edges: .bottom)

            
        } else {
            // Fallback on earlier versions
            // Our iOS13 Code
        }
                
    }
}

ignoresSafeArea 有效,因此引出了第二个问题:

问题 2 ignoresSafeArea 修饰符是否也有错误?这是应该报告的事情吗?

屏幕中心及其周围的 TextField 似乎存在潜在问题?

问题 3 有人知道解决这些问题的方法吗?因为现在它在 iOS14 上是个大问题。键盘回避不起作用,任何尝试关闭它的尝试也不起作用。

我们使用的是 Xcode 12.0 (12A7209)

更新

我们发现将 TextField 包装在 Geometry Reader 中似乎会“关闭”TextField 的键盘避免功能。然而,我们认为这是一个令人愉快的 hack,它以一种方式解决了问题,但同时也暴露了键盘避免在几何阅读器中不起作用,这对其他人来说可能是一个错误/问题......

struct ContentView: View {
    
    @State var text:String = ""
    
    var body: some View {

        if #available(iOS 14.0, *) {
            
            GeometryReader { _ in
                VStack {
                    Spacer().frame(height:500) //Compensate for other Views in the Stack
                    TextField("Testing", text: $text)
                }
            }
            
        } else {
            // Fallback on earlier versions
            // Our iOS13 Code
        }
                
    }
}

【问题讨论】:

  • 我们遇到了同样的问题。我们在屏幕顶部有一个文本字段,当键盘打开时它会一直滚动到视图之外。请注意,将 .ignoresSafeArea(.keyboard, edges: .bottom) 放在 App 结构中的 ContentView() 行似乎有点帮助,但并不完全。

标签: ios swiftui


【解决方案1】:

在这里您可以看到我根据您的 GeometryReader hack 得出的结论:https://stackoverflow.com/a/63971318/2645599

【讨论】:

    【解决方案2】:

    你描述的行为都是意料之中的。我们需要了解三件事:

    1. SwiftUI 布局系统
    2. 键盘安全区
    3. 忽略安全区域

    (1) SwiftUI 布局系统中最相关的概念是视图可以有固定大小或灵活大小。例如,单行文本具有固定大小。所以一个只有 Texts 的 VStack 也有一个固定的大小。试试这个

    struct ContentView: View {
        var body: some View {
            VStack {
                Text("Hello")
                Text("World")
            }
            .border(Color.green)
        }
    }
    

    您可以看到绿色边框只环绕文本。

    另一方面,Spacers、Colors、GeometryReaders 等具有灵活的尺寸,它们往往会扩展以占据所有可用空间。

    (2) 当键盘显示时,容器上应用了一个安全区域。容器的高度会降低。

    (3) ignoresSafeArea 修饰符通常应用于具有灵活高度的视图。以底边为例,只有当视图原来的底边正好与底边安全区的顶边对齐或被底边安全区覆盖时,修改器才会生效。所以如果视图的底边距离底部安全区的顶边较远,ignoresSafeArea 将不起作用。

    现在我将解释为什么您的所有示例都有预期的行为。

    示例 1

    struct ContentView: View {
    
        @State var text: String = ""
    
        var body: some View {
    
            TextField("Testing", text: $text)
    
        }
    }
    

    行为:当键盘显示时,文本字段会向上移动一点,即使它没有被键盘覆盖。

    原因:安全区域在容器上,当键盘显示时,容器高度减小。由于文本字段位于容器的中心,因此它会向上移动一点。

    示例 2

    struct ContentView: View {
    
        @State var text: String = ""
    
        var body: some View {
    
            VStack {
                Spacer()
                TextField("Testing", text: $text)
            }
    
        }
    }
    

    行为:当键盘显示时,文本字段在键盘上方移动。

    原因:VStack中有一个Spacer,所以VStack会一直延伸到容器提供的高度。当容器的高度因为键盘而降低时,VStack的高度也会降低。

    示例 3

    struct ContentView: View {
    
        @State var text: String = ""
    
        var body: some View {
            VStack {
                TextField("Testing", text: $text)
            }
            .ignoresSafeArea(.keyboard, edges: .bottom)
        }
    }
    

    行为:当键盘显示时,文本字段会向上移动一点。 ignoresSafeArea 修饰符没有任何作用。

    原因:ignoresSafeArea 是应用在 VStack 上的,而 VStack 有一个固定的高度,并且它的底边距离底部安全区很远,ignoresSafeArea 没有效果。但是容器并没有忽略SafeArea,所以当键盘显示时容器的高度还是会减小。

    示例 4

    struct ContentView: View {
    
        @State var text: String = ""
    
        var body: some View {
            VStack {
                Spacer()
                TextField("Testing", text: $text)
            }
            .ignoresSafeArea(.keyboard, edges: .bottom)
        }
    }
    

    行为:当键盘显示时文本字段不移动,ignoresSafeArea 正在工作。

    原因:这次 Spacer 将使 VStack 扩展它的高度。请记住,容器不会忽略安全区域,因此如果未应用 ignoresSafeArea 修饰符,当键盘显示时,VStack 的底部边缘将与底部安全区域的顶部边缘对齐。所以这一次如果ignoresSafeArea被应用了,它会起作用并使VStack扩展它的高度来忽略键盘底部的安全区域。

    另一个示例可以帮助您了解父视图如何尊重安全区域,而子视图可以忽略它们。

    示例 5:

    struct ContentView: View {
        var body: some View {
            ZStack {
                Color.yellow
                Color.green
                    .frame(width: 200)
                    .ignoresSafeArea()
            }
            .border(Color.blue, width: 10)
        }
    }
    

    行为:

    从蓝色边框,我们看到父 ZStack 尊重安全区域,而绿色子视图忽略安全区域。

    关闭键盘回避

    要关闭 iOS 14 键盘避免功能,您可以将 ignoresSafeArea 应用于扩展其高度的父视图,例如

    struct ContentView: View {
    
        @State var text: String = ""
    
        var body: some View {
            ZStack {
                Color.clear
                VStack {
                    TextField("Testing", text: $text)
                }
            }
            .ignoresSafeArea(.keyboard)
        }
    }
    

    在本例中,Color.clear 扩展了它的高度,使 ZStack 扩展了它的高度,因此 ZStack 将忽略键盘安全区域。 VStack 位于 ZStack 的中心,因此不受键盘影响。

    【讨论】:

    • 用 Color.clear 包裹在 ZStack 中正是我所需要的。谢谢!
    • 令人难以置信的布局系统知识和解释。感谢您花时间解释这一点。
    【解决方案3】:

    我的情况是我不想避免任何键盘,并且我的主视图控制器中有多个主机控制器。

    这是我对EditTextOverlayView 类型的处理方式:

    struct EditTextOverlayViewWrapper: View {
    
        var observed: ObservedTextEditing
    
        var body: some View {
            GeometryReader { geometry in
                EditTextOverlayView(observing: observed)
            }.ignoresSafeArea(.keyboard, edges: .bottom)
        }
    }
    

    这使所有内容都保持在键盘出现时的位置,让我可以进行自定义调整,而不会干扰键盘回避。 (观察到的 var 只是通过包装器传递;否则,视图的功能与包装的 EditTextOverlayView 完全相同。)

    【讨论】:

      猜你喜欢
      • 2019-12-29
      • 2021-12-31
      • 2021-04-18
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 2014-10-13
      • 2017-02-15
      • 1970-01-01
      相关资源
      最近更新 更多