更新 nslayoutconstraints 以适应新的视图大小?

update nslayoutconstraints to accommodate new view size?

我有一个 UIView,一旦 UITextView 中的文本达到某个点,它的大小就会增加。两个视图都在调整大小,但是我无法让视图向上移动以适应我在 UIView 上设置的 bottomconstraint。这是问题的屏幕截图。

我尝试过同时使用 view.setNeedsLayout()view.layoutIfNeeded(),但我假设我可能实施错误...

这是项目的代码

import UIKit

class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UITextViewDelegate {


var bottomConstraint: NSLayoutConstraint?
var phconstraint: NSLayoutConstraint?

let searchBarContainer: UIView = {
    let sBarContainer = UIView()
    sBarContainer.backgroundColor = UIColor.gray
    sBarContainer.layer.cornerRadius = 3
    return sBarContainer
}()

let searchBar: UITextView = {
    let sBar = UITextView()
    sBar.textAlignment = .left
    sBar.font = .systemFont(ofSize: 12)
    sBar.backgroundColor = UIColor.red
    sBar.sizeToFit()

    return sBar
}()

let placeholder: UILabel = {
    let pholder = UILabel()

    pholder.font = UIFont.systemFont(ofSize: 16, weight: 0.20)
    pholder.frame.size = pholder.intrinsicContentSize
    pholder.backgroundColor = UIColor.clear
    pholder.textColor = UIColor.gray

    pholder.text = "Share!"

    pholder.textAlignment = .left


    return pholder
}()

let dividerLine: UIView = {
    let line = UIView()
    line.backgroundColor = UIColor.init(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
    return line
}()

let bubbleview : UIView = {
   let bView = UIView()
    bView.backgroundColor = UIColor.clear
    return bView

}()

func setupKeyboardObservers() {

    NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func handleKeyboardNotification(_ notification: Notification) {

    if let userInfo = notification.userInfo {
        let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue
        print(keyboardFrame as Any)

        let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
        let isKeybobardNotShowing = notification.name == NSNotification.Name.UIKeyboardWillHide

        bottomConstraint?.constant = isKeyboardShowing ? -keyboardFrame!.height : 0

        UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {

            self.view.layoutIfNeeded()
        }, completion: { (completed) in


        })
    }
}


override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.title = "Search Bar"
    collectionView?.backgroundColor = UIColor.white

    setupKeyboardObservers()
    self.searchBar.delegate = self    
    view.addSubview(searchBarContainer)
    searchBarContainer.addSubview(searchBar)
    searchBar.translatesAutoresizingMaskIntoConstraints = false
    searchBarContainer.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint(item: searchBarContainer, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: 0.75, constant: 0).isActive = true

    NSLayoutConstraint(item: searchBarContainer, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: 0.05, constant: 0).isActive = true

    NSLayoutConstraint(item: searchBarContainer, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0).isActive = true

     bottomConstraint = NSLayoutConstraint(item: searchBarContainer, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)

            view.addConstraint(bottomConstraint!)

}


func textViewDidChange(_ textView: UITextView) {
            let currentString: String = textView.text!
            let length: Int = (currentString.characters.count )

        let messageText = currentString
        let size = CGSize(width: searchBarContainer.frame.width , height: searchBarContainer.frame.height * 2)
        let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
        let estimatedFrame = NSString(string: messageText).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 12)], context: nil)
    print("this is the ewidth \(estimatedFrame.width)")
    searchBarContainer.addSubview(bubbleview)
    bubbleview.frame = estimatedFrame

    searchBar.sizeToFit()
    searchBar.frame.size.width = searchBarContainer.frame.width


            if estimatedFrame.height >= (searchBarContainer.frame.height) {

            searchBarContainer.frame.size.height = estimatedFrame.height


                self.view.addConstraint(self.bottomConstraint!)

                UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {

                    self.view.layoutIfNeeded()
                }, completion: { (completed) in



                })


        }
    view.setNeedsLayout()
    view.layoutIfNeeded()

}

}


