SwiftUI 按钮在键盘关闭时出现故障
SwiftUI button glitches on keyboard dismiss
我的视图中有一个按钮,按下时会调用一个 hideKeyboard
函数,如下所示:
func hideKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
但是,当按下按钮并关闭键盘时,会出现一个故障,即按钮停留在原位而视图向下移动:
https://giphy.com/gifs/Z7qCDpRSGoOb9CbVRQ
可重现的例子:
import SwiftUI
struct TestView: View {
@State private var username: String = ""
@State private var password: String = ""
@State private var isAnimating: Bool = false
var lightGrey = Color(red: 239.0/255.0,
green: 243.0/255.0,
blue: 244.0/255.0)
var body: some View {
ZStack() {
VStack() {
Spacer()
Text("Text")
.font(.title)
.fontWeight(.semibold)
.padding(.bottom, 15)
.frame(maxWidth: .infinity, alignment: .leading)
Text("Text")
.font(.subheadline)
.padding(.bottom)
.frame(maxWidth: .infinity, alignment: .leading)
TextField("username", text: $username)
.padding()
.background(self.lightGrey)
.cornerRadius(5.0)
.padding(.bottom, 20)
SecureField("password", text: $password)
.padding()
.background(self.lightGrey)
.cornerRadius(5.0)
.padding(.bottom, 20)
Button(action: {
self.hideKeyboard()
login()
})
{
if isAnimating {
ProgressView()
.colorScheme(.dark)
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.cornerRadius(10.0)
.padding(.bottom, 20)
}
else {
Text("Text")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.cornerRadius(10.0)
.padding(.bottom, 20)
}
}
.disabled(username.isEmpty || password.isEmpty || isAnimating)
Text("Text")
.font(.footnote)
.frame(maxWidth: .infinity, alignment:.leading)
Spacer()
}
.padding()
.padding(.bottom, 150)
.background(Color.white)
}
}
func hideKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
func login() {
isAnimating = true
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
这是由于按钮里面的视图替换,下面找到固定的重新布局。
使用 Xcode 13.2 / iOS 15.2 进行测试(为演示激活了慢动画)
Button(action: {
self.hideKeyboard()
login()
})
{
VStack { // << persistent container !!
if isAnimating {
ProgressView()
.colorScheme(.dark)
}
else {
Text("Text")
}
}
.foregroundColor(.white)
.font(.headline)
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.cornerRadius(10.0)
.padding(.bottom, 20)
}
.disabled(username.isEmpty || password.isEmpty || isAnimating)
看起来 UIApplication.shared.sendAction
和 SwiftUI 之间存在冲突:隐藏键盘会启动动画,但更新 isAnimating
属性 会重置它。
更改调用顺序解决问题:
Button(action: {
login()
self.hideKeyboard()
})
我的视图中有一个按钮,按下时会调用一个 hideKeyboard
函数,如下所示:
func hideKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
但是,当按下按钮并关闭键盘时,会出现一个故障,即按钮停留在原位而视图向下移动: https://giphy.com/gifs/Z7qCDpRSGoOb9CbVRQ
可重现的例子:
import SwiftUI
struct TestView: View {
@State private var username: String = ""
@State private var password: String = ""
@State private var isAnimating: Bool = false
var lightGrey = Color(red: 239.0/255.0,
green: 243.0/255.0,
blue: 244.0/255.0)
var body: some View {
ZStack() {
VStack() {
Spacer()
Text("Text")
.font(.title)
.fontWeight(.semibold)
.padding(.bottom, 15)
.frame(maxWidth: .infinity, alignment: .leading)
Text("Text")
.font(.subheadline)
.padding(.bottom)
.frame(maxWidth: .infinity, alignment: .leading)
TextField("username", text: $username)
.padding()
.background(self.lightGrey)
.cornerRadius(5.0)
.padding(.bottom, 20)
SecureField("password", text: $password)
.padding()
.background(self.lightGrey)
.cornerRadius(5.0)
.padding(.bottom, 20)
Button(action: {
self.hideKeyboard()
login()
})
{
if isAnimating {
ProgressView()
.colorScheme(.dark)
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.cornerRadius(10.0)
.padding(.bottom, 20)
}
else {
Text("Text")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.cornerRadius(10.0)
.padding(.bottom, 20)
}
}
.disabled(username.isEmpty || password.isEmpty || isAnimating)
Text("Text")
.font(.footnote)
.frame(maxWidth: .infinity, alignment:.leading)
Spacer()
}
.padding()
.padding(.bottom, 150)
.background(Color.white)
}
}
func hideKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
func login() {
isAnimating = true
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
这是由于按钮里面的视图替换,下面找到固定的重新布局。
使用 Xcode 13.2 / iOS 15.2 进行测试(为演示激活了慢动画)
Button(action: {
self.hideKeyboard()
login()
})
{
VStack { // << persistent container !!
if isAnimating {
ProgressView()
.colorScheme(.dark)
}
else {
Text("Text")
}
}
.foregroundColor(.white)
.font(.headline)
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.cornerRadius(10.0)
.padding(.bottom, 20)
}
.disabled(username.isEmpty || password.isEmpty || isAnimating)
看起来 UIApplication.shared.sendAction
和 SwiftUI 之间存在冲突:隐藏键盘会启动动画,但更新 isAnimating
属性 会重置它。
更改调用顺序解决问题:
Button(action: {
login()
self.hideKeyboard()
})