【问题标题】:CollectionView dynamic height with Swift 3 in iOS在 iOS 中使用 Swift 3 的 CollectionView 动态高度
【发布时间】:2017-12-25 12:50:51
【问题描述】:

最近,我正在尝试做一些练习项目。于是,我观看了斯坦福大学 CS193P,2017 年的课程“Developing Apps for iOS”。

现在,我正在做“Smashtag”项目,但我遇到了一些问题。 我想使用带有两个 UICollectionViewFlowLayout 的 CollectionView 通过 SegmentedControl 在 CollectionView 的每个 xib 中显示两种类型(一种类似于 tableView,另一种以正方形显示图像)。

我的问题如下:

  1. 如何让 CollectionViewCell 像 TableView 的 UITableViewAutomaticDimension 一样以动态高度显示?

    这就是我刚刚在下面尝试过的:

    我尝试使用 UICollectionViewFlowLayoutAutomaticSize 使 CollectionView 的 autoResize 设置与 TableView 的 autoDimension 一样。 (@available(iOS 10.0, *))

enum CollectionViewType {
  case tweet
  case image
}

// MARK: - Decide What Kind Of FlowLayout
func decideFlowLayout(type: CollectionViewType) -> UICollectionViewFlowLayout {

    // just for .image type
    var howManyImageShowing = 3  
    var imageShowingWidth: Double {
        return Double(self.view.frame.width) / Double(howManyImageShowing)
    }

    // .tweet is a type showing like TableViewCell -> this make me confused !!
    // another type is just showing a image in square -> I have no problem here
    let estimatedItemSize = type == .tweet ? CGSize(width: self.view.frame.width, height: 155.0) :
                                                CGSize(width: imageShowingWidth, height: imageShowingWidth)

    let collectionViewLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

    // --------- Make this setting as TableView does --------- 
    // ------------ Somethis just like this below ------------
    /*
        tableView.estimatedRowHeight = 155.0
        tableView.rowHeight = UITableViewAutomaticDimension
    */

    collectionViewLayout.estimatedItemSize = estimatedItemSize
    collectionViewLayout.itemSize = UICollectionViewFlowLayoutAutomaticSize
    // ------------------------------------------------------- 

    collectionViewLayout.minimumLineSpacing = 0
    collectionViewLayout.minimumInteritemSpacing = 0

    return collectionViewLayout
}

我的 CollectionViewCell xib 的约束在这里: 但是当我运行这个应用程序时它有错误

我不知道我错过了什么或误解了什么。

  1. 为什么这个解决方案会起作用?

    我已经在 Stack Overflow 中搜索了很多这个问题的解决方案,但我发现这个让我很困惑:(

    此解决方案为视图提供了一个 Width Constraint,它位于 CollectionViewCell xib 中,并将此约束的常量设置为一个值,它就可以工作了!我无法配置为什么 Width Constraint 会使单元格的高度动态变化?这让我很困惑,我觉得这很奇怪......

    https://github.com/tttsunny/CollectionViewAutoSizingTest

class Cell: UICollectionViewCell {
    @IBOutlet weak var headerLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var widthConstraint: NSLayoutConstraint!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.contentView.translatesAutoresizingMaskIntoConstraints = false
        let screenWidth = UIScreen.main.bounds.size.width
        widthConstraint.constant = screenWidth - (2 * 12)
    }
}
  1. 如何在不使用 UICollectionViewFlowLayoutAutomaticSize 的情况下做 CollectionViewCell 动态高度。 (@available(iOS 10.0, *)) ?

    因为我想处理不同的 iOS 版本,不仅在 iOS 10 中,而且在 iOS 10 以下。

我真的很想成为 iOS 开发方面的专业人士和优秀的开发人员,但我仍在学习和尝试。任何答复我将不胜感激。

谢谢!!

