Passing data without view transition in Swift - swift

I have two UITableView connected through segue. I am performing segue to pass data from my firstTableView to secondTableView using tableViewCell swipe action method and saving the passed data using NSUserDefaults code works ok. But, Every time when I passed data it calls segue(I know it supposed to) and pushing to my secondTableView..I want to pass data without pushing my firstTableView to secondTableView when passing data and I have action button to access my secondController directly from mainViewController.
func tableView(_ tableView: UITableView, editActionsForRowAtIndexPath indexPath: IndexPath) -> [UITableViewRowAction]? {
let shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: "xxxxxxxxxx") { (UITableViewRowAction, NSIndexPath) -> Void in
self.performSegue(withIdentifier: "shareSegue", sender: indexPath)
// self.dismiss(animated: true, completion: nil)
}
return shareAction
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "shareSegue") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! ViewController
// your new view controller should have property that will store passed value
viewController.labelcell = (sendSelectedData as String as String)
}
//// I also tried the following method with no segue but the results pretty much same....
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewControllerWithIdentifier("secondTableView")
self.presentViewController(secondVC, animated: true, completion: nil)
Thanks in advance!!!

Related

Change destination viewController when editing is true or false

I have a viewControllerwith a tableViewwhich contains all clubs, the user added. The user can reorder the tableViewCellswith the moverowatfunction. If he selects a club, he comes to a tableViewwith all members from the club. Now I want to implement the ability, to get to another viewController where he can edit the content of the cell.
I have created this, but now it doesn't work:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewClub.isEditing == true{
self.navigationController?.pushViewController(EditClubViewController() as UIViewController, animated: true)}
if tableViewClub.isEditing == false{
self.navigationController?.pushViewController(MemberViewController() as UIViewController, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if !tableViewClub.isEditing {
guard let destination = segue.destination as? MemberViewController,
let selectedRow = self.tableViewClub.indexPathForSelectedRow?.row else {
return
}
destination.club = clubs[selectedRow]
}}
When tableView.isEditing, then a viewController with a black background is shown. When !tableView.isEditing, this error is shown:Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value. I think it is because I have no storyboard segues. But is it possible, to have two segues from the tableViewCell? Or how should I solve this problem?
Don't use Interface Builder's segue. Instantiate the destination view controller manually so you can control which one you are transitioning to:
Interface Builder
Select the EditClubViewController and give it a Storyboard ID. Do the same to MemberViewController.
In code
In the first view controller:
override func viewDidLoad() {
super.viewDidLoad()
// Turn this on so rows can be tapped during editing mode
tableView.allowsSelectionDuringEditing = true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC: UIViewController
switch tableView.isEditing {
case true:
// If your storyboard isn't named "Main.storyboard" then put the actual name here
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "editClubViewController") as! EditClubViewController
// do your setup....
destinationVC = vc
case false:
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "memberViewController") as! MemberViewController
// do your setup...
destinationVC = vc
}
self.navigationController?.pushViewController(destinationVC, animated: true)
}

How can I categorize my TODOs?

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

Swift 3 Dismiss after performing segue

I have three controllers connected with segues.
Controller one is MyNotif, controller 2 is AddNotif and controller three is SelectInterval.
From controller 2 I have a segue to controller 3 and then back to controller 2.
This is because in controller 3 i will select from a tableview a value (string) which will be inserted in a field in controller 2.
When the segue from 3 to 2 is performed the controller is not dismissed. This means that if from controller 2 I press the back button (I do not use a navigation controller, the back button is a simple button. Here's the code:)
#IBAction func backBtnPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
I will land on controller 3 instead that on controller one.
To summarise I'm trying to achieve this:
From controller 1 I go to controller 2. From here I can go to controller 3, select the string I need, perform the segue, go back to controller 2 (and till here everything works fine). Once back on controller 2 i f I click the back button i would like to go back on controller 1 (now what happens is that i go back on controller 3).
A bit complicate explain... hopefully it is a bit clear.
here's my segue code:
AddNotif:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SelectRepeatInterval" {
if let destination = segue.destination as? SelectRepeatIntervalVC {
destination.selectedRepeatingInterval = repeatingInterval
}
}
}
SelectInterval:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as! SelectRepetaIntervalCell
selectedInterval = currentCell.repeatIntervalLbl.text!
performSegue(withIdentifier: "IntervalSelected", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "IntervalSelected" {
if let destination = segue.destination as? AddNotificationVC {
destination.repeatingInterval = selectedInterval
}
}
}
What you are looking for is presentingViewController https://developer.apple.com/documentation/uikit/uiviewcontroller/1621430-presentingviewcontroller
if let destination = self.presentingViewController as? AddNotificationVC {
destination.repeatingInterval = selectedInterval
}
dismiss(animated: true, completion: nil)
Here is the complete code
tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
// Get Cell Label
let currentCell = tableView.cellForRow(at: indexPath)! as! SelectRepetaIntervalCell
selectedInterval = currentCell.repeatIntervalLbl.text!
if let destination = self.presentingViewController as? AddNotificationVC {
destination.<your textfield name>.text = selectedInterval
}
dismiss(animated: true, completion: nil)
}

UIViewController's prepare for segue method is not being called

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]
}

Why is my data not passing to my second view controller from a table view?

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.