How to change variable value declared in a class from another class? - swift

I have a 2 viewcontrollers, in vC1 a variable isShowListAsked : Bool = false is declared , on clicking map button it goes to vC2 . In vC2 there is a button named List.
I want that :
after clicking List button it go back to vC1 and vC1 variable value should changed to true. But it is still remains False.
Please help me .
Thanks in advance.
On clicking List button i am able to go back to vC1 but not able to set isShowListAsked = true . i tried get{} set {}.
In vC1 :
class vC1 : UIViewController
{
var isShowListAsked : Bool = false
var IsShowListAsked : Bool {
get {
return isShowListAsked
}
set{
isShowListAsked = newValue
}
}
}
Then after clicking In vC2 :
class vC2 : UIViewController
{
var vc = vC1()
#IBAction func mapListSegmentTapped(_ sender: Any) {
if mapListSegment.selectedSegmentIndex == 1
{
vc.IsShowListAsked = true
}
if mapListSegment.selectedSegmentIndex == 0
{
vc.IsShowListAsked = false
}
}
}
After going back i am checking variable value in viewWillappear()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(" isshowListAsked = \(IsShowListAsked) ") // print false
}
Expected Result :
print(" isshowListAsked = \(IsShowListAsked) ") // print True
Actual Result :
print(" isshowListAsked = \(IsShowListAsked) ") // print false

Use Closures to solve your problem statement.
Create a closure in VC2 of type ((Bool)->())? and call it with the relevant true/false values in mapListSegmentTapped(_:) method, i.e.
class VC2: UIViewController {
var handler: ((Bool)->())?
#IBAction func mapListSegmentTapped(_ sender: Any) {
if mapListSegment.selectedSegmentIndex == 1 {
self.handler?(true)
} else if mapListSegment.selectedSegmentIndex == 0 {
self.handler?(false)
}
}
}
Now, set this closure when you're presenting the instance of VC2 from VC1 i.e.
class VC1 : UIViewController {
var isShowListAsked: Bool = false
func onTapMapButton(_ sender: UIButton) {
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "VC2") as? VC2 {
controller.handler = {[weak self] (value) in
self?.isShowListAsked = value
print(self?.isShowListAsked)
}
self.present(controller, animated: true, completion: nil)
}
}
}

Related

After passing Object to another ViewController the object becomes nil, Swift?

I am passing a Venue object from the PreferencesVC to DiscoverVC by declaring an instance of the class and setting the venue variable to the object being passed.
By the time the code executes beginLoadView() the Venue object is nil and therefore the if statement incorrectly executes
Console output:
Class PreferencesVC: UIViewController{
#IBAction func doneBtnTapped(_ sender: Any) {
print("inside doneBtnTapped selectedVenue.name: \(selectedVenue?.name ?? "selectedVenue is nil")")
guard let discoverVC = storyboard?.instantiateViewController(withIdentifier: "DiscoverVC") as? DiscoverVC else { return }
discoverVC.venue = self.selectedVenue!
DataService.run.updateUserDiscoveryPreferences(forUID: Auth.auth().currentUser!.uid, discoverableBool: discoverable!, preferences: preferences) { (success) in
self.dismissDetail()
}
}//end func
}
Class DiscoverVC: UIViewController{
var venue: Venue?{
didSet{
print("DiscoverVC venue name: \(venue?.name ?? "venue name")")
}
}
override func viewWillDisappear(_ animated: Bool) {
print("inside viewWillDisappear")
venue = nil
}//end func
override func viewWillAppear(_ animated: Bool) {
print("inside viewWillAppear")
beginLoadView()
}
func beginLoadView(){
print("inside beginLoadView venue.name: \(venue?.name ?? "'venue' is nil")")
if venue != nil {
print("Venue var is not empty venue.name: \(String(describing: venue?.name))")
Utilities.run.showSVHUDWithStatus(uiView: self.view, status: "Discovering")
setupViews()
getCurrentLoggedUserProfile()
showHudAndStartUpdatingLocation()
observeUsersChanges()
} else {
print("Venue var is empty venue.name: \(String(describing: venue?.name))")
Utilities.run.showSVHUDWithStatus(uiView: self.view, status: "Discovering")
fetchNearestVenue()
getCurrentLoggedUserProfile()
}// end if-else
}//end func
}
}
Class VenueDetailsVC: UIViewController{
func presentDiscoverVC(){
guard let discoverVC = storyboard?.instantiateViewController(withIdentifier: "DiscoverVC") as? DiscoverVC else { return }
discoverVC.venue = self.venue!
discoverVC.showBackButton = true
DispatchQueue.main.async {
self.presentDetail(discoverVC)
}
}//end func
}
As I saw, discoverVC variable is not use to present and discoverVC variable you did set venue is diffirent DiscoverVC object presented. You can try print address of discoverVC and in viewWillAppear.

