Size of cell is not equal to collectionview size - swift

I wrote a code that I got collectionview with 4 cells and I can swipe to see each one of them.
My collectionview backgroundcolor is red and in my cell I put an image (before that I put blue color instead of an image) and I got a problem that I see little red line form the collection view in my cell.
I must say that I had problem that I had bigger line before that and I fix that by hidden navigation view of my viewcontroller.
class CheckViewController: UIViewController, UICollectionViewDataSource,
UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .red
cv.dataSource = self
cv.delegate = self
cv.isPagingEnabled = true
return cv
}()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
view.addSubview(collectionView)
// collectionView.frame = view.frame
//use autolayout instead
collectionView.anchorToTop(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
collectionView.register(PageCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
// func numberOfSections(in collectionView: UICollectionView) -> Int {
// return 4
// }
//
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
// cell.backgroundColor = .white
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
extension UIView {
func anchorToTop(top: NSLayoutYAxisAnchor? = nil, left:
NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right:
NSLayoutXAxisAnchor? = nil) {
anchorWithConstantsToTop(top: top, left: left, bottom: bottom, right: right, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0)
}
func anchorWithConstantsToTop(top: NSLayoutYAxisAnchor? = nil, left:
NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right:
NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0,leftConstant:
CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0)
{
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: topConstant).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: bottomConstant).isActive = true
}
if let left = left {
leftAnchor.constraint(equalTo: left, constant: leftConstant).isActive = true
}
if let right = right {
rightAnchor.constraint(equalTo: right, constant: rightConstant).isActive = true
}
}
}
And this is the image.

There are two problems
1) You are setting the cell's size relative to the view rather than the collectionView.
Your error in setting the cell's size..
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
2) It looks like your backgroundImage for the view doesn't fit 100%. You want to use another aspect ratio to fill the view. You probably want to use .scaleToFill.
Difference between UIViewContentModeScaleAspectFit and UIViewContentModeScaleToFill?

Related

Adding a CollectionViewController as a subView

