collectionview grid layout issue with different devices - ios

Hello i am creating App using Swift and i am creating grid view using Collectionview but i got different output on different devices here is the code for populating and setting layout of collectionview
var imageArray = [UIImage]()
var nameArray = ["General album","Videos","New album","Custom name album"]
private var numberOfItemsInRow = 2
private var minimumSpacing = 10
private var edgeInsetPadding = 10
extension GalleryViewController:UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! GalleryCollectionViewCell
cell.imgView.image = imageArray[indexPath.row]
cell.lblName.text = nameArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let inset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
edgeInsetPadding = Int(inset.left+inset.right)
return inset
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(minimumSpacing)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(minimumSpacing)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (Int(UIScreen.main.bounds.size.width) - (numberOfItemsInRow - 1) * minimumSpacing - edgeInsetPadding) / numberOfItemsInRow
return CGSize(width: width, height: width)
}
}
from this code i am not able to achive exact grid layout so i tried below code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 50
let collectionViewSize = collectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
I got this code from this link:
Display UICollectionView with 2 columns of equal size square cells
but i am not able to achive exact grid layout its changed based on device i am sharing two screen shot
iPhone 11 Max Pro screen shot:
iPhone 7 Screen Shot:
in iphone 7 i am getting layout perfect but on iPhone 11 Max Pro i am not getting perfect any solution? than please help me

Add the following code in the "viewDidLoad" method of your controller:-
collectionView.delegate = self
collectionView.dataSource = self
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical //.horizontal
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
collectionView.setCollectionViewLayout(layout, animated: true)
Add only "insetForSectionAt" and "sizeForItemAt" these methods to handle the layout of your collection View. As I have modified the delegate and data source you can directly use it.
extension GalleryViewController:UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! GalleryCollectionViewCell
cell.imgView.image = imageArray[indexPath.row]
cell.lblName.text = nameArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)//here your custom value for spacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let lay = collectionViewLayout as! UICollectionViewFlowLayout
let widthPerItem = collectionView.frame.width / 2 - lay.minimumInteritemSpacing
return CGSize(width:widthPerItem, height: widthPerItem)
}
}

Im assuming your issue here is that the horizontal spacing is too much in iphone 11 Pro Xmax . The reason to this is that the iphone xsMax screen is much larger than the iphone 7 Screen. Take a look at this link below it has all the IOS screen sizes and widths and pixels which may come in handy.
https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions
As for your issue , the width of the device is different from iphone 7 to iphone 11 so the issue is you are setting the minimumSpacing ,width , height for an iphone 7 screen and you need to use constraints for the sides and use the constarints to adjust the widths , and heights and the spacing needs to be edited so that it is similar to all screens .
The chances of it coming perfectly equal in all your screens are minor but you can do much better with this approach .

Related

How to create 2 x 2 grid layout using Collection View?

