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
}
}
}
Related
I have a TableViewController embedded in a ViewController. My UITableView has toggles. When one of these toggles are toggled I need my number on the ViewController to increase by 1. The issue I am having is not being able to get the text to change. I have supplied a screenshot to help better illustrate.
What I have tried doing is setting a variable var colorViewController: ViewController? in the TableViewController and then trying to set the text from there using colorViewController?.displayNumber.text = screenCount when toggling a toggle. screenCount is the variable that increasing as the toggles are toggled.
How are you setting your colorViewController variable?
You need to set it to the current instance of your ViewController in your ViewController's prepare for segue.
First, you'll need to give the embed segue an identifier. Select the segue in the storyboard and give it an identifier, say ColorPickerTableViewControllerSegue.
Then handle this segue in your ViewController (this is Swift 3 code):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ColorPickerTableViewControllerSegue" {
if let colorPickerTableViewController = segue.destination as? ColorPickerTableViewController {
colorPickerTableViewController.colorViewController = self
}
}
}
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
}
}
}
Lets say I have 3 ViewControllers - A, B and C. A shows B, B shows C. Then I want to go back to A without going through (visually) B (like home button). My problem currently is that when I unwind to A, for short period of time B is shown (viewWillAppear and etc. methods are called). How can I resolve this?
Note1: The above example is highly simplified compared to my real app navigation tree and for me using NavigationController as container to all A,B and C is not possible (or at least not desirable). Few of the reasons are in the middle of the transition there are complex embed segues and different custom transition(almost all animated transitions are absolutely different) across all views.
Note2: I found some pseudo solution with defining custom segue and using it as a Custom Unwind Segue. That is not desirable too because I want to use my already done transition animators.
Any help is highly appreciated.
Please note that this is an amended answer - in response to the first comment below.
I have a possible solution which seems to work. But it may not be appropriate. I am not certain and offer it for consideration only.
The 3 view controllers are set up as described in the question (the second is red in order for me to notice whether it is visible during the unwind).
The unwind segue is created in the third view controller (C) by dragging to the Exit. Before doing this the required actions have to be added to the first and second view controllers.
Below is the code for the 3 view controllers. My fix - I am using a a global boolean called unwindCheck as a flag which is set true before the unwind but is otherwise false. If it is true then self.view.hidden = true in viewWillAppear for the second view controller. ie hidden during the unwind.
Please note that I have also added a second button on the third view controller - it calls the same unwind segue - the identifier property of which is set to "UnwindToFirstSegue". The second button is not a necessary part of this possible solution.
class FirstViewController: UIViewController {
#IBAction func unwindToFirstViewController(segue: UIStoryboardSegue) {
}
override func viewDidLayoutSubviews() {
unwindCheck = false
}
}
class SecondViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if unwindCheck == true {
self.view.hidden = true
}
}
#IBAction func unwindToSecondViewController(segue: UIStoryboardSegue) {
}
}
class ThirdViewController: UIViewController {
#IBAction func backToA(sender: AnyObject) {
performSegueWithIdentifier("UnwindToFirstSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier{
switch identifier {
case "UnwindToFirstSegue":
unwindCheck = true
default:
break
}
}
}
}
The unwind segue's identifier can be set by selecting it in the Document Outline and then going to the Attributes inspector.
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.
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)
}
}