return 前关闭控制器时的 InputAccessoryView 错误

InputAccessoryView bug when dismiss controller and return ago

谁能帮帮我?我通过 UITableViewController、UINavigationController 创建聊天控制器,并使用 InputAccessoryView。如果我向左滑动屏幕(关闭此控制器)并 return 向右滑动(取消关闭)- table 将 adjustContentInset 底部设置为零并且 InputAcessoryView 关闭底部内容 tableView。此问题在 ViewWillAppear 事件中产生。我的代码:

UITableViewController:

// MARK: - Controller data

lazy var inputContainerView = ChatAccessoryView(frame: .zero, buttonSelector: #selector(sendMessage(sender:)), controller: self)

public var ticketID: Int = 0

private var lastMessageID: Int = 0
private var tableSections: [String] = []
private var tableRows: [[SupportTicketMessage]] = []
private var sendingMessage: Bool = false
private var URLTaskGetMessages: URLSessionDataTask?
private var URLTaskSendMessage: URLSessionDataTask?

// MARK: - Controller overrides

override func loadView() {
    super.loadView()

    tableView.register(ChatHeaderView.self, forHeaderFooterViewReuseIdentifier: "chatHeader")
    tableView.register(ChatFromMessageTableViewCell.self, forCellReuseIdentifier: "chatFromMessage")
    tableView.register(ChatToMessageTableViewCell.self, forCellReuseIdentifier: "chatToMessage")

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)

    if let messages = supportClass.getTicketMessages(ticketID) {
        tableSections = messages.sections
        tableRows = messages.rows
        lastMessageID = messages.lastMessage

        scrollTableToBottom(false)
    } else {
        tableView.setLoaderBackground("Загрузка сообщений...")
    }
}

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

    loadMessages()
}

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

    URLTaskGetMessages?.cancel()
    URLTaskSendMessage?.cancel()
}

override var inputAccessoryView: UIView? {
    return inputContainerView
}

override var canBecomeFirstResponder: Bool {
    return true
}

聊天附件视图:

class ChatAccessoryView: UIView {

// MARK: - Get params to init

let buttonSelector: Selector
let controller: UIViewController

// MARK: - Data

private let textView = ChatTextView()
private let sendButton = LoadingButton(frame: .zero, text: "Отпр.")
private let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .extraLight))

// MARK: - Init

required init(frame: CGRect, buttonSelector: Selector, controller: UIViewController) {
    self.buttonSelector = buttonSelector
    self.controller = controller

    super.init(frame: frame)

    configureContents()
    blurEffectConfigure()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

// MARK: - Overrides

override var intrinsicContentSize: CGSize {
    return .zero
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    blurEffectConfigure()
}

override func didMoveToWindow() {
    super.didMoveToWindow()

    if let window = window {
        textView.bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true
    }
}

// MARK: - Private methods

private func configureContents() {
    backgroundColor = .clear
    autoresizingMask = .flexibleHeight

    textView.placeholder = "Напишите сообщение..."
    textView.maxHeight = 160

    textView.font = .systemFont(ofSize: 17)
    textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    textView.layer.cornerRadius = 8
    textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 4)

    sendButton.titleLabel?.font = .boldSystemFont(ofSize: 17)
    sendButton.setTitleColor(controller.view.tintColor, for: .normal)
    sendButton.addTarget(controller, action: buttonSelector, for: .touchUpInside)

    blurView.translatesAutoresizingMaskIntoConstraints = false
    textView.translatesAutoresizingMaskIntoConstraints = false
    sendButton.translatesAutoresizingMaskIntoConstraints = false

    self.addSubview(blurView)
    self.addSubview(textView)
    self.addSubview(sendButton)

    blurView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
    blurView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
    blurView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
    blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true

    textView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
    textView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
    textView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8 ).isActive = true
    textView.trailingAnchor.constraint(equalTo: sendButton.leadingAnchor, constant: -20).isActive = true

    sendButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true
    sendButton.widthAnchor.constraint(equalToConstant: 48).isActive = true
    sendButton.centerYAnchor.constraint(equalTo: textView.centerYAnchor).isActive = true
}

private func blurEffectConfigure() {
    if #available(iOS 13.0, *) {
        if traitCollection.userInterfaceStyle == .light {
            blurView.effect = UIBlurEffect(style: .extraLight)
        } else {
            blurView.effect = UIBlurEffect(style: .dark)
        }
    }
}

// MARK: - Public methods

public func successSend() {
    textView.text = ""
    sendButton.loadingMode(false)
}

}

聊天文本视图:

class ChatTextView: UITextView {

// MARK: - Data

var maxHeight: CGFloat = 0.0

public let placeholderTextView: UITextView = {
    let textView = UITextView()

    textView.translatesAutoresizingMaskIntoConstraints = false
    textView.backgroundColor = .clear
    textView.isScrollEnabled = false
    textView.isUserInteractionEnabled = false
    textView.textColor = UIColor.black.withAlphaComponent(0.33)

    return textView
}()

var placeholder: String? {
    get {
        return placeholderTextView.text
    }

    set {
        placeholderTextView.text = newValue
    }
}

// MARK: - Init

override init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)

    backgroundColor = UIColor.black.withAlphaComponent(0.06)
    isScrollEnabled = false
    autoresizingMask = [.flexibleWidth, .flexibleHeight]

    NotificationCenter.default.addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: UITextView.textDidChangeNotification, object: self)

    placeholderTextView.font = font

    addSubview(placeholderTextView)

    NSLayoutConstraint.activate([
        placeholderTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
        placeholderTextView.trailingAnchor.constraint(equalTo: trailingAnchor),
        placeholderTextView.topAnchor.constraint(equalTo: topAnchor),
        placeholderTextView.bottomAnchor.constraint(equalTo: bottomAnchor),
    ])

    colorThemeConfigure()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

// MARK: - Overrides

override var text: String! {
    didSet {
        invalidateIntrinsicContentSize()
        placeholderTextView.isHidden = !text.isEmpty
    }
}

override var font: UIFont? {
    didSet {
        placeholderTextView.font = font
        invalidateIntrinsicContentSize()
    }
}

override var contentInset: UIEdgeInsets {
    didSet {
        placeholderTextView.contentInset = contentInset
    }
}

override var intrinsicContentSize: CGSize {
    var size = super.intrinsicContentSize

    if size.height == UIView.noIntrinsicMetric {

        layoutManager.glyphRange(for: textContainer)
        size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom
    }

    if maxHeight > 0.0 && size.height > maxHeight {
        size.height = maxHeight

        if !isScrollEnabled {
            isScrollEnabled = true
        }
    } else if isScrollEnabled {
        isScrollEnabled = false
    }

    return size
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    colorThemeConfigure()
}

// MARK: - Private methods

private func colorThemeConfigure() {
    if #available(iOS 13.0, *) {
        if traitCollection.userInterfaceStyle == .light {
            backgroundColor = UIColor.black.withAlphaComponent(0.06)
            placeholderTextView.textColor = UIColor.black.withAlphaComponent(0.33)
        } else {
            backgroundColor = UIColor.white.withAlphaComponent(0.08)
            placeholderTextView.textColor = UIColor.white.withAlphaComponent(0.33)
        }
    }
}

// MARK: - Obj C methods

@objc private func textDidChange(_ note: Notification) {
    invalidateIntrinsicContentSize()

    placeholderTextView.isHidden = !text.isEmpty
}

}

截图错误:

TableView loaded (all right)

Swipe screen to left and cancel this action (swipe right)

When I returned to controller I see this Bug (last message go to bottom)

谢谢!

您可以尝试这样的操作:

UITableViewController:

override func viewDidLayoutSubviews() {
   super.viewDidLayoutSubviews()

   updateTableViewInsets()
}

private func updateTableViewInsets() {

   let offSet = inputContainerView.frame.height

   tableView?.contentInset.bottom = offSet
   tableView?.scrollIndicatorInsets.bottom = offSet
}

感谢大家的关注和帮助!我解决问题如下:

    tableView.contentInsetAdjustmentBehavior = .never
    tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 53, right: 0)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)

我禁用自动 tableView.contentInsetAdjustmentBehavior 并使用 tableView.conentInset。在 open/close 键盘上,我更改为 tableView.contentInset。

这对我来说效果很好,解决方案也很简单:

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

    reloadInputViews()
    prepareTableViewForViewPresented()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    prepareTableViewForViewDismissed()
}

private func prepareTableViewForViewPresented() {
    self.tableView.contentInset.bottom = 0
}

private func prepareTableViewForViewDismissed() {
    self.tableView.contentInset.bottom = view.safeAreaInsets.bottom + (inputAccessoryView?.frame.height ?? 0)
}

已知问题: - 当显示键盘时,tableview 仍然落后于键盘