prepareForSegue collectionView indexPath using swift - swift

I don't find any good example to do what I want using swift so I allow myself to ask the question.
Im using a collectionView to display PFObjects and I want to send the displayed cell data to a second controller using prepareForSegue.
At this point, Im struggling to make this part of the code works:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "goto_answerquestion"){
var indexpath : NSIndexPath = self.collectionView.indexPathsForSelectedItems()
}
}
this line:
var indexpath : NSIndexPath = self.collectionView.indexPathsForSelectedItems()
triggers the following error:
(UICollectionView, numberOfItemsInSection: Int)-> Int does not have a member named 'indexPathsForSelectedItems'
please let me know if Im using the wrong method, or if you need additional data to have the appropriate overview of the problem.
ANSWER
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segue_identifier"){
// check for / catch all visible cell(s)
for item in self.collectionView!.visibleCells() as [UICollectionViewCell] {
var indexpath : NSIndexPath = self.collectionView.indexPathForCell(item as CollectionViewCell)!
var cell : CollectionViewCell = self.collectionView!.cellForItemAtIndexPath(indexpath) as CollectionViewCell
// Grab related PFObject
var objectData:PFObject = self.questionData.objectAtIndex(indexpath.row) as PFObject
// Pass PFObject to second ViewController
let theDestination = (segue.destinationViewController as answerPageViewController)
theDestination.questionObject = objectData
}
}
}

If you just are trying to just find the index path of the cell tapped, and not have multiple, you could do this in your prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = collectionView.indexPathForCell(sender as UICollectionViewCell)
// do what you need now with the indexPath
}
sender in this situation is the cell you tapped, so you just need to cast it to UICollectionViewCell (or a custom cell subclass, if you made one).
UPDATE:
Swift 1.2 introduced as! to replace as for this purpose, so to keep with safety, you can try this inside prepareForSegue using multiple bindings:
if let cell = sender as? UICollectionViewCell, indexPath = collectionView.indexPathForCell(cell) {
// use indexPath
}

This may Solve your problem :
var indexPath : NSArray = self.collectionView.indexPathsForSelectedItems()

Related

segue to detailed tableview controller

I am passing data from one view controller to another. The first view controller has a collectionviewcell, and the controller I am segueing to has a tableviewcell. When I click on the cell of the collection view which is also clicking on a user, I want to segue into a detailed view controller that has a list of all the post the user made (which is stored in firebase)
Here is what I have so far: -This is my controller with the collectionviewcell
class WelcomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
var posts = NSMutableArray()
var databaseRef = FIRDatabase.database().reference()
var loggedInUser = FIRAuth.auth()?.currentUser
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData(){
if (FIRAuth.auth()?.currentUser) != nil{
FIRDatabase.database().reference().child("books").observeSingleEvent(of: .value, with: { (snapshot:FIRDataSnapshot) in
let loggedInUserData = snapshot
if let postsDictionary = snapshot .value as? [String: AnyObject] {
for post in postsDictionary {
self.posts.add(post.value)
}
self.CollectionView.reloadData()
}})}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
if segue.identifier == "UsersProfile" {
if let indexPath = sender as? IndexPath{
let vc = segue.destination as! UsersProfileViewController
let post = self.posts[indexPath.row] as! [String: AnyObject]
let posted = self.posts[indexPath.row] as! [NSMutableArray: AnyObject]
let username = post["username"] as? String
let userpicuid = post["uid"] as? String
let userpostuid = post["uid"] as? NSMutableArray
vc.username = username
vc.userpicuid = userpicuid
vc.posts = posts
print(indexPath.row)
}
}}
Right now when I click on the cells I segue into the detailed view controller and it displays all the posts made by all the users in my database and I know it is because in my segue I write vc.posts = posts. I am new to swift and I don't know how I will set it up so that when I click a cell (which is also when I click a user) then a detailed tableviewcontroller shows me all the post, only that user has made
Again, I am trying to only display the post uploaded by indiviual users
Overview:
A collection view displays a list of posts.
Each cell represents a post.
Each cell has a button with the user image when tapped on it, the app segues into UsersProfileViewController.
UsersProfileViewController contains a property posts which represents the posts made by the user.
Question:
How to set the UsersProfileViewController in such a way that it displays only the posts made by the selected user ?
Approach:
Each cell is dequeued and is reused.
When a button is tapped it needs to know which index path the button corresponds to.
Once the indexPath of the cell containing the button is known then we can determine the user associated with it.
Once the user is known filter the posts based on the selected user.
Steps:
Create a subclass of button with indexPath as a property
Use that custom button in your cell
In cellForItemAtIndexPath set the button indexPath
In cellForItemAtIndexPath set the target for the button
When the button is pressed, determine the corresponding username and store it as selectedUserName (view controller property).
In prepareForSegue filter posts based on the selectedUserName
Sample Code:
class CellButton : UIButton {
var indexPath : IndexPath?
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! CustomCollectionViewCell
let post = posts[indexPath.row] as? [String : Any]
cell.backgroundColor = .red
cell.titleLabel.text = post?["title"] as? String
cell.button.setTitle(post?["username"] as? String, for: .normal)
cell.button.addTarget(self, action: #selector(tappedOnUserButton(button:)),
for: .touchUpInside)
cell.button.indexPath = indexPath
return cell
}
#objc private func tappedOnUserButton(button: CellButton) {
guard let indexPath = button.indexPath else {
return
}
print("button pressed - \(indexPath)")
let post = posts[indexPath.row] as? [String : Any]
selectedUserName = post?["username"] as? String
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "UsersProfile" {
//Filter posts based on username
//Assumption: posts contains all the posts.
let postsForUser = posts.filter {
guard let post = $0 as? [String : Any] else {
return false
}
return post["username"] as? String == selectedUserName
}
print("postForUser = \(postsForUser)")
}
}
Preferred Approach:
Create a struct called User (instead of using a dictionary, which is error prone while coding)
Create a struct called Post, Post would have a property called User to map the user - https://itunes.apple.com/us/book/the-swift-programming-language-swift-3-1/id881256329?mt=11
Please learn Swift, each language has some features that you can take advantage of.
Don't force unwrap variables unless you are 100% sure it would contain a non-nil value
Use native types whenever possible (example use Swift Array instead of NSMutableArray)
Please explain your question clearly so that others don't have to spend time trying hard to understand. (Be precise and clear and isolate the problem, and use markdown for formatting)
You can setup segue from collectionViewController to tableViewController in your storyboard, then give it an identifier (very important).
In your collectionViewController, override prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let vc = segue.destinationViewController as? YourTableViewControllerClassName {
// assign data you want to pass here
}
}
Then in your collectionView:didSelectItemAt method, call performSegueWithIdentifier("yourID", sender: self)
Hope this helps!

