【问题标题】:Resize inputAccessoryView dynamically in iOS 8在 iOS 8 中动态调整 inputAccessoryView 的大小
【发布时间】:2015-08-04 18:46:31
【问题描述】:

基本上我正在尝试在 inputAccessoryView 属性中创建一个调整大小的 UITextView。

我有一个 viewController,其方法 canBecomeFirstResponder 返回 true,还有一个我通过自定义类(从 XIB 获取)实例化的视图。在这个视图中是一个 UITextView。

我尝试从该类内部调整整个 inputAccessoryView 的大小。我尝试了几种方法:直接设置框架,尝试使用高度约束。它似乎只调整了一半:

这基本上是我想要的(有或没有自动布局,但在 iOS 7/8+ 中工作):

class MessageInputAccessory: UIView, UITextViewDelegate
{
    @IBOutlet weak var textView: UITextView!

    func textViewDidChange(textView: UITextView)
    {
        var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))

        self.frame.size.height = contentSize.height + 20

        self.textView.reloadInputViews()
    }
}

我找到了这个帖子:Changing the frame of an inputAccessoryView in iOS 8。说明正在创建一个自动创建的约束。问题是,它不会为我创造约束。绝不会禁用或启用自动布局(也通过 setTranslatesAutoresizingMaskIntoConstraints())。

【问题讨论】:

    标签: ios objective-c swift inputaccessoryview


    【解决方案1】:

    它适用于 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()
        }
    }
    

    编辑: 这也适用于 xib (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;
        }
    }
    

    有我的xib:

    这不是一个很好的解决方案,但它确实有效。如果你知道更好的方法,请告诉我。

    【讨论】:

    • 这确实有效,但在我的情况下,我从 NIB 加载视图。我真的很想从 NIB 文件加载视图,而不是以编程方式创建它。
    • 是的,它有效,它真的很糟糕,你必须用私人约束来设置高度!我现在会接受它是正确的,也许有更好的解决方案。
    • addConstraint(_:) 不是已弃用吗?
    • 你在哪里初始化heightConstraint
    【解决方案2】:

    SWIFT 5解决方案

    如果有人像我一样尝试在 stackoverflow 中寻找输入附件视图的解决方案,我已经制作了一个简单的项目演示,用于使用 UITextView 动态调整输入附件的大小。它还在 UITextView 顶部带有一个可点击的按钮,以演示在 Input Accessory View 顶部添加的视图。所有这些都加载到 XIB 中以便于定制。用 Swift 5 编写,还负责 X 设备的安全区域。

    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
    }
    }
    

    这是一个用法示例: https://github.com/jaysalvador/InputAccessoryView

    【讨论】:

      【解决方案3】:

      对此的一个相当简单的解决方法是根本不调整实际的 inputAccessoryView 的大小 - 如果您知道它需要的最大高度,您可以在该最大高度创建它,使其透明,并添加一个子视图可以随意调整大小。

      你需要让它工作的细节是 inputAccessoryView 需要是一个包含以下内容的 UIView 子类:

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

      (其中 self.innerView 是您动态调整大小的子视图)。

      这使得它在子视图内声明水龙头,但传递任何落在它之外的水龙头。

      需要注意的主要缺点是键盘通知会为您提供包含整个最大高度 inputAccessoryView 的帧。因此,如果您使用它们来(例如)调整插图,则需要在那里做一些额外的数学运算。

      【讨论】:

        猜你喜欢
        • 2014-09-25
        • 1970-01-01
        • 1970-01-01
        • 2015-05-13
        • 1970-01-01
        • 2014-11-05
        • 2014-11-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多