UICollectionView Rotation incorrect layout after rotation - ios

I have a UICollectionView which is exactly 7 cells wide, with no padding in between. I have done this by adding a flow layout with no interim or line spacing and calculating the width as follows:
public func collectionView(
_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath
) -> CGSize {
return CGSize(
width: self.frame.width / CGFloat(7),
height: self.frame.height - headerHeight
)
}
This works fantastically within my iPhone app
where rotation isn't enabled. On iPad, when in portrait mode this works fine.
When rotating, firstly the app doesn't redraw, I've attempted to invalidateLayout however this didn't solve the issue. I have to scroll down and I get the following layout
When I tap a cell it does however redraw, but with padding added pushing the 7th cell down onto another row.
I am looking to redraw the cells so all 7 fit on a single row when the app is either rotated or grown and shrunk in split view. How should I handle this correctly?

Add this in your UIViewController class
It will adjust your layout automatically while you change device orientation.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
guard let columnLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
return
}
columnLayout.invalidateLayout()
}

Related

Collectionview with embeded tableview is clipped when rotating screen

I have created a collectionview with a tableview embeded into the collectionview cell. The idea is that the collectionview makes it able to scroll horizontal, while the tableview makes it able to scroll vertical for each section. Everything is set up in storyboard using autolayout, and works as it should, the only issue is that when i rotate the screen from portrait to landscape, the top if the collectionview or tableview is clipped.
How it currently looks in Portait mode
How it currently looks in Landscape mode (This is where the problem is)
I have attached two images: 1 from portait mode, which is how it should look like, and a second image of landscape mode, where the first item in the tableview and the tableview header is clipped. I suspect something in the view is not updated correct, but i am not sure what?
Thanks in advance
please set collection view cell width equal of collection view
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width: CGFloat = collectionView.frame.size.width
let height: CGFloat = SET_HEIGHT_THAT_YOU_WANT //collectionView.frame.size.height
return CGSize(width: width, height: height)
}
don't forget to set UICollectionViewDelegateFlowLayout

UICollection View causes "UICollectionViewFlowLayoutBreakForInvalidSizes" on smaller devices

On an iPhone 6 Plus, the collection view cells are fine, but when testing on another device size like the iPhone 5, i'm bombarded with "
017-06-15 01:59:25.744 HyperTest[3865:8825385] The behavior of the UICollectionViewFlowLayout is not defined because:
2017-06-15 01:59:25.744 HyperTest[3865:8825385] the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
2017-06-15 01:59:25.745 HyperTest[3865:8825385] The relevant UICollectionViewFlowLayout instance is , and it is attached to ; layer = ; contentOffset: {0, 0}; contentSize: {414, 219}> collection view layout: .
2017-06-15 01:59:25.745 HyperTest[3865:8825385] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
especially when I do this:
let layout = billFeedCollectionView.collectionViewLayout as? UICollectionViewFlowLayout // casting is required because UICollectionViewLayout doesn't offer header pin. It's feature of UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true
layout?.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
anyway, I can resolve this problem? I need to ensure that the cell scales and fits all devices.
By confirming UICollectionViewDelegate from your class. The delegate method sizeForItemAtIndexPath will call from UICollectionViewDelegateFlowLayout and this will call in your class now you can return the size of UICollectionViewCell.
This works for me
method:
collectionView.delegate = self
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(collectionView.frame.size.width, collectionView.frame.size.height)
}
It's also possible that when you have a collectionView in a UITableViewCell this cell actually resizes the collectionView height to 0.
In my case, the collectionView is showing an array of UIImageView to display some image gallery in each cell.
If the cell is not supposed to show any images (text without image for example), the collectionView is hidden.
The collectionView, if put within a StackView might need to have a height constraint and to avoid conflicts, you might need to set it to priority 999.
Then, when you hide the collectionView, it will actually try to set the height to 0.
The collectionView can still contain images in it and that will trigger the error.
SOLUTION: empty your datasource and reload the collectionView when you configure your cell.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let cellsize = CGSize(width: (CollectionView.bounds.size.width/2) - 12, height:(CollectionView.bounds.size.height/3) - 20)
return cellsize
}
here CollectionView is outlet of UICollectionView

UICollectionView referenceSizeForHeaderInSection animation

