CollectionView function "didSelectItemAt" is not getting called - swift

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

Related

how to put a char in a separate collectionViewCell in swift

enter image description hereI make the word game, I have a word, this word needs to be divided into characters and each character should be added to a separate cell and there would be a space between the words, while using a custom class for the label. To do this, I use collection viewcells, I could not add a character to each cell, I was only able to transfer a whole word to each cell. Please help solve this problem.
P.S I added screenshots as it should be and as it isenter image description here
My code
ViewController.swift
import UIKit
class ViewController: UIViewController {
let cellID = "Cell"
let word = "Hello My People"
var index = 0
var targets = [TargetView]()
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 12
layout.minimumInteritemSpacing = 3
let collectionView1 = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView1.backgroundColor = .red
collectionView1.translatesAutoresizingMaskIntoConstraints = false
return collectionView1
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .brown
view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CharCell.self, forCellWithReuseIdentifier: cellID)
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 400).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -70).isActive = true
//var anagram1 = word.count
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//
// var anagram1 = word
// //var leg = anagram1.count
////
//// //targets = []
// for (index,letter) in anagram1.enumerated() {
// let target = TargetView(letter: letter, sideLength: 15)
// if letter != " " {
// // let target = TargetView(letter: letter, sideLength: 15)
//// target.center = CGPoint(x: xOffset + CGFloat(index) /* * 20 */ * (15 + tileMargin) - view.frame.minX, y: UIScreen.main.bounds.size.height - 100) //100 //UIScreen.main.bounds.size.height - CGFloat(index) * 50
////
//// view.addSubview(target)
////
// //targets.append(target)
// //stackView.addArrangedSubview(target)
// targets.append(target)
// return 15
// }
// }
if index < word.count {
return word.count
} else {
return 0
}
//return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! CharCell
for (index,letter) in word.enumerated() {
let target = TargetView(letter: letter, sideLength: 15)
if letter != " " {
// let target = TargetView(letter: letter, sideLength: 15)
targets.append(target)
cell.label.text = String(letter)
}
}
cell.label.text = word
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 30, height: 30)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 15, left: 0, bottom: 0, right: 0)
}
}
CharCell.swift
import UIKit
class CharCell: UICollectionViewCell {
var label1 = [TargetView]()
lazy var label: UILabel = {
let label = UILabel()
label.backgroundColor = .cyan
label.textAlignment = .left
label.font = .boldSystemFont(ofSize: 10)
label.text = "_"//String(letter).uppercased()
label.textColor = .black
label.numberOfLines = 3
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupCell()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupCell() {
backgroundColor = .white
label.frame = CGRect(x: 0, y: 1, width: frame.width , height: frame.height )
self.addSubview(label)
}
}
TargetView.swift
import UIKit
class TargetView: UILabel {
var letter: Character!
var isMatch = false
init(letter: Character, sideLength: CGFloat) {
self.letter = letter
//let image = UIImage(named: "slot")
//super.init(image: image)
//let scale = CGRect(x: 0, y: 0, width: (image?.size.width)! * scale, height: (image?.size.height)! * scale)
super.init(frame: CGRect(x: 0, y: 0, width: 15, height: 30))
self.backgroundColor = .red
self.textAlignment = .center
self.font = .boldSystemFont(ofSize: 60.0 / 3)
self.text = "_"//String(letter).uppercased()
self.textColor = .white
self.lineBreakMode = .byWordWrapping
self.adjustsFontSizeToFitWidth = true
self.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The problem is you’re setting cell.label.text to word in every cell. Just split the phrase into components and add them to an array. In my example, I simplified it.
You'll likely need to adapt it to your app, but here's a quick implementation just to get you going.
import UIKit
class ViewController: UIViewController {
let cellID = "Cell"
let word = "Hello My People"
var arr = [String]()
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 12
layout.minimumInteritemSpacing = 3
let collectionView1 = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView1.backgroundColor = .gray
collectionView1.translatesAutoresizingMaskIntoConstraints = false
return collectionView1
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.backgroundColor = .brown
view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellID)
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 400).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -70).isActive = true
for char in word {
arr.append(String(char))
}
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath)
cell.backgroundColor = .red
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
let cellText = arr[indexPath.item]
lbl.text = cellText
if cellText == " " {
cell.backgroundColor = .clear
}
cell.addSubview(lbl)
lbl.centerXAnchor.constraint(equalTo: cell.centerXAnchor).isActive = true
lbl.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 35, height: 35)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 15, left: 0, bottom: 0, right: 0)
}
}
Result:

Size of cell is not equal to collectionview size

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?

ActivityViewController Popup not on Top of the Screen