How to manage States between ViewControllers in Swift?

I am struggling to address of what I believe to be a State issue between ViewControllers.
The navigation path is as follows;
1.User navigates from VenueDetailsVC to DiscoverVC and passes a Venue object as such:
func presentDiscoverVC(){
guard let discoverVC = storyboard?.instantiateViewController(withIdentifier: "DiscoverVC") as? DiscoverVC else { return }
discoverVC.venue = self.venue!
discoverVC.showBackButton = true
DispatchQueue.main.async {
self.presentDetail(discoverVC)
}
}
2.User can then navigate from DiscoverVC to PreferencesVC and passes the same Venue object while clearing the local Venue object as such:
class DiscoverVC: UIViewController{
var venue: Venue?{
didSet{
print("DiscoverVC venue name: \(venue?.name ?? "venue name")")
}
}
#IBAction func preferencesBtnTapped(_ sender: Any) {
guard let preferencesVC = storyboard?.instantiateViewController(withIdentifier: "PreferencesVC") as? PreferencesVC else { return }
preferencesVC.selectedVenue = self.venue!
presentDetail(preferencesVC)
}
override func viewWillAppear(_ animated: Bool) {
print("inside viewWillAppear venue: \(venue?.name ?? "venue name")")
}
override func viewWillDisappear(_ animated: Bool) {
venue = nil
}
}
3.User then navigates back to DiscoverVC from PreferencesVC passing the same Venue object
#IBAction func doneBtnTapped(_ sender: Any) {
guard let discoverVC = storyboard?.instantiateViewController(withIdentifier: "DiscoverVC") as? DiscoverVC else { return }
discoverVC.venue = self.selectedVenue!
DataService.run.updateUserDiscoveryPreferences(forUID: Auth.auth().currentUser!.uid, discoverableBool: discoverable!, preferences: preferences) { (success) in
self.dismissDetail()
}
}
PROBLEM: Is that the Venue object inside the DiscoverVC is nil even though it has been passed back from PreferencesVC on step three above??
Here is the console output:
When you are doing this:
#IBAction func doneBtnTapped(_ sender: Any) {
guard let discoverVC = storyboard?.instantiateViewController(withIdentifier: "DiscoverVC") as? DiscoverVC else { return }
discoverVC.venue = self.selectedVenue!
You are not setting the venue of the Discover VC that presented the Preferences VC. You are setting the venue of a brand new Discover VC.
You can use the delegate pattern to pass the venue back.
Create a PreferencesVCDelegate:
protocol PreferencesVCDelegate: class {
func preferencesVC(_ vc: PreferencesVC, didDismissWithVenue venue: Venue)
}
Add a delegate property in PreferencesVC:
weak var delegate: PreferencesVCDelegate?
Replace the line that sets venue with a call to the delegate method:
#IBAction func doneBtnTapped(_ sender: Any) {
guard let discoverVC = storyboard?.instantiateViewController(withIdentifier: "DiscoverVC") as? DiscoverVC else { return }
delegate?.preferencesVC(self, didDismissWithVenue: self.venue)
When you present the PreferencesVC, set self as the delegate:
#IBAction func preferencesBtnTapped(_ sender: Any) {
guard let preferencesVC = storyboard?.instantiateViewController(withIdentifier: "PreferencesVC") as? PreferencesVC else { return }
preferencesVC.selectedVenue = self.venue!
preferencesVC.delegate = self
presentDetail(preferencesVC)
}
And conform DiscoverVC to PreferencesVCDelegate. This is where you set discover VC's venue
extension DiscoverVC: PreferencesVCDelegate {
func preferencesVC(_ vc: PreferencesVC, didDismissWithVenue venue: Venue) {
self.venue = venue
}
}

