Swift: Keep segue from executing if condition not met - ios

Currently I'm working on two different ViewControllers in my application, we can call them VC1 and VC2. VC1 displays a registration form, with a question in each row of a UITableView, and has an edit button. The edit button is as a segue from VC1 and VC2. VC2 displays an interface that will allow the user to edit the question. I want to write the application so that you cannot segue from VC1 to VC2 unless you have selected a question(row) to edit. Right now I have some of it set up, but it's possible that I have it backwards.
In VC1:
// this method isn't called until VC2 segues back to VC1... so I don't know how
// to make sure that a selection has been made before pressing the button
#IBAction func editQuestion(segue:UIStoryboardSegue) {
if let editQuestion = segue.sourceViewController as? VC2 {
}
}
In VC2:
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject!) -> Bool {
// check to make sure that user has completed all fields of question
// if they haven't completed all fields, return false (do not segue)
// else segue to VC1
}

Implement in VC1:
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if identifier == <your storyboard segue identifier> {
}
}
Give your storyboard segue an identifier, and then you can do the check in VC1, and return false if you do not want the segue to happen.

Related

How to add If statement before unwinding segue? [duplicate]

This question already has answers here:
how to perform Condition check, before segue unwind
(2 answers)
Closed 5 years ago.
I have 2 views: FirstView and SecondView.
On SecondView, there are a textfield and a Submit button.
If user click Submit button, check if textfield is empty or not before going back to FirstView.
On StoryBoard of SecondView, I create a Unwind Segue by hold Ctrl and click on Submit button, then drag to Exit and choose tapToBackToFirstView
On FirstViewController:
#IBAction func tapToBackToFirstView (segue: UIStoryboardSegue) {
}
On SecondViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "backToFirstViewFromSecondView" {
let destinationController = segue.destination as! FirstViewController
destinationController.resultLabel.text = secondViewTextField.Text
}
}
From my code, If I click on Submit button, it always go to FirstView.
How can I add a IF statement to check if the textfield is empty or not? If it is empty, show an alert and DO NOT go to FirstView.
If it is not empty, go to FirstView.
Thanks
On your FirstView Controller you have unwindseguemethod tapToBackToFirstView which is connected from button action right?. Now remove that and instead of connecting unwind segue through IBAction, just drag from SecondViewController to it's own exit so that you will get one manual segue created. Give a segue identifier to this.
Now connection IBAction normally just like any button. call performSegueWithIdentifier in this based on condition.
#IBAction func tapToBackToFirstView(sender: AnyObject) {
if //Add your condition here
{performSegueWithIdentifier("segue identifier", sender: self)}
else {
//Do this
}
}

content of the viewcontroller changes according to the segue identifier

I have multiple segues from one viewController to another viewController. In the destinationViewController I have Label and Text View that changes according to the identifier. Of course, I can manage this without identifiers and just copy and paste viewControllers, and make my storyboard even bigger, write more code. But I want to learn to write good code. So, please help me to manage the issue.
I have such picture in my storyboard:
So, each button has its own identifier. I don't pass any data from ABOUT viewController, I just change the data inside History View Controller. That is why I need to solve the problem without using prepareForSegue function (because again I don't pass any data). And if I use prepareForSegue, then I will need to manage protocols, and that means more code.
You need to pass the identifier as a parameter from the calling ViewController in prepareForSegue.
See also: Can a viewcontroller access the identifier of an incoming segue?
it's a fast example how you can do it.
//specify enum
enum SegueType: Int {
case history, about, howto
}
class AboutVC: UIViewController {
//button pressing
#IBAction func didPressHistoryButton(sender: UIButton) {
let segueIdent = "show.History"
self.performSegue(withIdentifier: segueIdent, sender: SegueType.history)
}
// in your about vc
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationVC = segue.destination as? HistoryVC {
destinationVC.segueType = sender as? SegueType
}
}
}
// in your history vc
class HistoryVC: UIViewController {
var segueType: SegueType? = nil
override func viewDidLoad() {
super.viewDidLoad()
switch segueType! {
case SegueType.history:
// segue from history button
break
case SegueType.about:
// segue from about button
break
case SegueType.howto:
// segue from how to button\
break
default:
// segue from ???
break
}
}
}

How can i create a segue that pass data to another view controller only if certain criteria are met

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.

Segue not loading although there is no error or no crash or freeze

