uicollectionview to count horizontally to make calendar - swift

how do i change the count of collectionview from vertical to horizontal count i want to make a calendar
this is what i have currently
this is the result that i am looking for
as you can see the first image count from top to bottom but the second one count from left to right. i want to change to the second one.
here is the code
i have 4 separates file to create the calendar using collectionview and custom collectionviewcell
calendarcollection
import UIKit
class calendarCollecction: UIView,UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! calendarCollecctionCell
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("my number is \(indexPath.row)")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.frame.width, height: self.frame.height)
}
lazy var collectionViews: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsetsMake(0,0,0,0)
layout.scrollDirection = UICollectionViewScrollDirection.horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.showsVerticalScrollIndicator = false
cv.backgroundColor = UIColor.clear
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews(){
collectionViews.register(calendarCollecctionCell.self, forCellWithReuseIdentifier: "cell")
collectionViews.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = UIColor.clear
addSubview(collectionViews)
collectionViews.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
collectionViews.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
collectionViews.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
collectionViews.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
collectionViews.widthAnchor.constraint(equalTo: widthAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
calendarcollectioncell
import UIKit
class calendarCollecctionCell: UICollectionViewCell {
let label = dateCollection()
override init(frame: CGRect) {
super.init(frame: frame)
framing()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func framing(){
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
label.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
label.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
label.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
}
}
datecollection
import UIKit
class dateCollection: UIView,UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
var dateNumber = 30
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dateNumber
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! dateCollectionCell
var value = indexPath.row + 1
cell.label.text = String(value)
cell.backgroundColor = .red
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("my number is \(indexPath.row)")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 40, height: 40)
}
lazy var collectionViews: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 5
layout.minimumInteritemSpacing = 5
layout.sectionInset = UIEdgeInsetsMake(5,5,5,5)
layout.scrollDirection = UICollectionViewScrollDirection.horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.showsVerticalScrollIndicator = false
cv.showsHorizontalScrollIndicator = false
cv.backgroundColor = UIColor.clear
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews(){
collectionViews.register(dateCollectionCell.self, forCellWithReuseIdentifier: "cell")
collectionViews.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = UIColor.clear
addSubview(collectionViews)
collectionViews.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
collectionViews.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
collectionViews.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
collectionViews.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
collectionViews.widthAnchor.constraint(equalTo: widthAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
datecollectioncell
import UIKit
class dateCollectionCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
framing()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func framing(){
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
addSubview(label)
label.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
label.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
label.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touched "+self.label.text!)
}
}

Just changing the scrollDirection of the flowLayout
layout.scrollDirection = .vertical

Related

How to scroll to a given indexPath.item?

I have 2 collection views in parallel. One of them is a menu and the other is a big one containing vertical table views that are populated with API queried data.
When the user scrolls right, then Order History is highlighted and the next cell in the big collection view at the bottom takes over the rest of the screen and it shows its own tableview data. It's just like the implementation here.
Everything works EXCEPT when I tap on Order History in the state indicated in the attached image, the collection view at the bottom does not scroll to the 2nd cell in the big collection view. If I tap on the Current Orders cell then it does scroll to the 1st cell in the big collection view. I have no idea what's causing this problem after spending the last 48 hours on this.
This is the menu bar:
class MenuBar: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
lazy var menuCollection: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionview = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionview.translatesAutoresizingMaskIntoConstraints = false
collectionview.backgroundColor = .white
collectionview.delegate = self
collectionview.dataSource = self
collectionview.register(InvoiceCell.self, forCellWithReuseIdentifier: InvoiceCell.identifier)
return collectionview
}()
var leftIndicatorConstraint: NSLayoutConstraint?
private let menuOptions = ["Current Orders", "Order History"]
var containerViewController: ContainerViewController? // I use an instance of the parent VC to pass on the value
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(menuCollection)
menuCollection.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
menuCollection.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
menuCollection.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
menuCollection.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
menuCollection.selectItem(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .left)
setupIndicator()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupIndicator() {
let indicatorBar = UIView()
indicatorBar.backgroundColor = UIColor(hex: Constants.Colors.primary)
addSubview(indicatorBar)
indicatorBar.translatesAutoresizingMaskIntoConstraints = false
leftIndicatorConstraint = indicatorBar.leadingAnchor.constraint(equalTo: self.leadingAnchor)
leftIndicatorConstraint?.isActive = true
indicatorBar.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
indicatorBar.heightAnchor.constraint(equalToConstant: 5).isActive = true
indicatorBar.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
containerViewController?.scrollToMenu(menuIndex: indexPath.item) // Here I pass on the value of the menu item that's tapped
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuOptions.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InvoiceCell.identifier, for: indexPath) as? InvoiceCell else { return UICollectionViewCell() }
cell.thisOption = menuOptions[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: menuCollection.frame.size.width / CGFloat(menuOptions.count), height: menuCollection.frame.size.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
}
}
And this is the parent UIView Controller that contains the menu collection view as well as the big base collection view
class ContainerViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
lazy var menuBar: MenuBar = {
let menuBar = MenuBar()
menuBar.containerViewController = self
menuBar.translatesAutoresizingMaskIntoConstraints = false
return menuBar
}()
lazy var baseCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(PastCell.self, forCellWithReuseIdentifier: PastCell.pastCellId)
collectionView.register(CurrentCell.self, forCellWithReuseIdentifier: CurrentCell.currentCellId)
collectionView.isPagingEnabled = true
collectionView.delegate = self
collectionView.dataSource = self
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.backgroundColor = .white
return collectionView
}()
var orderViewModel = OrderViewModel(order: Order(addressOne: "", addressTwo: "", city: "", postalCode: "", mpName: "", planType: 0, restaurantAddress: "", restaurantId: "", restaurantName: "", timeOfCreation: nil, currentTotal: 0.00, orderMenu: [], status: 0, itemsHTML: ""))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupView()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
orderViewModel.removeListener()
}
private func setupView() {
view.addSubview(menuBar)
menuBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
menuBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
menuBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
menuBar.heightAnchor.constraint(equalToConstant: 40).isActive = true
view.addSubview(baseCollectionView)
baseCollectionView.topAnchor.constraint(equalTo: menuBar.bottomAnchor, constant: 0).isActive = true
baseCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
baseCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
baseCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
func scrollToMenu(menuIndex: Int) {
// Here I am using the passed on menu index to scroll to the appropriate cell in the big collection view.
let indexPath = IndexPath(item: menuIndex, section: 0)
baseCollectionView.scrollToItem(at: indexPath, at: .left, animated: true) // For some reason it ALWAYS SCROLL TO THE FIRST CELL }
func scrollViewDidScroll(_ scrollView: UIScrollView) {
menuBar.leftIndicatorConstraint?.constant = scrollView.contentOffset.x / 2
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let index = Int(targetContentOffset.pointee.x / view.frame.width)
menuBar.menuCollection.selectItem(at: IndexPath(item: index, section: 0), animated: true, scrollPosition: .left)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.row {
case 0:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CurrentCell.currentCellId, for: indexPath) as? CurrentCell {
if let tabBarHeight = self.tabBarController?.tabBar.frame.size.height {
cell.adjustSize(top: menuBar.frame.height * 1.2, bottom: tabBarHeight * 0.5)
return cell
}
}
case 1:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PastCell.pastCellId, for: indexPath) as? PastCell {
cell.backgroundColor = .systemOrange
return cell
}
default:
return UICollectionViewCell()
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: view.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
Turns out there's a big bug with scrollToItem that Apple hasn't fixed. In my case it's caused by isPagingEnabled. So the best solution I found was here where you disable paging before scrolling to item and then enabling it right again.

to dismiss a viewcontroller using collectionView cell

i am having a difficulty on dismissing a viewController when i click a collectionviewcell. so my collectionview is placed inside a uiview that is then displayed in a viewcontroller. whenever the user click on collectionViewCell, i want the view to trigger bye() function that is placed inside the viewcontroller. i add print("bye") just to see if it work, and it does print the word but it does not execute dismiss(animated: true, completion: nil) to dismiss the viewcontroller along with the uiview and collectionview. why it does not dismiss the controller? is there another way that i can do the same thing? here is the code :
the view controller
class sideViewController: UIViewController {
let dismissBtn:UIButton = {
let content = UIButton()
content.backgroundColor = .green
content.addTarget(self, action: #selector(bye), for: .touchUpInside)
return content
}()
let sideTableViews: sideCollectionView = {
let content = sideCollectionView()
return content
}()
override func viewDidLoad() {
super.viewDidLoad()
dismissBtn.translatesAutoresizingMaskIntoConstraints = false
sideTableViews.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dismissBtn)
view.addSubview(sideTableViews)
dismissBtn.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
dismissBtn.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10).isActive = true
dismissBtn.widthAnchor.constraint(equalToConstant:40).isActive = true
dismissBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
sideTableViews.topAnchor.constraint(equalTo: dismissBtn.bottomAnchor, constant: 30).isActive = true
sideTableViews.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
sideTableViews.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
sideTableViews.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
#objc func membershipController(){
let next = self.storyboard?.instantiateViewController(withIdentifier: "membershipViewController") as! membershipViewController
self.present(next, animated: true, completion: nil)
}
#objc func bye(){
print("bye")
dismiss(animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
this is the collectionView code and the uiview
class sideCollectionView:UIView, UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
let arrayLbl = ["connection","achievement","template","setting"]
let arrayImg = ["connection","achievement","template","setting"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayLbl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! sideCollectionViewCell
cell.titleImg.image = UIImage(named: "\(arrayImg[indexPath.row])")
cell.titleLbl.text = arrayLbl[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.frame.width / 2) - 40, height: (self.frame.width / 2) - 40)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(25, 25, 10, 25)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0{
connectController()
print("connection")
let side = sideViewController()
side.bye()
}
if indexPath.row == 1{
let side = sideViewController()
side.bye()
print("achievement")
}
if indexPath.row == 2{
let side = sideViewController()
side.bye()
print("template")
}
if indexPath.row == 3{
let side = sideViewController()
side.bye()
print("setting")
}
}
lazy var collectionViews: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.clear
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews(){
collectionViews.register(sideCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionViews.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = UIColor.clear
addSubview(collectionViews)
collectionViews.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
collectionViews.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
collectionViews.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
collectionViews.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
collectionViews.widthAnchor.constraint(equalTo: widthAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func connectController(){
let side = sideViewController()
side.bye()
}
#objc func settingController(){
let side = sideViewController()
side.bye()
}
#objc func achievementController(){
let side = sideViewController()
side.bye()
}
#objc func templateController(){
let side = sideViewController()
side.bye()
}
}
Because this
let side = sideViewController()
is another instance other than the displayed one , so add this var to the view
class sideCollectionView:UIView {
var currentVc:sideViewController?
}
//
when you create the variable assign it
lazy var sideTableViews: sideCollectionView = {
let content = sideCollectionView()
content.currentVc = self
return content
}()
//
Inside
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0{
connectController()
print("connection")
currentVc?.bye()
}
}