Using Swift 5.1.3, iOS13.3, XCode11.3,
I try to display a horizontal collectionView that is wider than the view's width.
By dragging, the collectionView cells shall scroll horizontally.
The problem now is the following: As soon as I click to drag, the collectionView content "disappears" (i.e. turns transparent). If you look carefully, you can see that the scrollbar is still there.
Why is does the collectionView content turns transparent as soon as I click into it ?
Here is a video illustrating the error behaviour:
Below you will find the corresponding code:
Here is the code:
Inside my Main-ViewController I add the collectionView:
override func viewDidLoad() {
super.viewDidLoad()
let cardsHorizontalController = CardsHorizontalController()
self.view.addSubview(cardsHorizontalController.view)
cardsHorizontalController.view.translatesAutoresizingMaskIntoConstraints = false
cardsHorizontalController.view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100.0).isActive = true
cardsHorizontalController.view.heightAnchor.constraint(equalToConstant: 279).isActive = true
cardsHorizontalController.view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
}
Here is the collectionView controller class:
class CardsHorizontalController: BaseListController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = .clear
collectionView.register(CardHeaderCell.self, forCellWithReuseIdentifier: cellId)
if let layout = collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
collectionView.contentInset = UIEdgeInsets(top: 100, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: view.frame.width - 48, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return .init(top: 0, left: 16, bottom: 0, right: 0)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
return cell
}
}
And its corresponding parent class:
class BaseListController: UICollectionViewController {
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And here the collectionView cell at play:
class CardHeaderCell: UICollectionViewCell {
let imageView = UIImageView(cornerRadius: 8)
override init(frame: CGRect) {
super.init(frame: frame)
imageView.backgroundColor = .red
let stackView = VerticalStackView(arrangedSubviews: [
// companyLabel,
// titleLabel,
imageView
], spacing: 12)
addSubview(stackView)
stackView.fillSuperview(padding: .init(top: 16, left: 0, bottom: 0, right: 0))
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
I finally found a solution:
The collectionView-Controller (i.e. CardsHorizontalController()) needs to be added as a ChildController!
Inside Main-ViewController's viewDidLoad()(in first paragraph of code above), please replace the following line...
self.view.addSubview(cardsHorizontalController.view)
with the following:
self.addChild(cardsHorizontalController)
self.view.addSubview(cardsHorizontalController.view)
self.didMove(toParent: cardsHorizontalController)
Having done so, everything works as expected !
You find a working project (stripped-down with only the CollectionView) in this GitHub repo: https://github.com/iKK001/HorizontalCollectionViewExample
And since I'm so happy - here a video of the working horizontal CollectionView:
Have you tried changing return .init(width: view.frame.width - 48, height: view.frame.height) inside sizeForItemAt to return .init(width: collectionView.frame.width - 48, height: collectionView.frame.height)?

CollectionView function "didSelectItemAt" is not getting called

I have multiple ViewController with a CollectionView each. In every CollectionView I want to do something after a Cell is selected. In one ViewController it's not working, but in every other it works.
I checked the delegates, datasource and registration of Cells multiple times but it's like in the other ViewControllers too so I cant see, why it's not working. Also I dont have any Buttons in the Cell, I saw before that you can't use "didSelectItemAt" when you have Buttons in Cell. Additionally I commented out the part where I add the Subviews and constraints, but it still didn't get called.
ViewController which is not working:
class HomeController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, HomeControllerDelegate {
lazy var eventCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 25
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.dataSource = self
cv.delegate = self
return cv
}()
let eventCellId = "EventCellId"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(eventCollectionView)
eventCollectionView.anchorWithConstantsToTop(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topConstant: 94, leftConstant: 25, bottomConstant: 25, rightConstant: 25)
eventCollectionView.register(EventCell.self, forCellWithReuseIdentifier: eventCellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 24
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = eventCollectionView.dequeueReusableCell(withReuseIdentifier: eventCellId, for: indexPath) as! EventCell
cell.backgroundColor = UIColor(displayP3Red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
cell.layer.borderColor = UIColor(displayP3Red: 233/255, green: 233/255, blue: 233/255, alpha: 1).cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 2
cell.homeControllerDelegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if view.frame.width < 415.0 {
return CGSize(width: view.frame.width - 50, height: view.frame.width - 60)
}
else if view.frame.width < 828.0 {
return CGSize(width: (view.frame.width / 2) - 50, height: (view.frame.width / 2) - 60)
}
else {
return CGSize(width: (view.frame.width / 3) - 50, height: (view.frame.width / 3) - 60)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Selected Event Cell")
}
}
ViewController which is working:
class FavoritesController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, FavoritesControllerDelegate {
lazy var favoritesCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 5
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor(displayP3Red: 235/255, green: 235/255, blue: 235/255, alpha: 1)
cv.dataSource = self
cv.delegate = self
return cv
}()
let favoritesCellId = "FavoritesCellId"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(favoritesCollectionView)
favoritesCollectionView.anchorToTop(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
registerCells()
sideNavigationController = ""
}
fileprivate func registerCells() {
favoritesCollectionView.register(FavoritesCell.self, forCellWithReuseIdentifier: favoritesCellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = favoritesCollectionView.dequeueReusableCell(withReuseIdentifier: favoritesCellId, for: indexPath) as! FavoritesCell
cell.backgroundColor = .white
cell.layer.cornerRadius = 2
cell.favoritesControllerDelegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 125)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Selected Favorites Cell")
}
}
Constraints are added with this extension of UIView:
extension UIView {
func anchorToTop(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil) {
anchorWithConstantsToTop(top, left: left, bottom: bottom, right: right, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0)
}
func anchorWithConstantsToTop(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0) {
_ = anchor(top, left: left, bottom: bottom, right: right, topConstant: topConstant, leftConstant: leftConstant, bottomConstant: bottomConstant, rightConstant: rightConstant)
}
func anchor(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
translatesAutoresizingMaskIntoConstraints = false
var anchors = [NSLayoutConstraint]()
if let top = top {
anchors.append(topAnchor.constraint(equalTo: top, constant: topConstant))
}
if let left = left {
anchors.append(leftAnchor.constraint(equalTo: left, constant: leftConstant))
}
if let bottom = bottom {
anchors.append(bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant))
}
if let right = right {
anchors.append(rightAnchor.constraint(equalTo: right, constant: -rightConstant))
}
if widthConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
}
if heightConstant > 0 {
anchors.append(heightAnchor.constraint(equalToConstant: heightConstant))
}
anchors.forEach({$0.isActive = true})
return anchors
}
func addConstraintsWithFormat(format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
view.translatesAutoresizingMaskIntoConstraints = false
viewsDictionary[key] = view
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}

UICollectionView cells doesn't appear

I don't understand what's happening with my UICollectionView.
I'm apologise in advance because I think the answer is very simple
I just wan't to create a UICollectionView programmatically with 4 cells but 2 of them does not appear I don't know why
Here my code:
class NewsListViewController: UIViewController {
public var newsImagesCollectionView: UICollectionView!
override func viewWillLayoutSubviews() {
if self.newsImagesCollectionView == nil {
self.createNewsImagesCollectionView()
}
}
fileprivate func createNewsImagesCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
self.newsImagesCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
self.newsImagesCollectionView.dataSource = self
self.newsImagesCollectionView.delegate = self
self.newsImagesCollectionView.register(NewsImagesCollectionViewCell.self, forCellWithReuseIdentifier: NewsImagesCollectionViewCell.reuseIdentifier)
self.newsImagesCollectionView.backgroundColor = .yellow
self.newsImagesCollectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.newsImagesCollectionView)
self.newsImagesCollectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.newsImagesCollectionView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.newsImagesCollectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.newsImagesCollectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
}
}
extension NewsListViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NewsImagesCollectionViewCell.reuseIdentifier, for: indexPath) as! NewsImagesCollectionViewCell
switch indexPath.row {
case 0:
cell.imageView.backgroundColor = .purple
case 1:
cell.imageView.backgroundColor = .green
case 2:
cell.imageView.backgroundColor = .orange
case 3:
cell.imageView.backgroundColor = .red
default:
cell.imageView.backgroundColor = .blue
}
cell.imageView.contentMode = .scaleAspectFit
cell.imageView.image = #imageLiteral(resourceName: "euro")
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
print(self.view.frame.width)
return CGSize(width: self.view.frame.width / 4, height: self.view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
And I get this:
enter image description here
Thanks for your help
Does your collection view has the same size than your main view? because you calculating the size with the main view, is better if you use the collection view size.
return CGSize(width: collectionView.frame.width / 4, height: collectionView frame.height)

Infinite UICollectionview with UIPageControl

In below given code I can simply infinitely scroll through the cells. The page control also seems to be working at least while scrolling forward (to the right of screen). But frankly, when I scroll to the left, I see weird index path jump of 3 items (by the way my data source has 3 items for test purposes). In the screen I notice nothing wrong. But page control seems to be freezing for a moment and starts to work again but with a shift and shows wrong dot for the cell. Any Ideas? Thanks for any help...
import UIKit
class HomeHeaderCell: CategoryCell {
private let cellId = "cellId"
private var timer: Timer?
private let infiniteSize = 1000
private var onlyOnce = true
// ...
let pageControl: UIPageControl = {
let rect = CGRect(origin: .zero, size: CGSize(width: 200, height: 50))
let pc = UIPageControl(frame: rect)
pc.currentPage = 0
pc.numberOfPages = 3
pc.pageIndicatorTintColor = .gray
pc.currentPageIndicatorTintColor = .red
pc.isUserInteractionEnabled = false
return pc
}()
override func setupViews() {
addSubview(baseCollectionView)
addSubview(pageControl)
baseCollectionView.delegate = self
baseCollectionView.dataSource = self
baseCollectionView.register(HeaderCell.self, forCellWithReuseIdentifier: cellId)
baseCollectionView.backgroundColor = .white
baseCollectionView.isPagingEnabled = true
baseCollectionView.isScrollEnabled = true
baseCollectionView.showsHorizontalScrollIndicator = false
baseCollectionView.anchor(top: topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor)
pageControl.anchor(top: nil, leading: leadingAnchor, bottom: bottomAnchor, trailing: nil, padding: UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0))
}
#objc func autoScroll() {
guard let currentItemNumber = baseCollectionView.indexPathsForVisibleItems.first?.item else { return }
let nextItemNumber = currentItemNumber + 1
let nextIndexPath = IndexPath(item: nextItemNumber, section: 0)
baseCollectionView.scrollToItem(at: nextIndexPath, at: .left, animated: true)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
stopTimer()
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
startTimer()
}
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 4.0, target: self, selector: #selector(autoScroll), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return infiniteSize
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! HeaderCell
// Maybe can be done more elegantly...
if let foods = foodCategory?.foods {
let numberOfFood = indexPath.item % foods.count
cell.food = foods[numberOfFood]
pageControl.currentPage = numberOfFood
print(numberOfFood)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if onlyOnce {
let middleIndex = IndexPath(item: Int (infiniteSize / 2), section: 0)
baseCollectionView.scrollToItem(at: middleIndex, at: .centeredHorizontally, animated: false)
startTimer()
onlyOnce = false
}
}
override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width, height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
private class HeaderCell: ItemCell {
override func setupViews() {
addSubview(imageView)
imageView.layer.cornerRadius = 0
imageView.layer.borderColor = UIColor(white: 0.5, alpha: 0.5).cgColor
imageView.layer.borderWidth = 0.5
imageView.anchor(top: topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor)
}
}
}
I solved my own question. Here is the code you should be applying. First two methods are UIScrollView methods that comes free with UICollectionView, which by the way a subclass of UIScrollView. updatePageControl method is the one that I wrote just to keep code clean and simple. Hope this helps...
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
updatePageControl(scrollView: scrollView)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
updatePageControl(scrollView: scrollView)
}
func updatePageControl(scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.bounds.size.width)
guard let count = foodCategory?.foods?.count else {return}
let currentPageNumber = Int(pageNumber) % count
pageControl.currentPage = currentPageNumber
}

