I have a button on one view controller and want it to take me to another view controller while carrying over some user input.
I am able to build and run with no problem, but nothing happens when I click on the button. The unwind segue just doesn't work. I'd really appreciate some help... This is my code for the initial view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//configure this destination view controller only when save button is pressed
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
let mainGoal = mainGoalTextField.text ?? ""
let microGoal = microGoalTextField.text ?? ""
//set the habit to be passed on to tableViewController after the unwind segue
habit = Habit(mainGoal: mainGoal, microGoal: microGoal)
}
This is my code for the second view controller:
#IBAction func unwindToHabitList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as?ViewController, let habit = sourceViewController.habit {
let newIndexPath = IndexPath(row: habits.count, section: 0)
habits.append(habit)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
I connected the saveButton to the the unwind segue in the storyboard already and it still does not work. Do you have any ideas where to go forward from here?
Related
I have so strange issue. I have parent VC named NewAdCreationViewController it presents modally child VC named CollectionPicturesViewController.
In child VC I have link to the parent VC.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == picturesCollectionVCSegueName {
let collectionController = (segue.destination as! UINavigationController).viewControllers.first as! CollectionPicturesViewController
let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? PicturesCollectionTableViewCell
guard cell != nil else{ return }
collectionController.picturesArray = cell!.itemPictures
collectionController.parentVC = self // this is link!
}
}
In child VC I present another VC for picking photos - DKImagePickerController(pod). After picking all my photos appears in collectionView in child VC, and when I tap "Save" I want to pass all that data to parent VC and dismiss child,
#objc func savePictures(){
self.parentVC.pictures = self.picturesArray
self.parentVC.tableView.reloadData()
self.dismiss(animated: true, completion: nil)
}
but after I dismiss it my parent reloads completely and starts from viewDidLoad. It presents completely new VC(I've checked in console, it has another address in memory). I really don't know why is that?
On highlighted segue I've changed presentation to Over full screen. And it works perfectly without reloads!
I have an App in Swift3 that consists of 2 different UITableView that show details of a Class Asset. The content of the data in both table is same/similar (there are some filters), the presentation is different. So one of this UIViewTables shows for example comments for each asset and the other is more focused on the status.
If a user clicks on a table cell, both UITableViews open the same UIView for full details since both tables represent the same data at the end.
I embedded a UINavigationControl and made a segue for each TableCell. If I click on the cells, the correct detail screen opens.
But if I save it, it always brings me back to the first table, even if I start from the second.
Can anybody give me a hint?
Here is my DetailViewController prepare method that is called when I click on the save button.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
print(segue.identifier)
// Configure the destination view controller only when the save button is pressed.
guard let button = sender as? UIBarButtonItem, button === btnSave else
{
//os_log("The save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
asset?.name = tfDeviceName.text ?? ""
//here comes some other stored asset information...
}
Here is my first UITableViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "")
{
case "AddItem":
os_log("Adding a new asset.", log: OSLog.default, type: .debug)
case "ShowDetails":
guard let assetDetailViewController = segue.destination as? DetailViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedAssetCell = sender as? AssetTableViewCell else {
fatalError("Unexpected sender: \(String(describing: sender))")
}
guard let indexPath = tableView.indexPath(for: selectedAssetCell) else {
fatalError("The selected cell is not being displayed by the table")
}
selectedIndex = indexPath.row
let selectedAsset = assets[indexPath.row]
assetDetailViewController.asset = selectedAsset
default:
fatalError("Unexpected Segue Identifier; \(String(describing: segue.identifier))")
}
editMode = true
}
This is my second UITableViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "AddItem":
os_log("Adding a new asset.", log: OSLog.default, type: .debug)
case "ShowTask":
guard let taskDetailViewController = segue.destination as? DetailViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedAssetCell = sender as? TaskTableViewCell else {
fatalError("Unexpected sender: \(String(describing: sender))")
}
guard let indexPath = tableView.indexPath(for: selectedAssetCell) else {
fatalError("The selected cell is not being displayed by the table")
}
let selectedAsset = assets[indexPath.row]
taskDetailViewController.asset = selectedAsset
default:
fatalError("Unexpected Segue Identifier; \(String(describing: segue.identifier))")
}
}
And here my Story Board
Thank you!
BR
Stefan
EDIT WITH SOLUTION:
Thx to Duncans hints I found my problem. The unwind action is attached to the save button in the interface builder by crtl-click-drag the button to the exit event and select then the unwind method. And here comes the important thing: You can select only one method, but not the class. The class is selected automatically by the system that manages the navigation, but both methods (obviously) have to have the same unwind name. I made a typo so that the unwind method in the second UITableView was slightly different and then the systems doesn't find the method in the correct UITableView and jumps to a UITableView that has the correct method even is this Class was not the original segue
How are you returning from your DetailViewController back to the calling table view controller?
It sounds to me like you are using a normal segue, which is wrong.
You can either use an unwind segue, or the reverse of whatever method you use to get there (if you use a push segue, call pop(), if you use a modal present, call dismiss().)
So I'm just learning the basics of Swift. I have a table view of items and when an item is clicked I want to show a view that displays the details of that specific item. I have the below code in which I'm trying to achieve this, although, I'm getting a Bad Instruction runtime error. Any idea where I'm going wrong? Thanks!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
super.prepare(for: segue,sender: sender)
switch(segue.identifier ?? "") {
//the add item button is pressed
case "AddItem":
os_log("Adding a new donation.", log: OSLog.default, type: .debug)
//an existing item is pressed
case "ShowDetailDrill":
guard let itemViewController = segue.destination as? ItemViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedDonationItemCell = sender as? DonationItemCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedDonationItemCell) else {
fatalError("The selected cell is not being displayed by the table")
}
let selectedDonation = donatedItems.getItem(index: indexPath.row)
//TODO: load this to SubmissionVC
itemViewController.donatedItem = selectedDonation
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
Update
I forgot to mention that it breaks here
Update 2: Ok so I removed the navigation controller and changed the segue from a "present modally" to "show". It goes directly from the cell to the item controller. Everything now works, although I'm still somewhat confused on why it wasn't working with the navigation controller. If someone is able to explain the why, I'll mark that as the answer.
is the ShowDetailDrill segue's origin from the tableviewcell? if yes, try deleting it and then creating a new segue from the tableviewcontroller to ItemViewController instead.
I want to create a segue to pass data to another view controller but there are certain criteria that must happen for the segue to happen. If possible i would prefer to use the segue Id instead of the dragging method.
this is an example Im trying to accomplish
#IBAction func SubmitButtonPressed(sender: AnyObject) {
if 1<0 {
// dont perform segue
}else{
//Perform segue
// i want to pass this data in the next VC
var data = "foo"
//this is my segue id i want o use to go to the Second VC
var segueId = "segueForgotPasswordTwo"
// second VC
var secondVc = "viewController2"
// Iwant to to use prepare for segue but im getting errors in the parameters
prepareForSegue(UIStoryboardSegue, sender: AnyObject?){
}
}
}
Your question is a bit unclear but I believe this is what you are looking for...
func someFunction(){
if //some condition {
//code
}else if //some condition {
//code
} else {
//perform segue by using the folowing line. Assign the identifier to the segue in the storyboard.
//Do this by first creating a segue by dragging from a view controller to the destination view controller. Be sure to drag from the VIEWCONTROLLER, to the destination VIEWCONTROLLER. DO NOT just drag from the button. Next, choose the type of segue (eg. show or present modally), and then type in an indentifier for this segue.
performSegueWithIdentifier("SegueIdentifier", sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SegueIdentifier" {
//now find your view controller that you are seguing to.
let controller = segue.destinationViewController as! SomeViewController
//access the properties of your viewController and set them as desired. this is how you will pass data along
controller.property = someValue
}
}
Overview:
Hook the segue from the source view controller to the destination view controller (see left side red arrows)
Don’t hook it from the button to the destination view controller
Create an action for the button to do your custom condition check then perform segue
Screenshot:
Code:
var data = "foo"
#IBAction func buttonPressed(sender: UIButton) {
let someCondition = true
if someCondition {
performSegueWithIdentifier("showGreen", sender: self)
}
else {
performSegueWithIdentifier("showPink", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showGreen" {
let greenVC = segue.destinationViewController as! GreenViewController
// Make sure the data variable exists in GreenViewController
greenVC.data = data
}
}
You can implement the shouldPerformSegueWithIdentifier function in your ViewController. When the segue is triggered, this function can cancel the segue if it returns false, so you can simply include whatever logic is required in this function and return true/false as appropriate.
I am developing a UITableView app in Swift using storyboards. My app has two view controllers: a MasterViewController and a DetailViewController.
I am trying to replicate the behavior of Apple's Notes app. In Apple's Notes app, when a user clicks on the Trash Bar Button item displayed within the Detail View, the application shows an Action Sheet with two options: "Delete Note" and "Cancel". If the user clicks on "Delete Note", the current contents of the Detail View fade out, the current row is deleted, and the contents of the next row fade in.
I added a toolbar with a Trash Bar Button item to the detail view. I am able to call a method of the DetailViewController when the user clicks on the Trash Bar Button and am able to display the Action Sheet. I have been unable to make the current contents of the view fade out and be replaced with the contents of the next row.
How do I do that?
Here is the code that populates the detail view:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = objects[indexPath.row] as Note
println("setting detailItem! row: ", indexPath.row)
(segue.destinationViewController as DetailViewController).detailItem = object
(segue.destinationViewController as DetailViewController).delegate = self
}
}
}
You can go pretty straightforward way, creating a special setter in DetailViewController:
func setDetailItem(newItem: AnyObject, animated:Bool)
{
if animated
{
UIView.animateWithDuration(0.3, animations: {
<yourtextview>.alpha = 0.0
},
completion: {(finished:Bool) -> Void in
self.detailItem = newItem
<yourtextview>.alpha = 1.0 // back to visible state instantly
})
}
else
{
self.detailItem = newItem
}
}
then adding boolean flag in MasterViewController (for example, named isDeletion)and set it to true in remove button tap handler. And alter your prepareForSegue a bit:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = objects[indexPath.row] as Note
println("setting detailItem! row: ", indexPath.row)
// previous code
//(segue.destinationViewController as DetailViewController).detailItem = object
// new code
if isDeletion
{
isDeletion = false // don't forget to clear the flag
controller.setDetailItem(object, animated: true)
}
else
{
controller.setDetailItem(object, animated: false)
}
(segue.destinationViewController as DetailViewController).delegate = self
}
}
}
Now the text should fade out after delete and instantly change after plain selection.
Smells a bit Objective-C-ish, of course :)