I want to make it so that when I tap on a CollectionView Cell, it segues to another view. I also want to pass a user ID to this view so I can query the database. This is what I have implemented so far:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! UserCell
print(user[indexPath.row].imagePath!)
cell.userImage.sd_setImage(with: URL(string: user[indexPath.row].imagePath!))
cell.nameLabel.text = user[indexPath.row].username
cell.userID = user[indexPath.row].userID
let destinationVC = ProfileViewController()
destinationVC.sentUserID = user[indexPath.row].userID!
// Let's assume that the segue name is called playerSegue
// This will perform the segue and pre-load the variable for you to use
//destinationVC.performSegue(withIdentifier: "toProfileFromSearch", sender: self)
cell.addButtonTapAction = {
// implement your logic here, e.g. call preformSegue()
self.performSegue(withIdentifier: "toProfileFromSearch", sender: self)
}
//cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
//checkFollowing(indexPath: indexPath)
return cell
}
This is in the individual creation of each cell. When I click the user profile in the search area, nothing happens.
I followed other stack overflow questions to get this far. Can anyone make a complete solution for other people that need this too?
You can use the didSelectItemAt function, check this
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let VC1 = self.storyboard!.instantiateViewController(withIdentifier: "IDYOURVIEW") as! ProfileViewController
VC1.sentUserID = user[indexPath.row].userID!
self.navigationController?.pushViewController(VC1, animated: true)
}
If you're using segues
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "toProfileFromSearch", sender: user[indexPath.row].userID!)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toProfileFromSearch"
{
let vc = segue.destination as? ProfileViewController
if let id = sender as! String
{
vc?.sentUserID = id
}
}
}
There is another better way to handle this using protocol
protocol UserCellTapedDelegate: AnyObject {
func didTapButton(_ cell: UserCell, didSelectItemAt id: Int)
}
then your cell would be like:
class UserCell: UICollectionViewCell {
public var userID: Int!
public weak var delegate: UserCellTapedDelegate?
#objc private func addButtonTapAction() {
delegate?.didTapButton(self, didSelectItemAt: userID)
}
}
userID: to store the user ID.
delegate: use an optional weak (in terms of memory management) delegate reference in your cell.
addButtonTapAction func to handle target of the button/or tap gesture on Any UIView.
The next step is to go back to your UIViewController and conform to the custom delegate:
extension ViewController: UserCellTapedDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! UserCell
...
cell.userID = user[indexPath.row].userID!
cell.delegate = self
return cell
}
func didTapButton(_ cell: UserCell, didSelectItemAt id: Int) {
self.performSegue(withIdentifier: "toProfileFromSearch", sender: id)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toProfileFromSearch" {
let vc = segue.destination as! ProfileViewController
if let id = sender as! String {
vc.sentUserID = id
}
}
}
}
Hope this helps you 😊
Related
I have VC(a) with TableView
Inside the UITableViewCell.xib I installed a UICollectionView
UICollectionViewCell registered inside the UITableViewCell.xib of
course.
I want to perform segue from VC(a) to VC(b) when UICollectionViewCell didSelected
And since the UICollectionViewCell is registered inside the
UITableViewCell.xib so I can't use performSegue(withIdentifier:
String, sender: Any) inside the .xib file because it is not
inherited from UIViewController
I did some searches in StackOverFlow and I found methods to perform segue by creating a custom delegator protocol I applied the protocol and everything seems OK except the sender!!
I know how to use it inside VC without protocol but I don't know how to use it after applying the protocol
I'm new to Swift Language and so please help me to complete my code
Here below is the UITableViewCell.xib code
protocol MyCustomCellDelegator {
func cellWasPressed()
}
class AdminPList_TableViewCell: UITableViewCell {
var delegate: MyCustomCellDelegator?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setupCell()
getFProducts()
}
#IBOutlet weak var CollectionView: UICollectionView!
func getFProducts() {
productAPI.GetAllproducts { (appendThisProduct) in
self.FP_Array.append(appendThisProduct)
self.CollectionView.reloadData()
}
}
var FP_Array : [productObject] = []
}
extension AdminPList_TableViewCell : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func setupCell() {
CollectionView.delegate = self ; CollectionView.dataSource = self
CollectionView.register(UINib(nibName: "FproductsCell", bundle: nil), forCellWithReuseIdentifier: "FPcell")
// CollectionView.register(UINib(nibName: "TproductsCell", bundle: nil), forCellWithReuseIdentifier: "TPcell")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.CollectionView.frame.size.width-10, height: self.CollectionView.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { // make spacing between each cell
return 10
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return FP_Array.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let FPcell = CollectionView.dequeueReusableCell(withReuseIdentifier: "FPcell", for: indexPath) as! FproductsCell
FPcell.UpdateFP_cell_Content(RetrivedProducts: FP_Array[indexPath.row])
return FPcell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedProduct = FP_Array[indexPath.row] // I want to pass it to sender in perform segue un ProductsLibrary Class
// print (selectedProduct.productName)
self.delegate?.cellWasPressed()
}
}
Help me to pass selectedProduct to perform segue in below protocol
And here below is the VC(a) where the UITableView is installed:
class ProductsLibrary : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupCell()
}
#IBOutlet weak var TableView: UITableView!
}
extension ProductsLibrary : UITableViewDelegate, UITableViewDataSource, MyCustomCellDelegator {
func cellWasPressed(withData: productObject) {
performSegue(withIdentifier: "EditProduct", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let next = segue.destination as? ProductManagement{
print ("Editing Product is Active")
let product = sender as? productObject
print(product) //it shows nil !!!
next.EditingProduct = product
}
}
func setupCell() {
TableView.delegate = self ; TableView.dataSource = self
TableView.register(UINib(nibName: "AdminPList_TableViewCell", bundle: nil), forCellReuseIdentifier: "PLcell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PLcell", for: indexPath) as! AdminPList_TableViewCell
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.TableView.frame.size.height/3
}
}
When I started learning the delegation pattern I always made some mistakes. So I make a rule for myself. When you have to implement a delegate always remind 6 steps.
The First 3 steps are for the class that will pass the data or write protocol, here your AdminPList_TableViewCell is that class. The 3 steps are
Write a protocol (a bunch of method declaration) // you did it.
Declare a protocol variable inside the class that will pass the data. // you did that too (var delegate: MyCustomCellDelegator?)
Call the methods you declared. // you did that (self.delegate?.cellWasPressed())
The Last 3 steps are for the class that will conform that protocol, here ProductsLibrary is that class.
Conform that protocol in that class which will implement those methods. // here you did that (extension ProductsLibrary: ..., MyCustomCellDelegator)
Assign delegate to self, but what is self here? well, self is the ProductsLibrary which has been delegated! // you missed that
Implement the protocol methods. // you did that :)
How to solve this?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.delegate = self // you missed this line
...
return cell
}
Now for passing selectedProduct just change the protocol method definition everywhere.
protocol MyCustomCellDelegator {
func cellWasPressed(withData: productObject)
}
Then call from the didSelectItemAt method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedProduct = FP_Array[indexPath.row] // I want to pass it to sender in perform segue un ProductsLibrary Class
self.delegate?.cellWasPressed(withData: selectedProduct)
}
Now use it inside the method body.
func cellWasPressed(withData data: productObject) {
//now use the data and write your necessary code.
performSegue(withIdentifier: "EditProduct", sender: self)
}
Hope it will help you :).
how are you?
Maybe your delegate is nil, and thats why you func callWasPressed() didn't called.
You need to set delegate on VC after instantiate your cell.
Like this:
AdminPList_TableViewCell.delegate = self
I hope it helped you!
Happy coding =D
To pass object through other VC you need to follow these 3 steps:
1st: Create a var on your new VC:
var selectedObject: YourKindObject?
2nd: Pass your object to sender like this:
func cellWasPressed(withData data: productObject) {
// pass productObject as sender
performSegue(withIdentifier: "EditProduct", sender: productObject)}
3rd: On your override method, you should capture your segue ID and passthroug object, like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "YourID(Can be setted on storyboard segue identifier))" {
var object = sender as! YourKindObject
let controller = segue.destination as? VC
controller?. selectedObject = object
}
}
I have here my collection view controller and when I click on the collection view cell it is updating between true and false but my label is not updating from added to not added. any help would be greatly appreciated I have been at this for 4 days. I have tried reading the apple documentation and it doesn't give examples as to how to accomplish this with using a boolean value. What I want to happen is for the Label atop my collection view cell label to change from added to not added when the collection view cell has been pressed. I know it is working because when I tested it with print statements when I press the collection view cell it changes back and forth from true to false. I am just having trouble with the label changing from added to not added with it.
import UIKit
class ShoppingListCollectionViewController: UICollectionViewController {
var shoppingItemController = ShoppingItemController()
var shoppingItemCollectionViewCell = ShoppingItemCollectionViewCell()
override func viewDidLoad() {
super.viewDidLoad()
shoppingItemCollectionViewCell.updateViews()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShoppingListDetailSegue" {
guard let shoppingListDetailVC = segue.destination as? ShoppingListDetailViewController else {
return
}
}
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
// MARK: UICollectionViewDataSource
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return shoppingItemController.shoppingItems.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ShoppingItemCell", for: indexPath) as? ShoppingItemCollectionViewCell else {
fatalError("Collection view cell identifier is wrong or the cell is not a ShoppingItemCollectionViewCell")
}
// Configure the cell
let shoppingListItem = shoppingItemController.shoppingItems[indexPath.item]
cell.imageView.image = shoppingListItem.image
cell.shoppingItemLabel.text = shoppingListItem.imageName
if shoppingListItem.added == true {
cell.hasBeenAddedLabel.text = "Added"
} else {
cell.hasBeenAddedLabel.text = "Not Added"
}
return cell
}
// MARK: UICollectionViewDelegate
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
var chosenItem = shoppingItemController.shoppingItems[indexPath.item]
chosenItem.added = !chosenItem.added
shoppingItemController.shoppingItems[indexPath.item] = chosenItem
if chosenItem.added == true {
chosenItem.updateViews()
}
print(chosenItem.added)
}
You only need to get the cell from cellForItem(at:) method inside the didSelectItemAt method and update the label text to your desired string:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ShoppingItemCollectionViewCell
var chosenItem = shoppingItemController.shoppingItems[indexPath.item]
chosenItem.added.toggle()
cell.label.text = chosenItem.added ? "Added" : "Not Added"
}
So I have a main ViewController that has Horizontal Scrolling CollectionViews inside a TableView. When you click on an Item in the CollectionView it transfers all the data from the Item to a new ViewController to display it properly.
The problem I am facing is when you click on the item in the CollectionView a segue is performed that transfers data to an array in the new ViewController but instead of transferring only data from the particular item it send data from more than one item. On some debugging I also found that the items Custom class is what loads the multiple elements in the array and not the segue. Here is some code.
cellForRowAt in main VC
if let cell = tableView.dequeueReusableCell(withIdentifier: "HeadlineRow", for: indexPath) as? HeadlineRow {
cell.latest = self.latest
cell.collectionView.reloadData()
cell.delegate = self
return cell
}
Segue Code in main VC
func pushToNewsVC(latestNews: [LatestNews]) {
self.transferToNewsVC = latestNews
performSegue(withIdentifier: "NewsVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "NewsVC" {
if let destination = segue.destination as? NewsVC {
destination.latestNews.append(contentsOf: self.transferToNewsVC)
destination.SuggestedLatestNews.append(contentsOf: self.latest)
}
}
}
Headlinerow which is TableViewCell class
var latest = [LatestNews]()
var transferData = [LatestNews]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return latest.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HeadlineCell", for: indexPath) as? HeadlineCell {
let latest = self.latest[indexPath.row]
cell.updateUI(latest: latest)
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? HeadlineCell {
self.transferData.removeAll()
self.transferData = cell.transferData
delegate?.pushToNewsVC(latestNews: self.transferData)
}
}
HeadlineCell which is CollectioViewCell class
var transferData = [LatestNews]()
func updateUI(latest: LatestNews) {
storyTextView.text = latest.headline
self.thumbnail.sd_setImage(with: URL(string: "\(latest.app_img)"))
self.transferData.append(latest)
}
So I have a TableViewController with a CollectionView and a custom Cell, but I want to prepare a segue from that CollectionView to a TableView.
I tried this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "drawSegue" {
let dst = segue.destination as! DrawerViewController
guard let indexPath = cupCollectionView.indexPathsForSelectedItems else{
return
}
dst.cup = cupboards[indexPath.row] as! Cupboard
}
}
But I get an error Value of type '[IndexPath]' has no member 'row'. Is there another way to do that? Cause in another project I used this from a tableView to a tableView and it worked...
This is my CollectionView:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cupboards.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cupCell", for: indexPath) as! CupboardCollectionViewCell
let cupboard = cupboards[indexPath.row]
cell.cupLabel.text = cupboard.name
return cell
}
Thank you in advance
It looks like cupCollectionView.indexPathsForSelectedItems is returning an array of IndexPath objects, so instead of accessing a single IndexPath.row you're trying to call Array<IndexPath>.row which doesn't exist
In your didSelectCell when you call the performSegue, add your cell as the sender, then you can change it to let indexPath = cupCollectionView.indexPath(for: sender)
i have downloaded set of users photo from firebase and view it as collection view (instagram like)
but im trying to enlarge the photo clicked in another view controller using segue but its not working.
Any thoughts about my code:
class ProfileViewController: UIViewController{
var photoThumbnail: UIImage!
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! ProfileCollectionViewCell
cell.imageCollection.downloadImage2(from: currPosts[indexPath.row].photoUrl)
cell.imageCollection.image = photoThumbnail
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "photoViewSegue", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var destViewController : EnlargePhotoViewController = segue.destination as! EnlargePhotoViewController
destViewController.labelText = "test" //**** this works
destViewController.myImage = photoThumbnail //**** this doesnt work
}
}
and the collection view cell is:
class ProfileCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageCollection: UIImageView!
}
lastly the target viewController:
class EnlargePhotoViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var enlargedPhoto: UIImageView!
var labelText = String()
var myImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
myLabel.text = labelText
enlargedPhoto.image = myImage
}
}
Try to change to this code:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ProfileCollectionViewCell
photoThumbnail = cell.imageCollection.image
performSegue(withIdentifier: "photoViewSegue", sender: nil)
}
your only goal is to get the image of the selected cell, this can be done more efficiently. All you have to do is read/write to the cells imageView property, photoThumbnail is never set so always nil, just modify this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! ProfileCollectionViewCell
cell.imageCollection.downloadImage2(from: currPosts[indexPath.row].photoUrl)
//cell.imageCollection.image = photoThumbnail <-- No need of this
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// get the correct cell on select
if let cell = collectionView.cellForItem(at: indexPath) as! ProfileCollectionViewCell
{
photoThumbnail = cell.imageCollection.image <-- finally assign the imageview to image
performSegue(withIdentifier: "photoViewSegue", sender: self)
}
}
for safer retrieve the image, use like
override func viewDidLoad() {
super.viewDidLoad()
if let getImage = myImage
{
myLabel.text = labelText
enlargedPhoto.image = getImage
}
}
First View Controller
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0
{
let objstory = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
objstory.strd = imageView.image
_ = self.navigationController?.pushViewController(objstory, animated: true)
}
}
SecondViewController
var strd:UIImage!
#IBOutlet weak var imgprof: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imgprof.image = strd
// Do any additional setup after loading the view.
}
class ProfileViewController: UIViewController{
var photoThumbnail: UIImage!
var selectedPhotoURL: NSUrl!
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection_cell", for: indexPath) as! ProfileCollectionViewCell
cell.imageCollection.downloadImage2(from: currPosts[indexPath.row].photoUrl)
cell.imageCollection.image = photoThumbnail
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedPhotoURL = currPosts[indexPath.row].photoUrl
performSegue(withIdentifier: "photoViewSegue", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var destViewController : EnlargePhotoViewController = segue.destination as! EnlargePhotoViewController
destViewController.labelText = "test" //**** this works
//make image from selected url or pass url and download it in EnlargePhotoViewController
let downlodedImage = yourMethodToDownload(selectedPhotoURL)
destViewController.myImage = downlodedImage //**** this will work work
}
}