Increase a counter in the first interface controller by pressing a button in the second interface controller - swift

I would like to increase the value of the property in the first interface controller in the IBAction (Add1) method of the second interface controller
and then use the value of this property to update the label when the first view controller is activated
I was able to increase the value , but the value increases even if I press the back button.
I need to find a solution so that when I press the IBAction in the second interface controller , I can get that result and use it in the first interface controller and update the label.
here is the code:
First interface controller:
Blockquote
#IBOutlet weak var resultButtonLabel: WKInterfaceButton!
#IBAction func resultButton() {
pushControllerWithName("secondInterfaceController", context: self)
}
override func willActivate() {
super.willActivate()
resultButtonLabel.setTitle("\(counter++)")
}
Blockquote
Second interface controller:
Blockquote
var counter = 1
#IBAction func weScored() {
counter++
popController()
}
Blockquote

The easy way to implement this idea is use of NSUserDefaults
In your firstViewController you can read values form NSUserDefaults this way:
override func viewDidLoad() {
super.viewDidLoad()
let score = NSUserDefaults().integerForKey("Score")
resultButtonLabel.text = "\(score)"
}
and into your SecondViewController you can increase this counter with NSUserDefaults this way:
import UIKit
class SecondViewController: UIViewController {
var counter = Int()
override func viewDidLoad() {
super.viewDidLoad()
counter = NSUserDefaults().integerForKey("Score")
}
#IBAction func weScored(sender: AnyObject) {
counter++
NSUserDefaults().setInteger(counter, forKey: "Score")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
}
Hope this will help.

Related

No Storyboard Member within my ViewController when calling another view controller

When I run my code below within my view controller
import UIKit
class SecondViewController: UIViewController {
let fifthVC = self.storyboard?.instantiateViewController(withIdentifier: "fifthVC") as? FifthViewController
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func twentyLife() {
fifthVC.lifePointsInt = 20
}
#IBAction func thirtyLife() {
fifthVC.lifePointsInt = 30
}
#IBAction func fortyLife() {
fifthVC.lifePointsInt = 50
}
}
I am getting error
Value of type '(SecondViewController) -> () -> SecondViewController'
has no member 'storyboard'
How can I fix this?
I don't know what you are trying to do here but to remove that error , you should call the following line inside some function
let fifthVC = self.storyboard?.instantiateViewController(withIdentifier: "fifthVC") as? FifthViewController
You can either call it in viewDidLoad or you can make a separate function and call it. But first you need to define fifthVC variable in view controller.
var fifthVC:UIViewController?
Then inside viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
fifthVC = self.storyboard?.instantiateViewController(withIdentifier: "fifthVC") as? FifthViewController
}

How do you update the value you delegated from one class to the other?

