键盘关闭时的 SwiftUI 按钮故障

Posted

技术标签:

【中文标题】键盘关闭时的 SwiftUI 按钮故障【英文标题】:SwiftUI button glitches on keyboard dismiss 【发布时间】:2021-12-30 22:42:56 【问题描述】:

我的视图上有一个按钮,当按下该按钮时,它会调用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()
    

【问题讨论】:

你能加入minimal reproducible example吗?没有它,调试起来将非常具有挑战性。 我很抱歉。我根据您的建议编辑了问题。 【参考方案1】:

这是由于按钮内的视图替换,在下面找到一个固定的重新布局。

使用 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)

【讨论】:

【参考方案2】:

UIApplication.shared.sendAction 和 SwiftUI 之间似乎存在冲突:隐藏键盘会启动动画,但更新 isAnimating 属性会重置它。

改变调用顺序解决问题:

Button(action: 
    login()
    self.hideKeyboard()
)

【讨论】:

以上是关于键盘关闭时的 SwiftUI 按钮故障的主要内容,如果未能解决你的问题,请参考以下文章

在SwiftUI中按下按钮时的随机图像[关闭]

如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮

键盘处理 swiftui

SwiftUI - 如何关闭工作表视图,同时关闭该视图

SwiftUI 显示/关闭键盘

SwiftUI 键盘关闭问题