extension UIView {
func addConstraintsWithFormat(_ format: String, views : UIView...) {


    var viewsDictionary = [String: UIView]()

    for(index, view) in views.enumerated(){
        let key = "v\(index)"
        viewsDictionary[key] = view
        view.translatesAutoresizingMaskIntoConstraints = false

    }
    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))

}
}

只需使用 stackview.

  1. 这是将您的 textView 包装到 stackView
  2. 为 stackView 提取必要的布局约束。
  3. 使您的 TextView 在属性检查器中不可滚动。当文本增加时,您的 textView 会向上增加。

情侣笔记...

当尝试使用 "auto-sizing" UITextView 时,您会发现如果禁用滚动效果效果最好...因此将其添加到您的 searchBar 设置中:

    sBar.isScrollEnabled = false

您将自动布局/约束与显式设置框架大小混合在一起。这经常会给你带来麻烦。使用一个或另一个(约束是首选方法)。在您的代码中,您设置

    searchBar.translatesAutoresizingMaskIntoConstraints = false

但你从不给它任何限制。

编辑:经过一番尝试,您可以仅依靠自动布局就可以大大简化事情。

不要使用 "container" 视图来保存 searchBar 文本视图,而是创建一个位于 searchBar 下方的 "padding" 视图。那么:

  1. searchBar应该坐"on top of"paddingView
  2. paddingView 应该随键盘向上滑动
  3. searchBar 会自动向上滑动,因为它固定在 paddingView
  4. 的顶部
  5. searchBar的高度会根据文字内容自动变化

所以 -- paddingView 将控制位置、宽度和 Y 位置。

您已经有一个属性来控制Y轴位置

var bottomConstraint: NSLayoutConstraint?

更改 searchBar 定义并添加 paddingView 定义:

let searchBar: UITextView = {
    let sBar = UITextView()
    sBar.textAlignment = .left
    sBar.font = .systemFont(ofSize: 12)
    sBar.backgroundColor = UIColor.red
    sBar.isScrollEnabled = false
    sBar.translatesAutoresizingMaskIntoConstraints = false
    return sBar
}()

let paddingView: UIView = {
    let v = UIView()
    v.translatesAutoresizingMaskIntoConstraints = false
    v.backgroundColor = .gray
    v.layer.cornerRadius = 3.0
    return v
}()

viewDidLoad 结果是这样的:

override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.title = "Search Bar"
    collectionView?.backgroundColor = UIColor.white
    setupKeyboardObservers()
    self.searchBar.delegate = self

    // add bottom "padding" view
    view.addSubview(paddingView)

    // add searchBar text view
    view.addSubview(searchBar)

    // set width of padding view to 75% of view width
    paddingView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75).isActive = true
    // set center X of padding view to center X of view
    paddingView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 1.0).isActive = true
    // set height of padding view to 6.0 (we're rounding the corners with a radius of 3.0)
    paddingView.heightAnchor.constraint(equalToConstant: 6.0).isActive = true

    // assign a constraint to bottomConstraint property so we can change the Y-position of padding view (and searchBar) when desired
    // initially set the constant to 0 (so it sits on the bottom of the view)
    bottomConstraint = paddingView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0)
    bottomConstraint?.isActive = true

    // set searchBar width to padView width
    searchBar.widthAnchor.constraint(equalTo: paddingView.widthAnchor, multiplier: 1.0).isActive = true
    // set searchBar center X to padView center X
    searchBar.centerXAnchor.constraint(equalTo: paddingView.centerXAnchor, constant: 0.0).isActive = true
    // set searchBar bottom to padView Top + 3.0 (we want to cover the top 3 pts)
    searchBar.bottomAnchor.constraint(equalTo: paddingView.topAnchor, constant: 3.0).isActive = true
}

因为UITextView在使用自动布局时会自动调整自己的大小,所以你甚至不需要textViewDidChange函数:

func textViewDidChange(_ textView: UITextView) {
    // nothing to do here...
}

注意:我从你的 bubbleview 中只能看到它覆盖了 searchBar 文本视图的一部分...所以我忽略了它。

调用 sizeToFit()

后更新 textView 布局
searchBar.sizeToFit()
searchBar.frame.size.width = searchBarContainer.frame.width
searchBar.setNeedsLayout()