Dynamic Layout iOS with Collection View - ios

This might be a simple question -- I'm new to iOS Development.
I have a homepage with a Collection View with four cells that take up the whole screen (top left, top right, bottom left, bottom right) in portrait mode. In landscape mode, I want all four cells to be side by side in a row.
Is Collection View the right layout to use? Should I use Autoresizing to do this, or do I have to use some other layout constraints?
Thank you!

UICollectionView is the right layout to use.
you can use the following for making layout
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(4 - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(4))
return CGSize(width: size, height: size)
}
Following way you can use it inside your view controller
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#objc(collectionView:layout:sizeForItemAtIndexPath:)
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let totalSpace = flowLayout.sectionInset.left
+ flowLayout.sectionInset.right
+ (flowLayout.minimumInteritemSpacing * CGFloat(4 - 1))
let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(4))
return CGSize(width: size, height: size)
}
}

it should be a merge beetween UIStackView and Sizeclasses
the stackviews have to have fill equaly distribution.
the selected stackview has to have axis vertical by default, but you should add horizontal axis sizeclass width regular | height any

Related

Cells inside UICollectionView do not render properly

I'm new to iOS and Swift, I'm trying to create a UICollectionView for the first time.
Something is not quite right when running what I made. Here's what it looks like in my storyboard:
But when I run:
Top grey area became longer than before and bottoms of the cells seem clipped. I just wrote few lines of code to make the cells' height equal to the collection view's.
let height = collectionView.frame.height
cell.frame.size.height = height
return cell
Any suggestions?
Swift 4 code is here
1) Uncheck the Automatic field of the Row Height in the attribute inspector.
2) Then set the delegate funcitons
extension ViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var width = CGFloat(80) //change "80" with your cell width
return CGSize(width: width ,height : self.collectionView.frame.height)
}
}
Please use collectionViewLayout to give a size for an item,
class YourController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
and
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = collectionView.frame.height
let width = UIScreen.main.bounds.width / 3
let size = CGSize(width: width, height: height)
return size
}
You can make changes from storyboard or from code. I have done from storyboard as shown in image.
And also write this method in your view controller to give a size to your item. For that inherit UICollectionViewDelegateFlowLayout method in your view controller.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = collectionView.frame.width / 3
return CGSize(width: size - 20, height: size - 20 )
}
Note: This is just an example. You can set your collection view cell frame.
To change height in a collection view, do not change the cell frame height. There is another delegate for the collection view to set cell height and width.
extension ViewControll : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: anyWidth, height: collectionView.frame.size.height)
}
}
Here, anyWidth means whatever width you put. It's up to you

Calculating cell width in UICollectionView inside ContainerView

I have a UICollectionView and I have implemented the delegate to calculate the width of my cells for 0 spacing between cells.
It works great on its own, but when I have it inside a container view less than the size of the device, iOS incorrectly works out the spacing between the cells adding a horizontal space I don't want.
I have verified the width I am using to calculate the cell size is correct, so I'm not sure what is causing the problem.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let widthPerItem = view.frame.width / itemsPerRow
return CGSize(width: widthPerItem, height: 60)
}
You are calculating your cell size based upon the size of the view. Since the collectionView doesn't take up the full width of the screen, you are getting the incorrect value. Instead, you should base your calculation on the width of the collectionView itself.
Use the bounds of the collectionView instead of the frame of the view:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let widthPerItem = collectionView.bounds.width / itemsPerRow
return CGSize(width: widthPerItem, height: 60)
}
Ok, the problem was actually related to my uicollectionviewcontroller not correctly sizing to it's parent container view. Changing to a uiviewcontroller with an embedded uicollectionview plus constraints fixed the problem.

Determine UICollectionView cell height only by autolayout

