How to transition to a new controller when clicking on UICollectionViewCell (programatically) - ios

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.

Related

How to show ViewController from custom UITableViewCell

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.

Which item was selected in a UICollectionView, if there 're multiple UICollectionViews

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?

How to prevent dequeued collection view cells from overlapping each other?

I've been trying to set up a collection view where I have the user submit several strings which I toss in an array and call back through the collection view's cellForItemAt function. However, whenever I add a row to to the top of the collection view, it adds the cell label literally on top of the last cell label so they stack like this. Notice how every new word I add includes all the other previous words in the rendering.
The code I have at cellForItemAt is
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InterestsCell", for: indexPath) as? InterestsCell {
cell.interestLabel.text = array[indexPath.row]
return cell
} else {
return UICollectionViewCell()
}
}
and the code I have when the add button is pressed is
func addTapped() {
let interest = interestsField.text
array.insert(interest!, at: 0)
interestsField.text = ""
collectionView.reloadData()
}
I'm not sure what's going on. I looked everywhere and tried to use prepareForReuse() but it didn't seem to work. I later tried deleting cells by calling didSelect and the cells would not disappear again. Any help is appreciated!
This is the code I have in my custom collection view cell implementation in the event that this is causing the error
To do this paste these functions in your project
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
you can play around with the values :)
This can be easily implemented using UITableView. So try to use UITableView instead of UICollectionView.
It looks like you're adding a new label every time you set the text. If you want to lazily load the UILabel you need to add lazy
lazy var interestLabel: UILabel = {
...
}()
I wouldn't do it this way though, I would create a reference to the label
weak var interestLabel: UILabel!
Then add the label in your setupViews method
func setupViews() {
...
let interestLabel = UILabel()
...
contentView.addSubview(interestLabel)
self.interestLabel = interestLabel
}

Change Variable based on Cell Clicked

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

List of Images like Instagram

I want to make a scrollable list of images like instagram with 4 columns. I created a collection view with image views http://prntscr.com/d15rnx . But I get this result - http://prntscr.com/d15tsq
code -
// MARK: - UICollectionViewDataSource protocol
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.imageView.image = UIImage(named: "main/p\(self.items[indexPath.item].code)/main/main.jpg")
print("main_card_images/p\(self.items[indexPath.item].code)/main/main")
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
print(self.items[indexPath.item])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let yourNextViewController = (segue.destination as! ViewControllerCard)
let indexPath = collectionView.indexPath(for: sender as! UICollectionViewCell)
yourNextViewController.mainCardImage = self.items[(indexPath?.item)!]
}
Make sure you have set inter cell spacing to 0 and then all you have to do is implement:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return(collectionView.bounds.size.width/4,collectionView.bounds.size.height/4)
}
This will resize all cells to be equal to 1/4 the UICollectionView width and thus create 4 cells in each row(1/4 the height value is just cause i like symmetry in these types of UI). If there appears to be any less number of cells than that, its because the inter-item spacing has not been set to 0. If you want inter-item spacing, just subtract that value from the width value returned in the above function
Alternatively
You can implement your own custom flow layout but the above solution is far more simpler.
I have found it easiest to configure my collection view via the setting in Interface Builder. For a collection view with a flow layout here are the settings I use for the insets. I have found the item size width: 125.8 and height: 125.8 to give me the best size for displaying on most devices in portrait or landscape.
Here's what my collection view looks like.
You can of course set this programmatically using UICollectionViewDelegateFlowLayout or as Rikh suggests by subclassing CollectionViewFlowLayout

Resources