I am implementing a collection view horizontally, and I want to achieve UI just like in app store.
I want to achieve the spacing that look, i.e UICollectionview cell is in the middle, and showing other cells on the sides. What happening to my code is that I am not able to scroll it to the centre.
Below is the code I am using to achieve what I did so far.
lazy var cardCollectionView : UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = .blue
collectionView.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
return collectionView
}()
Constraints
cardCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
cardCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
cardCollectionView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 42)
cardCollectionView.heightAnchor.constraint(equalToConstant: 250),
CollectionViewDelegate Methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId, for: indexPath) as! CardCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 364, height: 240)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top:0 , left: 25, bottom:0 , right:0)
}
Related
I am getting the space between the cells (marked with blue color):
extension ProfileVC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == self.humanCollectionView {
let screenWidth = humanCollectionView.bounds.width
return CGSize(width: screenWidth/3-0, height: screenWidth/3-0)
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 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
Why are you declaring the delegate functions inside collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) delegate method?
Try to do like this:
import UIKit
class ProfileVC: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
//remember to set the delegates
self.collectionView.delegate = self
self.collectionView.dataSource = self
//this is optional just to test that it is working
collectionView.backgroundColor = .red
// Do any additional setup after loading the view.
}
}
extension ProfileVC: UICollectionViewDelegateFlowLayout {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//this is just a test number, here you should return the number of your items
return 20
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//you should set reuseIndetifier in storyboard or in code if you create your collectionView programmatically
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "reuseIdentifier", for: indexPath)
//again this is just a placeholder color to see that it's working
cell.backgroundColor = .green
return cell
}
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize {
let screenWidth = collectionView.bounds.width
//be sure to use floor or to use a number of items that perfectly fits the width of collectionView
return CGSize(width: floor(screenWidth/3), height: floor(screenWidth/3))
}
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 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
I want to add that if the width of your collectionView is not perfectly divisible by 3, you will always have some gap between the cells. You should do the math by yourself for each device width and choose a proper number of cells for the width. For example for a screen with aspect ratio like iPhone X, I would use 3 cells when in portrait and 4 cells when in landscape.
You can do like this:
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize {
let screenWidth = collectionView.bounds.width
let screenHeight = collectionView.bounds.height
let numberOfCells : CGFloat = screenWidth > screenHeight ? 4 : 3
return CGSize(width: floor(screenWidth/numberOfCells), height: floor(screenWidth/numberOfCells))
}
I have a UIViewController containing a UICollectionView for vertical paging. The UICollectionView (blue area in the picture) is pinned to the top of the superview, but UICollectionViewCell (yellow) is not. I tried to explicitly pin the top constraint of the UICollectionViewCell to the top of UICollectionView but the program crashes.
class HomeViewController: UIViewController, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.register(FeedCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView.backgroundColor = .blue
collectionView.showsVerticalScrollIndicator = false
collectionView.isPagingEnabled = true
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor)
])
collectionView.dataSource = self
collectionView.delegate = self
}
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: collectionView.bounds.height)
}
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 0
}
}
extension HomeViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = indexPath.item % 2 == 0 ? .yellow : .orange
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
Just pin your collection view to the top of View, not to the safeArea
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true;
And use to just your collection view to safe area insets:
collectionView.contentInsetAdjustmentBehavior = .never;
Instead of pinning to all safe area edges using pinEdgesToSafeArea(to:) you should pin the top constraint of the collection view to the top constraint of view. I don't know where pinEdgesToSafeArea(to:) comes from so can't comment on a replacement for that but you can use this to pin your collection view to the top of the screen:
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
Edit:
based on the clarifications you've posted, try setting automaticallyAdjustsScrollIndicatorInsets to false:
collectionView.automaticallyAdjustsScrollIndicatorInsets = false
docs for more info
I've been learning about UICollectionViews recently and I am currently trying to implement one. I need to display two columns but no matter what I do it defaults to one column. There are plenty of similar questions here that suggest adjusting the width of the UICollectionView cell using the collectionView function sizeForItemAt, but I can't seem to get it to work.
this is my viewcontroller code:
class PreferencesViewController: UIViewController {
lazy var collectionView : UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = UICollectionView.ScrollDirection.vertical
let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(CategorySelectorCell.self, forCellWithReuseIdentifier: "categorySelector")
cv.backgroundColor = .clear
return cv
}()
override func viewDidLoad(){
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(collectionView)
collectionView.backgroundColor = .blue
collectionView.contentInsetAdjustmentBehavior = .always
collectionView.delegate = self
collectionView.dataSource = self
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: view.frame.height/1.5).isActive = true
self.view = view
}
}
my delegate extension:
extension PreferencesViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "categorySelector", for: indexPath) as! CategorySelectorCell
cell.cat = categories[indexPath.section]
cell.backgroundColor = .red
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width/2.5, height: collectionView.frame.width/2)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
10
}
}
Any pointers appreciated!
Also add these UICollectionViewDelegateFlowLayout methods
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
Update
Also set inset to zero
lazy var collectionView : UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = .zero
layout.scrollDirection = UICollectionView.ScrollDirection.vertical
let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(CategorySelectorCell.self, forCellWithReuseIdentifier: "categorySelector")
cv.backgroundColor = .clear
return cv
}()
let's think your collection view UICollectionViewFlowLayout has 16 edge insets starts and end. And minimumInteritemSpacingForSectionAt,minimumLineSpacingForSectionAt to 16
Then you can calculate your cell width like this
(view.frame.width-flowlayoutInsets-sectionSpacing)/2
This is the codes
your collection view
let coll: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
let coll = UICollectionView(frame: .zero, collectionViewLayout: layout)
coll.backgroundColor = .systemPink
//other stuff
return coll
}()
Section and Line spacing
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 16
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 16
}
Collectionview cell size
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width-16-16-16)/2, height: 100)
//1st 16 - left gap - flowlayout left inset
//2nd 16 - middle gap - section spacing
// 3rd 16 = right gap - flowlayout right inset
}
I have a collection view in my VC, I scroll collection view horizontally, I want to center each cell on scroll horizontally, I have tried a code but when it scrolls it shows next cell more left then the second one .This is my code,
fileprivate let sectionInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
gridView.delegate = self
gridView.dataSource = self
gridView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "ItemCollectionViewCell")
gridView.register(UINib(nibName: "ItemCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "ItemCollectionViewCell")
gridView.showsHorizontalScrollIndicator = true
gridView.bounces = true
gridView.showsHorizontalScrollIndicator = false
gridView.showsVerticalScrollIndicator = false
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return auctions?.properties?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell:ItemCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCollectionViewCell", for: indexPath) as! ItemCollectionViewCell;
cell.propertyData = (auctions?.properties![indexPath.row])!
if auctions?.status == AuctionStatus.Live.rawValue {
cell.noOfBidsPlaced.isHidden = false
}
else {
cell.noOfBidsPlaced.isHidden = true
}
cell.populateCell()
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = CGSize(width: 335 , height: 337)
return size
}
//3
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
{
return sectionInsets.left
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return sectionInsets.left
}
How I can scroll each cell to center perfectly?
Here is how it shows my collection on start,
Thats what it shows on scroll, it get cut from left side
Don't use static height and width.
call this function.Maybe its helpful for you:-
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// return CGSize(width: yourCollectionView.frame.size.width, height: yourCollectionView.frame.size.height )
// }
I have a UICollectionView setup like so:
In viewDidLoad:
func setupCollectionView() {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UINib(nibName: "NewQuizzesCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "NewQuizzesCollectionViewCell")
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 20
flowLayout.minimumLineSpacing = 20
collectionView.isPagingEnabled = true
collectionView.setCollectionViewLayout(flowLayout, animated: false)
}
And my delegates:
extension NewQuizzesViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: NewQuizzesCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "NewQuizzesCollectionViewCell", for: indexPath) as! NewQuizzesCollectionViewCell
cell.backgroundColor = colors[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width*0.8, height: collectionView.frame.size.height*0.8)
}
}
When I run the app it looks like this:
My question is, how can I make the cells centered? I noticed I can adjust the spacing between the cells, but how would I add spacing to the left of the red cell? Any pointers on this would be really appreciated. Thanks!
Try this delegate function will help and you need to set width of cell according to you wants
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionat section: Int) -> CGFloat {
return 0
}
Hope it will help you
you try that code that may help you..
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let offSet = self.collectionView.contentOffset
let width = self.collectionView.bounds.size.width
let index = round(offSet.x / width)
let newPoint = CGPoint(x: index * size.width, y: offSet.y)
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
},completion: {(UIVIewTransitionCoordinatorContext) in
self.collectionView.reloadData()
self.collectionView.setContentOffset(newPoint, animated: true)
})
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width, height: collectionView.frame.size.height)
}
for swift 3.0
minimumLineSpacingForSectionAt function use for set spacing between the cells of UICollectionView Layout..
and you need to add subclass of UICollectionViewDelegateFlowLayout so you can use the function minimumLineSpacingForSectionAt like this
class HomeBestSallingCatgCell: DatasourceCell ,UICollectionViewDelegate,UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
.......
......
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0 //set return 0 for no spacing you can check to change the return value
}
}
i hope it will useful for you