I am new to iOS development and cannot seem to figure out why my collection view is spacing out. It almost seems as though a "margin-right" as in CSS has been applied to each cell. I have set the "Min Spacing" for cells and for lines to zero but the following is still occurring...
Screen shot of issue that is occurring
I have purposely set the collection view's background color to blue, the cell's background color to red and the label's border color to black to try to see what is what. The objective is to not see any of the 'blue' (collection view background), at all. Upon configuring the collection view, I resize the cell's width to fit the label's intrinsic width but it seems that the other cells' x-axis location stays the same... I need all the cells to come together. Any help would be greatly appreciated!
Current CollectionViewController code...
import Foundation
import UIKit
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var menu = [
("NEWS"),
("NEIGHBORHOODS"),
("CULTURE"),
("DEVELOPMENT"),
("TRANSPORTATION"),
("CITIES")
]
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Testing"
self.navigationController?.navigationBarHidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menu.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("UnderMenuCell", forIndexPath: indexPath) as! UnderMenuCollectionViewCell
cell.underMenuLabel.text = menu[indexPath.row]
let label_width = cell.underMenuLabel.intrinsicContentSize().width + 23
cell.underMenuLabel.layer.zPosition = 1000000
cell.underMenuLabel.layer.borderWidth = 2
cell.underMenuLabel.layer.borderColor = UIColor.blackColor().CGColor
cell.frame.size.width = label_width
return cell
}
}
You might need to set up layout attributes for the cells to specify their width:
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
attributes.frame = yourFrameHere
attributes.size = yourSizeHere
return attributes
}
Also have you tried setting a breakpoint at cell.frame.size.width = label_width to see what width is actually being set?
I ended up figuring out a way that worked perfectly:
import Foundation
import UIKit
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var underMenuCollection: UICollectionView!
var menu = [
("NEWS"),
("NEIGHBORHOODS"),
("CULTURE"),
("DEVELOPMENT"),
("TRANSPORTATION"),
("CITIES")
]
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Testing"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
let scrollToIndexPath = NSIndexPath(forItem: 2, inSection: 0)
self.underMenuCollection.scrollToItemAtIndexPath(scrollToIndexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menu.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("UnderMenuCell", forIndexPath: indexPath) as! UnderMenuCollectionViewCell
cell.underMenuLabel.text = menu[indexPath.row]
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let size: CGSize!
let testLabel = UILabel()
let labelText = menu[indexPath.row]
testLabel.text = labelText
testLabel.font = UIFont(name: "Avenir Next Condensed", size: 17)
testLabel.hidden = true
size = CGSize(width: testLabel.intrinsicContentSize().width + 13, height: self.underMenuCollection.frame.height)
return size
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.underMenuCollection.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)
}
}
So what happens is I create a hidden label that never evens gets added to the superview, populate it with the text to calculate the intrinsic width and then set the CGSize. It works perfectly...
Picture of collection view to the left
Picture of collection view to the right
The most important part in all this was the have equal spacing between all the cells dynamically based on the width of the string.
Related
I'm trying to create a simple collection view. I've got my custom cell "SectionCell" and the my custom class "Section", which contains two #IBOutlet properties: titleLabel and imageView. Both of these properties are hooked up to their respective storyboard views.
In storyboard, the collectionView scene has been linked to the MenuVC.swift file, which inherits from UICollectionView. The Cell view is linked to SectionCell. And I've set the cell's Identifier to "Section".
For debugging purposes I've set the view.backgroundColor to black and the cell's contentView background color to teal. Yet when I run the the simulator neither show. I get a white background and all that appears is the view title. Any ideas on what the fix is?
class MenuVC: UICollectionViewController {
var sections = [Section]()
override func viewDidLoad() {
super.viewDidLoad()
title = "Begin Learning"
view.backgroundColor = .black
}
// MARK:- CollectionView Methods
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Section", for: indexPath) as? SectionCell else {
fatalError("Unable to dequeue SectionCell ")
}
let section = sections[indexPath.item]
cell.titleLabel.text = section.title
cell.imageView.image = UIImage(named: section.image)
return cell
}
}
class SectionCell: UICollectionViewCell {
#IBOutlet var imageView: UIImageView!
#IBOutlet var titleLabel: UILabel!
}
class Section: NSObject {
var title: String
var image: String
init(title: String, image: String) {
self.title = title
self.image = image
}
}
Simulator
I'm also quite new to posting questions on SO. If you have any tips on how to better format questions, I'm all ears!
make outlet of your collectionview and give it to the delegate and data source...
make outlet like this in your view controller:
#IBOutlet weak var collectionView: UICollectionview!
then put this code in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.relaodData()
}
have you implemented the following protocol?
UICollectionViewDelegateFlowLayout
extension MenuVC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: 300.0, height: 300.0) // and set size for each item inside this function.
}
}
SOLVED
LOL you really do need eagle eyes as a developer. This:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
is supposed to be:
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
And voila!
I have implemented this:
By following this tutorial.
Here's the problem:
I am unable to scroll through the collection items.
I think this has something to do with the fact that the project I followed is for iOS and my project is for tvOS.
I've found a somewhat similar question. An answer linked to this GitHub Repo whose implementation doesn't seem that different from mine.
Here's the relevant code:
ViewController.swift
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell =
tableView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as? TableViewCell
else {
fatalError("Unable to create explore table view cell")}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
}
tableViewCell.swift
class TableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
collectionView.delegate = self
collectionView.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// For some reason he chose the measures of collectionViewCell and substracted 2
return CGSize(width: 139, height: 64)
}
// Highlight the current cell
// This doesn't work
func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
if let pindex = context.previouslyFocusedIndexPath, let cell = collectionView.cellForItem(at: pindex) {
cell.contentView.layer.borderWidth = 0.0
cell.contentView.layer.shadowRadius = 0.0
cell.contentView.layer.shadowOpacity = 0.0
}
if let index = context.nextFocusedIndexPath, let cell = collectionView.cellForItem(at: index) {
cell.contentView.layer.borderWidth = 8.0
cell.contentView.layer.borderColor = UIColor.orange.cgColor
cell.contentView.layer.shadowColor = UIColor.orange.cgColor
cell.contentView.layer.shadowRadius = 10.0
cell.contentView.layer.shadowOpacity = 0.9
cell.contentView.layer.shadowOffset = CGSize(width: 0, height: 0)
collectionView.scrollToItem(at: index, at: [.centeredHorizontally, .centeredVertically], animated: true)
}
}
}
In a separate project, I have implemented a collectionView independently. I.e: It was not embedded inside of a tableView. And it works just fine.
I copied the code from that project that highlights the selected cell and added it to the tableViewCell.swift but it had no impact at all.
So my question is:
Why am I unable to select a cell and/or scroll through the
collectionView?
Found the answer here:
By denying the focus of the table cell, the Focus engine will
automatically find the next focusable view on the screen. And in your
case, that is the collection view’s cell.
func tableView(_ tableView: UITableView, canFocusRowAt indexPath: IndexPath) -> Bool {
return false
}
(source: uimovement.com)
I want to implement layout like the above(auto line break when screen's width is not enough to accommodate buttons' widths).
But I can't come up with any idea about how to make that image like layout. I just can implement statically, not dynamically.
In Android, there is a layout that can implement the above.
But I don't know what can help me implement the above image in swift.
Please help me.
Following #Matthew Mitchell 's suggestion.
I implemented it like below.
My ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var hobbyArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
// self.collectionView!.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
hobbyArray.append("test1")
hobbyArray.append("test2")
hobbyArray.append("test3")
hobbyArray.append("test4")
hobbyArray.append("test5")
hobbyArray.append("test5")
hobbyArray.append("test5123123")
collectionView.reloadData()
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return hobbyArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.title.text = self.hobbyArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = self.hobbyArray[indexPath.row]
let cellWidth = text.size(withAttributes:[.font: UIFont.systemFont(ofSize:17)]).width + 25
return CGSize(width: cellWidth, height: 35.0)
}
}
Other codes are implemented exactly equal to #Matthew Mitchell's codes.
However, still I can't get what I wanted to implement.
I failed to make what I had wanted.
To do this efficiently you need to have a UICollectionView with a custom FlowLayout. I am going to do a storyboard example. This is quite complicated so I will try my best. All the code will be below the steps.
Step 1: Create a swift file named CollectionViewFlowLayout and use UICollectionViewLayout code in the newly created class.
Step 2: Add a UICollectionView to your ViewController
Step 3: Link new UICollectionView layout with the CollectionViewFlowLayout class
Step 4: Create a UICollectionViewCell inside the UICollectionView, add a label to that cell and constrain it to left and right in the cell and center it vertically. In the attributes inspector of the cell give it a reusable identifier ("cell" for this example)
Step 6: Create a swift file named collectionViewCell and use UICollectionViewCell class that links to your collectionViewCell (same way you linked your flowlayout in step 3).
Step 7: Add ViewController code to your ViewController Class. This code allows you to add cells to your collection view. The sizeForItemAt function will allow you to resize the cells according to the width of the string that you put inside each cell.
Code:
ViewController:
import UIKit
class viewController: UIViewController {
//Outlets
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return YOUR_ITEM_COUNT
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
self.title.text = YOUR_ITEMS_LIST[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = YOUR_ITEMS_LIST[indexPath.row]
let cellWidth = text!.size(withAttributes:[.font: UIFont.systemFont(ofSize:17)]).width + 25
return CGSize(width: cellWidth, height: 35.0)
}
}
UICollectionViewCell:
class CollectionViewCell: UICollectionViewCell {
//Outlets
#IBOutlet weak var title: UILabel!
}
UICollectionViewFlowLayout:
import UIKit
class CollectionViewFlowLayout: UICollectionViewFlowLayout {
var tempCellAttributesArray = [UICollectionViewLayoutAttributes]()
let leftEdgeInset: CGFloat = 0
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let cellAttributesArray = super.layoutAttributesForElements(in: rect)
//Oth position cellAttr is InConvience Emoji Cell, from 1st onwards info cells are there, thats why we start count from 2nd position.
if(cellAttributesArray != nil && cellAttributesArray!.count > 1) {
for i in 1..<(cellAttributesArray!.count) {
let prevLayoutAttributes: UICollectionViewLayoutAttributes = cellAttributesArray![i - 1]
let currentLayoutAttributes: UICollectionViewLayoutAttributes = cellAttributesArray![i]
let maximumSpacing: CGFloat = 8
let prevCellMaxX: CGFloat = prevLayoutAttributes.frame.maxX
//UIEdgeInset 30 from left
let collectionViewSectionWidth = self.collectionViewContentSize.width - leftEdgeInset
let currentCellExpectedMaxX = prevCellMaxX + maximumSpacing + (currentLayoutAttributes.frame.size.width )
if currentCellExpectedMaxX < collectionViewSectionWidth {
var frame: CGRect? = currentLayoutAttributes.frame
frame?.origin.x = prevCellMaxX + maximumSpacing
frame?.origin.y = prevLayoutAttributes.frame.origin.y
currentLayoutAttributes.frame = frame ?? CGRect.zero
} else {
// self.shiftCellsToCenter()
currentLayoutAttributes.frame.origin.x = leftEdgeInset
//To Avoid InConvience Emoji Cell
if (prevLayoutAttributes.frame.origin.x != 0) {
currentLayoutAttributes.frame.origin.y = prevLayoutAttributes.frame.origin.y + prevLayoutAttributes.frame.size.height + 08
}
}
}
}
return cellAttributesArray
}
func shiftCellsToCenter() {
if (tempCellAttributesArray.count == 0) {return}
let lastCellLayoutAttributes = self.tempCellAttributesArray[self.tempCellAttributesArray.count-1]
let lastCellMaxX: CGFloat = lastCellLayoutAttributes.frame.maxX
let collectionViewSectionWidth = self.collectionViewContentSize.width - leftEdgeInset
let xAxisDifference = collectionViewSectionWidth - lastCellMaxX
if xAxisDifference > 0 {
for each in self.tempCellAttributesArray{
each.frame.origin.x += xAxisDifference/2
}
}
}
}
You can use a UICollectionView with custom UICollectionViewFlowLayout or use a fully custom solution with UIView as root and different UIScrollViews with some custom content as lines (cells) here.
I have an example, but it's too huge to post here. Write me if you are inserting in.
I had the same problem and i found a shortest and super easy solution to make the height dynamic by subclassing UICollectionView and assign it to the CollectionView.
Here's the code:
class DynamicHeightCollectionView: UICollectionView {
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return self.collectionViewLayout.collectionViewContentSize
}
}
I am attaching reference link to that solution.
https://stackoverflow.com/a/49297382/9738186
I know this question is already ask many times but non of the solution will work.
I have collection view in uitableview cell and i prepare a cell of table view and collection view in story board and my problem is that suppose i scroll cell 1 collection view then cell 4 collection is auto scroll to that position also. How i can handle this situation..
Please note right now i make a static design of the screen
My code
// MARK:
// MARK: table view delegate method
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if Appconstant.isIpad()
{
return 215.0
}
return 185.0
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let strCell = "WorkoutCell" //+ "\(indexPath.row)"
let cell = tableView.dequeueReusableCellWithIdentifier(strCell) as? WorkoutCell
// if cell == nil
// {
// cell = work
// }
return cell!
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let cell2 = cell as! WorkoutCell
cell2.collecionWorkout.reloadData()
}
// MARK:
// MARK: collectionview delegate and data source
//1
func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
//2
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0.0, 10.0, 0.0, 10.0)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: NSIndexPath) -> CGSize {
// Adjust cell size for orientation
if Appconstant.isIpad() {
return CGSize(width: 160, height: 150)
}
return CGSize(width: 140.0, height: 130.0)
}
//3
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("WorkoutCollectionCell", forIndexPath: indexPath)
// Configure the cell
return cell
}
uitableview cell code
class WorkoutCell: UITableViewCell {
#IBOutlet weak var collecionWorkout: UICollectionView!
var flowLayout : UICollectionViewFlowLayout!
override func awakeFromNib() {
super.awakeFromNib()
self.flowLayout = UICollectionViewFlowLayout()
if Appconstant.isIpad()
{
self.flowLayout.itemSize = CGSize(width: 160, height: 150)
}
else
{
self.flowLayout.itemSize = CGSize(width: 140, height: 130)
}
self.flowLayout.scrollDirection = .Horizontal
self.flowLayout.minimumInteritemSpacing = 10.0
flowLayout.sectionInset = UIEdgeInsetsMake(0.0, 10.0, 0.0, 10.0);
collecionWorkout.collectionViewLayout = flowLayout
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Cell 1st which i scroll
cell 4th which auto scroll
There are two scenarios.
1 - Reset The UICollectionView offset position :
To Reset the collectionview position just call cell.collectionView.contentOffset = .zero in cellForRowAtIndexPath
2 - Maintain the previous scroll position :
To Maintain the previous scroll position, You'll need to have a list of offsets for each cell stored alongside your data models in the containing view controller. Then you might save the offset for a given cell in didEndDisplayingCell and setting it in cellForRowAtIndexPath instead of .zero .
UITableView works on the concept of reusability. Whenever the tableView is scrolled, the cells in the tableView will definitely be reused.
Now 2 scenarios arise with this,
Get a new cell - When the tableViewCell is reused, the collectionView inside it also will be reused and so this is the reason the contentOffset of the previous cell is retained.
Scroll to previous cell - if we've manually scrolled to a particular cell in the collectionView, we might want to retain its position when we scroll back to it.
To get this kind of functionality in tableView, we need to - manually reset the contentOffset for 1st case and need to retain the contentOffset in the 2nd one.
Another approach you can follow is using a combination of UIScrollview and UIStackView.
This is how it goes.
In storyboard, create a UIScrollView. Add a vertical UIStackView to it.
Add 4 UICollectionViews in the stackView. This number is configurable as per your requirement.
Add your controller as the dataSource and delegate of all the collectionViews.
i.e.
class ViewController: UIViewController, UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell
cell.label.text = "\(indexPath.row)"
return cell
}
}
class CustomCell: UICollectionViewCell {
#IBOutlet weak var label: UILabel!
}
Now, since we're using a stackView, none of the collectionViews will be reused. So, the contentOffsets of the all the collectionViews will remain intact.
What I have done is set the label to resize using label.sizeToFit() which is convenient, however I now need to set the cell to properly adjust its size too.
I used the sizeForIndexPath method from the FlowLayoutDelegate to set the cell size to:
messages[indexPath.row].sizeWithAttributes(nil)
however that gives me very thin/small CGSize's.
import UIKit
let reuseIdentifier = "Cell"
class PhotoCollectionViewController: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var messages = NSString[]()
override func viewDidLoad() {
super.viewDidLoad()
messages = ["Hello, How are you?", "Ldsfsd sdf dsfs s fs fs", "fsdfsdfsdfs fsdfsdfsdf sfs fs", "yoyo yoy oyo", "sdfd sfds fssfs"]
self.collectionView.registerClass(TextCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.collectionViewLayout = PhotoCollectionViewFlowLayout()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// #pragma mark UICollectionViewDataSource
override func numberOfSectionsInCollectionView(collectionView: UICollectionView?) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView?, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return messages.count
}
override func collectionView(collectionView: UICollectionView?, cellForItemAtIndexPath indexPath: NSIndexPath?) -> UICollectionViewCell? {
let cell = collectionView?.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as TextCollectionViewCell
cell.backgroundColor = UIColor.lightGrayColor()
cell.newLabel.text = messages[indexPath!.row]
cell.newLabel.sizeToFit()
return cell
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
//println(messages[indexPath.row].sizeWithAttributes(nil))
return messages[indexPath.row].sizeWithAttributes(nil)
}
}
Edit: I have got it to work, sorta (in an unoptimized fashion):
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
let cell = TextCollectionViewCell(frame: CGRect(x: 0, y: 8, width: 300, height: 100))
cell.newLabel.text = messages[indexPath.item]
cell.newLabel.sizeToFit()
println(cell.newLabel.intrinsicContentSize())
//var width = UIWindow.
return CGSizeMake(cell.newLabel.intrinsicContentSize().width+10, cell.newLabel.intrinsicContentSize().height+20)
//return cell.newLabel.intrinsicContentSize()
}
Now the text fits in most cases however:
It does not fit when it is longer in width than the screen size as obviously a new line is not automatically started.
It also is displayed in the middle of the screen, not sure where in the layout I can change this or whether it is dependent on something else.
If you want to use self-sizing cells with UICollectionViewFlowLayout you need to set estimatedItemSize to a non-CGSizeZero size.