InputAccessoryView 在关闭控制器并返回之前出现错误

huangapple go评论86阅读模式
英文:

InputAccessoryView bug when dismiss controller and return ago

问题

I'll provide a translation of the code portion you shared:

你可以帮助我吗?我通过UITableViewControllerUINavigationController创建了聊天控制器,并使用了InputAccessoryView。如果我向左滑动屏幕(关闭此控制器),然后再向右滑动(取消关闭),表格会将adjustContentInset底部设置为零,而InputAcessoryView会关闭底部内容表格视图。这个问题发生在ViewWillAppear事件中。我的代码如下:

UITableViewController

    // 控制器数据
    
    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?
    
    // 控制器重写
    
    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
    }

ChatAccessoryView

    class ChatAccessoryView: UIView {
    
    // 获取初始化参数
    
    let buttonSelector: Selector
    let controller: UIViewController
    
    // 数据
    
    private let textView = ChatTextView()
    private let sendButton = LoadingButton(frame: .zero, text: "Отпр.")
    private let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .extraLight))
    
    // 初始化
    
    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")
    }
    
    // 重写
    
    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
        }
    }
    
    // 私有方法
    
    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)
            }
        }
    }
    
    // 公共方法
    
    public func successSend() {
        textView.text = ""
        sendButton.loadingMode(false)
    }
    
}

ChatTextView

    class ChatTextView: UITextView {
    
    // 数据
    
    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
        }
    }
    
    // 初始化
    
    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: trailing

<details>
<summary>英文:</summary>

Who can help me? I create chat controller via UITableViewController, UINavigationController and I use InputAccessoryView. If I swipe screen to left (dismiss this controller) and return swipe to right (cancel dismiss) - table set adjustContentInset bottom to zero and InputAcessoryView close bottom content tableView. This problem created in ViewWillAppear event. My code:

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: &quot;chatHeader&quot;)
        tableView.register(ChatFromMessageTableViewCell.self, forCellReuseIdentifier: &quot;chatFromMessage&quot;)
        tableView.register(ChatToMessageTableViewCell.self, forCellReuseIdentifier: &quot;chatToMessage&quot;)
        
        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(&quot;Загрузка сообщений...&quot;)
        }
    }
    
    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
    }

ChatAccessoryView:

    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: &quot;Отпр.&quot;)
    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(&quot;init(coder:) has not been implemented&quot;)
    }
    
    // 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 = &quot;Напишите сообщение...&quot;
        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 = &quot;&quot;
        sendButton.loadingMode(false)
    }
    
}

ChatTextView:

    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(&quot;init(coder:) has not been implemented&quot;)
    }
    
    // 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 &gt; 0.0 &amp;&amp; size.height &gt; 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
    }
    
}

Screenshots error:

[TableView loaded (all right)][1]


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

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


  [1]: https://i.stack.imgur.com/AmTcP.jpg
  [2]: https://i.stack.imgur.com/YglfW.jpg
  [3]: https://i.stack.imgur.com/Vr7s7.jpg

Thanks!

</details>


# 答案1
**得分**: 0

以下是翻译好的部分:

你可以尝试类似这样的代码:

UITableViewController

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

updateTableViewInsets()
}

private func updateTableViewInsets() {

let offSet = inputContainerView.frame.height

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


<details>
<summary>英文:</summary>
You could try something like this:
UITableViewController:

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

updateTableViewInsets()
}

private func updateTableViewInsets() {

let offSet = inputContainerView.frame.height

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


</details>
# 答案2
**得分**: 0
感谢大家的关注和尝试帮助我!我按照以下方式解决了这个问题:
```swift
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.contentInset。在打开/关闭键盘时,我改变了tableView.contentInset。
<details>
<summary>英文:</summary>
Thank you all for your attention and trying to help me! I solved the problem as follows:
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)
I disable automatic tableView.contentInsetAdjustmentBehavior and use tableView.conentInset. On open/close keyboard I change tableView.contentInset. 
</details>
# 答案3
**得分**: 0
这对我来说很有效,而且是一个相当简单的解决方案:
```swift
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)
}
```
已知问题:
- 键盘显示时,表格仍然位于键盘后面
<details>
<summary>英文:</summary>
This works well for me, and pretty simple solution:
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)
}
Known issues:
- when keyboard is shown, tableview still goes behind keyboard
</details>

huangapple
  • 本文由 发表于 2020年1月6日 23:28:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/59614747.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定