I have added a collection view and added an image view for each cell. I was able to add and scale the image views for each cell, but they were slightly off centre. I set the image view's centre to the cell's centre, but this caused only one image to now show.
Here is the code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CarCell", for: indexPath)
let carImageView = UIImageView(image: UIImage(systemName: "car"))
carImageView.frame = cell.contentView.frame
carImageView.center = cell.center
carImageView.contentMode = .scaleAspectFit
cell.addSubview(carImageView)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let columns: CGFloat = 2
let collectionViewWidth = collectionView.bounds.width
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let spaceBetweenCells = flowLayout.minimumInteritemSpacing * (columns - 1)
let adjustedWidth = collectionViewWidth - spaceBetweenCells
let width: CGFloat = floor(adjustedWidth / columns)
let height: CGFloat = width
return CGSize(width: width, height: height)
}
Here is the outcome:
And the view hierarchy shows all cells are created with their UIViews, but not the image views:
Removing the line carImageView.center = cell.center makes all images reappear again, but off-centre. Does anyone know how to fix this?
Can you try with the following?
let carImageView = UIImageView(image: UIImage(systemName: "car"))
carImageView.frame = cell.contentView.bounds
carImageView.center = CGPoint(x: cell.bounds.width/2, y: cell.bounds.height/2)
carImageView.contentMode = .scaleAspectFit
cell.addSubview(carImageView)
Related
I am trying to automatically make a collectionView cell expand vertically to show a tableView that is on the cell.
The tableView is being correctly populated, and I am calling reload on the collection view cell after setting cell.tableView.isHidden = false
Swift 4.2, Xcode 10.3
Edit: To clarify my question, I am trying to toggle expand/collapse the cell (with auto resize) when I show/hide the tableView on the cell.
collectionView flowLayout var:
// FlowLayout of the collectionView
var flowLayout: UICollectionViewFlowLayout {
return self.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
}
collectionView viewDidLoad():
self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
collectionView cell:
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
let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
// Specify you want _full width_
let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
// Calculate the size (height) using Auto Layout
let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow)
let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize)
// Assign the new size to the layout attributes
autoLayoutAttributes.frame = autoLayoutFrame
return autoLayoutAttributes
}
collectionView cell when "show table view" button is tapped:
cell.tableView.isHidden = false
collectionView.reloadItems(at: [indexPath])
Thanks.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.cellForItem(at: indexPath) as! yourCell
if cell.tableView.isHidden {
return CGize(width: cellWidth, height:0)
}
return CGSize(width: tableViewWidth, height: TotalTableViewCellsHeight)
}
Or
#IBAction func showTableView(_ sender: Any) {
flowLayout.itemSize = CGSize(width: newCellWidth, height: newCellHeight)
}
I'm facing a weird problem. I'm using collectionViewFlowLayout sizeForItem to set cell size based on cell label width and somehow, I'm able to get the correct cell size but my content inside cell disappears. If I just put let say
CGSize(width: 30, height: frame.height)
I will see my label text. My collectionView scrollDirection is .horizontal
Below is my code:
let arrButtons = ["#B1","#ButtonButton2","#Bu3","#Buttontton11","#Buttodasfdsafasdn1","CongViecGiaDinh", "TinCongGiao"]
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let myText = arrButtons[indexPath.item]
let font = UIFont(name: HelveticaNeueFont.HelveticaNeueRegular.rawValue, size: 16)
return CGSize(width: myText.widthOfString(usingFont: font!), height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell
cell.data = arrButtons[indexPath.item]
cell.titleLabel.text = arrButtons[indexPath.item]
}
extension String {
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSAttributedStringKey.font: font]
let size = self.size(withAttributes: fontAttributes)
return size.width
}
}
//extension is used to calculate label width
It turns out that I used frame.height instead of collectionView.frame.height. Problem solved!
I'm trying to get 3 icons on the top bar.
import UIKit
class SectionHeader: UICollectionReusableView {
private let telButtonCellId = "TelButtonCell"
private let searchBarCellId = "SearchBarCell"
private let notificationButtonCellId = "NotificationButtonCell"
// MARK: - Properties
#IBOutlet weak var collectionView: UICollectionView!
// MARK: - Initialization
override func awakeFromNib() {
super.awakeFromNib()
self.collectionView.register(UINib(nibName:notificationButtonCellId, bundle: nil), forCellWithReuseIdentifier: notificationButtonCellId)
self.collectionView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width*0.2) // this is need to set the size of the collection view
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
extension SectionHeader : UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: notificationButtonCellId, for: indexPath) as! NotificationButtonCell
var image = UIImage(named: "globe")
cell.imageView.image=image?.addImagePaddingShift(x: 20 , y: 20,shiftX: 0, shiftY: 10)
cell.imageView.contentMode = .scaleAspectFit
cell.imageView.bounds=cell.bounds
cell.imageView.center = cell.center;
cell.backgroundColor = UIColor.lightGray
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameWidth = UIScreen.main.bounds.width
let frameHeight = UIScreen.main.bounds.width*0.2
return CGSize(width: frameWidth*0.15, height: frameHeight*1.0)
}
So only the first cell will have the globe displayed.
the rest of the 2 cells are empty even though i can change the background colour.
The SectionHeader is a nib
The imageView is subview of the cell, that needs to be with its size as I see. So it needs its frame (its position in relation to its superview coordinate system - the cell) to be set to the height and width of the cell with origin (0,0). The bounds of the cell provide these dimensions so the answer can be
cell.imageView.frame = cell.bounds
or better
cell.imageView.frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height)
I have created an UICollectionView horizontal scroll through programmatically. UICollectionView cell width is dynamically created based on the content.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! CustomCollectionViewCell
cell.label.text = dataSource[indexPath.item]
cell.label.font = UIFont(name: "Helvetica", size: 20.0)
if currentPageIndex == indexPath.item {
cell.currentPageIndicator.frame = CGRect(x: 0, y: cell.bounds.height - 2, width: cell.bounds.width, height: 2.5)
cell.currentPageIndicator.isHidden = false
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if let size = cellSizeCache.object(forKey: indexPath as AnyObject) as? NSValue {
return size.cgSizeValue
}
let string = dataSource[indexPath.item]
let height = collectionView.frame.size.height
let font = UIFont(name: "Helvetica", size: 20.0)
var size = string.boundingRect(with: CGSize(width: CGFloat.infinity, height: height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font!], context: nil).size
size.width += 20
size.height += 20
return size
}
Initially, an UICollectionView is looking fine, even when I scroll horizontally.
When I start scrolling fast, cell's are overlap with each other.
Your cells are overlapping because the width of the cell is lesser then the size of content. You have two options, first option is to increase the width of cell in storyboard. Second option is to set the size for each cell in collectionview method named "sizeforcellatindexpath", sample method is given below:
optional func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return size
}
you can calculate the size of string by using calculation mechanism. Sample for collection in given below:
How to calculate the width of a text string of a specific font and font-size?
I want to add a dynamic height nonScrollable UICollectionView into UICollectionViewCell. (It is easy to add a static height UICollectionView in UICollectionViewCell as explained by Ash Furrow)
After spending hours on searching through the web I still don't get it.
This is what I want
Lets call:
Main CollectionView: ParentCollectionView
Inside CollectionView: ChildCollectionView
I want to get the height of ChildCollectionView so that i can set the cell size of the ParentCollectionView in side:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = "I'm trying to get a label in a cell.."
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimatedFrameForEventText = (text as NSString).boundingRect(with: CGSize.init(width: view.frame.width - 30, height: 1000), options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 12)], context: nil)
return CGSize.init(width: view.frame.width - 10, height: 320 + estimatedFrameForEventText.height + 60)
}
Also want to change the height constant of the ChildCollectionView with this:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: eventCellId, for: indexPath) as! EventCell
cell.eventText.text = "I'm trying to get a label in a cell.."
// height constraint of ChildCollectionView in ParentCell
cell.collectionViewHeightConstraint.constant = cell.collectionView.intrinsicContentSize.height
return cell
}
The scrolling of ChildCollectionView is purposely disabled to show the ChildCollectionViewCells all together.
This is where I am stuck
I am not abel to preCalculate the ChildCollectionView to pass them in the ParentCollectionView delegates
Both the CollectionView uses UICollectionViewFlowLayout
I am new to iOS developing.
Any help would be appreciated :)
This solves my problem
ParentCollectionViewController
var height: CGFloat!
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: eventCellId, for: indexPath) as! EventCell
cell.collectionViewHeightConstraint.constant = height!
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
var sizeOfChildCell: CGRect!
var widthOfCells: CGFloat! = 0.0
let cellPadding = 10
let widthOfChildCollectionview = view.frame.width - 120
for tag in tags {
sizeOfChildCell = ("#\(tag)" as NSString).boundingRect(with: CGSize.init(width: widthOfChildCollectionview, height: 1000), options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 12)], context: nil)
widthOfCells = widthOfCells + sizeOfChildCell.width + cellPadding + 3.8
}
let numOfRows = ceil(widthOfCells / widthOfChildCollectionview)
height = numOfRows * 25
height = height + numOfRows * 3.8
return CGSize.init(width: view.frame.width - 10, height: 320 + height)
}
ParentCollectionViewCell
var childCollectionViewHeightConstraint: NSLayoutConstraint!
lazy var childCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.showsVerticalScrollIndicator = false
cv.isScrollEnabled = false
cv.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
cv.backgroundColor = .white
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
childCollectionViewHeightConstraint = NSLayoutConstraint(item: childCollectionView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 96)
addConstraint(childCollectionViewHeightConstraint)
}
Just for future searches on this subject. I'm using IGListKit with a cell that contains a UICollectionView that holds my tags. My problem was that IGListKit isn't built very well on the AutoLayout principles so to prevent the collectionview cells from overlapping each other I had to calculate the height for the section manually before it's shown.
My idea was to just add the width of each tag (+ padding + minimumInterimSpace) and determine when the next tag supersedes the collection view width.
func calculateDimensions() {
//This is how IGListKit forwards the width, could be replaced with collectionView.containerSize.width
let width = collectionContext!.containerSize.width
var lineWidth:CGFloat = 0.0
var lines = 0
for tag in data.arrTags {
//Calculate bounds by using boundingRect
let size = TextSize.size(tag, font: Font.fontRegular(size: 13), height: 30)
//16= 8 padding-left + 8 padding-right
//5 = minimumInterimSpace
let itemWidth = 16 + size.width + 5
let tmpWidth = lineWidth + itemWidth
if tmpWidth > width {
lines += 1
lineWidth = itemWidth
}
lineWidth += itemWidth
}
sectionHeight = CGFloat( lines * 30 + (lines * 5)) + 15
}
This method is called when IGListKit sets the size for the current section. So I did this:
override func sizeForItem(at index: Int) -> CGSize {
calculateDimensions()
let size = CGSize(width: collectionContext!.containerSize.width, height: sectionHeight)
return size
}
This will result in the following: