Cannot push view controller using editActionsForRow - ios

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...

Related

How do I fix this "Unknown Key Exception" error in Xcode?

I'm very new to coding so I really have no idea what's going on.
I've tried pasting the error into this website and looked around for people's responses but they either slightly altered from my error code or their explanation was too convoluted for me.
Code:
import UIKit
class BasicsListScreen: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension BasicsListScreen: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return basics.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell
cell?.verbLabel.text = basics[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "ConjViewController") as? ConjViewController
vc?.pastMe = mePast[indexPath.row]
vc?.pastYou = youPast[indexPath.row]
vc?.pastHe = hePast[indexPath.row]
vc?.pastShe = shePast[indexPath.row]
vc?.pastWe = wePast[indexPath.row]
vc?.pastYall = yallPast[indexPath.row]
vc?.pastThey = theyPast[indexPath.row]
vc?.pastPassive = passivePast[indexPath.row]
vc?.presentMe = mePresent[indexPath.row]
vc?.presentYou = youPresent[indexPath.row]
vc?.presentHe = hePresent[indexPath.row]
vc?.presentShe = shePresent[indexPath.row]
vc?.presentWe = wePresent[indexPath.row]
vc?.presentYall = yallPresent[indexPath.row]
vc?.presentThey = theyPresent[indexPath.row]
vc?.presentPassive = passivePresent[indexPath.row]
self.navigationController?.pushViewController(vc!, animated: true)
}
}
28784:1039713] Unknown class BasicsListScreen in Interface Builder
file. 2019-07-10 13:30:01.795516+0100 App[28784:1039713] *
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[< UIViewController 0x7fcc87619000>
setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key tableView.'
* First throw call stack: (
Attach your tableView IBOutlet in code to your UITableView in Interface Builder.
Start Developing iOS Apps (Swift): Connect the UI to Code
Take a look at this page Terminating app due to uncaught exception 'NSUnknownKeyException' : iOS app crash. There may be a broken connection to a referenced outlet. The page above shows you what that looks like.
Another thing to look at is to make sure your table view is connected to the right class as Daniel Storm said. Open your storyboard and click on the view that you think is connected BasicsListScreen. Open the "Inspector" by clicking the button in the far upper right of Xcode. Then click "Identity Inspector" which is the middle button. It should show BasicsListScreen in the "Custom Class". Make sure that is right.
The error message sounds like a broken connection in BasicMathLevelOne.xib. It is the result from KVC trying to set a value on your InheritController for the key "you" but the class has no KVC compliant accessor (any more?).
To find the exact spot where the error occurs set an exception breakpoint in Xcode (press Command-6, click the "+" in lower left corner, chose "Add Exception Breakpoint"). Running the app in the debugger should make it stop at the place where the error occurs.

Trigger segue from UITableViewRowAction

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.

Create and perform segue without storyboards

i got an app without storyboards, all UI creation is made in code and I got a splitView which I would make it usable on iPhone, because as the app as been first designed for iPad only, so that when you select a row in the list in the Master view it does nothing on iPhone but is working fine on iPad.
So my question is can I create and perform the segue that allows to show the Detail View on the didSelectRowAtIndexPath method ?
Here's what i've done so far :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let segue = UIStoryboardSegue(identifier: "test", source: self, destination: detailViewController!)
performSegueWithIdentifier("test", sender: self)
}
but when running and selecting a row the app was crashing telling it needed a performhandler so i added this :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let segue = UIStoryboardSegue(identifier: "test", source: self, destination: detailViewController!, performHandler: { () -> Void in
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = self.detailViewController!
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
})
performSegueWithIdentifier("test", sender: self)
}
and now when selecting a row xcode says that there is no segue with such identifier "test".
I also tried to call it by segue.perform() and add the performHandler content into the prepareForSegueMethod :
if segue.identifier == "test" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
and it does just nothing, doesn't crash, just highlight the row i selected and that's all
Can you guys help me ?
EDIT : As Oleg Gordiichuk said, it's not possible to do what I want to do without Storyboards, so thanks for his help :)
Segue it is component of the storyboard interaction it is possible to understand from name of the class UIStoryboardSegue. It is bad idea to create segues programmatically. If i am not making mistake storyboard creates them for you.
For solving of you're issue try to use some common ways like simply present ViewController.
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("id") as! MyController
self.presentViewController(vc, animated: true, completion: nil)
As i understand from our conversation in comments. You would like to create navigation for tableview to details view using segues without storyboard. For now it is impossible to do this without storyboard.
For future learning try to investigate this information.
One way is to use the didSelectRow method for tableView
Swift 3.x
// MARK: - Navigation & Pass Data
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected Row \(indexPath.row)")
let nextVC = YourNextViewController()
nextVC.YourLabel.text = "Passed Text"
nextVC.YourLabel.text = YourArray[indexPath.row]
// Push to next view
navigationController?.pushViewController(nextVC, animated: true)
}

