Reputation: 1
I want to make certain view in cell of collectionView tappable.
Code Of ViewController
class ViewController: UIViewController {
let collectionView:UICollectionView = {
let view = UICollectionViewFlowLayout()
view.scrollDirection = .horizontal
let cv : UICollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: view)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.showsHorizontalScrollIndicator = false
cv.isPagingEnabled = true
return cv
}()
let arrayData:[Data] = [Data(vectorImages: "Group 3", maskImages: "", rectangleImage: "", headingLabel: "Document Reader", detailText: "Lorem ipsum dolor sit amet consectetur. Sit tortor malesuada egestas velit magna. Egestas congue nisi tincidunt urna.",view: View(background: .systemPurple, cornerRadius: 12),labelInView: "Get Started"),
Data(vectorImages: "Group 10", maskImages: "Mask group-2", rectangleImage: "Rectangle 144", headingLabel:"Read and View", detailText: "Lorem ipsum dolor sit amet consectetur. Sit tortor malesuada egestas velit magna. Egestas congue nisi tincidunt urna.",view:View(background: .systemPurple, cornerRadius: 12),labelInView: "Get Started"),
Data(vectorImages: "Group 11", maskImages: "Mask group-3", rectangleImage: "Rectangle 144", headingLabel: "Read and View", detailText: "Lorem ipsum dolor sit amet consectetur. Sit tortor malesuada egestas velit magna. Egestas congue nisi tincidunt urna.",view: View(background: .systemPurple, cornerRadius: 12),labelInView: "Get Started"),
Data(vectorImages: "Group 12", maskImages: "Mask group-4", rectangleImage: "Rectangle 144", headingLabel: "Read and View", detailText: "Lorem ipsum dolor sit amet consectetur. Sit tortor malesuada egestas velit magna. Egestas congue nisi tincidunt urna.",view: View(background: .systemPurple, cornerRadius: 12),labelInView: "Get Started")]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPurple
setupView()
setDelegatesAndDatasource()
registerCell()
}
func setupView() {
view.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
func setDelegatesAndDatasource() {
collectionView.delegate = self
collectionView.dataSource = self
}
func registerCell() {
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
}
extension ViewController:
UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)as! CollectionViewCell
cell.customView.vectorImage.image = UIImage(named: "\(arrayData[indexPath.row].vectorImages)")
cell.customView.maskImage.image = UIImage(named: "\(arrayData[indexPath.row].maskImages)")
cell.customView.rectangleView.image = UIImage(named:"\(arrayData[indexPath.row].rectangleImage)")
cell.customView.heading.text = arrayData[indexPath.row].headingLabel
cell.customView.detailText.text = arrayData[indexPath.row].detailText
cell.customView.view = arrayData[indexPath.row].view as! View
cell.customView.labelInView.text = arrayData[indexPath.row].labelInView
cell.viewTapped = { [weak self] aCell in
let idx = self?.collectionView.indexPath(for: aCell)
print("bottomView in cell at indexPath: \(idx ?? nil) was tapped")
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 393 , height: 852)
}
}
In this cell of CollectioView cell.customView.view this View I want it to make it tappable
Code Of ScreenView that in this a view which I want to make tappable
import UIKit
class ScreenView: UIView {
let rectangleView = ImageView(image: "")
let maskImage = ImageView(image: "")
let vectorImage = ImageView(image: "")
let heading = Label(text: "Some Text", font: UIFont.systemFont(ofSize: 30, weight: .black), textColor: .black)
let detailText:UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.font = .systemFont(ofSize: 14, weight: .light)
lbl.numberOfLines = 0
return lbl
}()
var view = View(background: .systemPurple, cornerRadius: 12)
let labelInView = Label(text: "Get Started", font: UIFont.systemFont(ofSize: 14, weight: .black), textColor: .white)
init()
{
super.init(frame: .zero)
self.translatesAutoresizingMaskIntoConstraints = false
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
addSubview(rectangleView)
rectangleView.addSubview(maskImage)
rectangleView.addSubview(vectorImage)
addSubview(heading)
addSubview(detailText)
addSubview(view)
view.addSubview(labelInView)
NSLayoutConstraint.activate([
rectangleView.centerYAnchor.constraint(equalTo: centerYAnchor),
rectangleView.centerXAnchor.constraint(equalTo: centerXAnchor),
rectangleView.widthAnchor.constraint(equalToConstant: 345),
rectangleView.heightAnchor.constraint(equalToConstant: 441),
maskImage.topAnchor.constraint(equalTo: rectangleView.topAnchor),
maskImage.widthAnchor.constraint(equalToConstant: 345),
vectorImage.bottomAnchor.constraint(equalTo: rectangleView.bottomAnchor, constant: -10),
vectorImage.centerXAnchor.constraint(equalTo: rectangleView.centerXAnchor),
vectorImage.widthAnchor.constraint(equalToConstant: 313),
vectorImage.heightAnchor.constraint(equalToConstant: 251),
heading.topAnchor.constraint(equalTo: rectangleView.bottomAnchor, constant: 20),
heading.centerXAnchor.constraint(equalTo: centerXAnchor),
detailText.topAnchor.constraint(equalTo: heading.bottomAnchor, constant: 20),
detailText.centerXAnchor.constraint(equalTo: centerXAnchor),
detailText.widthAnchor.constraint(equalToConstant: 313),
view.topAnchor.constraint(equalTo: detailText.bottomAnchor, constant: 74),
view.leadingAnchor.constraint(equalTo: rectangleView.leadingAnchor ),
view.widthAnchor.constraint(equalToConstant: 345),
view.heightAnchor.constraint(equalToConstant: 52),
labelInView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
labelInView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
}
The Code of CustomCell in CollectionView import UIKit
class CollectionViewCell: UICollectionViewCell {
let customView = ScreenView()
var viewTapped: ((UICollectionViewCell) -> ())?
override init(frame: CGRect) {
super.init(frame: frame)
tapOnView()
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapOnView() {
let tapView = UITapGestureRecognizer(target: self, action: #selector(handleTap))
customView.view.addGestureRecognizer(tapView)
}
func setupView() {
addSubview(customView)
NSLayoutConstraint.activate([
customView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -140),
customView.centerXAnchor.constraint(equalTo: centerXAnchor)
])
}
@objc func handleTap() {
viewTapped?(self)
}
}
Upvotes: 0
Views: 155
Reputation: 77682
First, a couple Tips for the future...
When posting a question here, make sure your code will run as-is. It is much easier to offer help if we can copy/paste/run without having to guess at what the missing code may be.
Also, start simple!!! Begin with the minimal code necessary to accomplish your goal.
In this case, let's use a cell class with a single subview.
View Controller class - pretty standard. We'll use an array of colors, which we'll use to set the background of the "custom view" in each cell.
class ViewController: UIViewController {
let collectionView:UICollectionView = {
let view = UICollectionViewFlowLayout()
view.scrollDirection = .horizontal
let cv : UICollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: view)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.showsHorizontalScrollIndicator = false
cv.isPagingEnabled = true
return cv
}()
let arrayData: [UIColor] = [
.systemRed, .systemGreen, .systemBlue, .systemYellow,
]
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setDelegatesAndDatasource()
registerCell()
}
func setupView() {
view.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
func setDelegatesAndDatasource() {
collectionView.delegate = self
collectionView.dataSource = self
}
func registerCell() {
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
}
View Controller extension
extension ViewController: UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)as! CollectionViewCell
cell.customView.backgroundColor = arrayData[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 393 , height: 852)
}
}
Custom Cell class - with a single UIView
as a subview, to which we'll add the UITapGestureRecognizer
. When tapped, we'll print to the debug console, and we'll also "flash" the background color so we see that it responded to the tap.
class CollectionViewCell: UICollectionViewCell {
let customView = UIView()
@objc func handleTap(_ t: UITapGestureRecognizer) {
print("Got Tap!")
// let's "flash" the customView's background color
// so we have a visual indication it was tapped
let c = customView.backgroundColor
customView.backgroundColor = .yellow
UIView.animate(withDuration: 0.75, animations: {
self.customView.backgroundColor = c
})
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
// create and add Tap Gesture Recognizer to customView
let t = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
customView.addGestureRecognizer(t)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
customView.layer.cornerRadius = 12
customView.translatesAutoresizingMaskIntoConstraints = false
// make sure we addSubview to contentView !!
contentView.addSubview(customView)
NSLayoutConstraint.activate([
customView.widthAnchor.constraint(equalToConstant: 300.0),
customView.heightAnchor.constraint(equalToConstant: 400.0),
customView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -100),
customView.centerXAnchor.constraint(equalTo: centerXAnchor),
])
}
}
Edit - another quick example, showing a "bottom view" in the cell that can be tapped. On tap, it will inform the controller via a closure, allowing the controller to do something - in your case, push to a new controller.
class ViewController: UIViewController {
let collectionView:UICollectionView = {
let fl = UICollectionViewFlowLayout()
fl.scrollDirection = .horizontal
fl.minimumLineSpacing = 0
fl.minimumInteritemSpacing = 0
let cv : UICollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: fl)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.showsHorizontalScrollIndicator = false
cv.isPagingEnabled = true
return cv
}()
let arrayData: [String] = [
"First", "Second", "Third", "Fourth",
]
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setDelegatesAndDatasource()
registerCell()
}
// so we can set the cell size when the collection view
// has been laid-out
var currentCVWidth: CGFloat = 0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if currentCVWidth != collectionView.frame.width {
currentCVWidth = collectionView.frame.width
if let fl = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
fl.itemSize = collectionView.frame.size
}
}
}
func setupView() {
view.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
func setDelegatesAndDatasource() {
collectionView.delegate = self
collectionView.dataSource = self
}
func registerCell() {
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)as! CollectionViewCell
// set the cell UI elements
cell.customView.headingLabel.text = arrayData[indexPath.item]
// set the closure so we know when the "bottom view" in a cell was tapped
cell.gotTapClosure = { [weak self] aCell in
guard let self = self,
let idx = self.collectionView.indexPath(for: aCell)
else {
return
}
// here is where we would push to another controller on the navigation stack
// for now, let's just confirm the cell index
print("bottomView in cell at indexPath: \(idx) was tapped")
}
return cell
}
}
class CollectionViewCell: UICollectionViewCell {
// closure
var gotTapClosure: ((UICollectionViewCell) -> ())?
let customView = ScreenView()
@objc func handleTap(_ t: UITapGestureRecognizer) {
print("Got Tap!")
// call the closure to inform the controller that
// the "bottomView" was tapped
gotTapClosure?(self)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
// create and add Tap Gesture Recognizer to customView.bottomView
let t = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
customView.bottomView.addGestureRecognizer(t)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
customView.translatesAutoresizingMaskIntoConstraints = false
// make sure we addSubview to contentView !!
contentView.addSubview(customView)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
customView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
customView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
customView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
customView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
}
}
class ScreenView: UIView {
let headingLabel: UILabel = {
let v = UILabel()
v.text = "Some Text"
v.font = .systemFont(ofSize: 30, weight: .black)
v.textColor = .black
// so we can see the label frame
v.backgroundColor = .yellow
return v
}()
let bottomView: UIView = {
let v = UIView()
v.backgroundColor = .systemPurple
v.layer.cornerRadius = 12
return v
}()
let labelInView: UILabel = {
let v = UILabel()
v.text = "Get Started"
v.font = .systemFont(ofSize: 14, weight: .black)
v.textColor = .white
// so we can see the label frame
v.backgroundColor = .blue
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
[headingLabel, bottomView, labelInView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
}
addSubview(headingLabel)
addSubview(bottomView)
bottomView.addSubview(labelInView)
NSLayoutConstraint.activate([
headingLabel.topAnchor.constraint(equalTo: topAnchor, constant: 20),
headingLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
bottomView.widthAnchor.constraint(equalToConstant: 345),
bottomView.heightAnchor.constraint(equalToConstant: 52),
bottomView.centerXAnchor.constraint(equalTo: centerXAnchor),
labelInView.centerYAnchor.constraint(equalTo: bottomView.centerYAnchor),
labelInView.centerXAnchor.constraint(equalTo: bottomView.centerXAnchor),
bottomView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12.0),
])
headingLabel.backgroundColor = .yellow
labelInView.backgroundColor = .blue
// so we can see this view
self.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
}
}
Looks like this when running:
When you scroll to the next cell, the Title label will be updated.
When you tap on the purple "bottomView" - but not the rest of the cell's UI elements - it will call the closure.
Upvotes: 1