iOS中使用Swift 3的CollectionView动态高度

时间:2017-07-20 02:52:46

标签: ios dynamic swift3 uicollectionview autolayout

最近,我正在尝试做一些练习项目。所以我看了“开发iOS应用程序”课程,斯坦福大学,CS193P,2017。

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

我的问题如下:

  
      
  1. 如何使CollectionViewCell显示在动态高度,就像TableView的UITableViewAutomaticDimension一样?

         

    这就是我刚才尝试的内容:

         

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

  2.   
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的约束: enter image description here 但是当我运行这个应用程序时它有错误

我不知道我想念或误解了什么。 enter image description here

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

         

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

         

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

         

    https://github.com/tttsunny/CollectionViewAutoSizingTest

  2.   
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 10中处理不同的iOS版本,还要在iOS 10以下处理。

  2.   

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

谢谢!

4 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:3)

我解决了它:)

我只是尝试计算我的单元格的宽度,并期望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
}

答案 2 :(得分:1)

您需要做的就是为UICollectionView布局制作IBOutlet, 将estimatedItemSize设置为任意大小, 在您的单元格类中,您必须指定单元格宽度(如果您只想要高度为动态且宽度为静态)和/或awakeFromNib中的高度,并禁用translatesAutoresizingMaskIntoConstraints self.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

答案 3 :(得分:0)

  

estimatedSize提供给您的UICollectionViewLayout

     

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

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

在您的preferredLayoutAttributesFitting(_:)中覆盖方法UICollectionViewCell

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
}
  

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