【问题讨论】:

    标签: ios dynamic swift3 uicollectionview autolayout


    【解决方案1】:

    使用以下代码根据显示的文字改变高度:

     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        return CGSizeMake(view.frame.width , 64)
    }
    override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    {
      let approximateWidthOfContent = view.frame.width - x
        // x is the width of the logo in the left 
    
      let size = CGSize(width: approximateWidthOfContent, height: 1000)
    
      //1000 is the large arbitrary values which should be taken in case of very high amount of content 
    
     let attributes = [NSFontAttributeName: UIFont.systemFont(ofSize: 15)]            
     let estimatedFrame = NSString(string: user.bioText).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
     return CGSize(width: view.frame.width, height: estimatedFrame.height + 66)          
     }
    

    【讨论】:

    • 对不起,你的身高[indexPath.row]是什么意思?
    • 它给你当前行的高度
    • 但我没看到你的“身高”声明在哪里?
    • 我认为改变单元格的框架应该写在 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize ,对吧?
    • 谢谢,我使用你的逻辑并尝试在 sizeForItemAt 函数中计算我的单元格的高度,它按预期工作:)
    【解决方案2】:

    我解决了:)

    我只是尝试在 sizeForItemAt 函数中计算单元格的宽度和期望高度,它可以工作!

    代码如下:

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let imageShowingWidth: CGFloat = self.view.frame.width / CGFloat(self.howManyImageShowing)
    
        let labelName = "@\(self.tweetShowing[indexPath.row].user.screenName) (\(self.tweetShowing[indexPath.row].user.name))"
        let labelNameFont: UIFont = UIFont(name: "PingFangTC-Semibold", size: 16)!
        let labelNameWidth: CGFloat = self.view.frame.width - YourWidthOffSet// (YourWidthOffSet include all images' width and all margins)
        let labelNameHeight: CGFloat = self.getHeightForLable(labelWidth: labelNameWidth, labelText: labelName, labelFont: labelNameFont)
    
        let labelContentFont: UIFont = UIFont(name: "PingFangTC-Regular", size: 16)!
        let labelContentHeight: CGFloat = self.getHeightForLable(labelWidth: labelNameWidth, numberOfLines: 0, labelText: self.tweetShowing[indexPath.row].text, labelFont: labelContentFont)
    
        let cellHeight: CGFloat = labelNameHeight + labelContentHeight + YourHeightOffSet // (YourHeightOffSet means all margins)
    
        return self.typeControl.selectedSegmentIndex == 0 ? CGSize(width: self.view.frame.width, height: cellHeight) : CGSize(width: imageShowingWidth, height: imageShowingWidth)
    }
    
    func getHeightForLable(labelWidth: CGFloat, numberOfLines: Int = 1, labelText: String, labelFont: UIFont) -> CGFloat {
        let tempLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: labelWidth, height: CGFloat.greatestFiniteMagnitude))
        tempLabel.numberOfLines = numberOfLines
        tempLabel.text = labelText
        tempLabel.font = labelFont
        tempLabel.sizeToFit()
        return tempLabel.frame.height
    }
    

    【讨论】:

      【解决方案3】:

      您需要做的就是为 UICollectionView 布局创建一个 IBOutlet, 将estimatedItemSize 设置为任意大小, 在您的单元格类中,您必须在awakeFromNib 中指定单元格宽度(如果您只希望高度是动态的而宽度是静态的)和/或高度并禁用 translatesAutoresizingMaskIntoConstraintsself.contentView.translatesAutoresizingMaskIntoConstraints = false。 所以最终的结果应该是这样的

      class ProfileViewController: UIViewController {
          @IBOutlet var collectionView: UICollectionView!
          @IBOutlet var collectionLayout: UICollectionViewFlowLayout!
      
      
          var person: Person!
      
      override func viewDidLoad() {
              super.viewDidLoad()
      
              collectionView.register(UINib(nibName: "ProfileCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "Cell")
              collectionLayout.estimatedItemSize = CGSize(width: screenWidth * 0.4, height: 1)
              collectionView.reloadData()
          }
      
          func numberOfSections(in collectionView: UICollectionView) -> Int {
              return 1
          }
      
          func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
              return 1
          }
      
          func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
              let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ProfileCollectionViewCell
      
              return cell
          }
          }
      

      你的单元格应该是这样的

      class ProfileCollectionViewCell: UICollectionViewCell {
      @IBOutlet var containerWidthConstraint: NSLayoutConstraint!
       override func awakeFromNib() {
              super.awakeFromNib()
      
              self.contentView.translatesAutoresizingMaskIntoConstraints = false
              containerWidthConstraint.constant = screenWidth - (2 * 12)
          }
          }
      

      这是一个在 ios 中自我调整大小的好教程 https://engineering.shopspring.com/dynamic-cell-sizing-in-uicollectionview-fd95f614ef80

      【讨论】:

        【解决方案4】:

        estimatedSize 提供给您的UICollectionViewLayout

        估计的大小应该是您的 xib 的大小检查器中显示的大小。

        collectionViewLayout.estimatedItemSize = CGSize(width: collectionView.frame.width, height: 50)
        

        UICollectionViewCell 中覆盖方法preferredLayoutAttributesFitting(_:)

        override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
            setNeedsLayout()
            layoutIfNeeded()
                
            let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
                
            var frame = layoutAttributes.frame
            frame.size.height = ceil(size.height)
                
            layoutAttributes.frame = frame
                
            return layoutAttributes
        }
        

        您的收藏视图单元格现在将根据内容具有动态大小。

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-07
        • 1970-01-01
        相关资源
        最近更新 更多