How to pass data backwards to a view controller after passing forward?

Im developing a quiz app and there is a second view controller that appears after the initial view controller where you are asked to answer a question. On the second view controller you the user must press a button to return to the initial view controller to be asked another question. However when I segue back from the second view controller I believe a new instance of the initial view controller is being created and the user is asked questions they have already answered. The code in the swift file for my initial view controller is constructed that when once a user is asked a question once that question is removed from an array by it's index. How could I make it to where a new instance of the initial view controller isn't created from segueing from the second view controller? Or is there a way the second view controller can access the same methods as the initial view controller?
Here is my code for the initial view controller:
import UIKit
class ViewController: UIViewController {
var questionList = [String]()
func updateCounter() {
counter -= 1
questionTimer.text = String(counter)
if counter == 0 {
timer.invalidate()
wrongSeg()
counter = 15
}
}
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
questionTimer.text = String(counter)
}
let rand = Int(arc4random_uniform(UInt32(questionList.count)))
questionLabel.text = questionList[rand]
//matching answer values to go with question keys
var choices = QADictionary[questionList[rand]]!
questionList.remove(at: rand)
//create button
var button:UIButton = UIButton()
//variables
var x = 1
rightAnswerBox = arc4random_uniform(4)+1
for index in 1...4
{
button = view.viewWithTag(index) as! UIButton
if (index == Int(rightAnswerBox))
{
button.setTitle(choices[0], for: .normal)
}
else {
button.setTitle(choices[x], for: .normal)
x += 1
}
randomImage()
}
}
let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]
//wrong view segue
func wrongSeg() {
performSegue(withIdentifier: "incorrectSeg", sender: self)
}
//proceed screen
func rightSeg() {
performSegue(withIdentifier: "correctSeg", sender: self)
}
//variables
var rightAnswerBox:UInt32 = 0
var index = 0
//Question Label
#IBOutlet weak var questionLabel: UILabel!
//Answer Button
#IBAction func buttonAction(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerBox))
{
rightSeg()
print ("Correct!")
}
if counter != 0 {
counter = 15
questionTimer.text = String(counter)
}
else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
randomQuestion()
}
override func viewDidAppear(_ animated: Bool)
{
randomQuestion()
}
//variables
var counter = 15
var timer = Timer()
#IBOutlet weak var questionTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
Here's the code to the second view controller:
import UIKit
class ContinueScreen: UIViewController {
//correct answer label
#IBOutlet weak var correctLbl: UILabel!
//background photo
#IBOutlet weak var backgroundImage: UIImageView!
func backToQuiz() {
if let nav = self.navigationController {
nav.popViewController(animated: true)
}
else {
self.dismiss(animated: true, completion: nil)
}
}
#IBAction func `continue`(_ sender: Any) {
backToQuiz()
}
What you need, sir is a delegate or an unwind segue. I far prefer delegates because they're easier to understand and I think a bit more powerful.
In your ContinueScreen view controller define a protocol similar to this:
protocol QuizCompletedDelegate {
func completedQuiz()
func canceledQuiz()
}
Also in your ContinueScreen view controller you need to declare an optional delegate of type QuizCompletedDelegate
var delegate: QuizCompletedDelegate?
... and use that delegate to call the functions when appropriate:
#IBAction func didPressContinue(_ sender: Any) {
if allQuestionsAnswered == true {
delegate.completedQuiz()
} else {
delegate.cancelledQuiz()
}
}
In your initial view controller is where you implement the functions for the protocol:
extension ViewController: QuizCompletedDelegate {
func completedQuiz() {
//Handle quiz complete
}
func cancelledQuiz() {
//Handle quiz cancelled
}
}
Then the last thing you need to do is set the delegate of your ContinueScreen view controller in the prepareForSegue function of your initial view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showContinueScreen" {
let continueVC = segue.destination as! ContinueScreen
continueVC.delegate = self
}
}
Remove the call to randomQuestion() on your initial view controller's ViewDidAppear, and you're in business!
It shouldn't create a new View Controller when you go back. If it does - it's very bad - and you should re-architect the flow of your app... To pass data forward, we should use Dependency Injection. To pass data to the previous view controller - use the iOS Delegation Pattern. I recommend to spend 30-40 mins to read (and understand this article), and it will make things more clear for you in the future: http://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/.