I'm having an issue with Delegating. I'm relatively new to the concept, and but conceptually I get it and it's importance. I'm just having trouble using it. I can't seem to pass data from one class to the other. I know there are existing examples of delegation out there on stack overflow but they aren't quite capturing my misunderstanding. I get the use of protocols, delegation, and calling it in a class. I believe there just some small nuance that I'm missing... And it's visible in the lack of functionality in my code
//my protocol:
protocol StingHolder {
func StringPasser(ThisText text: String)
}
Creating the delegate protocol relation, places data to be passed then dismisses the View Controller
// my classes for placing data to be passed
class changeLabel: UIViewController,UITextFieldDelegate{
var Delegate: StingHolder?
#IBOutlet weak var TexrBeingPassed: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
TexrBeingPassed.delegate = self
// Do any additional setup after loading the view.
}
#IBAction func ButtonPassingDataOtherView(_ sender: Any) {
Delegate?.StringPasser(ThisText: TexrBeingPassed.text!)
dismiss(animated: true, completion: nil)
}
}
Creates an instance of the change lable class and its delegate and sets itself to be the delegate *supposedly changes the label, but It doesn't
///class to receive data
class ViewController: UIViewController{
#IBOutlet weak var LableName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
var lableChange = changeLabel()
lableChange.Delegate = self
// Do any additional setup after loading the view.
}
#IBAction func EditController(_ sender: Any) {
var storyBoard = UIStoryboard(name: "Test", bundle: nil)
var ViewController = storyBoard.instantiateViewController(withIdentifier: "TestView")
self.present(ViewController, animated: true, completion: nil)
}
}
inherits the protocol and tells it to change the label to whatever the changelabel class delegate has passes
// extension view controller inheriting the protocol
extension ViewController : StingHolder{
func StringPasser(ThisText text: String){
print("Delegate is working")
LableName.text = text
///
}
}
I want the one view controller to edit the text label of another view controller
The object which you have self as a delegate of, is not the same object presented on the screen.
override func viewDidLoad() {
super.viewDidLoad()
// "labelChange.delegate" is set...
var lableChange = changeLabel()
lableChange.Delegate = self
// Do any additional setup after loading the view.
}
#IBAction func EditController(_ sender: Any) {
var storyBoard = UIStoryboard(name: "Test", bundle: nil)
// but "ViewController" is presented
var ViewController = storyBoard.instantiateViewController(withIdentifier: "TestView")
self.present(ViewController, animated: true, completion: nil)
}
labelChange and ViewController are two different, independent objects. One created by calling init directly, and the other created by calling storyBoard.instantiateViewController. You should set the delegate of the latter instead:
override func viewDidLoad() {
super.viewDidLoad()
// "labelChange.delegate" can be deleted
}
#IBAction func EditController(_ sender: Any) {
var storyBoard = UIStoryboard(name: "Test", bundle: nil)
if let ViewController = storyBoard.instantiateViewController(withIdentifier: "TestView") as? labelChange {
ViewController.delegate = self
self.present(ViewController, animated: true, completion: nil)
}
}

Swift 4 Delegate doesn't work from childViewController

I have to code simple program. Just one login screen waiting for some password and after setting proper one, change screen to "program itself" and its features.
I decided to do it by container View and its subviews. everything works fine but delegate which should be responsible for triggering this change doesn't respond. In any option I tried, nothing has changed... it still doesn't respond.
This is code of ViewController (main):
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var container: NSView!
var vc1 : ViewController1 = ViewController1()
var vc2 : ViewController2 = ViewController2()
override func viewDidLoad() {
super.viewDidLoad()
//initialise of delegate:
vc1.delegate = self
// set subviews:
vc1 = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "ViewController1")) as! ViewController1
vc2 = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "ViewController2")) as! ViewController2
self.addChildViewController(vc1)
self.addChildViewController(vc2)
// set first view:
vc1.view.frame = self.container.bounds
self.container.addSubview(vc1.view)
}
func changeViews()
{
for sView in self.container.subviews {
sView.removeFromSuperview()
}
// set second view:
vc2.view.frame = self.container.bounds
self.container.addSubview(vc2.view)
}
}
extension ViewController: ViewController1_Delegate {
func passwdEntered(_ correctPasswd: Bool) {
if correctPasswd == true{
changeViews()
}
}
}
and this is First (SubView) ViewController code, where I'm entering passwd:
import Cocoa
protocol ViewController1_Delegate: class {
func passwdEntered(_ correctPasswd: Bool)
}
class ViewController1: NSViewController{
#IBOutlet weak var passwordField: PasswordField!
weak var delegate: ViewController1_Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func passwordFIeldEdited(_ sender: PasswordField) {
delegate?.passwdEntered(true/*for testing purpose always true*/)
}
}
Finally Second (SubView) ViewController code:
import Cocoa
class ViewController2: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
What I'm doing wrong?
First you say this:
var vc1 : ViewController1 = ViewController1()
override func viewDidLoad() {
super.viewDidLoad()
vc1.delegate = self
Then you say this:
vc1 = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"),
bundle: nil).instantiateController(
withIdentifier: NSStoryboard.SceneIdentifier(
rawValue: "ViewController1")) as! ViewController1
So, you set yourself as delegate on a ViewController1 instance, but then, in the very next line, you throw away that ViewController1 and replace it with another!
Move the line v1.delegate = self down to after you set the real value of vc1.

