I just started learning swift and don't understand how present ViewController from custom UITableViewCell when I clicked to my Collection Cell.
I have custom class MainTableViewCell.swift that has CollectionView
extension MainTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return videos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCollectionViewCell", for: indexPath) as! MainCollectionViewCell
let video = videos[indexPath.item]
cell.titleLabel.text = video.title
cell.dateLabel.text = video.date
cell.bgImageView.load(urlString: "https://i.ytimg.com/vi/\(video.image!)/hqdefault.jpg")
cell.layer.masksToBounds = true
cell.layer.cornerRadius = 15.0
return cell
}
What can I do in method didSelectItemAt?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
You just need to set a delegation pattern in order to present a new view controller from a class that contains your table view. The steps are:
Create a TableViewCellDelegate protocol, and inside a table view cell class inside didSelectItemAt method call the protocol's method (also pass the information of collection view cell in method parameters which is required).
Make your table view containing view controller conform to this protocol.
Inside the method where you conform the TableViewDelegate present a new view controller.
p.s don't forget to set the delegate property to self in cellForRowAt method of table view.
Related
I have a collectionView used for scrolling between pages, inside of one of these full page cells I have another collectionView with cells. How do I perform a segue when one of the cells inside of the inner most collectionView is tapped.
You will need a delegate on the cells with collection view, that will need to be notified when a particular cell is selected:
protocol CollectionCellDelegate: class {
func selectedItem()
}
class CollectionCell: UITableViewCell {
weak var delegate: CollectionCellDelegate?
// ...
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
self.delegate?.selectedItem()
}
}
And in the TableViewController you will have to implement that delegate to perform segue from it (you have to perform segue from UIViewController subclass, but the UITableViewCell is not subclassing it, that's why you need the delegate pattern).
class TableViewController: UITableViewController, CollectionCellDelegate {
// ...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
// set its delegate to self
cell.delegate = self
return cell
}
func selectedItem() {
// here you can perform segue
performSegue(withIdentifier: "mySegue", sender: self)
}
}
I haven't passed any argument to the delegate, but you can of course use arguments to pass any information that you need for the segue (e.g., the id of the collection cell that was selected, etc.).
When you tap on an item in collectionView, the following delegate method will be called (if you wired up everything properly) func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)... notice that the first param is collectionView itself.
Depending on how you set it up...
if you have two collectionViews within one UIViewController then you can do..
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
if collectionView == self.innerCollectionView {
// performSegue...
}
}
if you have two view controllers, one for outer and another for inner.. then you can create use delegate pattern to let the outer know which item got selected, and segue using that info.
I have more than one collectionView in a ViewController. The cell of those collectionViews has the same format.. so I'm reusing them. So my question is: How to identify in the method
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath)
I don't want to do a couple of if's
I've found this solution everywhere, but really don't like it. Here is the code
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
if let aCell = cell as? ItemCollectionViewCell{
aCell.setupCell(with: self.items[indexPath.item])
}
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.colletionViewTwo{
// goto viewController1
}else if collectionView == self.colletionViewOne{
// goto viewController2
}
}
Create two classes that implement the collection view delegate and data source and use one of each. So you'll have these two extra objects in your current view controller.
Seeing your code now, the above is probably too heavy. Alternatively, add a dictionary in which you store the collection view as key and a selector as value. This is extensible as you say you want.
To be honest, what's your issue an if (or switch) statement like you have now?
I need to get the next cell inside cellForItem within a collection view so that I can update a view object. When I try the following below it doesn't work. I've also tried indexPathForVisibleItems passing in indexPath.row + 1 and the produces an index out of range error.
let index = IndexPath(row: indexPath.row + 1, section: indexPath.section)
if let nextCell = collectionView.cellForItem(at: index) as! MKRCell {
nextCell.setupWaitView(time: timeToWait)
nextCell.waitViewHeightConstraint.constant = 80
nextCell.waitView.alpha = 1
nextCell.waitView.isHidden = false
}
Is this possible to achieve or will I need to do this via another way?
Thanks
No, it is not possible to get the cell object before initialization in cellForItemAt but here
you can receive the call before displaying the cell from UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView,willDisplay cell: UICollectionViewCell,forItemAt indexPath: IndexPath) {
if let myCell = cell as? MKRCell {
}
}
AND
If you want to set up the cell you have to setup view in the UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}
You should update the cell in:
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
Remember to modify only the cell you'll be returning from this method. Other cells might not have exist at that moment.
Alternatively you can keep a weak reference to the cell and update it when needed.
I have set up a ViewController with UICollectionViewCells, inside of a navigation controller. I want to be able to click on the cells and then have the user be taken to a new controller depending on which cell is selected (different controller for each cell). I want the navigation bar to still appear in the new controller, and have a back button that will take the user back to the original ViewController. I have the following code inside the initial view controller to set up the collection view cells:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: playlistCellId, for: indexPath) as! playlistCoverCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
I also register the cells correctly in viewDidLoad. What function do I use to perform an action when selecting a cell?
you have to use:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
let viewController = UIViewController() // or your custom view controller
self.navigationController?.pushViewController(viewController, animated: true)
}
else if indexPath.row == 1 {
// and so on....
}
}
Tells the delegate that the item at the specified index path was
selected. The collection view calls this method when the user
successfully selects an item in the collection view. It does not call
this method when you programmatically set the selection.
you can try UICollectionViewDelegate in the function
enter image description here
you can use indexPath to get elements of the current click;
push to next viewController you have to have navigationViewController, if navigationController is nil, you can try protocol or block. Sorry, my English is not good, maybe grammar is wrong.
I want to set a variable to different string when a certain CollectionView cell is tapped. So cell1 is tapped then var cellTapped = "cell1", cell 2 is tapped then var cellTapped = "cell2" and so on. It was suggested that I
"create a custom cell and hold the value as property and read this
value on didSelectCell()"
but I'm not sure how to do this (newbie).
(Swift 3)
You need to set UICollectionViewDelegate to your ViewController and then implement didSelectItemAt IndexPath that gets called when a cell is tapped.
Something like this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellTapped = "cell" + String(indexPath.row)
}
You could also have an array of Strings and index into the array based on the indexPath.row:
let cellStringValues = ["cell1", "cell2", "cell3", ... , "celln"]
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellTapped = cellStringValues[indexPath.row]
}
Setup your view controller to be the delegate of the UICollectionView. Your view controller should inherit from UICollectionViewDelegate. Then in the viewDidLoad() for the VC set the delegate variable for the UICollectionView to be the ViewController. To catch selection events override the following UICollectionViewDelegate function.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellTapped = "cell\(indexPath.row)"
}
Check out https://www.raywenderlich.com/136159/uicollectionview-tutorial-getting-started for more details on working with collection views