0

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 }
}
Chris
  • 431
  • 8
  • 33
  • Not quite clear what's going on... try providing enough code for a [mre]. – DonMag Apr 29 '20 at 16:33
  • @DonMag I have added more code related to the inputAccessoryView. Visually, I am adding an input textView at the bottom of the viewcontroller like a chatroom. I would like to toggle showing/hiding this and it appears the inputAccessoryView has a native bottom slide in/out animation, which I'm hoping to programmatically invoke but am unsure how...happy to offer more context! – Chris Apr 29 '20 at 16:49

1 Answers1

0

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.

DonMag
  • 69,424
  • 5
  • 50
  • 86