Swift - How an IBACTION performs segue - ios

I can pass data (authorID) to a view using DidSelectRowAtIndexPath successfully.
Now I try to perform the same segue with an Button IBACTION, But I couldn't find a way to perform it. I couldn't find a way to use NSIndexPath inside INACTION.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("ArticleSegue", sender: indexPath)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ArticleSegue"{
let toView = segue.destinationViewController as! ArticleTableViewController
let indexPath = sender as! NSIndexPath
let authorid = articleList?[indexPath.row]["author_id"].int!
toView.authorid = authorid
}
#IBAction func favButtonDidTouch(sender: AnyObject) {
//???
}

One way you could solve it, as you just need the row of the cell (here indexPath.row for your articleList), is to give your favButtons a tag (if you don't use the UIView-tags for something else).
This means when you configure your cell in tableView:cellForRowAtIndexPath: you just say:
cell.favButton.tag = indexPath.row
then in your IBAction, create an NSIndexPath with that tag and call the segue:
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
self.performSegueWithIdentifier("ManualSegue", sender: indexPath)
Make sure the sender in this case is an UIView (here UIButton).

If you have defined the segue as a manual segue between two view controllers, you can just call
performSegueWithIdentifier("ManualSegue", sender: self)
Another possibility is that you define the segue from the button itself to the target view controller. In that case, it happens automatically; you need neither an IBAction nor a call to performSegueWithIdentifier.

there's a method for it
performSegueWithIdentifier("ArticleSegue", sender:self)

Assign the Row to the tag of the UIButton in your
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// Setup cell etc..
....
cell.favButton.tag = indexPath.row
...
return cell
}
Then in your action method, note the change in the method from AnyObject to UIButton! so you can access the tag property.
#IBAction func favButtonDidTouch(sender: UIButton!) {
performSegueWithIdentifier("ArticleSegue", sender:NSIndexPath(forRow: sender.tag, inSection: 0))
}

Related

How to uniquely identify each cell in UITableView

I have a UITableView and I am trying to make a segue to another viewcontroller, I need the row number so that I can select a string from an array so that I can display this string in the next view controller, I have this code at the moment.
let tableFrontView = segue.destination as! FCTableFrontViewController
tableFrontView.frontText = path[FlashCardsTableViewCell.init().tag].flashCardFront
the FlashCardsTableViewCell.init().tag is currently returning an int for testing purposes though I am wanting to know what I can replace it with to get me the number of the row which was selected by the user.
Thanks
You can try like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let tableFrontView = segue.destination as! FCTableFrontViewController
let selectedIndexPath = tblView.indexPathForSelectedRow
let selectedRow = (selectedIndexPath?.row)!
print(selectedRow)
}
I assume you are writing the code snippet shown in prepare(for:)?
If that's the case, go to where you perform the segue, which is likely in the didSelectedRowAtIndexPath delegate method. If you don't have such a method, you should implement it.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showTableFrontVC", sender: nil)
}
Replace whatever it is that you are passing as sender now, and replace that with indexPath.row.
performSegue(withIdentifier: "showTableFrontVC", sender: indexPath.row)
Now in prepare(for:), you can unwrap sender as an Int:
let tableFrontView = segue.destination as! FCTableFrontViewController
let rowSelected = sender as! Int
tableFrontView.frontText = path[rowSelected].flashCardFront
Use TableView's delegate method :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// indetify cell using indexPath.row attribute
}

segue not called immediately

I have a view controller A and it has a table list created dynamically with cells
when calling didselectcell as
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if(cell?.tag == 1){
performSegueWithIdentifier("profile", sender: self)
print("perform segue for favourite")
}
and i am calling prepare for segue like this and saving the variable the variable is available in 2nd view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue == "profile"){
let vc = segue.destinationViewController as! MembersViewController
vc.title = " "
}
when i run the app it takes hardly more than 25 sec [ the second view controller has collectionview] any idea of doing it faster
I solved this by calling dispatch_async function it moves immediately to next page and the next page also has for loop in viewwillappear thanks #rakeshbs got answer from this link
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if(cell?.tag == 1){
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("profile", sender:self)
})
}
There are a few things wrong with this. Change your didSelectRow method to this.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if(indexPath.row == 0) {
performSegueWithIdentifier("profile", sender: nil)
}
}
You can fix your didSelectRowAtIndexPath method to remove needing to grab the cell. It has a reference to the cell's index already, which is what you were grabbing.
Also, your prepareForSegue method can be improved as follows:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "profile") {
let vc = segue.destinationViewController as? MembersViewController
vc?.title = "New Title"
}
}
You should never need to call dispatch_async with something like this, especially in Swift, unless you're already on a Background thread.
Before using the UITableView object and its UITableViewDelegate and UITableViewDataSource components, I suggest you muck about in an Xcode Playground, and read the Apple Documentation carefully to understand how everything works.

