I'm kind of stuck with a problem here.
So, I've got a UICollectionView with a textView inside. The problem is, that I need to change the collectionViewCell's height and therefor it's y-position in the collectionView if I'm adding a line of text to the textView. Unfortunately, everything I tried doesn't work. Here's what it looks like:
(I'm not allowed to post full size screenshots)
Green = collectionViewBackground, black = cell's background
Does somebody know how to make the green disappear?
Make your viewcontroller implements UICollectionViewDelegateFlowLayout
and override the sizeForItemAt function
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let data: String = list[indexPath.row] //Your data for specific index
let size = data.size(attributes: nil)
return size
}
This should help you resolve your problem and don't forget to set collectionview.delegate = self //Your view Controller
Related
I have created collectionview and collectionviewcell. In that cell i have labels. i am getting long string and trying to show that in multiline. but not sure how to do this in colletctionview.
Thanks for helping
First apply constraints to you image view like this
Second Apply constraints to label
I got some solution but not sure how to put dynamic height. By following code my width is as per screen size so label is getting in multiline. but trying to get dynamic height as per content
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let kWhateverHeightYouWant = 400
return CGSize(width: collectionView.bounds.size.width, height: CGFloat(kWhateverHeightYouWant))
}
I noticed a big issue where in right to left languages, the cells order is not properly reversed, only the alignment is correct. But only for horizontal flow layout, and if the collection view contain different cell sizes! Yes, I know this sound insane. If all the cells are the same size, the ordering and alignment is good!
Here is what I got so far with a sample app (isolated to make sure this is the actual issue, and not something else):
(First bar should always be drawn blue, then size increase with index.)
Is this an internal bug in UICollectionViewFlowLayout (on iOS 11)? Or is there something obvious that I am missing?
Here is my test code (Nothing fancy + XIB with UICollectionView):
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "test", for: indexPath)
cell.backgroundColor = (indexPath.item == 0) ? UIColor.blue : UIColor.red
return cell
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (10 * indexPath.item) + 20, height: 170)
}
Automatic right-to-left support on dynamically-sized UICollectionViews is not a supported configuration. For this to work, you need to explicitly sign up for automatic layout mirroring as follows:
Create a subclass of UICollectionViewFlowLayout
Override flipsHorizontallyInOppositeLayoutDirection, and return true in Swift or YES in Objective-C
Set that as the layout of your collection view
This property is defined on UICollectionViewLayout (parent of Flow), so you can technically use this property on any custom layout you already have.
I believe that for this you will have to implement your own custom collectionViewLayout - although I understand that one would expect that it would automatically work just as the right-to-left on the rest of the components.
I have a UICollectionView that uses flow layout and dynamic-width cells defined in a xib. The xib has constraints on all sides of a stack view that contains a dynamic label and a static image. These constraints should change based on the margins provided in configureCell(cellData:, padding:), which is called on cellForItemAt and sizeForItemAtIndexPath datasource/delegate methods. Unfortunately, the sizes returned by systemLayoutSizeFitting() are incorrect if I change the constraints to something else in configureCell(cellData:, padding:).
I've tried setNeedsLayout(), layoutIfNeeded(), setNeedsUpdateConstraints(), updateConstraintsIfNeeded() in every combination I can think of. I have also tried these solutions without success.
How does one update constraints inside a UICollectionViewCell and have it size properly in collectionView(collectionView:, layout:, sizeForItemAtIndexPath:)?
UICollectionViewDataSource:
private var _sizingViewCell: LabelCollectionViewCell!
//registers and init's sizing cell...
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
_sizingViewCell.configureCell(self.cellData[indexPath.row], padding: defaultItemPadding)
var size = _sizingViewCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
return size
}
UICollectionViewCell subclass:
func configureCell(_ cellData: LabelCollectionCellData, padding: UIEdgeInsets? = nil) {
removeButton.isHidden = cellData.removable
if let padding = padding {
leadingPaddingConstraint.constant = padding.left
topPaddingConstraint.constant = padding.top
trailingPaddingConstraint.constant = padding.right
bottomPaddingConstraint.constant = padding.bottom
contentStackView.spacing = padding.left
}
}
The problem turned out to be operator error.
I was giving configureCell(cellData:, padding:) a different padding in:
collectionView(_ collectionView:, cellForItemAt indexPath:)
than I was giving in:
collectionView(collectionView:, layout collectionViewLayout:, sizeForItemAtIndexPath indexPath:)
Which caused the views to not display correctly.
I am trying to make my cell height size fit with label. The thing is that the label text is set in the cellForItemAtIndexPath, and if i have understood correct, sizeForItemAtIndexPath runs before the cellForItemAtIndexPath. This is something i have tried so far:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let imageCell = collectionView.dequeueReusableCellWithReuseIdentifier("imageCell", forIndexPath: indexPath) as? CollectionViewCell
imageCell!.imageText.text = post.text // This set the UICollectionView Cell text
imageCell!.frame.size.height = (imageCell!.frame.size.height) + (imageCell!.imageText.frame.size.height)
return imageCell!
}
I am not using Auto Layout.
Any suggestions why the cell height not changes depending on the label?
This used to be an annoying problem to solve, but it gets substantially easier if you're able to use Auto Layout. See this answer for a thorough walkthrough.
Even if sizeForItemAtIndexPath runs first, that's the place to set the size of the cell. cellForItemAtIndexPath cannot change the size.
In sizeForItemAtIndexPath you need to find out the image size for each cell, then return that size.
Something like this:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let imageCell = collectionView.cellForItemAtIndexPath(indexPath) as! CollectionViewCell
let size = CGSize(width: imageCell.frame.width, height: imageCell.frame.size.height + imageCell.imageText.frame.size.height)
return size
}
I'm finding friction when trying to create responsive / adaptive UICollectionViewCells in the UIStoryboard.
The issue I'm seeing is that you don't seem to be able to set the Cell Size per Size Class and I'm trying to ascertain the right approach to this. I've designed the cells to adjust to their containers, so that they should autosize regardless of size class. This mostly works in that if I change the size class, select my cell view and do Update Frames then they all resize to fit their new size. However it's a one shot deal, if I go back to the Any/Any size class then I'm still seeing that resized version.
Here's what I'm aware I could try:
Create multiple cells, with fixed dimensions, one per size class and in the Storyboard view. I could then only use the right one at runtime but I could then see them at design time.
I could create a collection view Per Size class, each one only being installed for that size. This would work, but would be a pain to manage the multiple UICollectionViews
Create my interface and/or constraints programmatically (losing visibility of the design).
I'm hoping this is a solved scenario and I'm just missing something, but I'm aware that it could be that the IB tools don't match the code at this point.
The solution I came up with was just to implement the UICollectionViewDelegateFlowLayout and implement the sizeForItemAtIndexPath method.
This means that the cell dimensions can be set to match the available Size Class dimensions.
This still isn't ideal as you can't see the changes in the storyboard and you can't create a universal design and see it in each of the different formats.
I'm still hoping someone has a better option.
Here's a similar solution coded-out in Swift. I just styled both of my cells in the storyboard and leave them viewable for any size class combination. When the trait collection changes I update the cellSize and cellReuseID I want to use and tell the collectionView to reload all the visible cells. Then
collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
and
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
(not shown in sample code) are called which lets me update the size of the cell and update the cell's storyboard styling. So not entirely done in storyboard, but good enough until more support is provided in Xcode.
struct MyCollectionViewConstants{
static let CELL_ANY_ANY_REUSE_ID = "cell";
static let CELL_COMPACT_REGULAR_REUSE_ID = "cellSmall"
static let CELL_ANY_ANY_SIZE = 100;
static let CELL_COMPACT_REGULAR_SIZE = 70;
}
class MyCollectionView: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var cellSize = MyCollectionViewConstants.CELL_ANY_ANY_SIZE
var cellReuseID = MyCollectionViewConstants.CELL_ANY_ANY_REUSE_ID
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{
return CGSize(width: cellSize, height: cellSize)
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact
&& self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClass.Regular){
cellSize = MyCollectionViewConstants.CELL_COMPACT_REGULAR_SIZE
cellReuseID = MyCollectionViewConstants.CELL_COMPACT_REGULAR_REUSE_ID
} else {
cellSize = MyCollectionViewConstants.CELL_ANY_ANY_SIZE
cellReuseID = MyCollectionViewConstants.CELL_ANY_ANY_REUSE_ID
}
self.collectionView.reloadItemsAtIndexPaths(
self.collectionView.indexPathsForVisibleItems())
}
}
I was stuck with same problem after implementing size class(iPad and iPhone).Well, I figured out a solution. Hope it helps!
Implement UICollectionViewDelegateFlowLayout.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
var device = UIDevice.currentDevice().model
var cellSize:CGSize = CGSizeMake(155, 109)
if (device == "iPad" || device == "iPad Simulator") {
cellSize = CGSizeMake(240, 220)
}
return cellSize
}
Swift 4
Hi Fellow Developers,
This is easy to do if the height and width of the UICollectionViewCell are same.
Steps
1. Import ** UICollectionViewDelegateFlowLayout**
2. Add the sizeForItem IndexPath method of UICOllectionView as follows
func collectionView(_ collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAt indexPath:IndexPath) -> CGSize {
return CGSize(width: collectionVw.frame.size.height, height: collectionVw.frame.size.height)
}
Note: What happening is you are setting the height of the UICollectionView as height and width of the UICollectionViewCell
Happy Coding :)