iOS14中的SwiftUI键盘避免问题和忽略SafeArea修饰符问题

时间:2020-09-19 11:28:34

标签: ios swiftui

iOS13看到TextField没有适当的键盘回避处理方式。因此,我们创建了如何很好地避免键盘的机制。我们升级到iOS14,这导致TextFields内置了键盘避免功能。但是,键盘避免功能似乎无法按预期工作。

问题1 我们遇到的第一个问题是避免在屏幕中心及其周围的TextField使用键盘避免无法正常工作。给出以下代码:

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

    }
}

在和iPhone 8 Plus上,Textfield向上移动。我们认为这不应该发生,因为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修饰符是否也存在错误?这是应该报告的东西吗?

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

问题3 有人知道解决这些问题的方法吗?因为现在在iOS14上这是一个巨大的问题。避免键盘操作无效,并且尝试关闭键盘的操作也无效。

我们正在使用Xcode 12.0(12A7209)

更新

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

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
        }
                
    }
}

3 个答案:

答案 0 :(得分:8)

您描述的行为都是预期的。我们需要了解三件事:

  1. SwiftUI 布局系统
  2. 键盘安全区<​​/li>
  3. 忽略安全区域

(1) SwiftUI 布局系统中最相关的概念是视图可以具有固定大小或灵活大小。例如,单行文本具有固定大小。所以只有文本的 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 修饰符没有任何效果。

原因:在VStack上应用了ignoresSafeArea,而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)
    }
}

行为:

Screen shot

从蓝色边框,我们看到父 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的中心,不受键盘影响。

答案 1 :(得分:0)

在这里您可以看到我根据您的GeometryReader骇客发现的问题:https://stackoverflow.com/a/63971318/2645599

答案 2 :(得分:0)

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

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

struct EditTextOverlayViewWrapper: View {

    var observed: ObservedTextEditing

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

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