Swift tables click and pass information

I have a table in iOS. How to know what cell the user has clicked and pass information? I have been searching and I could find prepareForSegue. Is this the right method?. All the cases I could find were complicated and with a lot of elements. Can anyone apply to this simplified case and explain in a simple way, please. I am learning and for me is hard to understand this part.
let favoriteThings = [
"First",
"Second",
"Third",
]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.favoriteThings.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// create a new instance of UITableViewCell. I give the name "cell" in Attributes > Identifier:
let cell = tableView.dequeueReusableCellWithIdentifier("FavoriteThingCell") as! UITableViewCell
var favoriteThingForRow = self.favoriteThings[indexPath.row]
cell.textLabel?.text = favoriteThingForRow
return cell
}
// How to know what cell was clicked and pass the right information? Is this the right method?:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// the second screen. I select the icon of View Controller and Attributes Inspector > Class and Storyboard ID is: DetallViewController
var secondScene = segue.destinationViewController as! DetallViewController
if let indexPath = self.tableView.indexPathForSelectedRow() {
let selected = favoriteThings[indexPath.row]
}
}
The usual way is to implement the table view delegate method tableView:didSelectRowAtIndexPath:. It's called by the runtime engine when the user taps a cell. In the method you can call performSegueWithIdentifier:sender: and pass the NSIndexPath instance as parameter sender.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("MyIdentifier", sender: indexPath)
}
The method prepareForSegue:sender: is also called automatically right before the segue is performed to be able to setup things. As you have the selected index path you can retrieve the appropriate datasource item
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// the second screen. I select the icon of View Controller and Attributes Inspector > Class and Storyboard ID is: DetallViewController
var secondScene = segue.destinationViewController as! DetallViewController
let indexPath = sender as! NSIndexPath
let selected = favoriteThings[indexPath.row]
}

Push segue from UITableViewCell to ViewController in Swift

I'm encountering problems with my UITableViewCells. I connected my UITableView to a API to populate my cells.
Then I've created a function which grabs the indexPath.row to identify which JSON-object inside the array that should be sent to the RestaurantViewController.
Link to my Xcode Project for easier debugging and problem-solving
Here's how my small snippet looks for setting the "row-clicks" to a global variable.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
i = indexPath.row
}
And here's my prepareForSegue() function that should hook up my push-segue to the RestaurantViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let navigationController = segue.destinationViewController as UINavigationController
let vc = navigationController.topViewController as RestaurantViewController
vc.data = currentResponse[i] as NSArray
}
}
And here's how I've set up my segue from the UITableViewCell
Here's my result, I've tried to click every single one of these cells but I won't be pushed to another viewController...I also don't get an error. What is wrong here?
Tried solutions that won't work
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let vc = segue.destinationViewController as RestaurantViewController
//let vc = navigationController.topViewController as RestaurantViewController
vc.data = currentResponse[i] as NSArray
}
}
The problem is that you're not handling your data correctly.
If you look into your currentResponse Array, you'll see that it holds NSDictionaries but in your prepareForSegue you try to cast a NSDictionary to a NSArray, which will make the app crash.
Change the data variable in RestaurantViewController to a NSDictionary and change your prepareForSegue to pass a a NSDictionary
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = redditListTableView.indexPathForCell(cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destinationViewController as RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
For Swift 5
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = self.tableView.indexPath(for: cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destination as! RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
The following steps should fix your problem. If not, please let me know.
Remove your tableView(tableView, didSelectRowAtIndexPath:) implementation.
Make data on RestaurantViewController have type NSDictionary!
Determine the selected row in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = tableView.indexPathForCell(cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destinationViewController as RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
Dropbox link to stack3 directory
I am having difficulty understanding why your software is much different than a standard 2 level tableview structure. So I coded a short example which you can access from this link. I have also included the sources code below.
The program mimics what you have (as best as I understood it). Table Controller 1 segues to Table Controller 2 from the tableview cell. I had no issues with segue-ing. Notice that I do not have nor need to augment the Storybook to initiate the segue.
I have embedded both the controllers in Navigation Controllers. My experience is that it saves a lot of effort to set up the navigation.
Alternately, I could have control-dragged from the first TableViewController symbol on top of the screen to the second controller and set up the segue.
I used a global variable (selectedRow) although it is not a recommend practice. But you just as easily use the prepareForSegue to set a variable in the RestaurantTableViewController (I show an example)
Finally, I recommend checking the Connections Inspector (for the table view cell in the first controller) to confirm that there is a segue to the second controller. If you control-dragged properly there should be confirmation prompt as well as an entry in the Connections Inspector.
Unfortunately I just cant get the code properly formatter
import UIKit
var selectedRow = -1
class TableViewController: UITableViewController {
var firstArray = ["Item1","Item2","Item3","Item4"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return firstArray.count
}
let nameOfCell = "RestaurantCell"
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = firstArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = segue.destinationViewController as RestaurantTableViewController
// can write to variables in RestaurantTableViewController if required
vc.someVariable = selectedRow
}
}
import UIKit
class RestaurantTableViewController: UITableViewController {
var secondArray = ["Item 2.1", "Item 2.2", "Item 2.3", "Item 2.4"]
var someVariable = -1
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return secondArray.count
}
let nameOfCell = "RestaurantCell"
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = secondArray[indexPath.row]
if indexPath.row == selectedRow {
cell.textLabel!.text = cell.textLabel!.text! + " SELECTED"
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
}
I noticed that in your screenshot of your storyboard, the segue is connecting the first prototype cell to the RestaurantViewController. This prototype cell looks like it's the "Basic" style of cell with a disclosure indicator accessory on the right. But look at the screenshot of your app running. The table is being populated with cells that appear to be the "Subtitle" style of cell without a disclosure indicator accessory on the right.
The reason that your segue is never firing no matter what you do is that the segue is only configured to work for a specific prototype cell, but that prototype cell is never being used when you populate the table. Whatever you're doing in tableView:cellForRowAtIndexPath:, you're not using the prototype cell that you want.
#Starscream has the right idea dequeueing the right cell with the right identifier and matching it with the identifier of the prototype cell in Interface Builder. The crash that you're getting even after doing that might be because of the previous problem mentioned in the comments above. Your segue in the storyboard is clearly pointing to a UITableViewController. Your code in prepareForSegue:sender: should be let vc = segue.destinationViewController as RestaurantViewController, as long as RestaurantViewController is a subclass of UITableViewController. You'll crash if you try to cast it as a UINavigationController. Also make sure that the class for the destination UITableViewController in the storyboard is listed as RestaurantController in the Identity Inspector pane. You'll crash if your program compiles thinking that the storyboard just contains a generic UITableViewController there.
Getting back to the original problem more, I don't know how you've implemented tableView:cellForRowAtIndexPath:, which might be crucial. Maybe it's not so simple. Maybe you plan on handling many prototype cells or generate custom cells at runtime. In this case, one way to make this simple for you is to programmatically perform the segue when the user taps on a cell. Instead of using a specific prototype cell, make the segue a connection originating from the "Restauranger nära mig" UITableViewController going to the RestaurantViewController. (Connect in Interface Builder by control-click dragging from the Table View Controller icon at the top of the first one over to the body of the second). You must give this segue an identifier in the Attributes Inspector pane to make this useful. Let's say it's "toRestaurant". Then at the end of your tableView:didSelectRowAtIndexPath: method, put this line of code: self.performSegueWithIdentifier("toRestaurant", sender: self). Now no matter what cell is selected in the table, this segue will always fire for you.
Try creating cells like this in your cellForRow method:
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("MyTestCell", forIndexPath: indexPath)
Im going out on a whim here since I am just getting into swift right now but the way I do it in my prepareForSegue() is something like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let navigationController = segue.destinationViewController as UINavigationController
let vc = navigationController.topViewController as RestaurantViewController
//notice I changed [i] to [index!.row]
vc.data = currentResponse[index!.row] as NSArray
}
}
What it looks like to me is that you are calling the i variable which is kind of like a private variable inside a method of your class. You can do something like #Syed Tariq did with the selectRow variable and set it above your class SomeController: UIViewController /*, maybe some more here? */ { and then sign the variable inside your
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
method like above but both ways should work rather well.
I had the same problem and I found the solution to be:
performSegueWithIdentifier("toViewDetails", sender: self)
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var cellnumber = procMgr.processos[indexPath.row].numero
println("You selected cell #\(indexPath.row)")
println(cellnumber)
performSegueWithIdentifier("toViewDetails", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toViewDetails" {
let DestViewController : ViewDetails = segue.destinationViewController as! ViewDetails
}
}
You may need to get the selected cell index of the UItableview. Below code used the selected cell index (UItableview.indexPathForSelectedRow) to get a correct element of the array.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "seguaVisitCardDetial" {
let viewController = segue.destinationViewController as! VCVisitCardDetial
viewController.dataThisCard = self.listOfVisitCards[(tblCardList.indexPathForSelectedRow?.row)!]
}
}
I had this problem, too; the segue from UITableViewCell did not call.
After some searching, I found it is because I had chosen "No Selection" for "Selection" field.

What is the format to call accessoryButtonTappedForRowWithIndexPath in swift

In my iOS table view I have a function required by the TableViewDelegate:
override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) {
savedRow = indexPath.row
self.performSegueWithIdentifier("SubcategorySegue", sender: self)
}
I first get the indexPath by executing the following:
#IBAction func showSubcategoryEditor(sender: UIButton) {
let switchFrameOrigin = sender.frame.origin
if let indexPath: NSIndexPath = self.tableView.indexPathForRowAtPoint(switchFrameOrigin) {
tableView.delegate.accessoryButtonTappedForRowWithIndexPath!(indexPath)
}
}
This results in an error indicating that delegate is not recognized.Should I use: tableView.accessoryButtonTappedForRowWithIndexPath(indexPath)?
When you use the system provided accessory button, and you tap on the button, accessoryButtonTappedForRowWithIndexPath is called for you by the system, and the system passes in the correct indexPath. When you call the method yourself, you have to pass in the indexPath, which you are getting from indexPathForRowAtPoint (but you need to convert the point to the table view's coordinate system like I show in the code below). So, since you already have the indexPath in your button's action method, there's no need to call accessoryButtonTappedForRowWithIndexPath, it's just an extra step that doesn't do any more than what you can do in the button's action method. You only need to do this,
#IBAction func showSubcategoryEditor(sender: UIButton) {
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
if let indexPath: NSIndexPath = self.tableView.indexPathForRowAtPoint(hitPoint) {
savedRow = indexPath.row
self.performSegueWithIdentifier("SubcategorySegue", sender: indexPath)
}
}
Notice also, that in performSegue:sender:, I am passing the indexPath as the sender argument. You can then use that in prepareForSegue:sender: to get the row if you need to pass information to your destination view controller.
If you connect the segue from your button to the next controller, then there's no need to even have a button action method; everything can be done in prepareForSegue:sender:. The sender argument will be the button, so you can do this,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let button = sender as UIButton
let controller = segue.destinationViewController as DetailViewController // substitute the class of your destination view controller
let hitPoint = button.convertPoint(CGPointZero, toView: self.tableView)
if let indexPath: NSIndexPath = self.tableView.indexPathForRowAtPoint(hitPoint) {
// pass the index path to controller, or get data from your array based on the indexPath, and send that to controller
}
}
The answer was provided on the Apple Forum:
Within the UITableViewController subclass, which implements the
delegate protocol, you can call the delegate method like this:
tableView(tableView, accessoryButtonTappedForRowWithIndexPath:
indexPath)

Resources