I have prototype cell and in this cell there is an UIButton; so I need to perform a segue from the button to other controller, not from the cell. I tried to create an Outlet #IBOutlet var button: UIButton! and use performSegueWithIdentifier() method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
self.performSegueWithIdentifier("toSecondController", sender: button)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
but I have no idea how to set the prepareForSegue() method... i tried this
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if segue.identifier == "toSecondController"
{
if let indexPath = sender as? UIButton//tableView.indexPathForSelectedRow()
{
let controller = segue.destinationViewController as! CoriViewController
controller.coriSquadra = DataManager.sharedInstance.arrayCori[indexPath.row]
}
}
}
I also read that a solution would be implement a protocol, but I'm a beginner and I need a step by step guide.
Hope someone can help me!
Your on the right track. In your tableviewcontroller define
#IBAction func clicked(){
self.performSegueWithIdentifier("toSecondController", sender: button)
}
You can keep the
tableView.deselectRowAtIndexPath(indexPath, animated: true)
line in your didSelectRowAtIndexPath. Don't forget to connect your ibaction to the button in your storyboard as well.
Related
Every time I try to do the following, it passes on the index to the next view controller as being 0. I'm not sure what I'm doing wrong, can someone help? Thank you so much! Here is the relevant code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "a", for: indexPath) as! TableViewCell
cell.useButton.tag = tagBaseValue + indexPath.row
cell.useButton.addTarget(self, action: #selector(ListViewController.useButtonPressed(_:)), for: UIControlEvents.touchUpInside)
return cell
}
#IBAction func useButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "toDisplay", sender: self)
}
func prepare(for segue: UIStoryboardSegue, sender: UIButton) {
if segue.identifier == "toDisplay" {
let nextVC = segue.destination as! NextVC
nextVC.index = sender.tag
}
}
prepare(for is never called because the signature is wrong (the type of sender is Any?)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDisplay" {
let nextVC = segue.destination as! NextVC
nextVC.index = (sender as! UIButton).tag
}
}
and the IBAction must be
#IBAction func useButtonPressed(_ sender: UIButton) {
performSegue(withIdentifier: "toDisplay", sender: sender)
}
Prepare for segue must be overridden, and in button action while sending sender, you are sending the self, which is pointing to viewcontroller itself, hence you are receiving the tag always as 0.
Make changes for prepare for segue, make sender as Any and do overrride it. And in your button action while calling performSegue do mention sender as sender(which is button) and not self(which is view controller).
Hope it helps...
I have two ViewControllers in the first I created a tableView where I can insert just a text into a cell.
In the SecondViewController I also have a tableView with the same function, bu HOW can I make it that when I click a cell in the first tableView, I can get into a separate SecondTableView (Array).
So I'm this far, but I think the categorize function must be in the didSelectRowAt, when I click a Row.
FirstViewController:
todos: This is a String Array (I want this as the Category)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "hhh"{
//let destination = segue.destination as? UINavigationController
let vc = segue.destination as? SecondViewController
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
//label name
vc?.name = todos[indexPath.row]
self.present(vc!, animated: true, completion: nil)
tableView.deselectRow(at: indexPath, animated: true)
self.performSegue(withIdentifier: "hhh", sender: indexPath)
let indexpath = todos[indexPath.row]
print("indexpath:\(indexpath)")
print("row: \(indexPath.row)")
}
}
In the SecondViewController I have a SecondArray=[String]() these are actually the Todos.
On both ViewControllers I can insert a cell with text but don't know how to pass the data back:=?
In the comments I suggested using a UISplitViewController, as this works well with any kind of master/detail type of app. (In fact, it's an Xcode project type.)
But in case you require using a UINavigationController, here's what I to to "toggle" between two views this way:
FirstViewController:
#IBAction func showFrameVC() {
updateMaskImage()
self.performSegue(withIdentifier: "ShowSecondView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowSecondView" {
if let vc = segue.destination as? FrameViewController {
vc.someValue = someValue
vc.firstVC = self
}
}
}
Basically, pass the instance of the first view controller along with any data you need to pass.
SecondViewController:
var someValue:String!
weak var subjectVC:FirstViewController!
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParentViewController {
firstVC.someValue = someValue
}
}
Here you let the navigation controller do it's thing, which is pop the second view controller off the stack after passing back the data it changed. Since you are maintaining a weak value of the first view controller, you free up that memory when the second controller is released.
In your code, you are presenting your SecondViewController in 2 ways in same function.
self.present(vc!, animated: true, completion: nil)
and
self.performSegue(withIdentifier: "hhh", sender: indexPath)
intention of both lines are same, But 2 way of implementation.
if my thinking is right you don't have to perform segue (second line of code mentioned above and also No prepare for segue method).
I am trying to pass data from a tableview cell into a tab view controller to be used by the tab view controller's corresponding views. However the overriden func prepare method below is not being called and the program goes to the tab view controller bypassing this method totally.
Storyboard showing the views
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let selectedCarIndex = self.savedCarsTableVC.indexPathForSelectedRow?.row
let destVC = segue.destination as! TabBarController
destVC.selectedCar = savedCars[selectedCarIndex!]
}
I am expecting the selectedCar object to return data from the array however it's nil.
I would try setting the selectedCarIndex in tableView didSelectRow method. Also, make sure you want to be navigating to TabBarController and not some ViewController embedded in the TabBar.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
self.performSegue(withIdentifier: "youridentifier", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let selectedCarIndex = sender as! IndexPath
let destVC = segue.destination as! TabBarController //Are you sure this is the correct ViewController to be casting to?
destVC.selectedCar = savedCars[selectedCarIndex.row]
}
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.
I'm trying to pass the value to a second view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "headToDetail" {
let indexPath = self.tableView.indexPathForSelectedRow!.row
print("SELECTED INDEX \(indexPath)")
let destVC = segue.destinationViewController as! SecondViewController
print("Segue TEst \(self.toPass)")
destVC.vieSegue = self.toPass
}
I've remove the didSelectRowAtIndexPath and just going to use the Segue but when I tap on a row cell I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
This is a major update to the original code.
the problem is, that you are creating a new ViewController at line
var destination = SecondViewController()
but then the controller simply deallocates because nothing is pointing to it (there is no reference to it and it falls out of scope). You haven't pushed into it, nor you performed any segue.
How to fix it? Basically there are two options.
Storyboard
If you're working with storyboards, you have to create a segue between the first ViewController and SecondViewController. You have to give it an identifier.
Then in didSelectRowAtIndexPath you will write
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
self.toPass = fruits[row]
performSegueWithIdentifier("YOUR_SEGUE_IDENTIFIER", sender: nil)
}
then in prepareForSegue you'll set the property to the destination SecondViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YOUR_SEGUE_IDENTIFIER" {
let destVC = segue.destinationViewController as! SecondViewController
destVC.vieSegue = self.toPass!
}
}
Manual Push
The second option is to push the controller manually within the didSelectRowAtIndexPath method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
let destination = SecondViewController()
destination.vieSegue = fruits[row]
// if you are using navigation controller
navigationController?.pushViewController(destination, animated: true)
// if you want to present it modally
// presentViewController(destination, animated: true, completion: nil)
}
The second version doesn't force you to create segues.
It's empty because you create a new object...
var destination = SecondViewController()
...pass it your data...
destination.vieSegue = self.toPass!
...and then let it go out of scope without showing it, saving it, or doing anything else useful.