I have a share button to share a screenshot of my screen. Although I implemented a extension to get the rootViewController, the popup of my ActivityViewController is still behind the window from which I want to share the screenshot see image attached. I already tried to load my handleDismiss() function inside the handleShare(), but without success so far. Maybe someone can get me on the right track, what is going on here.
Thank you very much in advance.
Short gif of my Statistic View
import UIKit
class GameStatistics: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let blackView = UIView()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
let cellId = "cellId"
let sectionHeader = "sectionHeader"
let sectionFooter = "sectionFooter"
//
//
//
func showStatistics() {
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
// Dynamic Height of Collection View
let value: CGFloat = CGFloat(statistics.count)
let height: CGFloat = value * cellHeight + (value - 1) * cellSpacing + headerHeight + footerHeight
let y = window.frame.height - height
blackView.frame = window.frame
collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
blackView.alpha = 0
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.collectionView.frame = CGRect(x: 0, y: y, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}, completion: nil)
}
}
#objc func handleDismiss() {
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return statistics.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! GameStatisticCell
cell.statistic = statistics[indexPath.item]
cell.layoutIfNeeded()
//dump(statistics)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: cellHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return cellSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return cellSpacing
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionHeader, for: indexPath)
return supplementaryView
case UICollectionElementKindSectionFooter:
let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionFooter, for: indexPath)
return supplementaryView
default:
fatalError("Unexpected element kind")
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: headerHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: footerHeight)
}
override init() {
super.init()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(GameStatisticCell.self, forCellWithReuseIdentifier: cellId)
collectionView.register(GameStatisticHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: sectionHeader)
collectionView.register(GameStatisticFooter.self, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: sectionFooter)
}
func handleShare() {
if let window = UIApplication.shared.keyWindow {
let size = CGSize(width: window.frame.width, height: window.frame.height)
UIGraphicsBeginImageContext(size)
window.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let activityViewController = UIActivityViewController(activityItems: [image!], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 0, y: 0, width: 0, height: 0)
//UIApplication.shared.keyWindow?.rootViewController?.present(activityViewController, animated: true, completion: nil)
UIApplication.topViewController?.present(activityViewController, animated: true, completion: nil)
}
}
}
Extension
extension UIApplication {
static var topViewController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
}
}
extension UIViewController {
var visibleViewController: UIViewController? {
if let navigationController = self as? UINavigationController {
return navigationController.topViewController?.visibleViewController
} else if let tabBarController = self as? UITabBarController {
return tabBarController.selectedViewController?.visibleViewController
} else if let presentedViewController = presentedViewController {
return presentedViewController.visibleViewController
} else {
return self
}
}
}

Swift: UICollectionViewCell - Dismiss UICollectionView