How do I pass data from a UIViewController to UITabBarController?

In UIViewController:
class ViewController2: UIViewController {
var points = 0
var pressed = false
#IBOutlet weak var label: UILabel!
#IBAction func slider(_ sender: UISlider) {
number = Int(sender.value)
label.text = String(number)
}
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
}
}
I am trying to do something in a TabBarController if a button in a UIViewController is pressed and also add a number to the number in another TabBarConroller.
Image 1: This shows the connection between my ViewControllers.
Image 2: This shows the first two ViewControllers.)
Image 3: This shows the third and fourth ViewController
Here is my storyboard. I've put a few words to describe what I am trying to do in the images. Please tell me if you need a clearer description. Thank you!
If the ViewController is a child of the UITabBarController that you want to access, you can simply use tabBarController property of the UIViewController, e.g., use this to change selected controller to the first one:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
self.tabBarController?.selectedIndex = 0
}
So let's say that you have a custom UITabBarController subclass, e.g.:
class CustomTabBarController: UITabBarController {
func acceptData(points: Int) {
print(">>> Accepted: \(points)")
// or do anything you need to do with it
}
}
Then you can pass it data as follows:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
if let customTabController = self.tabBarController as? CustomTabBarController {
customTabController.acceptData(points: self.points)
}
}
UPDATE
Since it seems that the current VC is presented by one of the tabBarController child controllers, you will have to access it through the self.presentingViewController:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
if let customTabController = self.presentingViewController?.tabBarController as? CustomTabBarController {
customTabController.acceptData(points: self.points)
}
}
UPDATE 2
Your screenshot are of a very poor quality, your explanation of the problem would require a clarification too, since it is really hard to understand what you try to do. So after the whole discussion in comments I guess this is it:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
if let tabController = self.presentingViewController?.tabBarController,
let viewController3 = tabController.viewControllers?.filter({ $0 is ViewController3 }).first {
viewController3.acceptData(points: self.points)
}
}
You can pass data as normally
let vc:HomeVC = ApiUtillity.sharedInstance.getCurrentLanguageStoryboard().instantiateViewController(withIdentifier: "HomeVC") as! HomeVC
vc.tempCategoryArray = CategoryArray
self.navigationController?.pushViewController(vc, animated: true)
In your TabBarController class, take a variable say variableToBeSet
class TabBarController: UITabBarController
{
var variableToBeSet: Int = 0 // This is just an example. You can change it as per requirement.
// Rest of class implementation
}
Now in your ViewController :
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
let tabControllerInstance = self.tabBarController as! TabBarController
tabControllerInstance.variableToBeSet = localVariable // The value which you want to assign
}

Need to free memory after Modal Segues, but I need both my Segues to pass data from A to B and B to A