How to assign Two segues on a table cell in SWIFT?

I am totally new at IOS swift and working with android.
Here is my issue :
I'm developing a simple chatting App of IOS. I've already completed android ver and this is a kind of clone.
It has view controllers named VCList, VCMyFriends and VCChat. VCList is my very first view controller and has a table view. Touching a table view cell changes current view controller to VCMyFriends or VCChat.
When I touch a cell, app checks the member counts of the cell, so if it has other members then go to VCChat. If not, VCMyFriends on to invite my friends. Like following pic.
What I found is, I cannot assign two segues on a cell or split a segue with two ways. So I thought that can I change my VC without a segue? However I could not find any references or tutorial about it.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if( memberCnt == 0 ) {
self.presentViewController(VCMyFriends(), animated: true, completion: nil);
} else {
self.presentViewController(VCChat(), animated: true, completion: nil);
}
}
Above is my last try, and failed. It moves somewhere, but shows nothing at all. And if I can, I wish to use segues because have some datas to pass with segue.
Take care.
What I normally do is to create an instance of the controller via the instantiateViewControllerWithIdentifier and set the necessary member variables before calling presentViewController. You can also add a NavigationController if you want the capability the go back to the previous window.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let mainStoryBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if( memberCnt == 0 )
{
let vcMyFriends = mainStoryBoard.instantiateViewControllerWithIdentifier("VCMyFriend") as! VCMyFriends
vcMyFriends.membervar1 = membervar1
vcMyFriends.membervar2 = membervar2
self.presentViewController(vcMyFriends, animated: true, completion: nil)
}
else
{
let vcChat = mainStoryBoard.instantiateViewControllerWithIdentifier("VCChat") as! VCChat
vcChat.membervar1 = membervar1
vcChat.membervar2 = membervar2
self.presentViewController(vcChat, animated: true, completion: nil)
}
}
membervar and membervar2 are the data that you pass to the View Controllers. You need to set it before calling presentViewController so that they will be available when viewDidLoad is called.
Simply do this
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if( memberCnt == 0 ) {
performSegueWithIdentifier("VCMyFriends_segue", sender: self)
} else {
performSegueWithIdentifier("VCChat_segue", sender: self)
}
}
In your storyboard, select the first segue (the red line in your image) and choose Attributes inspector, then add VCMyFriends_segue as identifier.
Do the same thing for the second line (blue) and add VCChat_segue identifier.
Hope this helps

Swift: "Attempt to present ViewController whose view is not in the window hierarchy" when using presentViewController inside a delegate method

I am using a custom cell class with a button in it.
import UIKit
protocol TouchDelegateForShotsCell {
func touchedTwitterButton()
}
class ShotsCell : UITableViewCell {
let touchDelegateForShotsCell: TouchDelegateForShotsCell = MasterViewController()
#IBAction func twitterButtonPressed(sender: AnyObject) {
touchDelegateForShotsCell.touchedTwitterButton()
}
}
When the button is pressed I call a delegate function in the MasterViewController which contains the following standard code for sharing on Twitter:
func touchedTwitterButton() {
var shareToTwitter :SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
self.presentViewController(shareToTwitter, animated: true, completion: nil)
}
I receive the error :
"Attempt to present ViewController whose view is not in the window hierarchy!".
I've tried several workarounds. For example, creating a separate view controller and performing a segue from the button. That works in part, however, when I try to pass data I get another error :
"'NSInvalidArgumentException', reason: Receiver ViewController has no segue with identifier 'segueToTwitterShare'".
Which isn't true. For this I tried deleting my app and restarting xcode which worked for some people. However, the problem persists.
let touchDelegateForShotsCell: TouchDelegateForShotsCell = MasterViewController()
It's not the good delegate. When you write MasterViewController() you create a new instance of MasterViewController (so it is not in the window hierarchy) but you don't pass the intense who is executed.
I suggest you an example to better understand what is the problem and see where changes is needed.
I add an init() function into ShotsCell :
class ShotsCell : UITableViewCell {
var touchDelegateForShotsCell: TouchDelegateForShotsCell!
init(withDelegate delegate: MasterViewController){
super.init(style: UITableViewCellStyle.Default, reuseIdentifier: "cellIdentifier")
touchDelegateForShotsCell = delegate
}
#IBAction func twitterButtonPressed(sender: AnyObject) {
touchDelegateForShotsCell.touchedTwitterButton()
}
}
And pass your MasterViewController to your cell in cellForRowAtIndexPath :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = ShotsCell(withDelegate: self)
return cell
}
Hope that helps. It works for me.

Resources