Trying to dismiss a slide-in UICollectionView by tapping a cell in the collection view. I Am able to dismiss by tapping outside of the collection view by using a dismiss function. Thanks
Update: Found the solution using a delegate. I have marked parts of the code with the solution steps.
Find my Codes below.
HomeController:
import UIKit
class HomeController: UIViewController, UICollectionViewDelegate , UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
let userInfoCell = "userInfoCell"
let dashboardCell = "dashboardCell"
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .cyan
setupViews()
}
func setupViews() {
view.addSubview(mainCollectionView)
mainCollectionView.dataSource = self
mainCollectionView.delegate = self
mainCollectionView.register(UserInfoCell.self, forCellWithReuseIdentifier: userInfoCell)
mainCollectionView.register(DashboardCell.self, forCellWithReuseIdentifier: dashboardCell)
}
let mainCollectionView: UICollectionView = {
let windowWidth = UIScreen.main.bounds.width
let windowHeight = UIScreen.main.bounds.height
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 16
layout.scrollDirection = .vertical
let mainCV = UICollectionView(frame: .zero, collectionViewLayout: layout)
mainCV.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 20, height: UIScreen.main.bounds.height - 20)
mainCV.center = CGPoint(x: windowWidth - (windowWidth / 2), y: windowHeight - (windowHeight / 2))
mainCV.backgroundColor = .red
mainCV.layer.cornerRadius = 25
return mainCV
}()
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 1 {
return 1
}
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: dashboardCell, for: indexPath) as! DashboardCell
cell.plusButton.addTarget(self, action: #selector(plusButtonPressed), for: .touchUpInside)
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: userInfoCell, for: indexPath) as! UserInfoCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 1 {
return CGSize(width: (view.frame.width / 3) - 30 , height: 100)
}
return CGSize(width: (view.frame.width / 2) - 30 , height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
if section == 1 {
return UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15)
}
return UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15)
}
#objc func plusButtonPressed() {
print("Plus button pressed")
taggObjectLauncher.showSlideInImages()
}
lazy var taggObjectLauncher: SlideInViewLauncher = {
let launcher = SlideInViewLauncher()
launcher.homeController = self
return launcher
}()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class UserInfoCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class DashboardCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .green
setupDashboardViews()
}
func setupDashboardViews() {
addSubview(plusButton)
}
let plusButton: UIButton = {
let button = UIButton()
button.backgroundColor = .yellow
button.setTitle("Plus", for: .normal)
button.setTitleColor(.black, for: .normal)
button.setTitleColor(.gray, for: .highlighted)
button.titleLabel?.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
button.frame = CGRect(x: 5, y: 5, width: (UIScreen.main.bounds.width / 3) - 40, height: 90)
return button
}()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
SlideInViewLauncher Controller:
import UIKit
class SlideInViewLauncher: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var imageCategories: [ImageCategory]?
var homeController = HomeController()
var imageCell = ImageCell()
private let cellIdentifier = "ImageCell"
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
let blackView = UIView()
#objc func showSlideInImages() {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
if let window = UIApplication.shared.keyWindow {
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
let height = CGFloat(450)
let y = window.frame.height - height
collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
collectionView.register(SlideInViewCell.self, forCellWithReuseIdentifier: cellIdentifier)
window.addSubview(blackView)
window.addSubview(collectionView)
blackView.frame = window.frame
blackView.alpha = 0
imageCategories = ImageCategory.sampleImageCategories()
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 1
self.collectionView.frame = CGRect(x: 0, y: y, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}
}
}
#objc func handleDismiss() {
UIView.animate(withDuration: 0.5, animations: {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}
})
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("selected")
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = imageCategories?.count {
return count
}
return 0
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! SlideInViewCell
cell.imageCategory = imageCategories?[indexPath.row]
///////////////
// Solution: Part 5
cell.cellDelegate = self
///////////////
return cell
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width , height: 200)
}
override init() {
super.init()
collectionView.dataSource = self
collectionView.delegate = self
}
}
////////////
// Solution: Part 4
extension SlideInViewLauncher: MyCustomCellDelegate {
func didPressButton() {
UIView.animate(withDuration: 0.5, animations: {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(X: 0, y: window.frame.height, width:
self.collectionView.frame.width, height:
self.collectionView.frame.height)
}
})
}
}
/////////////
SlideInViewCell Controller:
import UIKit
////////////////
//Solution: Part 1
protocol MyCustomCellDelegate: class {
func didPressButton()
}
/////////////
class SlideInViewCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
/////////////////
// Solution: Part 2
weak var cellDelegate: MyCustomDelegate?
////////////////
var homeViewController = HomeController()
var slideInViewLauncher = SlideInViewLauncher()
var imageCategory: ImageCategory? {
didSet {
if let name = imageCategory?.name {
mainCategoryLabel.text = name
}
}
}
var image: Image?
private let cellID = "objectCellID"
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let mainCategoryLabel: UILabel = {
let label = UILabel()
label.text = ""
label.font = UIFont.systemFont(ofSize: 16)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var slideInImagesCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor.white
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
let dividerLineView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.4, alpha: 0.4)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
func setupViews() {
backgroundColor = UIColor.clear
addSubview(slideInImagesCollectionView)
addSubview(dividerLineView)
addSubview(mainCategoryLabel)
slideInImagesCollectionView.dataSource = self
slideInImagesCollectionView.delegate = self
slideInImagesCollectionView.register(ImageCell.self, forCellWithReuseIdentifier: cellID)
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-14-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": mainCategoryLabel]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-14-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": slideInImagesCollectionView]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[mainCategoryLabel(30)][v0][v1(0.5)]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": slideInImagesCollectionView, "v1": dividerLineView, "mainCategoryLabel": mainCategoryLabel]))
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = imageCategory?.images?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! ImageCell
cell.image = imageCategory?.images?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: frame.height - 30)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(35, 10, 10, 10)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let imageSelected = imageCategory!.images?[indexPath.item]
print(imageSelected?.name ?? "Error: No item at index path")
//////////////////
// Solution: Part 3
cellDelegate?.didPressButton()
//////////////////
}
}
class ImageCell: UICollectionViewCell {
var image: Image? {
didSet {
if let name = image?.name {
nameLabel.text = name
}
if let imageName = image?.imageName {
imageView.image = UIImage(named: imageName)
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFit
iv.layer.masksToBounds = true
return iv
}()
let nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 14)
label.numberOfLines = 2
label.textAlignment = .center
return label
}()
func setupViews() {
addSubview(imageView)
addSubview(nameLabel)
imageView.frame = CGRect(x: 0, y: 0, width: frame.width, height: 100)
nameLabel.frame = CGRect(x: 0, y: frame.width + 2, width: frame.width, height: 40)
}
}
Models Controller:
import UIKit
class ImageCategory: NSObject {
var name: String?
var images: [Image]?
static func sampleImageCategories() -> [ImageCategory] {
var someImages = [Image]()
let someImageCategory = ImageCategory()
someImageCategory.name = "Some Category"
let heartImage = Image()
heartImage.name = "Heart"
heartImage.imageName = "heart"
heartImage.category = "Some Category"
someImages.append(heartImage)
someImageCategory.images = someImages
// Return the array of image categories:
return [someImageCategory]
}
}
class Image: NSObject {
var id: NSNumber?
var name: String?
var category: String?
var imageName: String?
}
Was able to solve this by adding a delegate protocol. See comments in code. Also, sorry if my post does not quite follow protocol. This is my first post.
Thanks

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.