I have a view controller that is called. It has some checks in it that if they are not met, it doesn't need to be show this view controller, and the next view controller in my application will be called. This is partially not working.
I have the following IBAction:
#IBAction func nextBtnPressed(sender: UIButton!) {
if GlobalVars.diceRollFirst == true {
performSegueWithIdentifier("showCombatOutcomeFromRearSupport", sender: self)
}
if GlobalVars.diceRollFirst == false {
performSegueWithIdentifier("showDiceRollFromRearSupport", sender: self)
}
}
Which works correctly.
However in my viewDidLoad() I have the following code:
rearsupportfound is set earlier in the viewDidLoad after many checks. It's initially false and is set true if any of the checks are met.
if !rearsupportfound {
println("rearsupportfound is: \(rearsupportfound)")
if GlobalVars.diceRollFirst == true {
performSegueWithIdentifier("showCombatOutcomeFromRearSupport", sender: self)
}
if GlobalVars.diceRollFirst == false {
performSegueWithIdentifier("showDiceRollFromRearSupport", sender: self)
}
}
This code does nothing. By that I mean it does get to this code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "showCombatOutcomeFromRearSupport" {
println("segue: showCombatOutcomeFromRearSupport sender: \(sender)")
let vc = segue.destinationViewController as CombatOutcomeViewController
vc.army1 = army1
vc.army2 = army2
}
if segue.identifier == "showDiceRollFromRearSupport" {
println("segue: showDiceRollFromRearSupport")
let vc = segue.destinationViewController as DiceRollViewController
vc.army1 = army1
vc.army2 = army2
}
}
And I get the println entries, but the requested view controller is never presented and the current view controller is. The same perpareForSegue works perfectly when the button is pressed.
Any idea's why??
I realize that I should probably do the check for presenting this view controller before I even get here. I was trying to avoid writing the code twice, since the data displayed in the view hide's or display's certain labels and buttons. I haven't seen a good way for me to get around this, so since I had already written these checks, was trying to bypass the view controller within itself, depending on the values. Thoughts anyone? Thanks.
Your code isn't working because you're calling it from within viewDidLoad(). You need to call it from viewDidAppear() instead - see this question for more details.
As far as your code duplication issues, I would try to remove all game logic from your view controllers. Have a larger, higher level object that maintains the state of the game, and decides which view controllers are applicable at any given moment / scenario. Then this object can hand the appropriate segue identifier to the view controller. This way the view controller doesn't need to know about dice or combat or rear support.
Related
Salutations,
This is a follow up from my previous post (Incorrect data passed on first click of button). It was answered, but the updated edit of course occurred.
My inquiry is as follows, at the moment I have two IBActions funcs which empty, lack any sort of code, and their only purpose for existence is that they are connected to my other view controller, and as such if I remove them I have no way to differentiate between which segue should be used (My understanding is that although we can create segues between two view controllers, I do not think anything more than one would make sense (as in, how to decide which one to go with). I digress.
I tried using the following in my code before:
self.performSegue(withIdentifier: "slideTwo", sender: self);
This works well, however, it does cause double segueing. As such, I settled for the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let passedPic = segue.destination as! ViewControllerTwo;
if(segue.identifier == "slideOne") {
passedPic.picsChosen = sLS1;
} else {
passedPic.picsChosen = sLS2;
}
}
This works well, does what I need, but I do then have the problem of two empty IBActions:
#IBAction func slideShowOne() {
}
#IBAction func slideShowTwo() {
//self.performSegue(withIdentifier: "slideTwo", sender: self);
}
This is where I thought perhaps I could try to create a getter/setter (which swift makes far more complicated than it honestly should be). This is where I am lost.
The code looks as follows:
var picsChosen : [String] {
set(newData) {
for index in 0..<newData.count {
print("Index is: ", newData[index]);
}
self._picsChosen = newData;
} get {
return self._picsChosen;
}
}
Now, my issue is two fold. First, what is the correct way to actually access and pass values into my variable. I tried doing: myClass: ViewControllerTwo! = ViewControllerTwo();
and then use myClass.picsChosen = myArray; However, although it does appear that the data was passed successfully into my VC2, since when I loop through (inside the setter) I do see the values successfully being displayed, when I try to access picsChosen outside of that I get an index out of bounds error, in other words, the value was never associated with picsChosen. The current way I have it works well, but I want to know what is the correct way to use getters/setters, and why what I have did not work.
Cheers!
This works well, does what I need, but I do then have the problem of
two empty IBActions:
You don't need these empty #IBActions. They aren't doing anything for you. The segues are wired from the buttons in the Storyboard, so you don't need the #IBActions. If you delete the code, you also need to remove the connection from the Storyboard or your app will crash when it tries to call the removed #IBActions. To remove, control-click on the buttons in the Storyboard, and then click on the little x next to Touch Up Inside to remove the connection. Then you can delete #IBAction in your code.
if I remove them I have no way to differentiate between which segue
should be used
Not true. You have two different segues wired from two different buttons. The segue's identifiers are how you differentiate between the two segues.
I tried doing: myClass: ViewControllerTwo! = ViewControllerTwo();
When using segues, the segue creates the destination viewController for you.
This doesn't work because you are creating an entirely new ViewControllerTwo here. You are passing the values to this new instance, but this isn't the ViewControllerTwo that the Storyboard segue created for you.
When using segues, you should pass the data in prepare(for:sender:), just like you showed above. Get the destination ViewController and fill in the data you want to pass.
And now for the question you didn't ask:
How could I have done this using the #IBActions to trigger the
segue?
Remove the segues that are wired from your buttons.
Wire the segue from viewController1 to viewController2. Click on the segue arrow in the Storyboard and assign the identifier slideShow.
Assign unique tags to your two buttons. You can set this in Interface Builder. Give the first button tag 1, and the second button tag 2.
Your buttons can share the same #IBAction:
#IBAction func goToSlideShow(button: UIButton) {
self.performSegue(withIdentifier: "slideShow", sender: button)
}
In prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let passedPic = segue.destination as! ViewControllerTwo
if let button = sender as? UIButton {
if button.tag == 1 {
passedPic.picsChosen = sLS1
} else if button.tag == 2 {
passedPic.picsChosen = sLS2
}
}
}
Currently working on my first IOS application. I have a purchase button, on success this currently sets a test button on the same view controller to hidden. Code is as follows
Decleration
#IBOutlet weak var Test: UIButton!
hide button on successful purchase
Test.isHidden = true
Now this works on my Test button, which is sat in the PurchaseViewController,class is the MasterViewController.Swift. (Purchase button that initiates this method is also in the same view controller)
PlanViewController also has a button, and class is also linked to MasterViewController.Swift. This has a separate button that i wish to hide on success of the purchase button.
When I utilise the same code as above for the button, it crashes, is their a limitation on manipulating other view controllers while you are not in it? I would have thought this worked given that they both have the Masterviewcontroller.swift as the class
Thanks
Although sometimes possible, it's generally not a good idea to directly manipulate one view controller's view from another view controller, as you are trying to do. Here is how I would do what you are trying to do.
First, set a segue identifier between your two view controllers by clicking on the segue in the storyboard and going to the attributes inspector. I suggest goToMasterViewController
In both MasterViewController.swift and PurchaseViewController.swift declare a variable var buttonHidden = false
In PurchaseViewController.swift add the following code, which will be called just before your segue to MasterViewController is performed:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "goToMasterViewController") {
let destinationController = segue.destination as! MasterViewController
destinationController.buttonHidden = buttonHidden
}
}
When you hide the button in PurchaseViewController, also set buttonHidden = true
And finally in MasterViewController.swift:
override func viewWillAppear(_ animated: Bool) {
testButton.isHidden = buttonHidden
}
I have a registration view controller. In it I process the input provided by the user soon as he clicks on the register button. This button in the view is connected to an IBAction where I validate the input provided.
How is t possible to trigger the segue and pass the data within the IBAction (not override prepareforsegue) to the next view controller? If the validation fails it shouldn't attempt to execute segue but rather stay on same view to display validation errors.
Thanks for support. The question has been asked in different ways before surely but I wasn't able to find a good combine of segue in ibaction and passing data too.
You can create a segue from one view controller to another and can put a check in your button's IBAction, like this:
if (validationPasses)
{
self.performSegueWithIdentifier("segueIdentifier", sender: self)
}
And if you don't won't to override your prepareforsegue, you can pass the data using delegates.
However, by overriding prepareforsegue, you can pass data to your next view controller like this:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueIdentifier") {
// pass data to next view controller
let vc : NextViewController = segue!.destinationViewController as NextViewController
vc.someVariable = someValue
}
}
Here NextViewController is your next viewcontroller, change its name to your next view controller and in this view controller define variable to whom you want to pass value, in this case it is someVariable
help me please. how to make a button to do some operation or change to another view? I want that by pressing on it, commands were complete then need that moved on another view. please tell me how else to do to another kind in which a transition is updated when you press the button, all the same. And there were set data from datamodel
#IBAction func addNew(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("Items", inManagedObjectContext:managedObjectContext!)
var item = Items(entity: entity!, insertIntoManagedObjectContext:managedObjectContext)
// I make the init from row coredata
item.titleIt = titleItem.description
item.textIt = textViewItem.description
var error: NSError?
managedObjectContext?.save(&error)
// MasterViewController.setValue(Items(), fromKey: "titlIt")
}
Add this at the end of your method:
let viewControllerToGoTo = ClassNameOfViewController()
self.presentViewController(viewController, animated: true, completion: nil)
Note: You may need to instantiate the view controller you want to present in a different way than just (). For example, you may want to load a view controller defined in a nib or storyboard.
To move to another view controller, you write these lines:
var viewController = ViewControllerNew()
self.presentViewController(viewController, animated: true, completion: nil)
But if you want to pass data to another view, I'd recommend using this method instead:
In your storyboard, select the view controller and drag from the blue-outlined yellow square at the top of the view controller to another view controller. A popup will appear, showing a list of segues. Select the item named 'Show'. Segues are placeholders for presenting view controllers -- you can call them at any time in your program. Select the segue, and under the menu on the right hand side type a name, for example 'toOtherScreen' into the text field labeled 'Identifier'. This will let you call the segue from a specific name.
Now go back to your IBAction for the button, and type this:
self.performSegueWithIdentifier("toOtherScreen", sender: self)
This will transition to the other screen. But what if you want to pass data to a screen, or do something when a segue is about to happen? Luckily for you, UIViewController class has a method named 'prepareForSegue', as shown below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toOtherScreen" {
println("yes")
}
}
In this, we check if the segue has an identifier of "toOtherScreen", and if so, print "yes" to the logs. Now, to pass data to another view controller, it is a little more tricky. In the other view controller file, add a variable at the start of the class:
class OtherViewController: UIViewController {
var dataPassed: String = ""
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
And in your prepareForSegue method in the main view controller, type this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toOtherScreen" {
let newViewController = segue.destinationViewController as OtherViewController()
newViewController.dataPassed = "NEW DATA"
}
}
Now it will change the variable named dataPassed in OtherViewController to 'NEW DATA'. You can see how you can achieve a lot through this simple way of passing data to other view controllers.
Hope I helped.
I am developing an app in Swift that, in gist, tells people the price of Bitcoin in various currencies. To select the currency, the user chooses from a list in a view controller with a UITableView. This is currencyViewController, and it is presented from my main screen, viewController.
What I want to happen is that, when the user dismisses currencyViewController, it passes a string to a UIButton in the main viewController.
Here's the prepareForSegue function that should pass the data:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentCurrency") {
currencySelector.setTitle("\currencySelected", forState: UIControlState.Normal)
}
}
CurrencySelector is a UIButton in the main viewController, and currencySelected is a variable in the second view controller, currencyViewController.
It gives the error "Invalid Escape Sequence In Literal"
So, I've narrowed it down to one of two issues:
The variables from viewController can't be "seen" from currencyViewController. If so, how can I modify the text of CurrencySelector from CurrencyViewController?
For some reason, when the user exits the pushed CurrencyViewControler, prepareForSegue isn't called.
What is going on here? Thanks, and apologies - I am but a swift newbie.
2 - "prepareForSegue" is called when you push a new view controller via the segue, but not when you dismiss it. No segue is called upon dismissal.
1 - A good way to do this would be the delegate pattern.
So the main view controller would be the delegate for the currencyViewController, and would receive a message when that controller is dismissed.
In the start of the currencyViewController file you prepare the delegate:
protocol CurrencyViewControllerDelegate {
func currencyViewControllerDidSelect(value: String)
}
and you add a variable to the currencyViewController:
var delegate : CurrencyViewControllerDelegate?
Now, the mainViewController has to conform to that protocol and answer to that function:
class MainViewController : UIViewController, CurrencyViewControllerDelegate {
//...
func currencyViewControllerDidSelect(value: String) {
//do your stuff here
}
}
And everything is prepared. Last steps, in prepareForSegue (MainViewController), you will set the delegate of the currencyViewController:
var currencyVC = segue.destinationViewController as CurrencyViewController
currencyVC.delegate = self;
And when the user selects the value in currencyViewController, just call that function in the delegate:
self.delegate?.currencyViewControllerDidSelect("stuff")
A bit complex maybe, but it's a very useful pattern :) here is a nice tutorial with more info if you want it:
http://www.raywenderlich.com/75289/swift-tutorial-part-3-tuples-protocols-delegates-table-views
You have to use the parantheses to eval variables in strings, i.e. println("\(currencySelected)")
To access variables in the second view controller (the one which is the destination of the segue) you have to get a reference to it:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentCurrency") {
let currencyViewController = segue.destinationViewController as CurrencyViewController // the name or your class here
currencySelector.setTitle("\(currencyViewController.currencySelected)", forState: UIControlState.Normal)
}
}