Creating UICollectionView which can scroll horizontally and vertically with Swift

I'm trying to create an UICollectionView which can scroll horizontally and vertically.
Here are my codes:
Here is the Model:
import UIKit
class AppCategory: NSObject {
var name: String?
var apps: [App]?
static func sampleAppCategories() -> [AppCategory] {
// Chapter 1
let chapter1 = AppCategory()
chapter1.name = NSLocalizedString("CHAPTER 1: ", comment: "1") + NSLocalizedString("19 Sections", comment: "title")
var apps1 = [App]()
let chapter1App = App()
chapter1App.imageName = "IMG_2487"
let chapter11App = App()
chapter11App.imageName = "IMG_2502"
let chapter12App = App()
chapter12App.imageName = "IMG_2507"
apps1.append(chapter1App)
apps1.append(chapter11App)
apps1.append(chapter12App)
chapter1.apps = apps1
// Chapter 2
let chapter2 = AppCategory()
chapter2.name = NSLocalizedString("CHAPTER 2: ", comment: "2") + NSLocalizedString("19 Sections", comment: "title")
var apps2 = [App]()
let chapter2App = App()
chapter2App.imageName = "IMG_2508"
apps2.append(chapter2App)
chapter2.apps = apps2
// Chapter 3
let chapter3 = AppCategory()
chapter3.name = NSLocalizedString("CHAPTER 3: ", comment: "title") + NSLocalizedString("19 Sections", comment: "title")
var apps3 = [App]()
let chapter3App = App()
chapter3App.imageName = "IMG_2510"
apps3.append(chapter3App)
chapter3.apps = apps3
// Chapter 4
let chapter4 = AppCategory()
chapter4.name = NSLocalizedString("CHAPTER 4: ", comment: "title") + NSLocalizedString("19 Sections", comment: "title")
var apps4 = [App]()
let chapter4App = App()
chapter4App.imageName = "IMG_2511"
apps4.append(chapter4App)
chapter4.apps = apps4
return [chapter1, chapter2, chapter3, chapter4]
}
}
class App: NSObject {
var imageName: String?
}
Here is the FeatureViewController:
import UIKit
class FeaturedViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var appCategories: [AppCategory]?
let verticalCellId = "verticalCellId"
let horizontalCellId = "horizontalCellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .white
appCategories = AppCategory.sampleAppCategories()
navigationItem.title = NSLocalizedString("Original", comment: "Original")
navigationController?.navigationBar.prefersLargeTitles = true
collectionView?.register(FeaturedVerticalCell.self, forCellWithReuseIdentifier: verticalCellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategories?.count {
return count
}
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: verticalCellId, for: indexPath) as! FeaturedVerticalCell
cell.appCategory = appCategories?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch UIDevice.current.userInterfaceIdiom {
case .phone:
collectionView.reloadData()
return CGSize(width:view.frame.width, height: view.frame.width / 5 * 4 )
case .pad:
let padding: CGFloat = 50
let collectionViewSize = collectionView.frame.size.width - padding
collectionView.reloadData()
return CGSize(width: collectionViewSize / 5 * 4, height: collectionViewSize / 5 * 3 )
case .tv:
break
case .carPlay:
break
case .unspecified:
break
}
return CGSize(width: 0, height: 0)
}
}
Here is the FeaturedVerticalCell:
import UIKit
struct Titles {
var title: String?
var images:[String]
}
class FeaturedVerticalCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let horizontalCellId = "horizontalCellId"
var appCategory: AppCategory? {
didSet {
if let name = appCategory?.name {
titleLabel.text = name
}
}
}
let horizontalCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor.clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
if UIDevice.current.userInterfaceIdiom == .phone {
label.font = UIFont.systemFont(ofSize: 14.0, weight: UIFont.Weight.medium)
} else {
label.font = UIFont.systemFont(ofSize: 20.0, weight: UIFont.Weight.medium)
}
label.textAlignment = .left
label.textColor = UIColor.darkGray
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
horizontalCollectionView.dataSource = self
horizontalCollectionView.delegate = self
horizontalCollectionView.register(HorizontalCollectionViewCell.self, forCellWithReuseIdentifier: horizontalCellId)
addSubview(horizontalCollectionView)
horizontalCollectionView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 24).isActive = true
horizontalCollectionView.topAnchor.constraint(equalTo: self.topAnchor, constant: 36).isActive = true
horizontalCollectionView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -24).isActive = true
horizontalCollectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 8).isActive = true
addSubview(titleLabel)
titleLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 24).isActive = true
titleLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -24).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: horizontalCollectionView.topAnchor, constant: 0).isActive = true
titleLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 24).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategory?.apps?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: horizontalCellId, for: indexPath) as! HorizontalCollectionViewCell
cell.app = appCategory?.apps?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.height * 4 / 5, height: frame.height * 4 / 5)
}
}
class HorizontalCollectionViewCell: UICollectionViewCell {
var app: App? {
didSet {
if let imageName = app?.imageName {
photoImageView.image = UIImage(named: imageName)
}
}
}
let photoImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 10
iv.image = #imageLiteral(resourceName: "IMG_2545")
iv.layer.masksToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(photoImageView)
photoImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
photoImageView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
photoImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
photoImageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But now I got a problem: the rows in horizontal sections will change somehow when I scroll vertically. Any way to get it back to work normally?
Don't know what's wrong there on earth, but finally I got it work. Here you go:
import UIKit
class FeaturedViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
print("SeachController tapped.")
}
let firstCellId = "cellfirstCellIdId"
var appCategories: [AppCategory]?
override func viewDidLoad() {
super.viewDidLoad()
appCategories = AppCategory.sampleAppCategories()
collectionView?.backgroundColor = UIColor.clear
collectionView?.register(CategoryCell.self, forCellWithReuseIdentifier: firstCellId)
navigationItem.title = NSLocalizedString("Original", comment: "Original")
navigationController?.navigationBar.prefersLargeTitles = true
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
navigationItem.hidesSearchBarWhenScrolling = true
self.navigationItem.searchController = searchController
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: firstCellId, for: indexPath) as! CategoryCell
cell.appCategory = appCategories?[indexPath.item]
return cell
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategories?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 300)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
Here is the cell:
import UIKit
class CategoryCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var appCategory: AppCategory? {
didSet {
if let name = appCategory?.name {
firstChapterLabel.text = name
}
}
}
let secondCellId = "secondCellId"
let appsCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor.clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
let firstChapterLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
if UIDevice.current.userInterfaceIdiom == .phone {
label.font = UIFont.systemFont(ofSize: 14.0, weight: UIFont.Weight.medium)
} else {
label.font = UIFont.systemFont(ofSize: 20.0, weight: UIFont.Weight.medium)
}
label.textAlignment = .left
label.textColor = UIColor.darkGray
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(appsCollectionView)
appsCollectionView.dataSource = self
appsCollectionView.delegate = self
appsCollectionView.register(AppCell.self, forCellWithReuseIdentifier: secondCellId)
appsCollectionView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 16).isActive = true
appsCollectionView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -16).isActive = true
appsCollectionView.topAnchor.constraint(equalTo: self.topAnchor, constant: 50).isActive = true
appsCollectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
addSubview(firstChapterLabel)
firstChapterLabel.leftAnchor.constraint(equalTo: appsCollectionView.leftAnchor, constant: 16).isActive = true
firstChapterLabel.rightAnchor.constraint(equalTo: appsCollectionView.rightAnchor, constant: -16).isActive = true
firstChapterLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 24).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategory?.apps?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: secondCellId, for: indexPath) as! AppCell
cell.app = appCategory?.apps?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.height, height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class AppCell: UICollectionViewCell {
var app: App? {
didSet {
if let imageName = app?.imageName {
photoImageView.image = UIImage(named: imageName)
}
}
}
let photoImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 9
iv.layer.masksToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(photoImageView)
photoImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 16).isActive = true
photoImageView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -16).isActive = true
photoImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -36).isActive = true
photoImageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 36).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And the model stays as it was described in the question.