I am making a game for iOS with SpriteKit.
I have 2 Viewcontrollers. One is the GameViewController and the other one is the MenuViewController. Let's call them A and B respectively.
When the player dies, a function is called in GameScene.swift that launches a modal "Lost" Segue to B. There, the player can restart the game or buy a life and a "Back" Segue is called to A.
I need to dismiss the additional Views that get created each time I call a segue.
Problem is: I need the "Lost" Segue to send data about the Score to View B and I need the "Back" Segue to send data to View A about wether or not the player used a life.
I have implemented all this. But now I need to find how to dismiss old views that keep eating the device's memory, thus leading to lag and crash.
I have googled for hours and hours. No solution was adapted to my situation.
The solutions I found either caused my app to bug, data not to be passed or views not to be generated.
I will not add code here since there is a LOT. But I am sure the answer is actually really easy, just not for a beginner like me.
I think a possible solution would be an unwind segue from B to A ?
But do unwind segues pass data along ?
Moreover, I found no answer I could understand on how to use an unwind segue.
I exhausted all my possibilities. Stack Exchange is my last chance.
You definitely should use an unwind segue to return to the previous viewController, otherwise as you have found your memory usage increases until your apps quits.
I created the following example from your description. It uses a standard segue to move from the GameViewController to the MenuViewController and it uses an unwind segue to move from the MenuViewController back to the GameViewController.
The GameViewController has a Player Dies UIButton, a UITextField for entering a score, and a UILabel for displaying the lives.
The MenuViewController has a UILabel for showing the score, a Buy a Life UIButton for adding lives, and a Restart UIButton for returning to the GameViewController.
Here's the code:
GameViewController.swift
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var scoreTextField: UITextField!
#IBOutlet weak var livesLabel: UILabel!
var lives = 3
func updateLivesLabel() {
livesLabel.text = "Lives: \(lives)"
}
override func viewDidLoad() {
super.viewDidLoad()
updateLivesLabel()
}
// This is the function that the unwind segue returns to.
// You can call it anything you want, but it has to be in
// the viewController you are returning to, it must be tagged
// with #IBAction and it must take a UIStoryboardSegue as its
// only parameter.
#IBAction func returnFromMenu(segue: UIStoryboardSegue) {
print("We're back in GameViewController")
// Update the lives label based upon the value passed in
// prepareForSegue from the MenuViewController.
updateLivesLabel()
}
#IBAction func goPlayerDies(sender: UIButton) {
lives--
self.performSegueWithIdentifier("Lost", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Lost" {
let destinationVC = segue.destinationViewController as! MenuViewController
destinationVC.score = Int(scoreTextField.text ?? "") ?? 0
destinationVC.lives = lives
}
}
}
MenuViewController.swift
import UIKit
class MenuViewController: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
var lives = 0
override func viewDidLoad() {
super.viewDidLoad()
scoreLabel.text = "Score: \(score)"
}
#IBAction func buyLife(sender: UIButton) {
lives++
}
#IBAction func goRestart(sender: UIButton) {
self.performSegueWithIdentifier("Back", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Back" {
let destinationVC = segue.destinationViewController as! GameViewController
destinationVC.lives = lives
}
}
}
This is how you wire up the forward segue to be called programmatically:
Control-drag from ViewController icon to the MenuViewController:
Select Present Modally from the pop-up:
Click on the segue arrow between the viewControllers and give it an identifier in the Attributes Inspector:
This is how you wire up the unwind segue to be called programmatically:
Control-drag from ViewController icon to Exit icon:
Choose returnFromMenu from pop-up:
Click on the Unwind Segue in the Document Outline and give it the identifier "Back" in the Attributes Inspector on the right:
Alternate Answer
Instead of using segues, you can present and dismiss viewControllers manually. The advantage for your app is that the MenuViewController will be allocated only once and will persist for the life of the app. This same viewController will be presented and dismissed repeatedly, but it will not be deallocated which I suspect is leading to your crashes.
The GameViewController will be the initialViewController that is created by the Storyboard. The MenuViewController will be loaded in viewDidLoad of the GameViewController.
To make this work, you need to add an identifier to the MenuViewController so that it can be instantiated by name. Click on the MenuViewController in the Storyboard and set its Storyboard ID in the Identity Inspector:
Here is the code. Note that all mention of segues is gone. Note how viewWillAppear is used to update the viewControllers.
GameViewController.swift
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var scoreTextField: UITextField!
#IBOutlet weak var livesLabel: UILabel!
var menuViewController: MenuViewController?
var lives = 3
func updateLivesLabel() {
livesLabel.text = "Lives: \(lives)"
}
override func viewDidLoad() {
super.viewDidLoad()
menuViewController = self.storyboard!.instantiateViewControllerWithIdentifier("MenuViewController") as? MenuViewController
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
updateLivesLabel()
}
#IBAction func goPlayerDies(sender: UIButton) {
lives--
menuViewController?.score = Int(scoreTextField.text ?? "") ?? 0
menuViewController?.lives = lives
self.presentViewController(menuViewController!, animated: true, completion: nil)
}
}
MenuViewController.swift
import UIKit
class MenuViewController: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
var lives = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
scoreLabel.text = "Score: \(score)"
}
#IBAction func buyLife(sender: UIButton) {
lives++
}
#IBAction func goRestart(sender: UIButton) {
let destinationVC = self.presentingViewController as! GameViewController
destinationVC.lives = lives
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
}