scrollToItem horizontal scrolling collectionView crash

I am having an issue with implementing a horizontal scrollToItem collectionView. I have a collectionView (menuBar) that on tap of a cell the page collectionView will horizontally scroll to the appropriate cell. On tap of the menuBar collectionViewCell the app crashes. Not sure where the error is. Thanks in advance!
// controller class
class CurrentUserPlaceDetailsVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var titleText: String?
let cellId = "cellId"
// UI elements
lazy var contentCollectionView: UICollectionView = {
let cv = UICollectionView()
cv.backgroundColor = UIColor.red
cv.layer.zPosition = 0
cv.translatesAutoresizingMaskIntoConstraints = false
cv.layer.masksToBounds = true
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
setupMenuBar()
setupCollectionView()
}
func scrollToMenuIndex(_ menuIndex: Int) {
let indexPath = IndexPath(item: menuIndex, section: 0)
contentCollectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition(), animated: true)
}
lazy var menuBar: MenuBar = {
let mb = MenuBar()
mb.currentUserPlaceDetailsVC = self
return mb
}()
private func setupMenuBar() {
view.addSubview(menuBar)
view.addConstraintsWithFormat("H:|[v0]|", views: menuBar)
view.addConstraintsWithFormat("V:|[v0(114)]", views: menuBar)
}
func setupCollectionView() {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
// layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: self.view.frame.width, height: self.view.frame.height)
let contentCollectionView:UICollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
contentCollectionView.dataSource = self
contentCollectionView.delegate = self
// register cells
contentCollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
contentCollectionView.backgroundColor = .white
// contentCollectionView.scrollIndicatorInsets = UIEdgeInsets(top:114, left: 10, bottom: 10, right: 10)
self.view.addSubview(contentCollectionView)
_ = contentCollectionView.anchor(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topConstant: 114, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)
view.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
contentCollectionView.isPagingEnabled = true
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print(scrollView.contentOffset.x)
menuBar.horizontalBarLeftAnchorConstraint?.constant = scrollView.contentOffset.x / 4
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
let colors: [UIColor] = [.blue, .red, .yellow, .green]
myCell.backgroundColor = colors[indexPath.item]
return myCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
// menu bar class
class MenuBar: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.headerReferenceSize = .zero
layout.sectionInset = .zero
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .green
cv.dataSource = self
cv.delegate = self
return cv
}()
let cellId = "cellId"
var currentUserPlaceDetailsVC: CurrentUserPlaceDetailsVC?
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(MenuCell.self, forCellWithReuseIdentifier: cellId)
addSubview(collectionView)
addConstraintsWithFormat("H:|[v0]|", views: collectionView)
addConstraintsWithFormat("V:|[v0]|", views: collectionView)
let selectedIndexPath = IndexPath(item: 0, section: 0)
collectionView.selectItem(at: selectedIndexPath, animated: false, scrollPosition: UICollectionViewScrollPosition())
setupHorizontalBar()
}
var horizontalBarLeftAnchorConstraint: NSLayoutConstraint?
func setupHorizontalBar() {
let horizontalBarView = UIView()
horizontalBarView.backgroundColor = .white
horizontalBarView.translatesAutoresizingMaskIntoConstraints = false
addSubview(horizontalBarView)
horizontalBarLeftAnchorConstraint = horizontalBarView.leftAnchor.constraint(equalTo: self.leftAnchor)
horizontalBarLeftAnchorConstraint?.isActive = true
horizontalBarView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
horizontalBarView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1/4).isActive = true
horizontalBarView.heightAnchor.constraint(equalToConstant: 4).isActive = true
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.item)
let x = CGFloat(indexPath.item) * frame.width / 4
horizontalBarLeftAnchorConstraint?.constant = x
UIView.animate(withDuration: 0.75, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.layoutIfNeeded()
}, completion: nil)
currentUserPlaceDetailsVC?.scrollToMenuIndex(indexPath.item)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MenuCell
cell.imageView.image = UIImage(named: imageNames[indexPath.item])?.withRenderingMode(.alwaysTemplate)
cell.tintColor = UIColor.rgb(91, green: 14, blue: 13)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width / 4, height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In your comment you said:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'. I believe there is something wrong in my func scrollToMenuIndex()... when I remove that func the app does not crash.
That happens, because you inside your View Controller your collectionView declared as lazy, that means it will be calculated on-demand.
The problem is in initializing your collectionView (reason: 'UICollectionView must be initialized with a non-nil layout parameter')
In your view controller replace UICollectionView initialization with this code:
lazy var contentCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.red
cv.layer.zPosition = 0
cv.translatesAutoresizingMaskIntoConstraints = false
cv.layer.masksToBounds = true
return cv
}()
P.S. As I wrote in my comment to your question, you have reference cycle. class MenuBar should hold weak reference to your view controller to prevent this.