I come here with a very very strange error.
I have 3 View controllers in my storyboard.
1) Main Page with list of products ViewController
2) Single Product Detail ProductViewController
3) Barcode scanner BarcodeViewController
In the MAIN and PRODUCT DETAIL ViewController, i have a button saying "Scan Barcode" on top.
I first coded the button of "Scan Barcode" in the ViewController and segue it to the BarcodeViewController. It works perfectly.
For the same Scan Button in ProductViewController, i coded it differently.
I drag the button to EXIT. So that the ViewController loads again and as soon as it is loaded i programmatically load the Barcode scanner.
It does not work.
There is no error, there is no crash, nothing. Even Prepare for segue function is called. It goes into the right block everywhere.
Just the BarcodeViewController dont show up.
The code in ViewController
#IBAction func returned (segue: UIStoryboardSegue) {
NSLog("Returned Segue \(segue.identifier)")
if ( segue.identifier == "unwindToMainViewViaBarcode" )
{
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier(self.segueIdentifierForBarcode, sender: nil)
});
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
NSLog("PrepareForSegue \(segue.identifier)")
if ( segue.identifier == segueIdentifierForBarcode )
{
// Reset barcode String
barcodeString = ""
// get reference for ViewController and set as a var
let destination = segue.destinationViewController as! BarcodeViewController
destination.mainView = self;
print("open barcode called")
}
}
U can refer so some answer in this SO Answer
Basically make a BOOL before perform unwind segue and check it at ViewDidAppear, if its set then perform segue from there and turn the BOOL to false
I found the problem.
In other projects it is working because I am showing segue Modally
Here i by mistake was just showing it.
Now that i have changed it to show as Modal
It is working fine.

Passing data from Pop-over ViewController UITextfield back to Master ViewController

I have an app setup to have a Master root viewController with a Navigation bar that has a "Settings button". When the user taps the settings button, it brings up a 'settingsTableViewController' that is not dynamic, but rather static so i can format the tableviews with settings like style.
in this settings view, i have a UITextField that takes a Person's name.
When the user clicks "Done" in the navigation bar, i want to pass that name back to the Master Root ViewController so i can use it in the title to display the Person's name. (I want to pass the name upon dismissing the view)
I have tried to use segue's but no luck. Here is a bit of what i tried.
SettingsViewController (pop's over the MasterVC)
class SettingsTableViewController: UITableViewController {
#IBOutlet weak var nameTextfield: UITextField!
#IBAction func done(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
override viewDidLoad(){
self.title = "Settings"
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Trying to pass what was typed in the textfield to the Root ViewController and store this name in a variable of type String called 'name'
if segue.identifier == "masterView"{
let masterViewController = segue.destinationViewController as! MasterTableViewController
masterViewController.name = nameTextField.text
print("Segue was used")
}
}
}
MasterTableViewController.swift
class MasterTableViewController: UITableViewController {
var name: String!
// I am trying to then display the name entered in the pop-over view in this root view controller's title
override viewDidLoad(){
self.title = name!
}
}
My question is, what did i do wrong? I have tried to use delegation but i get nil and the data doesn't seem to get passed either way so any leads will greatly help. Thanks
There is a different kind of segue called Unwind Segue to do that. It doesn't create a new mvc and its used to pass data back to the vc that presented the current one. here is how to do it
First, go to your storyboard and control drag from settingVc to the exit button on top of it (to itself). it will give you 'Selection Segue' and choose IBAction goBack. This means any vc that presented the current one will get to prepare if they implement this method. In other words, you're putting out a protocol and the presenter vc will conform by implementing goBack IBAction. here is how to implement that. In your Mater vc
//You must have this function before you do the segue in storyboard
#IBAction func goBack(segue: UIStoryboardSegue){
/* this is optional
if let stv = segue.sourceViewController as? SettingsTableViewController{
self.name = stv.nameTextfield?.text
}
*/
}
In your setting vc (current vc)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//make sure you set the id of your segue to "Your Go back Unwind segue". ofc u can name it whatever
if segue.identifier == "Your Go back Unwind segue"{
if let mtvc = segue.destinationViewController as? MasterTableViewController{
mtvc.name = nameTextField.text
print("Segue was used")
}
}
}
and done method should be something like this
#IBAction func doneEditing(sender: UIButton) {
presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}

Resources