iOS14 中的 SwiftUI 键盘回避问题和 IgnoresSafeArea 修饰符问题 [英] SwiftUI in iOS14 Keyboard Avoidance Issues and IgnoresSafeArea Modifier Issues

查看:21
本文介绍了iOS14 中的 SwiftUI 键盘回避问题和 IgnoresSafeArea 修饰符问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

iOS13 saw TextField not having any sort of keyboard avoidance handling in place. As such, we created our how keyboard avoidance mechanism which works well. We upgraded to iOS14 and this resulted in TextFields having keyboard avoidance built in. However, the keyboard avoidance does not seem to work as expected.

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

Issue 1 The first issue we experienced was keyboard avoidance not working expected for TextFields in and around the centre of the screen. Given this code:

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

    }
}

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

On an and iPhone 8 Plus the Textfield is moved up. In our opinion this shouldn't be happening as the TextField will not be hidden by the keyboard and as such it should remain in the same place.

问题 1:这是一个错误吗,应该向苹果报告吗?我们认为这是一个错误/问题.

Question 1: Is this a bug and should it be reported to Apple? We believe this to be a bug / issue.

我们发现使用以下内容:

We have found that using the following:

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

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

The TextField will be moved just above the keyboard. Which is the expected behaviour. We've also found that with the code below:

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

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

The TextField is not moved as it would never be covered by the TextField. Once again, this is the behaviour we expect. However, any TextField in and around the centre of the screen it would seem that the keyboard avoidance moves the TextField in scenario's where it shouldn't.

问题 2我们的应用程序在某些屏幕的中心和周围保留了 TextFields,因此上面发现的问题只会增加糟糕的用户体验,因此我们希望关闭"屏幕.提供给我们的这个键盘避免.我们希望使用 ignoresSafeArea 修饰符如下:

Issue 2 Our app holds TextFields in and around the centre on certain screens and as such the issue discovered above simply adds to a poor user experience and so we looked to "switch off" this keyboard avoidance provided to us. We looked to use the ignoresSafeArea modifier as follows:

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 仍然向上移动.但是,当使用这样的东西时:

The observed result is that the modifier simply doesn't work. The TextField is still moved upwards. However when using something like this:

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 有效,所以这就引出了第二个问题:

the ignoresSafeArea works and so this leads to the second question:

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

Question 2 Is there are bug with the ignoresSafeArea modifier as well? Is this something that should be reported?

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

It would seem that there is an underlying issue with TextFields in and around the centre of the screen?

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

Question 3 Anyone know of ways around these issues? Because right now it's a huge problem on iOS14. The keyboard avoidance doesn't work and any attempt to try and switch it off doesn't work either.

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

We are using Xcode 12.0 (12A7209)

更新

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

We have found that wrapping the TextField in a Geometry Reader that this seems to "switch off" Keyboard Avoidance for a TextField. However, we believe this to be a delightful hack that fixes the problem in one way but then also exposes that Keyboard Avoidance doesn't work in Geometry readers which could be a bug / issue for other people...

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

推荐答案

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

The behaviors you described are all expected. We need to understand three things:

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

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

(1) The most relevant concept in SwiftUI layout system is that views can have a fixed size or a flexible size. For example, a single-lined Text has a fixed size. So a VStack that only has Texts also has a fixed size. Try this

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

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

You can see the green border only wraps around the texts.

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

On the other hand, Spacers, Colors, GeometryReaders, etc. have flexible sizes, they tend to expand to occupy all the space available.

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

(2) When the keyboard is showing, there is a safe area applied on the container. The height of the container will decrease.

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

(3) The ignoresSafeArea modifier is typically applied on views that have a flexible height. Taking the bottom edge for example, the modifier will have an effect only when the original bottom edge of the view is just aligned with the top edge of the bottom safe area or is covered by the bottom safe area. So if the view's bottom edge is far away from the top edge of the bottom safe area, ignoresSafeArea will have no effect.

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

Now I'll explain why all your examples have expected behavior.

示例 1

struct ContentView: View {

    @State var text: String = ""

    var body: some View {

        TextField("Testing", text: $text)

    }
}

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

Behavior: The text field moves up a little when the keyboard shows even if it's not covered by the keyboard.

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

Reason: The safe area is on the container, when the keyboard shows, the height of the container decreases. Since the text field is placed at the center of the container, it moves up a bit.

示例 2

struct ContentView: View {

    @State var text: String = ""

    var body: some View {

        VStack {
            Spacer()
            TextField("Testing", text: $text)
        }

    }
}

行为:当键盘显示时,文本字段移动到键盘正上方.

Behavior: When the keyboard shows, the text field moves just above the keyboard.

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

Reason: There is a Spacer in the VStack, so the VStack will extend its height all the way to the height provided by the container. When the height of the container decreases because of the keyboard, the height of the VStack decreases too.

示例 3

struct ContentView: View {

    @State var text: String = ""

    var body: some View {
        VStack {
            TextField("Testing", text: $text)
        }
        .ignoresSafeArea(.keyboard, edges: .bottom)
    }
}

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

Behavior: The text field moves up a little when the keyboard shows. The ignoresSafeArea modifier doesn't have any effect.

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

Reason: The ignoresSafeArea is applied on the VStack, while the VStack has a fixed height, and its bottom edge is far away from the bottom safe area, ignoresSafeArea has no effect. But the container does not ignoresSafeArea, so the height of the container still decreases when the keyboard shows.

示例 4

struct ContentView: View {

    @State var text: String = ""

    var body: some View {
        VStack {
            Spacer()
            TextField("Testing", text: $text)
        }
        .ignoresSafeArea(.keyboard, edges: .bottom)
    }
}

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

Behavior: The text field does not move when the keyboard shows, ignoresSafeArea is working.

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

Reason: This time the Spacer will make the VStack extend its height. Keep in mind the container doesn't ignoresSafeArea, so if the ignoresSafeArea modifier were not applied, when the keyboard shows, the bottom edge of the VStack will be just aligned to the top edge of the bottom safe area. So this time if ignoresSafeArea is applied, it will work and makes the VStack extend its height to ignore the keyboard bottom safe area.

另一个例子可以帮助你理解父视图如何尊重安全区域而子视图可以忽略它们.

Another example may help you understand how a parent view can respect safe areas while a subview can ignore them.

示例 5:

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

行为:

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

From the blue border, we see the parent ZStack respects safe areas while the green subview ignores safe areas.

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

To switch off the iOS 14 keyboard avoidance, you can apply ignoresSafeArea to a parent view that extends its height, for example

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的中心,不受键盘影响.

In this example, the Color.clear extends its height, making the ZStack extend its height, so the ZStack will ignore the keyboard safe area. The VStack is placed at the center of the ZStack, thus not affected by the keyboard.

这篇关于iOS14 中的 SwiftUI 键盘回避问题和 IgnoresSafeArea 修饰符问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