Swift prepare for segue - Send data from one view controller to another

I want to move from one view controller to another and send userId:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("chosenPerson", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destinationViewController as? chosenPersonViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()!
let indexPath = indexPaths[0] as NSIndexPath
chosenPerson!.userID = self.usersArray[indexPath.row].userId
}
by clicking I get: "fatal error: unexpectedly found nil while unwrapping an Optional value"
what I do wrong?
If you have given segue in StoryBoard DON'T Call this self.performSegueWithIdentifier("chosenPerson", sender: self) method in didSelectItem
If you have given segue in storyboard override func prepareForSegue - this method calls first after that didSelectItem calls
Please refer storyBoard once (below Image for Sample)
I think problem is in self.usersArray[indexPath.row].userId May this returns nil
Swift2:
self.performSegueWithIdentifier("chosenPerson", sender: self)
Swift3:
self.performSegue(withIdentifier: "chosenPerson", sender: self)
Swift2:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destinationViewController as? chosenPersonViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()!
let indexPath = indexPaths[0] as NSIndexPath
chosenPerson!.userID = self.usersArray[indexPath.row].userId //May it found nil please re - check array values
}
Swift3:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destination as! chosenPersonViewController
if let indexPath = collectionView.indexPathForSelectedItem {
chosenPerson!.userID = self.usersArray[indexPath.row].userId //May it found nil please re - check array values
}
}
}
When performing the segue, pass the indexPath as the sender and try using this switch statement. If you see "unknown segue" printed out when you select a cell, the destination controller is not of type chosenPersonViewController.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("chosenPerson", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch (segue.destinationViewController, sender) {
case (let controller as chosenPersonViewController, let indexPath as NSIndexPath):
controller.userID = usersArray[indexPath.row].userId
default:
print("unknown segue")
break
}
}
One possible issue could be that in your storyboard you have not set the identifier for your segue to be "chosenPerson". To fix this first make sure you have a segue between your first and second view controllers, which can be created by control dragging one from one view controller to another.
Then make sure the segue has an identifier, specifically: "chosenPerson". To do this: click on the segue between the first two view controllers, then navigate to the attributes tab, and then set the identifier box to be "chosenPerson". Save the storyboard with your changes and now you should be able to call prepareForSegue with that identifier and not experience a fatal error.