Swift 3: Be able to tap view behind a collectionViewCell?

I am working on a new feature in my app where I want a user to have an image and then be able to swipe through a collectionView on top of that image, adding "filters". The issue that I have is that I want the user to still be able to interact with the image, but it is behind the collectionView. If I set the collectionViews inUsersInteractionEnabled to false, of course I then cannot use the collectionView at all. I am going for a similar functionality to Snapchats "scroll through filters" feature. Any help will be highly appreciated. Thank you guys. Code is below:
import UIKit
class FirstCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() {
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class SecondCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
let cellView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.alpha = 0.25
return view
}()
func setupViews() {
backgroundColor = .clear
addSubview(cellView)
cellView.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class SecondController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear
cv.dataSource = self
cv.delegate = self
cv.translatesAutoresizingMaskIntoConstraints = false
cv.isPagingEnabled = true
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(FirstCell.self, forCellWithReuseIdentifier: "firstCell")
collectionView.register(SecondCell.self, forCellWithReuseIdentifier: "secondCell")
view.backgroundColor = .white
setupViews()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCell
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCell
return cell
}
}
lazy var imageView: UIImageView = {
let iv = UIImageView()
let image = UIImage(named: "img")
iv.image = image
iv.contentMode = .scaleAspectFill
iv.translatesAutoresizingMaskIntoConstraints = false
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
return iv
}()
func handleTap() {
print(123)
}
func setupViews() {
view.addSubview(imageView)
view.addSubview(collectionView)
imageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
imageView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
}
}
There is a hitTest(_::) method available to override in any of the UIView subclass, which you can implement in your custom UICollectionView subclass and forward taps from it to any other view (the image behind collection view in your case).
override func hitTest(_ point: CGPoint,
with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if self.parentViewController.forwardTapToImageIfNeeded(point) {
return nil
}
return hitView
}
Where parentViewController is a reference to your SecondController instance, which should have forwardTapToImageIfNeeded(_) function to check if user's tap is on top of the image frame, and if it is — automatically pass tap event to it.
func forwardTapToImageIfNeeded(_ p: CGPoint) -> Bool {
/* Convert point to local coordinates */
let pointInSuperView = self.convert(p, from: collectionView)
/* Check if tap is on top of the image frame */
if imageBehindCollection.frame.contains(pointInSuperView) {
// Forward tap to the image `imageBehindCollection`
return true
}
return false
}
Note: I didn't test this particular code by myself, so it might need a few fixes in Xcode.

