I have 2 classes in 2 different Swift files (UIViewController).
In the first class I declare a var:
class HomeScreen: UIViewController {
var Score = 0
let blackColor = UIColor.blackColor()
#IBOutlet weak var ScoreLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
ScoreLabel.text = "Score: \(Score)"
}}
In the second class/file I want to increment this var:
class SecondVC: UIViewController {
#IBAction func ButtonPressed(sender: AnyObject) {
HomeScreen().Score++
}}
As you can see, I want to display the Score-var in a Label.
BUT there always stays "0"
What is the mistake?!
Thanks!!!
Your code HomeScreen().Score++ is creating a new instance and incrementing the score variable of that new instance, then that instance is being thrown away.
You need a reference to the actual HomeScreen instance being used. I recommend Passing Data between View Controllers as a reference on a few ways to do this.
This:
#IBAction func ButtonPressed(sender: AnyObject) {
HomeScreen().Score++
}
You are creating a new instance of HomeScreen, incrementing it's value, and then, at the end of the method, it is going out of scope, so all changes are lost.
You should be changing the values on an initialised object instead, something like instead:
#IBAction func ButtonPressed(sender: AnyObject) {
// homeScreen is a reference to an already existing local variable
homeScreen.Score++
}
Related
in the past 2 weeks I tried to solve this problem in Swift which I had.
I am quite new to Swift and developing IOS APP's. I want to change a property of an Label from a other Class. I know from other languages like Java that you can get the instance of an Class and change the Label from outside. But I don't know how to do this in Swift because I don't know where and how the ViewController is created as an Object.
I have a lot of sourcecode. That's why I created a simple Game and ViewController Class to show you my problem.
class ViewController: UIViewController {
#IBOutlet weak var Points1: UILabel!
var game = Game()
override func viewDidLoad() {
super.viewDidLoad()
}
}
class Game {
func IncrementPoints() {
//Here I want to Increment the Points by getting the Label from the ViewController
}
}
In my code it's very important that I change the Label or have access to the label from outside.
In this easy Example it would be possible to execute this function in the ViewController but that is not possible.
I would be very happy if anyone could help me :)
This is not a good approach and there are better ways to do it. But if you want to access label in game you need to pass current viewcontroller as argument in a function
class Game {
weak var parentVC: ViewController?
var points = 0
func setVC(vc:ViewController){
self.parentVC = vc
}
func incrementPoints(by value: Int) {
points += value
parentVC?.points1 = "\(points)"
}
}
And in viewController pass self as argument
class ViewController: UIViewController {
#IBOutlet weak var Points1: UILabel!
var game = Game()
override func viewDidLoad() {
super.viewDidLoad()
game.setVC(vc:self)
}
}
I have 2 controllers
and have got 1 global variable, the problem is if I go to controller 2 and click on button northAmericaClick, it will navigate back to control 1, but the value of global variable won't change!
this is my code
controller 1
class OurViewController: UIViewController {
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var selectedServer: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
selectedServer.setTitle(selected server, forState: UIControlState.Normal) // selected server this is global variable
}
controller 2
class selectServerController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func northAmericaClick(sender: AnyObject) {
selectedserver = "North America"
self.navigationController?.popViewControllerAnimated(true)
}
From
You need to use a delegate. Here is an example how do use a delegate in Swift.
On your first ViewController, set your delegate when you load the second VC:
For example, if you are using the Storyboard Editor:
var secondViewController = (segue.destinationViewController.visibleViewController as MySecondViewControllerClass)
secondViewController.delegate = self
Write a Protocol and define a func to write you values back
For example, create a file called "Protocol.swift" and write something like that:
protocol writeValueBackDelegate {
func writeValueBack(value: String)
}
Add the function to your FirstViewController
func writeValueBack(value: String) {
// this is my value from my second View Controller
}
And to your ViewControllerClass
class ViewController: UIViewController, writeValueBackDelegate
Go to the Second View Controller, and add the delegate here:
class SecondViewController: ViewController {
// delegate for FirstViewController
var delegate: writeValueBackDelegate?
On your Second View Controller, you can now use this to call the func in the first View Controller an pass data.
delegate?.writeValueBack("That is a value")
You also need to indicate that your first view controller implements the protocol: class ViewController: UIViewController, writeValueBackDelegate {
A part of doing it with delegate you also can create singleton class ViewControllersDataModel class and share the variable using it:
import Foundation
class ViewControllersDataModel {
static let sharedInstance = ViewControllersDataModel()
var selectedserver: String = ""
private init() {
}
}
And call it like this:
ViewControllersDataModel.sharedInstance.selectedserver = "Selected Option";
Ok, I can do this with this code, only check when viewWillDisapear and call the parent of this view controller in the navicationController:
override func viewWillDisappear(animated: Bool) {
if ((self.navigationController!.viewControllers.last?.isKindOfClass(ActivityMyViewController)) == true){
let backView:MyViewController = self.navigationController!.viewControllers.last as! MyDetailViewController
backView // do whatever you want
}
}
I hope this code can help you, good luck
thanks guys for helping ;)
it was very simple
i just use then when it comeback ^^"
override func viewWillAppear(animated: Bool) {
selectedServer.setTitle(selectedserv, forState: UIControlState.Normal)
}
Is it possible to have an #IB Action function inside of viewDidLoad() ?
The action is a simple one - a Stepper that increases other label.text values accordingly. However, the values that the stepper needs to work with depend on the return content of a url - which are only known after the viewDidLoad() of course.
So I think I can't have the IBaction way up on top before the viewDidLoad(), and the error I get if I try to do my IB action inside of the viewDidLoad() is:
"Only instance methods can be declared ‘IBAction' ”
EDIT
Let me clarify myself, sorry for the confusion. I know I need an outlet to get the UIStepper values from. I have that:
#IBOutlet weak var stepper: UIStepper!
I then have an action also connected to same UIStepper that will increase/decrease value of a label's text (new_total) accordingly:
#IBOutlet weak var new_total: UILabel!
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value))"
}
However, I want to start out with a value (todays_price) I'm getting back from a json request and use that as a starting point, to multiply it using the stepper and put the multiplied value into the label's text.
I have a struct in a separate file that defines my object so:
struct PassengerFromOtherBus {
var fname: String?
var lname: String?
var todays_price: Int?
init(json: NSDictionary) {
self.fname = json["fname"] as? String
self.lname = json["lname"] as? String
self.todays_price = json["todays_price"] as? Int
}
}
So later on in the view controller, inside of the viewDidLoad(), after connecting to the URL and then parsing it using NSJSONSerialization and a bunch of other code here (that I don't need to confuse you with) I finally have my value todays_price. So my question is, how do I get my action to use that value when it's only known inside of my viewDidLoad()? Xcode will not even let me connect the IBAction to anywhere inside the viewDidLoad function!
This is not done with an Action but with an Outlet. Connect the Stepper from IB as an Outlet to your ViewController. Then just set the values of the Stepper in ViewDidLoad.
I would never go directly from a UIStepper.value to UILabel.text.
Use an intermediary variable to store the value.
Do the same for the return from the JSON. By setting a didSet function on those variables you can update the UI when any of the values is updated.
class FirstViewController: UIViewController {
var todays_price: Int = 0 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
var stepperValue : Int = 1 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
#IBOutlet weak var myStepper: UIStepper!
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
//
let returnValueFromJson = 10
todays_price = returnValueFromJson
}
#IBAction func stepperUpdate(sender: AnyObject) {
stepperValue = Int(myStepper.value)
}
}
Just add a variable to the top of your view controller to hold the value from your json request. Then in viewDidLoad you update that variable, and then you can use it to set your label and inside the IBAction (that doesn't have to be inside viewDidLoad).
So you would do something like this:
class WhateverViewController: UIViewController {
var todays_price: Int!
override func viewDidLoad() {
super.viewDidLoad()
todays_price = // The value you got from json goes here
new_total.text = "\(todays_price)"
}
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value) * todays_price)"
}
}
As you can appreciate in this pic we have an App with three different VC's and a Last one with some variable data depending of the options selected on the previous ones.
So, for instance, in this case the user had selected a blue color, a suited style and a L as size.
Our idea is to pass data from the first VC, second VC, and the third VC to the gaps in Last VC.
Any suggestions? It would be very appreciated.
Create a model class where you can store those properties in :
class MyChoices {
var color : String? // or you could use enums for each of them
var style : String? // that would be a better choice, but for the
var size : String? // sake of simplicity I use strings in this example
}
then you pass a variable of type MyChoices from one VC to another in your prepareForSegue method
EDIT (some more info, see answer from FactorJose)
In VC 1 add your variable
class VC1: UIViewController {
#IBOutlet weak var nextOutlet: UIButton!
#IBOutlet weak var colourLabel: UILabel!
var choice : MyChoice?
...
and then further on :
#IBAction func redButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Red colour selected"
choice.color = "Red"
}
for all those IBActions.
then in your prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextVC = segue.destinationViewController as! VC2
nextVC.choice = self.choice
}
VC2 and VC3 are very similar again
Hi all! As you can appreciate in this pic we have an App with three different VC's and a Last one with some variable data depending of the options selected on the previous ones.
So, for instance, in this case the user had selected a blue color, a suited style and a L as size.
Our idea is to pass data from the first VC, second VC, and the third VC to the gaps in Last VC.
Any suggestions guys? It would be very appreciated
Code :
class VC1: UIViewController {
#IBOutlet weak var nextOutlet: UIButton!
#IBOutlet weak var colourLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
nextOutlet.hidden = true
}
#IBAction func redButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Red colour selected"
}
#IBAction func blueButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Blue colour selected"
}
#IBAction func greenButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Green colour selected"
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {}
}
VC2 and VC3 are the same as VC1 (same outlets and buttons)
lastVC
#IBOutlet weak var colourLabel: UILabel!
#IBOutlet weak var styleLabel: UILabel!
#IBOutlet weak var sizeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
And the last class to store the strings
import UIKit
class MyChoices {
var colour : String?
var style : String?
var size : String?
}
What can we do?
An alternative to Glenn's answer is to use NSUserDefaults. Think of it like a mini key-value database for PropertyLists.
You can add a new key-value to it with
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(clothingColor, forKey: "Clothing Color")
And then retrieve a previously saved key-value by doing
let defaults = NSUserDefaults.standardUserDefaults()
let clothingColor = defaults.objectForKey("Clothing Color")
Since the user defaults is shared and stored in disk, the information will persist across your different view controllers. It also respects encapsulation, which is good object orientation practice, since your size controller won't have to know anything about color or style and vice versa.
I have an experience where a user is rating a product, they do this by dragging sliders. Each slider has a related UILabel for the title and a UILabel for the value. I would like to avoid creating 12 functions, one for each slider and the associated label. I am new to development generally. I am guessing a class or an Array would be useful here, but am not sure how to use either. Here's the code that just updates the one value, and I know why that is, I'm just hoping to avoid declaring 12 variables for the value and 12 functions for each one.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var slider1: UISlider!
#IBOutlet weak var value1: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sliderSlide(sender: UISlider) {
value1.text = round(sender.value*100).description
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Well,you can use Outlet Collection and 'tag',
Drag every label into a same Outlet Collection
Drag every Slider IBAction into a same function
Then set the tag of slider as the index of label in Outlet Collection.
For example,you first drag labelA to Collection,then the tag is 0
Then all the code
import UIKit
class ViewController: UIViewController {
#IBOutlet var labels: [UILabel]!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sliderSlide(sender: UISlider) {
let index = sender.tag
let label = labels[index]
label.text = round(sender.value*100).description
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You can connect more than one slider to the same function. That's the purpose of the sender argument. You can do a switch statement on the pointer in swift, or by a set tag of the slider if you prefer. If it's all updating the same label, why even care which slider is updating?
I can think of two ways of doing this. Make a custom view controller that looks exactly the same as the code you have. It will have the slider and label as subviews. Then you'd only have 12 container views on your storyboard (not the greatest).
The other way would be using IBOutletCollections. These are what they sound like, collections of IBOutlets. Assign each slider that you place on the storyboard a unique tag from 0-11 (this is done under the attributes inspector). The tag will act as an index for the values array. Make sure that when you add each label to the collection, you do it in the correct order (it does matter!). Using IBOutletCollections, your code would look like this:
class ViewController: UIViewController {
#IBOutlet weak var sliders: [UISlider]!
#IBOutlet weak var values: [UILabel]!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sliderSlide(sender: UISlider) {
values[sender.tag].text = round(sender.value*100).description
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}