4

I keep trying to implement input accessory VC in my app and I faced with the following issue. When I'm trying to modify the height of the root view of my custom UIInputViewController it's working well despite the one problem. The problem is that in the logs I see the following:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    <NSAutoresizingMaskLayoutConstraint:0x174490cc0 UIInputView:0x10a80bb80.(null) == 46.5>,    
    <NSAutoresizingMaskLayoutConstraint:0x17448cd50 UIInputView:0x10a80bb80.height == 76>,  
    <NSLayoutConstraint:0x17429b710 UIInputView:0x10a80bb80.top == UIInputSetHostView:0x10402e940.top>  
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x17429b710 UIInputView:0x10a80bb80.top == UIInputSetHostView:0x10402e940.top>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Code of my custom UIInputViewController:

import UIKit
import RxSwift
import RxCocoa


@objc protocol InputViewControllerDelegate: NSObjectProtocol {
    func answerTextViewDidChange(_ textView: UITextView)
}

class InputViewController: UIInputViewController {
    fileprivate var closeButton: UIButton?
    private var separatorView: UIView?
    private var tipLabel: UILabel?
    private var answerTextView: ConstrainedTextView?

    private var buttonHeightConstraint: NSLayoutConstraint?
    private var separatorHeightConstraint: NSLayoutConstraint?
    private var answerTextViewBottomConstraint: NSLayoutConstraint?

    weak var delegate: InputViewControllerDelegate?

    private let junk = DisposeBag()

    private var appropriateMaxLines: Int {
        let isPortrait = UIDevice.current.orientation.isPortrait
        return isPortrait ? 5 : 3
    }

    var answerText: String {
        get {
            return answerTextView?.text ?? ""
        }

        set {
            answerTextView?.text = newValue
        }
    }

    var isAnswerTextViewFirstResponder: Bool {
        get {
            return answerTextView?.isFirstResponder ?? false
        }

        set {
            _ = newValue ? answerTextView?.becomeFirstResponder() : answerTextView?.resignFirstResponder()
        }
    }


    // MARK: - Life Cycle

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    override func loadView() {
        super.loadView()

        let notifName = Notification.Name.UIDeviceOrientationDidChange
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(rotated),
                                               name: notifName,
                                               object: nil)

        configureView()
    }

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

        answerTextView?.maxLines = appropriateMaxLines
        setCloseButtonDisabledIfNeeded()
    }


    // MARK: - Layout

    private func configureView() {

        view.backgroundColor = RGB(0xF6F6F6)
        view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: 70)
        view.autoresizingMask = [.flexibleWidth]


        // Separator

        separatorView = UIView()
        separatorView?.backgroundColor = UIColor.lightGray
        separatorView?.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(separatorView!)
        AutoLayoutEqualizeSuper(separatorView, .left, 0)
        AutoLayoutEqualizeSuper(separatorView, .right, 0)
        AutoLayoutEqualizeSuper(separatorView, .top, 0)
        separatorHeightConstraint = AutoLayoutSetAttribute(separatorView, .height, 1)


        // Close Button

        closeButton = UIButton(type: .system)
        closeButton?.setTitle("Hide", for: .normal)
        closeButton?.titleLabel?.font = UIFont.systemFont(ofSize: 17)
        closeButton?.translatesAutoresizingMaskIntoConstraints = false
        closeButton?.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
        view.addSubview(closeButton!)
        AutoLayoutSetAttribute(closeButton, .width, 70)
        buttonHeightConstraint = AutoLayoutSetAttribute(closeButton, .height, 35)
        AutoLayoutEqualizeSuper(closeButton, .right, -5)
        view.addConstraint(NSLayoutConstraint(item: closeButton!, attribute: .top, relatedBy: .equal, toItem: separatorView, attribute: .bottom, multiplier: 1, constant: 0))


        // Tip Label

        tipLabel = UILabel()
        tipLabel?.textColor = UIColor.darkGray
        tipLabel?.text = "Your answer:"
        tipLabel?.font = UIFont.systemFont(ofSize: 17)
        tipLabel?.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tipLabel!)
        AutoLayoutEqualizeSuper(tipLabel, .left, 5)
        AutoLayoutSetAttribute(tipLabel, .height, 35)
        view.addConstraint(NSLayoutConstraint(item: tipLabel!, attribute: .right, relatedBy: .equal, toItem: closeButton, attribute: .left, multiplier: 1, constant: 0))


        // Text View

        answerTextView = ConstrainedTextView()
        answerTextView?.backgroundColor = UIColor.white
        answerTextView?.delegate = self
        answerTextView?.scrollsToTop = false
        answerTextView?.showsVerticalScrollIndicator = false
        answerTextView?.font = REG_FONT(15)
        answerTextView?.translatesAutoresizingMaskIntoConstraints = false

        answerTextView?.layer.masksToBounds = true
        answerTextView?.layer.cornerRadius = 7

        answerTextView?.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.7).cgColor
        answerTextView?.layer.borderWidth = 1

        view.addSubview(answerTextView!)
        AutoLayoutEqualizeSuper(answerTextView, .left, 5)
        AutoLayoutEqualizeSuper(answerTextView, .right, -5)
        answerTextViewBottomConstraint = AutoLayoutEqualizeSuper(answerTextView, .bottom, -5)

        answerTextView?
            .rx
            .observe(CGRect.self, "bounds")
            .distinctUntilChanged {
                $0?.height == $1?.height
            }
            .subscribe(onNext: { [unowned self] newBounds in
                if var newHeight = newBounds?.height,
                    let separatorHeight = self.separatorHeightConstraint?.constant,
                    let buttonHeight = self.buttonHeightConstraint?.constant,
                    let bottomSpace = self.answerTextViewBottomConstraint?.constant {

                    newHeight = newHeight < 35 ? 35 : newHeight

                    let generalHeight = newHeight + separatorHeight + buttonHeight + abs(bottomSpace)

                    var frame = self.view.frame
                    frame.size.height = generalHeight
                    self.view.frame = frame
                }
            })
            .addDisposableTo(junk)
    }


    // MARK: - Other methods

    fileprivate func setCloseButtonDisabledIfNeeded() {
        closeButton?.isEnabled = answerTextView?.isFirstResponder ?? false
    }

    func rotated() {
        answerTextView?.maxLines = appropriateMaxLines
    }
}


// MARK: - UITextViewDelegate Protocol Conformance

extension InputViewController: UITextViewDelegate {

    func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
        textView.inputAccessoryView = view
        return true
    }

    func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
        textView.inputAccessoryView = nil
        return true
    }

    func textViewDidBeginEditing(_ textView: UITextView) {
        setCloseButtonDisabledIfNeeded()
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        setCloseButtonDisabledIfNeeded()
    }

    func textViewDidChange(_ textView: UITextView) {
        delegate?.answerTextViewDidChange(textView)
    }

}
Dmitriy Stupivtsev
  • 832
  • 1
  • 8
  • 17

0 Answers0