Segue from UITableViewCell by tapping on an image inside of cell - ios

Been trying to figure this out for a while now and after a couple hours of searching for a solution I decided it was time to ask.
I have a tableview that gets populated by custom UITableViewCells and currently when you tap on a cell it takes you to a detail view.
Within the custom cell there is an image, I would like the user to be able to tap on that image and segue to a popover VC that shows the image.
What I'm having trouble with is creating the segue when the image is tapped.
In the file for the custom cell, I've set up a tap gesture recognizer on the image (pTap):
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: #selector(PostCell.voteTapped(_:)))
let ptap = UITapGestureRecognizer(target: self, action: #selector(PostCell.imageTapped(_:)))
tap.numberOfTapsRequired = 1
ptap.numberOfTapsRequired = 1
voteImage.addGestureRecognizer(tap)
voteImage.userInteractionEnabled = true
featuredImg.addGestureRecognizer(ptap)
featuredImg.userInteractionEnabled = true
}
I also have a function in the custom cell file for the tap:
func imageTapped(sender: UIGestureRecognizer) {
print("image tapped")
}
In my view controller file I've added a segue in did select row at index path:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let post: Post!
if inSearchMode {
post = filteredVenues[indexPath.row]
} else {
post = posts[indexPath.row]
}
print(post?.venueName)
performSegueWithIdentifier("imageTapped", sender: nil)
performSegueWithIdentifier("DetailsVC", sender: post)
}
Also, in the storyboard I have created a segue from the VC that holds the tableview with the custom cells to the VC I'd like to show when the image is tapped.
I've tried several different methods of getting this to work and haven't had any luck, the code you see above are what remains after my many failed attempts. I feel that the function for the tap in the custom cell file and the segue in the VC file are a part of the solution so that is why I have left them in.
Any help would be appreciated. Thanks!
Updates to code from answers below:
Added protocol
protocol ImageSegueProtocol: class {
func imageTapped(row: Int)
}
class PostCell: UITableViewCell {
Added IAB Func
#IBAction func imageTapped(sender: UIGestureRecognizer) {
guard let row = row else { return }
delegate?.imageTapped(row)
print("image tapped func")
}
Declared delegate in the Main VC
weak var delegate:postCell?
Assigned Delgate
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("PostCell") as? PostCell {
var img: UIImage?
var vImg: UIImage?
postCell?.delegate = self
Added extension function
extension FeedVC: ImageSegueProtocol {
func imageTapped(row: Int) {
if inSearchMode == true {
let object = filteredVenues[row]
performSegueWithIdentifier("imageTapped", sender: object)
print("ext func")
} else {
let object = posts[row]
performSegueWithIdentifier("imageTapped", sender: object)
print("ext func")
}
}

You could do it like this:
Within the cell file, declare a protocol at the top, and then set up some properties within the cell class itself and the delegate behaviour in response to the image being tapped:
protocol SomeCellProtocol: class {
func imageTapped(row: Int)
}
class SomeCell: UITableViewCell {
weak var delegate: SomeCellProtocol?
var row: Int?
#IBAction func imageTapped() {
guard let row = row else { return }
delegate?.imageTapped(row)
}
}
You must then make your ViewController adopt SomeCellProtocol and call the segue within like so:
extension SomeViewController: SomeCellProtocol {
func imageTapped(row: Int) {
//get object from array and call segue here
}
}
And you must set your ViewController as the delegate of your cells by calling:
someCell.delegate = self
and pass the row to the cell:
someCell.row = indexPath.row
within your cellForRowAtIndexPath method of the ViewController.
So, when the button is tapped within the cell (or you can do it with a GestureRecognizer on the ImageView if you want) it will force the delegate (the ViewController) to call its imageTapped function, passing a row parameter which you can use to determine which object in the table (its corresponding data array) should be passed via the Segue.

As OhadM said, create a button and set the background image as the image you want displayed. From there, you don't even need an IBAction. Control-drag from the button to the next view controller and create the segue.
Then, if you want to do any setup before the segue, in the first VC you'd have something like:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "imageButtonPressed" { // Make sure you name the segue to match
let controller = segue.destinationViewController as! SecondVC
controller.someText = "Hi"
controller.someInt = 5
}
}

What you need to do is to create a button instead of the image and this button will hold the actual image:
The rest is easy, ctrl drag an IBAction into your custom cell file.
Now you need to communicate with your View Controller in order to invoke your segue method.
You can achieve that using 2 design patterns:
Using post notification:
Inside your View Controller add an observer:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(YourViewController.cellSelected(_:)),
name: "CellSelectedNotificationName",
object: nil)
Your method cellSelected inside your View Controller should look like this:
func cellSelected(notification : NSNotification)
{
if let passedObject = notification.object as? YourCustomObject
{
// Do what ever you need with your passed object
// When you're done, you can invoke performeSegue that will call prepareForSegue
// When invoking performeSegue you can pass your custom object
}
}
Inside your CustomCell class at the IBAction method of your button:
#IBAction func buttonTapped()
{
// Prepare the object you want to pass...
NSNotificationCenter.defaultCenter().postNotificationName("CellSelectedNotificationName", object: YourCustomObjectYouWantToPass)
}
In a nut shell, a button inside a cell was tapped, you created an object inside that cell and you pass it to a View Controller using the notification centre. Now, you can segue with your custom object.
NOTE: If you don't need to pass an object you can basically ctrl + drag from your UIButton (at your storyboard) to another View Controller.
Using a delegate that is pointing to the View Controller.
Good luck.

Related

How to get selected table view cell row in segue?

I am developing an iOS app using Swift and I have a view controller that segues from a table view cell content view by selecting a cell row or selecting a button inside of that cell row's content view. The original view controller that contains the table view performs a segue on two different occasions: one segue when the cell row itself is selected (segues to an avplayerviewcontroller and plays a video depending on the cell row that was selected) and the second segue happens when you press a button that is inside of the content view of the table view cell. In the first segue, I am able to pass the the cell row that is selected with if let indexPath = self.tableview.indexPathForSelectedRow when I override the first segue. However when I try to pass the cell row that was selected when I try to override the second segue that happens when you press the button it doesn't work. Is this because the button inside of the table view cell doesn't know which row it was selected in? If so, how can I solve this problem, and if not what is a viable solution to solve such issue? Reminder: The "playDrill" segue is trigged when you select a cell row, the "Tips" segue is trigged when you selected a button inside of that same cell row's content view
Code for first segue that happens when you select a cell row (this segue functions as desired):
class DrillsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "playDrill" {
if let indexPath = self.tableView.indexPathForSelectedRow {
if initialRow == 1 {
drillVid = videoURL[indexPath.row]
playerViewController = segue.destination as! PlayerController
playerViewController.player = AVPlayer(playerItem: AVPlayerItem(url: drillVid))
playerViewController.player?.play()
print(indexPath) //prints correct value which is "[0,6]"
}
if initialRow == 3 {
drillVid = videoUrl[indexPath.row]
playerViewController = segue.destination as! PlayerController
playerViewController.player = AVPlayer(playerItem: AVPlayerItem(url: drillVid))
playerViewController.player?.play()
}
Code for second segue that triggers when you select a button inside of the cell's content view (I want this segue to have the value of indexPath as in the first segue, but when I try to use that code it doesn't return the correct value):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Tips" {
if let indexPath = self.tableview.indexPathForSelectedRow {
if initialRow == 1 {
print(indexPath) //the value printed is "6", I want "[0,6]" like the first code block
let tipVC = segue.destination as! KeysController
}
}
}
}
I had this issue also a couple of months ago... finally I was able to solve it with the following tasks:
Create an Outlet and Action of the button in your corresponding TableViewCell
Create a XYTableViewCellDelegate protocol in your TableViewCell.swift file
Define a delegate of your previous created TableViewCell delegate in your TableViewCell class
Define your delegate in cellForRowAt: function of your tableview
Add the delegate to your ViewController class where the TableView is also implemented
Finally just create this function in your ViewController to receive the tapped buttons tableview indexpath
If you need more help on this, please just copy / paste your whole ViewController & TableViewCell class here - We can make then the changes directly in the code.
It should look like the following code:
// FILE VIEWCONTORLLER
// -> 5.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, XYTableViewCellDelegate {
// ... view did load stuff here
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TripDetailsTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
// -> 4.
cell.delegate = self
// ...
}
// -> 6.
func buttonTappedViewCellResponse(cell: UITableViewCell) {
let indexPath = tableView.indexPath(for: cell)
// do further stuff here
}
}
// FILE TABLEVIEWCELL
// -> 2.
protocol XYTableViewCellDelegate {
func buttonTappedViewCellResponse(cell:UITableViewCell)
}
class XYTableViewCell: UITableViewCell {
// -> 1.
#IBOutlet weak var button: UIButton!
// -> 3.
var delegate: XYTableViewCellDelegate?
// -> 1.
#IBAction func buttonAction(_ sender: Any) {
self.delegate?.buttonTappedViewCellResponse(cell: self)
}
}
You are not able to get the selected index of the selected cell because you are not actually selecting a cell. You are pressing a button inside the cell.
So, what you do is get a reference to the button, get the button's superview (the cell) and then you can get the indexPath of that cell.
class TableViewController: UITableViewController {
var indexPathForButton: IndexPath?
#IBAction func buttonPressed(_ sender: UIButton) {
let button = sender
let cell = button.superview!.superview! as! MyCell // The number of levels deep for superviews depends on whether the button is directly inside the cell or in a view in the cell
indexPathForButton = tableView.indexPath(for: cell)
}
Then in prepare(for segue:)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Tips" {
if initialRow == 1 {
print(indexPathForButton)
let tipVC = segue.destination as! KeysController
}
}
}
I have never used tableview.indexPathForSelectedRow (and while I think it is not good practice, I am not sure if it is responsible for your issue), however one other approach you might try is to send the indexPath object as sender. This would avoid having to check for tableview.indexPathForSelectedRow inside prepareForSegue. For instance,
You can trigger your "playDrill" segue in tableView(UITableView, didSelectRowAt: IndexPath) where you have access to this indexPath and can simply pass it as sender.
When triggering your "Tips" segue, you can also pass this indexPath object as sender. One way to have a reference is to keep the indexPath object when you dequeue your cell and set the button action in ableView(UITableView, willDisplay: UITableViewCell, forRowAt: IndexPath)
Let me know if that helps you! Cheers,
Julien

How to present view controller from UILabel with tap gesture recognized in custom table view cell?

I have a custom dynamic table view cell with a label that has a tap gesture recognized added. When the user taps the label, not anywhere else in the cell, I want to present a view controller.
The instagram app has this feature. Ie. when you tap likes, it takes you to a likes table view, when you tap comments, it shows you to a comments table view. This is the same experience I want.
I am not looking to use didSelectRow because then it kind of defeats the purpose of having the specific target area to tap to show a new view controller.
So, how can I present a view controller from a tap gesture recognizer in a subclass of UITableViewCell?
UPDATED:
I am passing a closure to my custom TableViewCell which is successfully being called when the button is pressed. But I am stuck in the TableView and cannot pass information to the next View Controller I want to present. And I can't actaully perform the segue either :\
// From UITableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let story = stories[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "Fun Cell", for: indexPath) as? FunTableViewCell {
cell.configureCell(title: story.title, info: story.info)
cell.buttonAction = { [weak self] (cell) in
print("the button was pressed for \(story.title)")
self?.buttonWAsTapped(title: story.title)
}
return cell
} else {
return FunTableViewCell()
}
}
func buttonWAsTapped(title: String) {
// Need to pass something to the next View Controller... but how???
if let nextVC = UIViewController() as? DetailViewController {
nextVC.storyTitle = title
performSegue(withIdentifier: "Button Pressed", sender: self)
}
}
// Custom TableViewCell
class FunTableViewCell: UITableViewCell {
#IBOutlet weak var funLabel: FunLabel!
#IBOutlet weak var standardLabel: TappedLabel!
#IBOutlet weak var funButton: FunButton!
var buttonAction: ((UITableViewCell) -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: #selector(readMoreTapped))
tap.numberOfTapsRequired = 1
standardLabel.addGestureRecognizer(tap)
standardLabel.isUserInteractionEnabled = true
}
#IBAction func btnPressed(sender: UIButton) {
print("Button pressed")
buttonAction?(self)
}
When creating the cell, pass a block to it. That's the handler for the button.
When the button tapped, call the block.
You can save the block as a property of the subclass of UITableViewCell.
In your tableViewCell class, add a property:
class CustomTableViewCell: UITableViewCell {
open var completionHandler: (()->Void)?
}
In your viewController that has the tableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = CustomTableViewCell()
cell.completionHandler = {
() -> Void in
let newViewController = UIViewController()
//configure the VC here base on the indexPath
self.present(newViewController, animated: true, completion: nil)
}
return cell
}

How to add a segue programmatically?

I know, I know this has been asked a lot of times. I also found this question but the solution it suggested did not work for me.
I am just trying to build an app to demonstrate how to use those things in UIKit (in case I want to use them later on. I can just copy the code).
I have created a View Controller with a table view in it. I wrote a class called PrototypeTableController to act as the view controller class for the view controller I created in the storyboard.
When the user taps on one of the cells, I want another view controller to show, called Prototype Table Content. And different text will be shown if you tap on different cells.
In the storyboard, it's like this:
The text of the label in Prototype Table Content will be different when the user taps on a different cell. This means I need to send data from one view controller to another.
The post mentioned above suggested that I should give the segue an identifier, so I did:
Here is my code:
View controller class for the table view:
class PrototypeTableController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let data = ["Cell1", "Cell2", "Cell3", "Cell4", "Cell5"]
let contents = ["Hello", "Nice", "OMG", "Jesus", "Peace"]
var content: String?
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = data[indexPath.row]
return cell
}
func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "This is a prototype table view created by Sweeper"
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "my table"
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
content = contents[indexPath.row]
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("showContent", sender: tableView)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showContent" {
let destination = segue.destinationViewController as! PrototypeTableContentViewController
destination.contentString = content
}
}
}
View controller class for Prototype Table Content view:
class PrototypeTableContentViewController: UIViewController {
#IBOutlet var tableContent: UILabel!
var contentString: String?
override func viewDidLoad() {
super.viewDidLoad()
tableContent.text = contentString
}
}
I think I did all the things suggested in the post mentioned above. I added an identifier, I called performSegueWithIdentifier
, I also deselected the cell after the tapping.
However, it just doesn't go to the other view controller! It stays on the same controller! Like this:
When the user taps on one of the cells, I want another view controller to show, called Prototype Table Content. And different text will be shown if you tap on different cells.
While you can programmatically call performSegueWithIdentifier, it's a lot of effort that the storyboard can automatically handle for you. Just use a show storyboard segue from your prototype cell to PrototypeTableContentViewController.
prepareForSegue knows which cell you selected because the cell is the sender. All you have to do is set the destination view controller's contentString.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let controller = (segue.destinationViewController as? PrototypeTableContentViewController where segue.identifier == "showContent", let cell = sender as? UITableViewCell, textLabel = cell.textLabel else {
return
}
controller.contentString = textLabel.text
}
This is very similar to how a template like Master-Detail segues from a cell to show details about a cell (although Apple uses indexPathForSelectedRow to pass the cell's details):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row] as! NSDate
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
In either case, the SDK performs the storyboard segue for you; a segue didn't need to be programmatically added or performed.
Make sure your tableview delegate is set. If you are using storyboard, make sure delegate outlet in your storyboard is connected properly. If you are creating tableview by code, then you should do tableView.delegate=self; to set the delegate.
Your code is fine.
And one more thing:
You might need to change this line:
performSegueWithIdentifier("showContent", sender: tableView)
you need to make the sender as the row but not the tableview,so that the prepare for segue will get the sender as row instead of whole tableview.
As you are calling the prepareForSegue overtime you select a row, it makes sense to make the row as sender in performSegueWithIdentifier.
So it would be:
let row=indexPAth.row
performSegueWithIdentifier("showContent", sender: row)

Swift: Pass UITableViewCell label to new ViewController

I have a UITableView that populates Cells with data based on a JSON call. like so:
var items = ["Loading..."]
var indexValue = 0
// Here is SwiftyJSON code //
for (index, item) in enumerate(json) {
var indvItem = json[index]["Brand"]["Name"].stringValue
self.items.insert(indvItem, atIndex: indexValue)
indexValue++
}
self.tableView.reloadData()
How do I get the label of the cell when it is selected and then also pass that to another ViewController?
I have managed to get:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
println(currentCell.textLabel.text)
}
I just cant figure out how to pass that as a variable to the next UIViewController.
Thanks
Passing data between two view controllers depends on how view controllers are linked to each other. If they are linked with segue you will need to use performSegueWithIdentifier method and override prepareForSegue method
var valueToPass:String!
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel.text
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "yourSegueIdentifer") {
// initialize new view controller and cast it as your view controller
var viewController = segue.destinationViewController as AnotherViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
If your view controller are not linked with segue then you can pass values directly from your tableView function
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
let storyboard = UIStoryboard(name: "YourStoryBoardFileName", bundle: nil)
var viewController = storyboard.instantiateViewControllerWithIdentifier("viewControllerIdentifer") as AnotherViewController
viewController.passedValue = currentCell.textLabel.text
self.presentViewController(viewContoller, animated: true , completion: nil)
}
You asked:
How do I get the label of the cell when it is selected and then also pass that to another ViewController?
I might suggest rephrasing the question as follows: "How do I retrieve the data associated with the selected cell and pass it along to another view controller?"
That might sound like the same thing, but there's an important conceptual distinction here. You really don't want to retrieve the value from the cell label. Our apps employ a MVC paradigm, so when you want to pass data information from one scene to another, you want to go back to the model (the items array), not the view (the text property of the UILabel).
This is a trivial example, so this distinction is a bit academic, but as apps get more complicated, this pattern of going back to the model becomes increasingly important. The string representation from the cell is generally is a poor substitute for the actual model objects. And, as you'll see below, it's just as easy (if not easier) to retrieve the data from the model, so you should just do that.
As an aside, you don't really need a didSelectRowAtIndexPath method at all in this case. All you need is a segue from the table view cell to the destination scene, give that segue a unique identifier (Details in my example), and then implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailsViewController {
let selectedRow = tableView.indexPathForSelectedRow!.row
destination.selectedValue = items[selectedRow]
}
}
Alternatively, if your segue is between the cell and destination scene, you can also use the sender of the prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailsViewController {
let cell = sender as! UITableViewCell
let selectedRow = tableView.indexPath(for: cell)!.row
destination.selectedValue = items[selectedRow]
}
}
But the idea is the same. Identify what row was selected, and retrieve the information from the model, the items array.
The above is Swift 3. For Swift 2.3, please see the previous version of this answer.
Okay..Its been 2 days I was searching for the answer that how could I be able to save the selected UITableViewCell label text data and display that data to an another label on an another View Controller which will come out after tapping on a cell. At last I have completed with the task and its successful. Here is the complete code with steps using Swift.I am using Xcode 6.4.
Step 1.
I have Two class assigned to the storyboard view controllers named "iOSTableViewControllerClass.swift" which is a Table View Controller and "iOSTutorialsViewControllerClass.swift" which is a normal View Controller.
Step 2.
Now make segue from iOSTableViewControllerClass to iOSTutorialsViewControllerClass by Control-dragging on the storyboard area and choose "show" from drop down menu. Click on this highlighted button according to the below image and perform the segue.
Step 3.
Now select the segue by clicking on the storyboard and give it an identifier on the Attributes Inspector. In this case I named it as "iOSTutorials"
Step 4.
Now on this step put a label on your cell as well as on the other view controller and make outlets of them on their corresponding classes.
In my case those are "#IBOutlet weak var iOSCellLbl: UILabel!" and " #IBOutlet weak var iOSTutsClassLbl: UILabel!".
Step 5.
Make a string type variable on the first Table View Controller Class. I did this as "var sendSelectedData = NSString()" also Make a string type variable on the second class. I did this as "var SecondArray:String!".
Step 6.
Now we are ready to go.
Here is the complete Code for first Class --
// iOSTableViewControllerClass.swift
import UIKit
class iOSTableViewControllerClass: UITableViewController, UITableViewDataSource,UITableViewDelegate {
// Creating A variable to save the text from the selected label and send it to the next view controller
var sendSelectedData = NSString()
//This is the outlet of the label but in my case I am using a fully customized cell so it is actually declared on a different class
#IBOutlet weak var iOSCellLbl: UILabel!
//Array for data to display on the Table View
var iOSTableData = ["Label", "Button", "Text Field", "Slider", "Switch"];
override func viewDidLoad() {
super.viewDidLoad()
//Setting the delegate and datasource of the table view
tableView.delegate = self
tableView.dataSource = self
//Registering the class here
tableView.registerClass(CustomTableViewCellClassiOS.self, forCellReuseIdentifier: "CellIDiOS")
//If your using a custom designed Cell then use this commented line to register the nib.
//tableView.registerNib(UINib(nibName: "CellForiOS", bundle: nil), forCellReuseIdentifier: "CellIDiOS")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return iOSTableData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellIDentifier = "CellIDiOS"
//In this case I have custom designed cells so here "CustomTableViewCellClassiOS" is the class name of the cell
var cell:CustomTableViewCellClassiOS! = tableView.dequeueReusableCellWithIdentifier(CellIDentifier, forIndexPath: indexPath) as? CustomTableViewCellClassiOS
if cell == nil{
tableView.registerNib(UINib(nibName: "CellForiOS", bundle: nil), forCellReuseIdentifier: CellIDentifier)
cell = tableView.dequeueReusableCellWithIdentifier(CellIDentifier) as? CustomTableViewCellClassiOS
}
//Here we are displaying the data to the cell label
cell.iOSCellLbl?.text = iOSTableData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label text here and storing it to the variable
let indexPathVal: NSIndexPath = tableView.indexPathForSelectedRow()!
println("\(indexPathVal)")
let currentCell = tableView.cellForRowAtIndexPath(indexPathVal) as! CustomTableViewCellClassiOS!;
println("\(currentCell)")
println("\(currentCell.iOSCellLbl?.text!)")
//Storing the data to a string from the selected cell
sendSelectedData = currentCell.iOSCellLbl.text!
println(sendSelectedData)
//Now here I am performing the segue action after cell selection to the other view controller by using the segue Identifier Name
self.performSegueWithIdentifier("iOSTutorials", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Here i am checking the Segue and Saving the data to an array on the next view Controller also sending it to the next view COntroller
if segue.identifier == "iOSTutorials"{
//Creating an object of the second View controller
let controller = segue.destinationViewController as! iOSTutorialsViewControllerClass
//Sending the data here
controller.SecondArray = sendSelectedData as! String
}
Here is the complete code for the second Class..--
// iOSTutorialsViewControllerClass.swift
import UIKit
class iOSTutorialsViewControllerClass: UIViewController {
//Creating the Outlet for the Second Label on the Second View Controller Class
#IBOutlet weak var iOSTutsClassLbl: UILabel!
//Creating an array which will get the value from the first Table View Controller Class
var SecondArray:String!
override func viewDidLoad() {
super.viewDidLoad()
//Simply giving the value of the array to the newly created label's text on the second view controller
iOSTutsClassLbl.text = SecondArray
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I do it like this.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedName = nameArray[indexPath.row]
let newView: nextViewName = self.storyboard?.instantiateViewController(withIdentifier: "nextViewName") as! nextViewName
newView.label.text = selectedValue
self.present(newView, animated: true, completion: nil)
}

Swift: Make button trigger segue to new scene

Alright, so I have a TableView scene and I'm going to put a button in each of the cells and I need each cell to, when clicked, segue to its own ViewController scene. In other words, I need to know how to connect a button to a scene (The only button I have right now is "milk")
I know how to create an IBAction linked to a button, but what would I put in the IBAction?
I'm a beginner so I need a step-by-step explanation here. I've included a picture of my storyboard. I haven't written any code yet.
If you want to have a button trigger the segue transition, the easiest thing to do is Control+Click from the button to the view controller and choose a Segue option (like push). This will wire it up in IB for you.
If you want to write the code to do this yourself manually, you can do so by naming the segue (there's an identifier option which you can set once you've created it - you still need to create the segue in IB before you can do it) and then you can trigger it with this code:
V2
#IBAction func about(sender: AnyObject) {
performSegueWithIdentifier("about", sender: sender)
}
V3
#IBAction func about(_ sender: Any) {
performSegue(withIdentifier: "about", sender: sender)
}
You can use the delegation pattern. Presuming that you have implemented a custom table cell, you can define a property in its class to hold whatever you think is helpful to identify the row - it can be its index, or (my preferred way) an instance of a class which represents the data displayed in the cell (I'm calling it MyCellData.
The idea is to let the cell notify the table view controller about a tap on that button, passing relevant data about (the data displayed in) the row. The table view controller then launches a segue, and in the overridden prepareForSegue method it stores the data passed by the cell to the destination controller. This way if you have to display details data about the row, you have all the relevant info, such as the details data itself, or an identifier the destination view controller can use to retrieve the data for example from a local database or a remote service.
Define a protocol:
protocol MyCellDelegate {
func didTapMilk(data: MyCellData)
}
then declare a property named delegate in the cell class, and call its didTapMilk method from the IBAction
class MyTableCell : UITableViewCell {
var delegate: MyCellDelegate?
var data: MyCellData!
#IBAction func didTapMilk() {
if let delegate = self.delegate {
delegate.didTapMilk(self.data)
}
}
}
Next, implement the protocol in your table view controller, along with an override of prepareForSegue
extension MyTableViewController : MyCellDelegate {
func didTapMilk(data: MyCellData) {
performSegueWithIdentifier("mySegueId", sender: data)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "mySegueId" {
let vc: MyDestinationViewController = segue.destinationViewController as MyDestinationViewController
vc.data = sender as? MyCellData
}
}
}
Of course you need a data property on your destination view controller for that to work. As mentioned above, if what it does is displaying details about the row, you can embed all required data into your MyCellData class - or at least what you need to retrieve the data from any source (such as a local DB, a remote service, etc.).
Last, in cellForRowAtIndexPath, store the data in the cell and set its delegate property to self:
extension MyTableViewController : UITableViewDataSource {
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let data: MyCellData = retrieveDataForCell(indexPath.row) // Retrieve the data to pass to the cell
let cell = self.tableView.dequeueReusableCellWithIdentifier("myCellIdentifier") as MyTableCell
cell.data = data
cell.delegate = self
// ... other initializations
return cell
}
}
Use self.performSegueWithIdentifier("yourViewSegue", sender: sender) under your event for handling button's click:
#IBAction func redButtonClicked(sender: AnyObject) {
self.performSegueWithIdentifier("redView", sender: sender)
}
In the above code, redView is the segue identifier.

Resources