iosswiftinputaccessoryview

Hiding/Showing Input Accessory View


I have a custom inputAccessoryView and am trying to toggle hiding/showing it. I don't want to utilize .isHidden or .removeFromSuperView(), rather, use the bottom slide in/out, which seems to be native as I present other viewControllers onto the hierarchy and this animation executes.

I've tried to resignFirstResponder with no luck and there doesn't seem to be any existing commentary around this. Any thoughts would be appreciated as I am admittedly stumped.

class CustomInputAccessoryView: UIView {

let customTextView: CustomInputTextView = {
    let tv = CustomInputTextView()
    tv.isScrollEnabled = false
    return tv
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = UIColor.white
    autoresizingMask = .flexibleHeight

    addSubview(customTextView)
    customTextView.topAnchor.constraint(equalTo: topAnchor, constant: 12).isActive = true
    customTextView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: 8).isActive = true
    customTextView.leftAnchor.constraint(equalTo: leftAnchor, constant: 10).isActive = true
    customTextView.rightAnchor.constraint(equalTo: rightAnchor, constant: 10).isActive = true
    customTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: frame.height - 20).isActive = true
}

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

override var intrinsicContentSize: CGSize {
    return .zero
}
}

class CustomInputTextView: UITextView {
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }

    override var canResignFirstResponder: Bool {
        return true
    }

    override var canBecomeFirstResponder: Bool {
        return true
    }

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

//in viewcontroller
lazy var inputContainerView: CustomInputAccessoryView = {
    let frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60)
    let customInputAccessoryView = CustomInputAccessoryView(frame: frame)
    return customInputAccessoryView
}()        

//onLoad
override var inputAccessoryView: UIView? {
    get { return inputContainerView }
}

Solution

  • I don't know if this is really what you're going for, but give it a try.

    Tapping anywhere in the view will show/hide the input accessory view:

    class TestInputViewController: UIViewController {
    
        //in viewcontroller
        lazy var inputContainerView: CustomInputAccessoryView = {
            let frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60)
            let customInputAccessoryView = CustomInputAccessoryView(frame: frame)
            return customInputAccessoryView
        }()
    
        override var canBecomeFirstResponder: Bool {
            return true
        }
    
        //onLoad
        override var inputAccessoryView: UIView? {
            get { return inputContainerView }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // so we can see the frame
            inputContainerView.backgroundColor = .blue
    
            // tap anywhere in view to show / hide input accessory view
            let g = UITapGestureRecognizer(target: self, action: #selector(didTap(sender:)))
            view.addGestureRecognizer(g)
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
        }
    
        @objc func didTap(sender: UITapGestureRecognizer) {
            if self.isFirstResponder {
                resignFirstResponder()
            } else {
                becomeFirstResponder()
            }
        }
    
    }
    
    class CustomInputAccessoryView: UIView {
    
        let customTextView: CustomInputTextView = {
            let tv = CustomInputTextView()
            tv.isScrollEnabled = false
            return tv
        }()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            backgroundColor = UIColor.white
            autoresizingMask = .flexibleHeight
    
            addSubview(customTextView)
            customTextView.translatesAutoresizingMaskIntoConstraints = false
            customTextView.topAnchor.constraint(equalTo: topAnchor, constant: 12).isActive = true
            customTextView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -8).isActive = true
            customTextView.leftAnchor.constraint(equalTo: leftAnchor, constant: 10).isActive = true
            customTextView.rightAnchor.constraint(equalTo: rightAnchor, constant: -10).isActive = true
            customTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: frame.height - 20).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override var intrinsicContentSize: CGSize {
            return .zero
        }
    }
    
    class CustomInputTextView: UITextView {
        override init(frame: CGRect, textContainer: NSTextContainer?) {
            super.init(frame: frame, textContainer: textContainer)
        }
    
        override var canResignFirstResponder: Bool {
            return true
        }
    
        override var canBecomeFirstResponder: Bool {
            return true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init coder has not been implemented")
        }
    }
    

    Not related to showing / hiding, but a few of your constraints were wrong, causing the text view to be misplaced.