Is it possible to animate the frame change of a UICollectionView header (also known as UICollectionElementKindSectionHeader) size change?
In other words, I would like my UICollectionView header to have two different possible frames, an expanded (larger height) and collapsed mode (smaller height). By tapping a button somewhere, I would like the header to switch between the expanded and collapsed mode with a custom animation.
Current I have it set to this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: 0, height: HEADER_HEIGHT)
}
where I am changing the collection view height by calling this function upon a button tap
HEADER_HEIGHT = HEADER_HEIGHT == 100 ? 200 : 100
self.collectionView?.collectionViewLayout.invalidateLayout()
This works but I have yet to find documentation about animating the frame change. Is this even possible?
You can animate any animatable view changes by calling them in the UIView's animation block:
UIView.animate(withDuration: 0.5) {
self.collectionView?.collectionViewLayout.invalidateLayout()
}

SWIFT - Collection View Cell size

I have an app that uses photos from web and put them into the collection view.
in collection view i have 3 rows of cells 1:1 size which calculates from screen width / 3.
every thing is working good but there is a thing, on for ex. iphone 6s+ the cells are all tightly get together with no spacings at all. but on iphone 5s i getting some spacing between cells, in only vertical way as on the screenshot.
iPhone 6s+ Screenshot
iPhone 5s Screenshot
there is some code:
let screenSize: CGRect = UIScreen.mainScreen().bounds
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
return CGSize(width: screenSize.width / 3, height: screenSize.width / 3)
}
i have also checked if it is an image view problem but it is not.
What can i do to remove those spacings?
Creating CollectionView and then fit cells and spacing programmatically, you can try to add minimumInteritemSpacingForSectionAtIndex and minimumLineSpacingForSectionAtIndex on your own code.
func collectionViewLaunch() {
// layout of collectionView
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
// item size
layout.itemSize = CGSizeMake(self.view.frame.width / 3, self.view.frame.size.width / 3)
// direction of scrolling
layout.scrollDirection = UICollectionViewScrollDirection.Vertical
// define frame of collectionView
let frame = CGRectMake(0, 0,
self.view.frame.size.width, self.view.frame.size.height - self.tabBarController!.tabBar.frame.size.height - self.navigationController!.navigationBar.frame.size.height)
// declare collectionView
collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.alwaysBounceVertical = true
collectionView.backgroundColor = .whiteColor()
self.view.addSubview(collectionView)
self.collectionView.hidden = false
// define cell for collection view
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
// call function to load posts
loadPosts()
}
// cell line spacing
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
// cell inter spacing
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
// cell numb
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return picArray.count
}
Hope help you.
Do the original sum, round it down and multiply back by the number of columns.
Adjust the frame width of the collectView to this value and then cell widths will always fit the view perfectly.
Since the iPhone 5s' width is 640, dividing it by 3 would result in 213,33333333. Since iOS doesn't like those values, it will correct the value to 213, which will create a spacing when having three of those next to each other, since 213*3 does not equal 640. On the iPhone 6s Plus, the width is 1080 which is equally dividable by 3, resulting in 360, Here, no spacing will occur.
Try finding a divider which equals a natural number for any screen size and the spaces should be gone, or you try to find another solution.
I had a similar issue with horizontal and vertical collection views.
I was using a slider to set the number of columns and then resize the UIImages in the collection accordingly to the new cell size
On top of this I cut an image of an arrow in two and placed each half at the sides of the collection view so that when it displayed it had the result of drawing a full arrow in between images... I couldn't be bothered with custom seperator inserts at the time...which is where the arrow "should" have lived.
Hence every now and then I had a pixel wide gap in the arrows depending on screensize and orientation.
It IS all in the rounding up of the division result.
You have to find a width/height that divides as best you can to fit the screen most used.
You can apply some conditional resizing of the views if the values from the sums are not integers but finding the correct value to replace it with meant having to specify every eventuality.
In the end I gave up and "lived" with the single-pixel-wide gap

How can I get a UICollectionViewCell to be the entire width of the device?

I have a UIView inside my UICollectionViewCell that I need to be reproduced. I need this view to be pinned to the left and right of the screen. However, I can't find any way to control the width of the UICollectionViewCell.
Am I just missing something?
You're right. UICollectionViewCell doesn't play by the same AutoLayout rules as other views. Each cell is controlled by a layout class set on the UICollectionView.
Technically, you can drag the handles in Interface Builder and resize that way but it won't be dynamic for different screen widths and orientations.
I can think of two options:
Override the sizeForItemAtIndexPath: in the view controller and set to width of parent container programmatically. See Update 1 on question here for how this is done or check out the Apple documentation here for more background information
Consider using a UITableView if all cells will always be full width
You can use collectionView layouts. This code should help you:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var size = CGSize.zero
if let layout = collectionViewLayout as? UICollectionViewFlowLayout {
size = layout.itemSize;
size.width = collectionView.bounds.width
}
return size
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView.collectionViewLayout.invalidateLayout()
}

Resources