Swift:如何在写入时将 UICollectionViewCell 高度连续调整为 UITextView? [英] Swift: How to continuously adapt a UICollectionViewCell height to a UITextView while writing in it?

查看:26
本文介绍了Swift:如何在写入时将 UICollectionViewCell 高度连续调整为 UITextView?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下午好.

几个小时以来,我一直在为我的 iOS 应用使用这个功能"而苦苦挣扎,我需要一些帮助.

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

问题:我应该如何实现,以便当用户在 UITextView 中键入时增加大小(仅底部边距)并且单元格增加其高度以适应UITextView 同时动态?我不知道如何解决它.

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.

研究:经过一番搜索,我发现 动态变化键入文本时单元格的高度,并重新加载包含 tableview 以调整大小 以及 您可以在选择时为 UITableViewCell 的高度变化设置动画吗?我还阅读了 https://developer.apple.com/documentation/uikit/uicollectionview 但我觉得到目前为止我的快速知识不如这些帖子中的预期.

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.

换句话说,我不知道如何为我的 UICollectionView 实现它.

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

我的代码现在看起来像这样(我在 EventsCell 中有更多的 UI 元素,但为了节省空间而将它们剪掉,因此它只是我想更改的 UITextView 的底部边距).我已经为我的 UITextView 将 RootCell 设置为 delegate 然后我打算在编辑时获取 textView 的高度以获得我想要的高度,但它不是'工作.

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.

单元类:

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

}
}

第一次回答后尝试:

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)

}

感谢您阅读我的帖子!

推荐答案

重新加载集合视图或单个单元格会使单元格闪烁,并且用户在键入时会失去对 TextView 的关注.如果用户失去焦点,他们必须再次点击 UITextView 才能继续输入.

Reloading the collection view or the individual cell will flicker the cells and the user would lose focus on the TextView as they type. If the user loses focus, they have to click on the UITextView again to keep on typing.

当您检测到 UITextView 需要调整大小时,您可以使集合视图的布局无效 - 如果您的自动布局参数全部正确,这将导致您的单元格调整大小.请参见下面的示例:

When you detect that your UITextView needs resizing, you can invalidate your collectionview's layout - which will cause your cells to be resized if your auto-layout parameters are all correct. See example below:

  1. 我有一个 UICollectionCell,里面只有 UITextView,并且在 UITextView 上滚动被禁用.使用 AutoLayout 将 UITextView 正确约束到单元格的所有边缘.最后,单元格也是 UITextView 的委托:

  1. I have a UICollectionCell with only UITextView inside of it, and scrolling on the UITextView is disabled. UITextView is properly constrained to all edges of the cell using AutoLayout. Finally, the cell is also the UITextView's delegate as such:

class MyCollectionCell: UICollectionViewCell {
  @IBOutlet private weak var myTextView: UITextView!

  //this local variable will be used to determine if the intrinsic content size has changed or not
  private var textViewHeight: CGFloat = .zero
  ...
  ...
  override func awakeFromNib() {
   super.awakeFromNib()
   myTextView.delegate = self
  }
  ...
  ...
}

  • 使这个单元格符合 UITextViewDelegate

  • Conform this cell to UITextViewDelegate

    extension MyCollectionCell: UITextViewDelegate { 
    
    func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
      //this assumes that collection view already correctly laid out the cell
      //to the correct height for the contents of the UITextView
      //textViewHeight simply needs to catch up to it before user starts typing
      let fittingSize = textView.sizeThatFits(CGSize(width: myTextView.frame.width, height: CGFloat.greatestFiniteMagnitude))
      textViewHeight = fittingSize.height
    
      return true
    }
    
    func textViewDidChange(_ textView: UITextView) {
      //flag to determine whether textview's size is changing
      var shouldResize = false
      //calculate fitting size after the content has changed
      let fittingSize = textView.sizeThatFits(CGSize(width: myTextView.frame.width, height: CGFloat.greatestFiniteMagnitude))
    
      //if the current height is not equal to the fitting height
      //save the new fitting height to our local variable and inform the delegate 
      //that collection view needs resizing
      if textViewHeight != fittingSize.height {
        shouldResize = true
        //save the new height 
        textViewHeight = fittingSize.height
      }
    
      //notify the cell's delegate (most likely a UIViewController) 
      //that UITextView's intrinsic content size has changed
      //perhaps with a protocol such as this:
      delegate?.textViewDidChange(newText: textView.text, alsoRequiresResize: shouldResize)
    }
    

  • 然后当代理收到通知时,可能会保存更新的文本并更新布局,例如:

  • Then when the delegate receives the notification, perhaps saves the updated text and updates the layout such as this:

    myCollectionView.collectionViewLayout.invalidateLayout()
    

  • 此解决方案的最佳部分是您不必调用 collectionView(_:layout:sizeForItemAt:).

    The best part of this solution is that you do not have to invoke collectionView(_:layout:sizeForItemAt:).

    干杯!

    这篇关于Swift:如何在写入时将 UICollectionViewCell 高度连续调整为 UITextView?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