Resize inputAccessoryView dynamically in iOS 8

后端 未结 3 1356
半阙折子戏
半阙折子戏 2021-02-04 12:27

Basically I am trying to create a resizing UITextView within a inputAccessoryView property.

I have a viewController with the method canBecomeFirstResponder returning tru

相关标签:
3条回答
  • 2021-02-04 13:03

    A fairly simple fix for this is to not make the actual inputAccessoryView resize at all - if you know the maximum height you'll need for it, you can create it at that maximum height, make it transparent, and add a subview that resizes however you like.

    The detail you need to make it work is that that the inputAccessoryView needs to be a UIView subclass containing the following:

    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    {
        point = [self.innerView convertPoint:point fromView:self];
        return [self.innerView pointInside:point withEvent:event];
    }
    

    (where self.innerView is your dynamically resizing subview).

    This makes it claim taps inside the subview, but pass on any that fall outside it.

    The main drawback to be aware of is that keyboard notifications will give you frames containing the entire max-height inputAccessoryView. So if you're using them to (eg) adjust insets, you need to do some extra math there.

    0 讨论(0)
  • 2021-02-04 13:05

    It works for me in iOS7/iOS8:

    class MessageInputAccessory: UIView, UITextViewDelegate {
        private var textView: UITextView!
        private var heightConstraint: NSLayoutConstraint?
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            commonInit()
        }
    
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            commonInit()
        }
    
        func commonInit() {
            self.userInteractionEnabled = true
    
            textView = UITextView()
            textView.delegate = self
            textView.bounces = false
            textView.scrollEnabled = false
            textView.layer.cornerRadius = 5
            textView.layer.borderWidth = 1
            textView.layer.borderColor = UIColor.blueColor().CGColor
    
            self.addSubview(textView)
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            textView.frame = self.bounds
        }
    
        override func addConstraint(constraint: NSLayoutConstraint) {
            self.heightConstraint = constraint
    
            super.addConstraint(constraint)
        }
    
        func textViewDidChange(textView: UITextView) {
            var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
            self.frame.size.height = contentSize.height
    
            if let heightConstraint = self.heightConstraint {
                heightConstraint.constant = self.frame.size.height
            }
    
            self.textView.reloadInputViews()
        }
    }
    

    EDIT: This works with xib too (iOS7/iOS8):

    class MessageInputAccessory: UIView, UITextViewDelegate {
        @IBOutlet var textView: UITextView!
        @IBOutlet var textViewHeightConstraint: NSLayoutConstraint!
        private var heightConstraint: NSLayoutConstraint?
    
        override func awakeFromNib() {
            textView.layer.cornerRadius = 5
            textView.layer.borderWidth = 1
            textView.layer.borderColor = UIColor.blueColor().CGColor
        }
    
        override func layoutSubviews() {
            textViewHeightConstraint.constant = self.bounds.size.height
    
            super.layoutSubviews()
        }
    
        override func addConstraint(constraint: NSLayoutConstraint) {
            if constraint.firstItem === self {
                self.heightConstraint = constraint
            }
    
            super.addConstraint(constraint)
        }
    
        override func addConstraints(constraints: [AnyObject]) {
            super.addConstraints(constraints)
        }
    
        func textViewDidChange(textView: UITextView) {
            var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
            self.frame.size.height = contentSize.height
    
            if let heightConstraint = self.heightConstraint {
                heightConstraint.constant = self.frame.size.height
            }
    
            self.textView.reloadInputViews()
        }
    
        override func intrinsicContentSize() -> CGSize {
            return self.bounds.size;
        }
    }
    

    There is my xib: enter image description here

    It is not a very good solution, but it works.. Please tell me if you know a better way.

    0 讨论(0)
  • 2021-02-04 13:30

    SWIFT 5 Solution

    If anyone has been trying to find for a solution in stackoverflow like me for input accessory view, I have made a simple project demo for a dynamically resizing input accessory with UITextView. It also comes with a tappable button on top of the UITextView to demonstrate added views on top of the Input Accessory View. All of these are loaded in a XIB for easy customisation. Written in Swift 5, and also takes care of the safe area for X devices.

    class InputAccessoryView: UIView, UITextViewDelegate {
    
    @IBOutlet weak var textView: UITextView!
    
    @IBOutlet weak var accessoryButtonViewHeightConstraint: NSLayoutConstraint!
    
    @IBOutlet weak var accessoryButtonView: UIView!
    // MARK: - Init
    
    required init?(coder aDecoder: NSCoder) {
    
        super.init(coder: aDecoder)
    
        self.setup()
    }
    
    override init(frame: CGRect) {
    
        super.init(frame: frame)
    
        self.setup()
    }
    
    private func setup() {
    
        let view = self.viewFromNibForClass()
    
        view?.frame = self.bounds
    
        view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    
        self.accessoryButtonViewHeightConstraint.constant = 0
    
        if let view = view {
    
            self.addSubview(view)
    
            self.textView.translatesAutoresizingMaskIntoConstraints = false
        }
    }
    
    override func didMoveToWindow() {
    
        super.didMoveToWindow()
    
        if let window = self.window {
    
            self.bottomAnchor.constraint(lessThanOrEqualTo: window.safeAreaLayoutGuide.bottomAnchor).isActive = true
        }
    }
    
    private func viewFromNibForClass() -> UIView? {
    
        let bundle = Bundle(for: type(of: self))
    
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
    
        let view = nib.instantiate(withOwner: self, options: nil).first as? UIView
    
        return view
    }
    
    func textViewDidChange(_ textView: UITextView) {
    
        self.accessoryButtonViewHeightConstraint.constant = 40
    
        self.accessoryButtonView.alpha = 1
    
        self.updateHeight(textView.contentSize.height + 20 + self.accessoryButtonViewHeightConstraint.constant)
    }
    
    private func updateHeight(_ height: CGFloat) {
    
        for constraint in self.constraints where constraint.firstAttribute == .height {
    
            constraint.constant = height
        }
    }
    
    @IBAction func accessoryButtonTouchUpInside(_ sender: Any) {
    
        let height = self.frame.size.height - 40
    
        self.updateHeight(height)
    
        self.accessoryButtonViewHeightConstraint.constant = 0
    }
    }
    

    Here is an example of the usage: https://github.com/jaysalvador/InputAccessoryView

    0 讨论(0)
提交回复
热议问题