Recently, I'm trying to do some projects for practicing. So I watched the course "Developing Apps for iOS", Stanford University, CS193P, 2017.
And now, I'm doing "Smashtag" project but I have some problems in it.
I want to use a CollectionView with two UICollectionViewFlowLayout to show two types in each xib (one is like tableView, another is showing a image in square) of CollectionView by SegmentedControl.
My problems below :
How to make CollectionViewCell showing in dynamic height just like TableView's UITableViewAutomaticDimension?
And this is what I just tried below :
I tried to make CollectionView's autoResize setting as like TableView's autoDimension with using UICollectionViewFlowLayoutAutomaticSize. ( @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
}
Constraint of my CollectionViewCell xib here :
But it has bug when I run this app
I don't know what I miss or misunderstand.
Why this solution will work ?
I already search so many solutions of this problem in Stack Overflow but I just find this and make me really confused :(
This solution give the view a Width Constraint which is in CollectionViewCell xib and set this constraint's constant a value and it works! I can not configure out why Width Constraint will make the cell's height in dynamic? This make me really confused, I think this is weird...
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)
}
}
How to do CollectionViewCell dynamic height without using UICollectionViewFlowLayoutAutomaticSize. ( @available(iOS 10.0, *) ) ?
Because I want to handle different iOS Versions not only in iOS 10 but also below iOS 10.
I really want to be pro in iOS development and a great developer but I'm still learning and trying. Any replies I will be greatly appreciated.
Thank you !!
Use the following code to change the height according to the text displayed:
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)
}
I solved it :)
I just tried to compute my cell's width and expect height in sizeForItemAt function and it works !
Code below :
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
}
all what you need to do is making an IBOutlet for the UICollectionView layout,
set the estimatedItemSize
to any size,
in your cell class you have to specify the cell width (if you just want the height to be dynamic and the width is static) and/or height in awakeFromNib
and disable translatesAutoresizingMaskIntoConstraintsself.contentView.translatesAutoresizingMaskIntoConstraints = false
.
so the final result should be something like this
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
}
}
and your cell class should be something like this
class ProfileCollectionViewCell: UICollectionViewCell {
@IBOutlet var containerWidthConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.translatesAutoresizingMaskIntoConstraints = false
containerWidthConstraint.constant = screenWidth - (2 * 12)
}
}
and this is a good tutorial for self sizing in ios
https://engineering.shopspring.com/dynamic-cell-sizing-in-uicollectionview-fd95f614ef80