I am trying to create 2x2 grid layout using collection view.
I have used below code. It's kind of working fine for iPhone 5 but with tweaks. I am trying to write code that can be used on all screen sizes. This is not working on iPhone 6.
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if let cell1 = cell as? CollectionViewCell {
return cell1
}
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 20
let collectionViewSize = collectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2+15)
}
public func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int
) -> UIEdgeInsets {
return UIEdgeInsets(
top: 5, left: 5, bottom: 5, right: 5
)
}
public func collectionView(
_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
I am trying to achieve following layout:
In future this grid can be like 2 columns only and N rows. Each row will always have 2 items.
First of all, get three variables together depending on your needs. Those are the minimum spacing (Both InteritemSpacing and Line spacing - Should be same UXwise but you can change that in delegate methods if you like. The interitem space however must always be equal to minimumSpacing), edgeInsetPadding and the number of items you want in a row.
private var numberOfItemsInRow = 2
private var minimumSpacing = 5
private var edgeInsetPadding = 10
In your code, you have already defined your Edge Insets as:
UIEdgeInsets(
top: 5, left: 5, bottom: 5, right: 5
)
The left and right insets are important for correctly determining the size of the item. left+right gives us a grand sum of 10. This should be assigned to edgeInsetPadding like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let inset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
edgeInsetPadding = inset.left+inset.right
return inset
}
Now lets get to your UICollectionViewDelegateFlowLayout. Update the following methods.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return minimumSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return minimumSpacing
}
Now lets get to the main part. Modify your sizeForItemAt in UICollectionViewDelegateFlowLayout as:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (Int(UIScreen.main.bounds.size.width) - (numberOfItemsInRow - 1) * minimumSpacing - edgeInsetPadding) / numberOfItemsInRow
return CGSize(width: width, height: width)
}
And this is it. You now get two equal size tiles in 2x2 grid. If you want to change this in future, just change the numberOfItemsInRow variable to something else. Like 3 for 3x3.
This will be done using the item size of the collectionViewCell
collectionView.delegate = self
extension ViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding = 5
let width = (collectionView.frame.size.width - CGFloat(padding) * 2) / CGFloat(2)
let height = width / 200 * 110 // or what height you want to do
return CGSize(width: width, height: height)
}
}
Use:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 20
let size = self.view.frame.width - padding
return CGSize(width: size/2, height: size/2+15)
}

Show 3 images on 1st row and 3 images on second row on collectionview

I dragged a collectionview on to my xib, set the constraints as leading,trailing,top and height.
The coding part is given as...
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 5
layout.minimumInteritemSpacing = 5
collectionview2.delegate = self
collectionview2.dataSource = self
collectionview2.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "ident")
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.item {
case 0,1:
return CGSize(width: (UIScreen.main.bounds.width - 16) / 2, height: (UIScreen.main.bounds.width - 16) / 2)
default:
return CGSize(width: (UIScreen.main.bounds.width - 32) / 3, height: (UIScreen.main.bounds.width) / 3)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ident", for: indexPath)
cell.backgroundColor = .red
return cell
}
This is the link I have referred.
But it is not working and I'm seeing just 5 equal width and height cells one after the other.
What am I doing wrong...?
EDIT 1:
I would also like to show one long cell on left and 2 small cells on right like so...
What changes do I make to my answer so that I can achieve this...?
Your code looks perfect but you may forgot to confirm the FlowLayout Protocol.
so,
you just need to confirm the below protocol in your code,
UICollectionViewDelegateFlowLayout
Hope this way may help you.
You've to try this
Confirm UICollectionViewDelegateFlowLayout
e.g.
class yourViewController: UIViewController,UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let screeSize = UIScreen.main.bounds
let numOfCell:Int = 3 //Number of items you want
let spaceOfCell:CGFloat = (CGFloat((numOfCell + 1)*10)) //Space between Cells
return CGSize(width: Int(screeSize.width - spaceOfCell) / numOfCell, height: Int(screeSize.width - spaceOfCell) / numOfCell)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10.00
//Verticle space between line
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10.00 //Space Between Items You want
}
Hope it helps :)

UICollectionView Paging view is cut off - paging not working

