问题描述
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 我们遇到的第一个问题是避免在屏幕中心及其周围的TextField使用键盘避免正常工作.给出以下代码:
Issue 1The 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上,Textfield向上移动.我们认为这不应该发生,因为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:这是一个错误,应该报告给Apple吗?我们认为这是一个错误/问题.
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 我们的应用在某些屏幕上的中心及其周围保留了TextField,因此,上面发现的问题只会增加糟糕的用户体验,因此我们希望关闭"屏幕.向我们提供了这种避免键盘操作的方法.我们希望使用 ignoresSafeArea
修饰符,如下所示:
Issue 2Our 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:
问题2 ignoresSafeArea
修饰符是否也存在错误?这是应该报告的东西吗?
Question 2Is there are bug with the ignoresSafeArea
modifier as well? Is this something that should be reported?
在屏幕中心及其周围似乎存在TextFields的潜在问题?
It would seem that there is an underlying issue with TextFields in and around the centre of the screen?
问题3 有人知道解决这些问题的方法吗?因为目前在iOS14上这是一个巨大的问题.避免键盘操作无效,并且尝试关闭键盘的操作也无效.
Question 3Anyone 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的键盘.但是,我们认为这是一个令人愉悦的技巧,它以一种方式解决了该问题,但同时也揭示了键盘避免功能在Geometry读取器中不起作用,这可能是其他人的错误/问题...
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:
- SwiftUI布局系统
- 键盘安全区域
- 忽略安全区
(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.
另一方面, Spacer
s, Color
s, GeometryReader
s等具有灵活的大小,它们倾向于扩展以占据所有可用空间.
On the other hand, Spacer
s, Color
s, GeometryReader
s, 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修饰符通常应用于高度灵活的视图.以底边为例,仅当视图的原始底边刚好与底部安全区域的顶部边缘对齐或被底部安全区域覆盖时,修改器才会生效.因此,如果视图的底部边缘与底部安全区域的顶部边缘相距较远,则忽略SafeArea将无效.
(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中有一个垫片,因此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.
原因:ignoresSafeArea应用于VStack,而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)
}
}
行为:键盘显示时,文本字段不会移动,忽略SafeArea正在工作.
Behavior: The text field does not move when the keyboard shows, ignoresSafeArea is working.
原因:这次,Spacer将使VStack扩展其高度.请记住,容器不会忽略ignoreSafeArea,因此,如果未应用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键盘避免问题和忽略SafeArea修改器问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!