How to Push ViewController in UITableViewCell's #IBAction func

I have a UITableViewController and UITableViewCell and Cell's XIB Files.
My question is how to push UIViewController from cell?
in UITableViewCell.
#IBAction func pushBtnTapped(sender: AnyObject) {
// navigate to post view controller
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("GuestCollectionVC") as! GuestCollectionVC
self.navigationController?.pushViewController(guest, animated: true)
}
I got an error message. self.storyboard is not UITableCell's member.
I know there is another way to implement this. But I have to implement in TableViewCell.
Ideally you'd push from the table view controller, in the didSelectRowAtIndexPath method call. Why do you say you have to implement it in the cell? There are other ways you could do that but from what you're saying this might be the best shot...
self.performSegueWithIdentifier("YourSegueId", sender: nil)
Then in prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//get the index path of the selected row
let indexPath = self.tableView.indexPathForSelectedRow
//just an example if you want to pass something to the other vc
let item = self.responseItems[indexPath!.row]
if segue.identifier == "YourSegueId" {
YourVC = segue.destinationViewController as! YourVC
YourVC.item = item
}
}
If you want to do it from the button you could do something like this in the prepareForSegue method... now you have the button and its index path
let button = sender as! UIButton
let view = button.superview
let cell = view?.superview as! YourCell
let indexPath = tableView.indexPathForCell(cell)

how can I pass data using prepareForSegue in TableView?

I get an error trying to set indexPath: initializer for conditional binding must have optional type
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
if segue.identifier == "toStation" {
let detailViewController = segue.destinationViewController
as! toStation
if let indexPath = tableView.indexPathForSelectedRow! {
detailViewController.selectedStation = stationNameArray[indexPath.row]
}
}
}
if let indexPath = tableView.indexPathForSelectedRow!
You are binding indexPathForSelectedRow to indexPath view an if let statement to unwrap the optional indexPathForSelectedRow. This is a good approach. But at the same time you are using the bang statement ! to unwrap the optional. Effectively you are unwrapping the optional twice. Either use ! or an if let statement.
You receive this error because tableView.indexPathForSelectedRow! is no longer an optional value (you've unwrapped it). To fix this error, try removing the force unwrap operator ! at the end of if let.
if let indexPath = tableView.indexPathForSelectedRow { ... }
According to your question subject, I think you are trying to pass Indexpath into prepareForSegue, in your case, you can use didSelectRowAtIndexPath to pass a sender:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("toStation", sender: indexPath);
}
then in prepareForSegue :
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "toStation") {
let detailViewController = segue.destinationViewController
as! toStation
let row = (sender as! NSIndexPath).row; //we know that sender is an NSIndexPath here.
detailViewController.selectedStation = stationNameArray[row]
}
}
Hope this solve your problem.

I want to send data when I tap a button in tableView Cell

I am implementing a commentView for my app. I have a main view which is tableview contains picture and a button to go comment view.
I want that when user tap comment button in table view, view shows comment view and pass PFObject by prepareforSegue method.
now comment button works, but I have an error from prepareforsegue
here is my code.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "mainToComment") {
let destViewController : CommentVC = segue.destinationViewController as! CommentVC
destViewController.parentObjectID = parentObjectID
let selectedRowIndex = self.tableView.indexPathForSelectedRow
destViewController.object = (postsArray[(selectedRowIndex?.row)!] as? PFObject)
and here is my how my button works.
#IBAction func commentButtonTapped(sender: AnyObject) {
let button = sender as! UIButton
let view = button.superview!
let cell = view.superview as! MainTVCE
let indexPath = tableView.indexPathForCell(cell)
parentObjectID = postsArray[(indexPath?.row)!].objectId!!
when I debug, selectedRowIndex has no value(nil)
I think it cause of I tap button instead of cell.
How can I set indexPath for this?
or
How can I make it work?
I don't know name of your main TableViewCell view controller. Assume that, I name this view controller is MainTableViewCell.
I create a closure in MainTableViewCell:
var didRequestToShowComment:((cell:UITableViewCell) -> ())?
When button comment is tapped:
#IBAction func commentButtonTapped(sender: AnyObject) {
self.didRequestToShowComment?(self) // self is this UITableViewCell
}
In table cellForRowAtIndex... of your main view controller.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
mainTableViewCell.didRequestToShowComment = { (cell) in
let indexPath = tableView.indexPathForCell(cell)
let objectToSend = postsArray[indexPath.row] as? PFObject
// Show your Comment view controller here, and set object to send here
}
...
return cell
}