If (Bools = true) not executing -Swift

Attempting to execute a modal segue when multiple different Bool variables are all true (activated true through IBAction button push), however, nothing is happening- here is how they are all setup-
UIViewController {
// INITIAL TO CHECK WHICH BUTTONS HAVE BEEN PUSHED //
var 1Check = Bool()
// Checks //
#IBAction func 1(_ sender: AnyObject) {
1Check = true
}
and here is the execution-
viewDidLoad() {
super.viewDidLoad()
MoveOn()
}
func MoveOn(){
if (1Check == true && 2Check == true ...) {
self.performSegue(withIdentifier: "NewScreen", sender: nil)
}
}
what am I missing? Thanks!
The call to MoveOn() needs to be in a place where it will be called every time one of those checked values changes:
UIViewController {
// INITIAL TO CHECK WHICH BUTTONS HAVE BEEN PUSHED //
var 1Check = Bool()
// Checks //
#IBAction func 1(_ sender: AnyObject) {
1Check = true
MoveOn()
}
viewDidLoad() {
super.viewDidLoad()
}
func MoveOn(){
if (1Check == true && 2Check == true ...) {
self.performSegue(withIdentifier: "NewScreen", sender: nil)
}
}
}

Segue causes model data to disappear

I am trying to pass data from my model in my file, CalculatorBrain, to a ViewController through a segue. The var that I am trying to pass is designated as a PropertyList as shown below:
var program: PropertyList { // guaranteed to be PropertyList
get {
return opStack.map { $0.description }
}
set {
if let opSymbols = newValue as? Array<String> {
var newOpStack = [Op]()
for opSymbol in opSymbols {
if let op = knownOps[opSymbol] {
newOpStack.append(op)
} else if let operand = NSNumberFormatter().numberFromString(opSymbol)?.doubleValue {
newOpStack.append(.Operand(operand))
}
}
opStack = newOpStack
}
}
}
This is a multiple MVC project, and the override segue function is in a ViewController called "CalculatorViewController." The function tries to pass the data from the model to the ViewController known as "GraphingViewController" like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var destination: UIViewController? = segue.destinationViewController
if let navCon = destination as? UINavigationController {
destination = navCon.visibleViewController
}
if let gvc = destination as? GraphingViewController {
gvc.program = brain.program
}
}
I set-up the variable program in my GraphingViewController as follows:
var program: AnyObject?
When I press the button that calls the segue, it does fire. What happens is that the data in brain.program gets copied into gvc.program. However, it is then LOST to the model. I don't know why this happens. When I print the program out from the controller in the segue, I do see the program as it should be shown. However, when I print it anywhere after the segue, it has disappeared.
Why would data, after the override segue function is called, be removed?
EDIT: This is the delegate function in the GraphingViewController. When I print out brain.program from here, it doesn't print out the full opStack.
func graphForGraphView(xAxisValue: CGFloat, sender: GraphView) -> CGPoint? {
print("BRAIN PROGRAM 2: \(brain.program)")
brain.variableValues[brain.variableM] = 40.0
if let yValue = brain.returnEvaluate() {
print(yValue)
print("The returned value is: \(yValue)")
let point = CGPoint(x: 40.0, y: CGFloat(yValue))
if !point.x.isNormal || !point.y.isNormal {
return nil
} else {
return point
}
}
return nil
}