我有下面的例子来说明这个问题.

我主要想展示一个类似于iMessage的用户界面,其中底部有一个用于输入消息的UITextView.

一旦键盘显示在UITextView中,即使稍微滚动UITableView,内容也会位于导航栏下方,而导航栏保持透明.这看起来像是一个奇怪的用户界面错误.

import UIKit
import SnapKit

let numberOfRows = 15

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet var myTableView: UITableView!
    
    let textView = UITextView()
    let bottomView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        myTableView.keyboardDismissMode = .interactive
        
        bottomView.backgroundColor = .darkGray
        bottomView.addSubview(textView)
     
        view.addSubview(bottomView)
        bottomView.snp.makeConstraints { make in
            make.left.right.bottom.equalToSuperview()
            make.height.equalTo(100)
        }
        
        textView.backgroundColor = .black
        textView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(10)
        }
        textView.text = "Type something.."
        
    }
    
    override var inputAccessoryView: UIView {
        return bottomView
    }
        
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
    
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 200
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numberOfRows
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
        
        cell.textLabel?.text = "Row \(indexPath.row)"
        cell.detailTextLabel?.text = "Tastes very close to Snickers chocolate bar. Only tried because of reviews. Would purchase again if price wasn’t so high, and available in Canada."
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        navigationController?.pushViewController(UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailVC"), animated: true)
    }

}

屏幕截图后的错误滚动后,甚至一点点键盘显示:

enter image description here

推荐答案

我在使用此代码时体验到了与您相同的行为.肯定有什么东西把顶级安全区域指南搞糊涂了.

我完全放弃了这个inputAccessoryView模式,从而解决了这个问题.我只是

  • 将表视图的底部锚点设置为此底部视图的顶部锚点;以及
  • 将底视图的底锚设置为主视图的keyboardLayoutGuide.topAnchor.

enter image description here

这样做之后,你就可以享受"保持文本视图在键盘上方"的功能,而不会有这种特殊的导航栏行为/错误.

这种方法还可以自动调整表视图滚动区域,这样即使出现键盘,您仍然可以滚动到底部.它也是一种零码解决方案.


FWIW, if doing this in IB, you may have to select the main view and explicitly enable the keyboard layout guides: enter image description here


在iOS 15之前的iOS版本中,我们过go 必须添加键盘观察器.因此,我们将为SuperView的底部约束添加一个@IBOutlet,然后手动更改该底部约束(在演示和取消键盘的同时为其设置动画).例如,在我的iOS 15之前的项目中,我做了一些类似的事情:

@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
private var keyboardWillShowObserver: NSObjectProtocol?
private var keyboardWillHideObserver: NSObjectProtocol?

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    addKeyboardObservers()
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    removeKeyboardObservers()
}

有:

private extension ViewController {
    func addKeyboardObservers() {
        keyboardWillShowObserver = NotificationCenter.default.addObserver(
            forName: UIApplication.keyboardWillShowNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let self else { return }
            showKeyboard(bottom: bottomConstraint, notification: notification)
        }

        keyboardWillHideObserver = NotificationCenter.default.addObserver(
            forName: UIApplication.keyboardWillHideNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let self else { return }
            hideKeyboard(bottom: bottomConstraint, notification: notification)
        }
    }

    func removeKeyboardObservers() {
        if let keyboardWillShowObserver {
            NotificationCenter.default.removeObserver(keyboardWillShowObserver)
        }

        if let keyboardWillHideObserver {
            NotificationCenter.default.removeObserver(keyboardWillHideObserver)
        }
    }
}

它使用这个UIViewController扩展名:

//  UIViewController+KeyboardChange.swift

import UIKit

extension UIViewController {
    func showKeyboard(bottom constraint: NSLayoutConstraint, notification: Notification) {
        animateAlongsideKeyboardChange(notification) { [self] frame in
            constraint.constant = view.bounds.height - frame.minY
            view.layoutIfNeeded()
        }
    }

    func hideKeyboard(bottom constraint: NSLayoutConstraint, notification: Notification) {
        animateAlongsideKeyboardChange(notification) { [self] _ in
            constraint.constant = 0
            view.layoutIfNeeded()
        }
    }

    private func animateAlongsideKeyboardChange(_ notification: Notification, animation: @escaping (CGRect) -> Void) {
        let userInfo = notification.userInfo!
        guard
            var keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
            let curve = (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber).flatMap({
                return UIView.AnimationCurve(rawValue: $0.intValue)
            }),
            let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber as? CGFloat
        else {
            return
        }

        keyboardFrame = view.convert(keyboardFrame, from: nil)

        UIViewPropertyAnimator(duration: duration, curve: curve) {
            animation(keyboardFrame)
        }.startAnimation()
    }
}

这一切都有点麻烦,这就是iOS 15键盘布局指南如此受欢迎的原因.但在早期的iOS版本中,我们会手动观察键盘的显示和取消.

Ios相关问答推荐

Swift addtarget方法的默认参数不生效

缺少预期的键:';NSPrival yCollectedDataTypes';

如何在SWIFT中处理不可发送的类型?

底部导航栏在iOS上浮动

带有RadStudio 11.3/12的Mac Mini M2(Sonoma 14.2.1)上的PAServer-测试连接按钮有效,但部署不起作用

一堆UIGraphics方法在iOS 17中被弃用,没有明确的替代方法?

创建 Flutter 项目时,如何从我的系统中删除特定的开发者身份?

.frame() 修饰符的位置如何影响视图?

如何在 SwiftUI 的 TabView 中添加底部曲线?

SwiftUI 我自己的渲染函数的返回类型是什么?

try 在 iOS 中分发 Flutter 应用程序时出现Invalid Provisioning Profile Signature错误

-1103 错误域=NSURLErrorDomain 代码=-1103资源超过最大大小iOS 13

如何在 Swift 3 中根据 UIImage 的大小/比率调整 UIImageView 的大小?

快速禁用 UITextfield 的用户输入

未找到签名证书​​iOS Distribution

Watchkit AppIcon - 名为AppIcon的应用图标集没有任何适用的内容

iOS 11 navigationItem.titleView 宽度未设置

如何用 Swift 圆化 UILabel 的边缘

由于 NSInvalidUnarchiveOperationException 导致 iOS11 WKWebview 崩溃

如何关闭 iOS 键盘?