Swift: How to continuously adapt a UICollectionViewCell height to a UITextView while writing in it?

微笑、不失礼 提交于 2020-01-04 09:06:16

问题


Good afternoon.

I've struggled with this "feature" for my iOS app for some hours now and I'm in need of some help.

Question: How should I implement so that when a user types in the UITextView increases in size (only the bottom margin) and the cell increases its height to fit the UITextView dynamically meanwhile? I can't figure out how to tackle it.

Research: After some searching I came upon Dynamically change cell's height while typing text, and reload the containing tableview for resize as well as Can you animate a height change on a UITableViewCell when selected? I've also read https://developer.apple.com/documentation/uikit/uicollectionview but I feel my swift knowledge so far is inferior to whats expected in these posts.

In other words I cant figure out how to implement it for my UICollectionView.

My code at moment looks like this (I have more UI elements in the EventsCell but cut them out to save space, thus its only the bottom margin of the UITextView I want to change). I have set the RootCell as delegate for my UITextView and then i intend to grab the textView height as it is edited in order to get the height I want, but it isn't working.

Cell class:

class EventsCell: UICollectionViewCell {
  override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = UIColor.white
    setupViews()


  }
  let textView: GrowingTextView = { // GrowingTextView is a cocoapod extending UITextView
    let rootCellDelegate = RootCell()
    let tv = GrowingTextView()
    tv.backgroundColor = .clear
    tv.allowsEditingTextAttributes = true
    tv.isScrollEnabled = false
    tv.font = UIFont(name: "Didot", size: 16)
    tv.textColor = .black
    tv.textContainerInset = UIEdgeInsetsMake(4, 4, 4, 6)
    tv.placeholder = "Write your event text here"
    tv.placeholderColor = UIColor.lightGray
    tv.autoresizingMask = .flexibleHeight
    tv.isUserInteractionEnabled = false
    tv.delegate = rootCellDelegate
    return tv
}()

let eventPlaceholderMarkImage: UIButton = {
    let iv = UIButton()
    let image = UIImage(named: "placeHolderEventTitleMark")
    iv.setImage(image, for: .normal)

    return iv
}()

func setupViews() {


    //MARK: - Constraints for EventsCell
    //horizontal
    addConstraintsWithFormat(format: "H:|-16-[v0(\(frame.width - 32))]-16-|", views: textView)
    addConstraintsWithFormat(format: "V:|-47-[v0]-16-|", views: textView)

    addConstraint(NSLayoutConstraint(item: eventPlaceholderMarkImage, attribute: .bottom, relatedBy: .equal, toItem: textView, attribute: .top, multiplier: 1, constant: -7))
    addConstraint(NSLayoutConstraint(item: eventPlaceholderMarkImage, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 20))
    addConstraintsWithFormat(format: "H:|-12-[v0(27)]-\(cellWidth - 39)-|", views: eventPlaceholderMarkImage)
}
}

UICollectionView class:

class RootCell: BaseCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

      var textViewHeight : CGFloat?

func textViewDidChange(_ textView: UITextView) {
    textViewHeight = textView.textContainer.size.height + 63
    collectionView.reloadInputViews()
    collectionView.reloadData()
    collectionView.layoutSubviews()


}

  lazy var collectionView: UICollectionView = {
      let layout = UICollectionViewFlowLayout()
      let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
      cv.backgroundColor = UIColor.white
      cv.delegate = self
      cv.dataSource = self
      return cv
  }()

override func setupViews() {
    super.setupViews()

    addSubview(collectionView)
    addConstraintsWithFormat(format: "H:|[v0]|", views: collectionView)
    addConstraintsWithFormat(format: "V:|[v0]|", views: collectionView)

    collectionView.register(EventsCell.self, forCellWithReuseIdentifier: EventCellID)
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: EventCellID, for: indexPath)
    return cell
}



func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let height = textViewHeight ?? frame.width * 1/3
    let width = frame.width - 8
    return CGSize(width: width, height: height)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

//MARK: - Design selected cells background

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    if let cell = collectionView.cellForItem(at: indexPath) as? EventsCell {

        cell.textView.isUserInteractionEnabled = true
        cell.eventTitleLabel.isUserInteractionEnabled = true

        cell.layer.borderColor = UIColor.flatBlack.cgColor
        cell.layer.borderWidth = 2
        cell.layer.cornerRadius = 7
        cell.backgroundColor = GradientColor(.topToBottom, frame: cell.frame, colors: [UIColor.flatRed.withAlphaComponent(0.2), UIColor.white])


    } else {return}

}

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {

    if let cell = collectionView.cellForItem(at: indexPath) {
        cell.layer.borderColor = UIColor.clear.cgColor
        cell.layer.borderWidth = 0
        cell.layer.cornerRadius = 0
        cell.backgroundColor = .white
    } else {return}

}
}

Attempt after first answer:

class RootCell: BaseCell, UITextViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

var textViewText : String?

func textViewDidChange(_ textView: UITextView) {

    textViewText = textView.text
    DispatchQueue.main.async {
        self.collectionView.reloadInputViews()
        self.collectionView.reloadData()
    }


}

func heightWithConstrainedWidth(width: CGFloat, font: UIFont) -> CGFloat {

    let constraintRect = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
    guard let boundingBox = textViewText?.boundingRect(with: constraintRect, options: [NSStringDrawingOptions.usesLineFragmentOrigin], attributes: [NSAttributedStringKey.font: font], context: nil) else {return frame.width * 1/3}

    return boundingBox.height
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let width = frame.width - 8
    let height = heightWithConstrainedWidth(width: width, font: UIFont(name: "Didot", size: 16)!)
    return CGSize(width: width, height: height)

}

Thank you for reading my post!


回答1:


First store textView text in some variable then you can get height of your text by this method

func heightWithConstrainedWidth(width: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
    let boundingBox = self.boundingRect(with: constraintRect, options: [NSStringDrawingOptions.usesLineFragmentOrigin], attributes: [NSAttributedStringKey.font: font], context: nil)
    return boundingBox.height
}

then pass this in height for cell + your paddings and don't forget to reload your collectionView when user enters something



来源:https://stackoverflow.com/questions/51642331/swift-how-to-continuously-adapt-a-uicollectionviewcell-height-to-a-uitextview-w

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!