I am implementing a UICollectionView that needs to page horizontally. The UICollectionViewCell needs to be the same size as the UICollectionView. The cell has 2 embedded rows. The first has a UITextView and a UIButton and the second has a UITextView.
The UICollectionviewCell and the constraints work in preview for different sizes of the iPhone (e.g. 4s, 8, 8plus) but when running in the simulator paging doesn't work correctly and the cell's width, I guess, isn't centered and the cell doesn't move far enough to reach the next cell making the cell look cutoff.
I have tried the solution at this url: UICollectionView Horizontal Paging not centered
but I can't seem to get the width and centering correct. The code I'm using is below.
internal class AccessViewController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
//...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PagerContentCollectionViewCell
//Set the cell title
cell.txtTitle.text = "title"
//add a target for the call capability
cell.btnCall.addTarget(self, action: #selector(didButtonClick), for: .touchUpInside)
//description
cell.txtDescription.text = "description"
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//let i = IndexPath(item: titles.count, section: 0)
collectionView.scrollToItem(at: indexPath, at: .right, animated: true)
print("Selected")
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameSize = collectionView.frame.size
return CGSize(width: frameSize.width, height: frameSize.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameSize = collectionView.frame.size
return CGSize(width: frameSize.width, height: frameSize.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
}
The UICollectionView width and height are 267, 153.
The UICollectioViewCell width and height 262, 150
The Section Insets are 0,0,0,0.
Any help is much appreciated.
Thanks
Edit: I added in code I was trying to use for the layout. I am probably missing something else too.
I was able to fix using these two functions:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameSize = collectionView.frame.size
return CGSize(width: frameSize.width, height: frameSize.height)
}

UICollectionView with multiple rows

I use UICollectionView (views with UIButtons) to book the time:
override func viewDidLoad() {
super.viewDidLoad()
collectionViewTime.register(UINib(nibName: "TimeCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "timecell")
collectionViewTime.dataSource = self
collectionViewTime.delegate = self
if let layout = collectionViewTime.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .vertical
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Timeline.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: 0))
let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1) * flowLayout.minimumInteritemSpacing)
let padding = (collectionView.frame.width - combinedItemWidth) / 2
return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "timecell", for: indexPath) as! TimeCollectionViewCell
let time = Timeline[indexPath.row][0]
cell.btnTime.setTitle(DateManager.dateToTime(date: time.0), for: .normal)
return cell
}
And I got all views (UIButtons) in one line:
How can I set automatically moving UIViews in new rows?
So, in my case (iPad) one row will contain 7 UIViews, iPhone will contain 4-5 UIViews in one row.
For that you need to implement UICollectionViewDelegateFlowLayout method collectionView(_:layout:sizeForItemAt:) and change the collectionView's Scroll direction to Vertical
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let minCellSpace: CGFloat = 10 //Set minimum cell space that you want
if UIDevice.current.userInterfaceIdiom == .pad {
//For iPad return 7 cell as you mention in question
return CGSize(width: (collectionView.frame.size.width / 7) - minCellSpace, height: yourCellHeight)
}
else {
//For iPhone return 4 cell, if you want to return 5 cell divide width to 5
return CGSize(width: (collectionView.frame.size.width / 4) - minCellSpace, height: yourCellHeight)
}
}
Please use one more flow delegate method as below
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize;
------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:(button width), height:button hieght);
}

Swift 3 - UICollectionView Center Cell with Padding

I have a UICollectionView that scrolls horizontally and I would like to have one cell visible on the screen at a time.
I am programmatically setting the cell width and height in the following method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! Cell
let width = collectionView.bounds.size.width - 40
let height = collectionView.bounds.size.height - 40
cell.bounds.size = CGSize(width: CGFloat(width), height: CGFloat(height))
return cell
}
And after struggling to figure out how to work with insets, spacing, section insets (both programmatically and in the inspectors in XCode) I can't seem to figure out what I need to set and where I need to set it.
Here is a diagram of my desired spacing:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width - 40, height: yourCollectionViewHeight - 40)
}
left 20 - add Footer Size and Header Size in storyboard with width = 20
spacing between cells - add Minimum Spacing in storyboard = 40
I could have made some mistakes and you will not get exactly your layout, but I hope you would get the idea.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width * 0.7, height: 300)
}
func collectionView(_ collectionView: UICollectionView, layoucollectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let margin = collectionView.frame.width * 0.3
return UIEdgeInsets(top: 10, left: margin / 2, bottom: 10, right: margin / 2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width * 0.3 / 2
}
If you want single cell visible on the screen at a time then important thing is that you should enable paging for your CollectionView. You can do it in swift as below :
collectionView.isPagingEnabled = true
Now conform your ViewController class to UICollectionViewDelegateFlowLayout and implement it's method sizeForItemAt indexPath which returns CGSize for your collectionview cell. For Example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height: 200)
}

Resources