didSelectItemAt: not being called (UICollectionView)

I have a UICollectionView in a UIView class because it's implemented in an other class.I set the delegate and dataSource and allowsSelection to true but didSelectItemAt method doesn't work and I can't figure out why.This is the code:
class NewRecipeViewCategoryCV: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.showsHorizontalScrollIndicator = false
cv.backgroundColor = UIColor.white
cv.translatesAutoresizingMaskIntoConstraints = false
cv.dataSource = self
cv.delegate = self
cv.allowsSelection = true
return cv
}()
let newRecipeModel = NewRecipeModel()
let cellId = "cellId"
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(NewRecipeViewCategory.self, forCellWithReuseIdentifier: cellId)
addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: topAnchor).isActive = true
collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
collectionView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
collectionView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return newRecipeModel.recipeCategory.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! NewRecipeViewCategory
cell.categoryText.text = newRecipeModel.recipeCategory[indexPath.item].uppercased()
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(123)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let category: UILabel = {
let category = UILabel()
category.text = "BREAKFAST"
category.font = UIFont(name: "SourceSansPro-Regular", size: 13)
category.translatesAutoresizingMaskIntoConstraints = false
category.textColor = UIColor.black
category.sizeToFit()
return category
}()
return CGSize(width: category.frame.width+15, height: category.frame.height+12)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}