繁体   English   中英

iOS14 中的 SwiftUI 键盘回避问题和 IgnoresSafeArea 修饰符问题

[英]SwiftUI in iOS14 Keyboard Avoidance Issues and IgnoresSafeArea Modifier Issues

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

问题 1我们遇到的第一个问题是键盘避免在屏幕中心和周围的文本字段中无法正常工作。 鉴于此代码:

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我们的应用程序在某些屏幕的中心和周围保留了 TextFields,因此上面发现的问题只会增加糟糕的用户体验,因此我们希望“关闭”提供给我们的键盘避免功能。 我们希望使用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
        }
                
    }
}

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

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

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

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

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

另一方面, Spacer s、 Color s、 GeometryReader s 等具有灵活的尺寸,它们倾向于扩展以占据所有可用空间。

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

行为:

截屏

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

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

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

这是我如何处理EditTextOverlayView类型的:

struct EditTextOverlayViewWrapper: View {

    var observed: ObservedTextEditing

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM