I've read many SO posts about this problem, but unfortunately none of the answers have solved my issue. I have a UIViewController with a TableView inside of it (I set it up this way for a specific UI format I wanted), and as of now if the user swipes to the left, a delete and edit button show up. My delete action works fine, but every iteration I've tried of edit so far has given me a SIGABRT / terminating with uncaught exception of type NSException error.
My ViewController class has the UIViewController, UITableViewDataSource, and UITableViewDelegate protocols added. The code for my tableView(editActionsForRowAt:) function is as follows:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
// Delete action
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Delete", handler: { (action, indexPath) -> Void in
// Delete action code works fine
})
deleteAction.backgroundColor = GlobalPropertyKeys.LovalyticsRed
let editAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Edit") {(action, indexPath) -> Void in
// Will explain what I've tried so far below this
print("Edit tapped.")
}
editAction.backgroundColor = GlobalPropertyKeys.LovalyticsBlue
return [deleteAction, editAction]
}
All the answers I found on SO involved variations of creating the segue in IB from my ViewController to the destination screen, setting an identifier for the segue, and then calling that segue using performSegue(withIdentifier:sender:).
So far I have tried:
1.
if let topController = UIApplication.topViewController() {
topController.performSegue(withIdentifier: "segueToEditListItem", sender: topController)
}
2.
self.performSegue(withIdentifier: "segueToEditListItem", sender: self)
3.
tableView.performSegue(withIdentifier: "segueToEditListItem", sender: tableView)
Tried connecting the segue from the TableView instead of from the ViewController
And probably a few more variations of the above that I deleted instead of commenting them out. Each time, I get a SIGABRT error when clicking edit, which I always associate with incorrectly connected #IBOutlets. I checked to make sure the segue was connected correctly, and when I remove any mention of the segue my app works fine (the edit button prints "Edit tapped."), so that makes me think there's a problem with my ViewController not recognizing the segue in my .swift file.
I've spent several hours trying to figure this out and have failed pretty miserably, so any help / advice would be greatly appreciated.
The self.performSegue(withIdentifier: "Identifier", sender: self) should works.
If your problem is about #IBOutlets you should check your Outlets again.
And if you can write your console log where there is description of your crash.
Related
Why TableView doesn't shows the new string added? How to fix it?
In first VC:
#IBAction func saveButtonPressed(_ sender: UIButton) {
var textField = UITextField()
let alert = UIAlertController(title: "Save current run", message: "", preferredStyle: .alert)
let action = UIAlertAction(title: "Save", style: .default) { (action) in
RunHistoryTableViewController().runArray.append(textField.text!)
RunHistoryTableViewController().tableView.reloadData()
}
alert.addTextField { saveRunTextField in
saveRunTextField.placeholder = "Name your run"
textField = saveRunTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}
In second VC:
var runArray = ["BMW M3 Run 1", "BMW M3 Run 2", "Renault Megane RS"]
//MARK: TableView DataSource:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return runArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RunCell", for: indexPath)
cell.textLabel?.text = runArray[indexPath.row]
return cell
}
}
Bear in mind I'm a beginner, could use some help as none of my ideas seemed to work.
I think you have two ways to achieve this:
1- To use protocols and pass the data in this protocol
2- To use segue and pass the data in this segue
The most recommended solution in such a case is to use the protocol to get the result and to have a good performance.
Everytime you are calling RunHistoryTableViewController() you are instantiating a new VC. If you are showing only one VC at the time you can pass data between one to another using the prepareForSegue method and getting ahold of the actual instance of your destination VC in the destination argument of the method.
The code RunHistoryTableViewController() invokes the initializer for your view controller. It creates a brand new instance of the view controller that has nothing to do with any other instance.
This code:
RunHistoryTableViewController().runArray.append(textField.text!)
Creates a new instance of RunHistoryTableViewController, and appends textField.text to its runArray, and then on the next line, that new instance of RunHistoryTableViewControllergets thrown away. Same issue with the next line. You create a newRunHistoryTableViewController`, try to reference it's table view and tell it to reload. I would expect that line to crash, since the new instance's table view won't exist.
You need a way to point to the other view controller that you are going to display on the screen (or have already displayed.)
How are you navigating between view controllers? That will determine how you pass the data between them.
I've encountered a problem while creating an iOS app. That's weird because I though it would be very easy to do.
I've got a TableView and editActionsForRowAt which returns one action. Clicking on it should navigate to my second ViewController which is loaded from .xib file. I know that this function is executed when clicking the button but the VC does not open.
MainViewController.swift
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let action = UITableViewRowAction(style: .normal, title: "more") { (uiTableViewRowAction, indexPath) in
let detailsVC = DetailsViewController(nibName: "DetailsViewController", bundle: nil)
self.navigationController?.pushViewController(detailsVC, animated: true)
}
return [action]
}
DetailsViewController.swift (it's empty for now)
import UIKit
class DetailsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
I've called this pushViewController method multiple of times in the past so it makes me wonder why it doesn't show up. What's the problem?
edit:
present doesn't work either.
present(detailsVC, animated: true, completion: nil)
returns
TableView[2646:125041] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "DetailsViewController" nib but the view outlet was not set.'
Don't know what was a problem but I deleted the DetailsVC .swift and .xib and created new one and above code works fine...
I'm attempting to create 2 different default view controllers that then get called and do their specified action depending on what was in the cell. Would you create the view controller in the main storyboard use an xib, etc?
Basically I have a xml file that I'm parsing and then creating table views for till I get to the last (details) page for the item. From my understanding it is better to use the same tableviewcontroller multiple times instead of creating 1 for each level. Am I supposed to be creating a segue loop?
There are checks currently in place to make sure I use the right segue.
Would you pefrom the segue with:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath as IndexPath)
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
segueLocation = hNTUL[(indexPath as NSIndexPath).row].locationLevel
segueTitle = hNTUL[(indexPath as NSIndexPath).row].locationTitle
if segueLocation == "TableView" {
performSegue(withIdentifier: "defaultDetailSeague", sender: cell)
let destinationVC = DefaultDetailViewController()
destinationVC.detailTitle = segueTitle
}
if segueLocation == "DetailView" {
performSegue(withIdentifier: "defaultTableSeague", sender: cell)
let destinationVC = DefaultTableViewController()
destinationVC.tableTitle = segueTitle
}
}
It is also possible that I'm trying to go about this the wrong way.
I've also been searching for project examples but haven't found any that might guide me in the right direction.
Thoughts?
If you call performSegue, you must not create a destinationVC; the segue creates it for you. To configure the destination view controller, implement prepare(for:sender:) and fetch the segue's destination, casting it down to a DefaultDetailViewController. Be sure to check that this is the right segue first!
I have a feed that is showing posts from the users that wanted to post something. What I am trying to do is display the name of the user that posted something in a button (that is working). Then when a user is pressing that button it should show another viewcontroller with the name of the button that the user pressed (that is semi-working).
What happens now is, when a user is pressing the name of another user from the post, the name that is showing in the other viewcontroller is not always the same as the name the user pressed.
I hope you understand :-)
Let me walk you through my code:
First viewController
before viewDidLoad, I am creating a string:
var sendName = "No name recieved"
Further down the page is this code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let update = updates[indexPath.row]
cell.nameButton.setTitle(update.addedByUser, forState: .Normal)
sendName = (cell.nameButton.titleLabel?.text)!
}
My prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "showProfileFromFeed") {
let yourNextViewController = (segue.destinationViewController as! profileTableViewController)
yourNextViewController.viaSegue = sendName
}
}
The button title is being changed to what it should in the feed, but there must be something wrong with sendName because it doesn't always show the same name on the other viewController as the buttonTitle says..
Hope you can understand, otherwise please let me know :-)
It seems to me that where you update the sendName variable is the problem. The cellForRowAtIndexPath is called whenever a new cell is about to be displayed, or when the table is redrawn.
I don't know exactly how your app works, but if you can have multiple posts on the screen at the same time, then this means that the sendName variable would only be correct if you were pressing the button for the bottom-most post.
If you pressed the button for any other post then you would still get the name of the bottom-most post.
What I would recommend is using an IBAction that is triggered when the button is clicked to change the sendName variable. Then you can manually initiate the segue.
Steps taken:
Created new single view project
Deleted default view controller
Added new Table View Controller
Embedded TVC into a Navigation Controller
Added a child TVC
Added custom class files and associated to each TVC
Created a Show segue from the first TVC to the child
Implemented required methods, #of sections, # of rows, cellForRowAtIndexPath
All of the tutorials I have watched and read online only include the steps I show above, and the segues start working fine. However, in my case I can not get it to work until I add the following:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("detail", sender: tableView)
}
I'm totally fine with implementing the didSelectRowAtIndexPath method if it is required. I'm just wondering if I am missing something, because it seems to work automatically in everything I've seen online.
Thanks in advance!
If you click on the segue (storyboard) and on the side menu you have to assign the text "detail" from
performSegueWithIdentifier("detail", sender: tableView)
on the Identifier field.
Like the image below
Edit:
Try the code above, rembember to change NAME_OF_THE_DETAIL_VIEW_CONTROLLER to your Detail ViewController.
In this code we set i set "self" before the performSegueWithIdentifier. And when the button is clicked you set the segue.destinationViewController to your new ViewController.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("detail", sender: indexPath);
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "detail") {
var detailView = segue.destinationViewController as! NAME_OF_THE_DETAIL_VIEW_CONTROLLER
}
}
Thank you all for your contributions. I figured out that the problem was the following was missing from the cellForRowAtIndexPath tableView function.
let cell = self.tableView.dequeueReusableCellWithIdentifier("custom", forIndexPath: indexPath) as! CustomCell
I had this line commented out, mostly because I have no idea what it does. :)