UICollectionView Centers Cell on Landscape only for Phone - ios

I have a UICollectionView that makes use of one cell per section. It scrolls vertically. I've made the collection view cell size 320X125 although I have played with width to no avail.
When on the iPhone in portrait mode the cell starts at the left of screen which is the desired behavior. However, when I rotate the phone to landscape, the cell centers in the collection view. On the iPad, however, it stays to the left in both orientations.
I would like the cell to stay stuck to the left side of the screen regardless of orientation. How can this be accomplished? While autolayout works for things inside the cell, there does not appear to be an autolayout that can be applied to the cell itself.

I solved the problem by creating a second cell for the iPad. So there is one for iPhone and the other iPad. I then used:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
Based on device orientation I set the preferredMaxLayoutWidth I then used:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
let deviceType = UIDevice.currentDevice().userInterfaceIdiom
if deviceType == .Phone
{
return CGSizeMake(self.view.bounds.width, 170)
}
else
{
return CGSizeMake(self.view.bounds.width, 400)
}
}
This resized the cell based on the phone or tablet.
As a last item, I used the following to redraw after the device was rotated:
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
coordinator.animateAlongsideTransition(nil) { (context) -> Void in
self.collectionsView.reloadData()
}
}

Related

UICollectionView Rotation incorrect layout after rotation

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()
}

Collection View Cell Size/Fitting issue

I am working on an iOS app in swift 4. I have implemented collection view and there are two cells at the front as shown in the image:
When I run my app on bigger screen sizes like iPhone 6/7/8 Plus, then it Shows two complete cells with one-half cell. What I want to achieve is that it shows two cells in front of every screen size, Please help.
Implement the UICollectionViewDelegateFlowLayout protocol in your ViewController, then implement:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellSize = //calculate cell size based on the width of the view and padding between cells
return cellSize
}
Check out the documentation for the UICollectionViewDelegateFlowLayout and implement its methods to modify the spacing between the cells.

Resizing UICollectionViewCells on rotation changes currently visible cells [duplicate]

This question already has answers here:
Keeping the contentOffset in a UICollectionView while rotating Interface Orientation
(24 answers)
Closed 6 years ago.
I have a paginated, horizontally scrolling UICollectionView where each cell takes up the size of the view/device screen, and one cell appears at a time. I'm experiencing an issue where on device rotation, the cells will transition to the correct new size, but the position of the cells will be off.
Before Rotation:
After Rotation:
On rotation, how can I not only resize the collection view cells properly but ensure that the current cell stays visible?
I've boiled this problem down to a very simple example without a custom flow layout.
Setting the size of the cells to the collectionView frame's size (the collection view is the size of the view controller holding it):
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return collectionView.frame.size
}
Attempting to handle rotation in viewWillLayoutSubviews:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
flowLayout.itemSize = collectionView.frame.size
flowLayout.invalidateLayout()
}
Actually, if I call invalidateLayout() in viewWillLayoutSubviews, setting the itemSize here shouldn't be necessary, as sizeForItemAtIndexPath will be called when the layout is recreated.
I've also tried manually scrolling to the first visible cell during rotation after setting the new itemSize, but that shows no improvement:
if let item = collectionView.indexPathsForVisibleItems().first {
collectionView.scrollToItemAtIndexPath(item, atScrollPosition: .CenteredHorizontally, animated: true)
}
You should handle content offset of your UICollectionView, so in your view controller try to override viewWillTransitionToSize function from UIContentContainer protocol, like this:
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
let offset = yourCollectionView?.contentOffset;
let width = yourCollectionView?.bounds.size.width;
let index = round(offset!.x / width!);
let newOffset = CGPointMake(index * size.width, offset!.y);
yourCollectionView?.setContentOffset(newOffset, animated: false)
coordinator.animateAlongsideTransition({ (context) in
yourCollectionView?.reloadData()
yourCollectionView?.setContentOffset(newOffset, animated: false)
}, completion: nil)
}

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()
}

How to configure a UICollectionView Cell Size per Size Class?

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 :)

Resources