I need a collection view which displays cells in a grid. So a standard flow layout is fine for me. However, I want to tell how many cells to show per row, while the cell height should be determined by the autolayout constraints that I put on the cell. Here is my cell layout:
It is quite simple - an image view and two labels below it. Now the image view has an aspect ratio constraint (1:1) which means whenever the width is known for the cell the height should automatically be known by the auto layout rules (there are vertical constraints going through: celltop-image-label1-label2-cellbottom).
Now, since I don't know any other good way to tell the collection view to show 2 items per row, I have overridden UICollectionViewDelegateFlowLayout methods:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let availableWidth = collectionView.frame.width - padding
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
As you can see, since I don't know the item height I return the same thing as the width, hoping that the autolayout will fix it later. I also set the estimatedItemSize in order the whole mechanism to start working.
The results are quite strange - it seems like the collection view doesn't event take into account the width I return there, mostly depending on the label lengths:
I have seen some other answers where people recommend manually calculating the cell size for width, like telling "layout yourself, then measure yourself, then give me your size for this width", and even though it would still run the autolayout rules under the hood, I would like to know if there is a way of doing this without manually messing with the sizes.
You can easily find out the height of your collectionView cell in the storyboard's Size inspector, as shown below:
Now, just pick up this height from here, and pass it to the overridden UICollectionViewDelegateFlowLayout method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let availableWidth = collectionView.frame.width - padding
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: **114**)
}
And you will get the desired output.
I ended up implementing a trick to move everything to autolayout. I completely removed the delegate method func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize and added a width constraint for my cell content in the interface builder (set the initial value to something, that's not important). Then, I created an outlet for that constraint in the custom cell class:
class MyCell: UICollectionViewCell {
#IBOutlet weak var cellContentWidth: NSLayoutConstraint!
func updateCellWidth(width: CGFloat) {
cellContentWidth.constant = width
}
}
Later, when the cell is created, I update the width constraint to the precalculated value according to the number of cells that I want per row:
private var cellWidth: CGFloat {
let paddingSpace = itemSpacing * (itemsPerRow - 1) + sectionInsets.left + sectionInsets.right
let availableWidth = collectionView.frame.width - paddingSpace
return availableWidth / itemsPerRow
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
...
cell.updateCellWidth(width: cellWidth)
return cell
}
And this, together with autolayout cell sizing enabled, will lay out the cells correctly.

iPhone X: Calculate perfect height of CollectionviewCell

I have a collectionview covering the whole screen. I want to use the collectionview so that all cells are visible at the same time which means that the height gets set dynamically. And as a plus: It should work in portrait and landscape.
In the past I solved it like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let heightOffset = (UIDevice.current.orientation.isPortrait) ? CGFloat(30) : CGFloat(0)
let width = UIScreen.main.bounds.width
let height = UIScreen.main.bounds.height - heightOffset
return CGSize(width: width, height: height / CGFloat(mainScreenItems.count))
}
But on iPhoneX I get problems, because the offset is not correct. Even if I take the height of the collectionview frame it doesn't work correctly.

Collectionview and paging

I want to use collection view for show user's instagram photos like Tinder. I placed a collectionview and enabled paging. But i don't know how to show 6 photos for each page. How can i do this?
This is what i want:
First page:
Second Page:
I used all spacing settings zero because setting spacing from storyboard problem for different screen sizes.
My collection view's settings and result:
Result:
So i want to show 6 photos per page. How to achive this?
To show the specific number of cells you want, you have to calculate the size of the cells. Something like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberOfRows = 2
let numberOfColumns = 3
let height = collectionView.frame.size.height / numberOfRows
let width = collectionView.frame.size.width / numberOfColumns
let cellSize = CGSize(width: width, height: height)
return cellSize
}
Since your layout configuration is static you can calculate it just once to make it faster:
class MyVC: UIViewController {
private let cellSize: CGSize = CGSize(width: collectionView.frame.width/numberOfColumns, height: collectionView.frame.height/numberOfRows) // or create a computed property
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return cellSize
}
}
This code is untested, maybe you have to add some padding but you get the idea. Another option would be to implement your own UICollectionViewLayout subclass

Resources