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)
}
}
Related
I have an onboarding user flow:
Name -> Age -> Gender
Each of the screens shares the same structure:
Question (top)
Input (middle)
Continue (bottom)
I have a class OnboardingHelper.swift that creates a class to set the question box and continue button:
class UserOnboardingHelper{
var text: String
var questionbox: UIView
var viewController: UIViewController
var continueButton: UIButton
init(text: String, questionbox: UIView, viewController: UIViewController, continueButton: UIButton){
self.text = text
self.questionbox = questionbox
self.viewController = viewController
self.continueButton = continueButton
}
func setQuestionBox(){
//sets question box
}
func setContinueButton(){
//sets continue button
enableContinueButton()
addContinueButtonPath()
}
func enableContinueButton(){
//enables continue button
}
func disableContinueButton(){
//disables continue button
}
func addContinueButtonPath(){
//sets path of continue button based on which view
}
}
In each of the onboarding ViewControllers I am setting the class in ViewDidLoad():
class NamePageViewController: UIViewController, UITextFieldDelagate {
#IBOutlet weak var questionbox: UIView!
#IBOutlet weak var continueButton: UIButton!
#IBOutlet weak var inputLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let namePageSettings = UserOnboardingHelper(text: "What is your name", questionbox: questionbox, viewController: self, continueButton: continueButton)
namePageSettings.setQuestionBox()
namePageSettings.setContinueButton()
inputLabel.delegate = self
if nameIsFilled {
namePageSettings.enableContinueButton()
} else{
namePageSettings.disableContinueButton()
}
}
}
The issue is that in the ViewController I textFieldDidEndEditing() function which needs to call the namePageSettings class from viewDidLoad()
func textFieldDidEndEditing(_ textField: UITextField){
if (textField.text?.empty)!{
//I want to call disableContinueButton() from UserOnboardingHelper
} else {
//I want to enable enableContinueButton() from UserOnboardingHelper
}
}
Trying to understand if:
The overall approach is correct and if not, what's the best way
If the above approach is in the right direction, how should disableContinueButton() and enableContinueButton() be called?
Thanks in advance! Sorry if the approach is really dumb - I'm still trying to wrap my head around classes.
You can have the view controller have a weak reference to the onboarding helper, so you can still call helper methods without creating a retain cycle.
In NamePageViewController, add a property:
weak var userOnboardingHelper: UserOnboardingHelper?
Then, in UserOnboardingHelper's initializer, add:
self.viewController.userOnboardingHelper = self
You can now call the onboarding helper's methods in the view controller:
userOnboardingHelper.disableContinueButton()
userOnboardingHelper.enableContinueButton()
When using the storyboard, you can draw 2 buttons onto it, and add then add them to an array by creating:
#IBOutlet private var cardButtons: [UIButton]!
This outlet collection is now an array of both buttons that loads before viewDidLoad as a variable.
How do I create this array of buttons programmatically without using storyboard?
I've tried this but it gives me a declaration error:
class ViewController: UIViewController {
var cardButtons = [CardView]()
let cardOne = CardView()
cardButtons.append(cardOne)
...rest of viewController standard class
}
There a several points of time in the ViewController's lifecycle where you might want to fill the cardButtons array. For example you could do this in viewDidLoad.
class ViewController: UIViewController {
var cardButtons = [CardView]()
override func viewDidLoad() {
super.viewDidLoad()
let cardOne = CardView()
cardButtons.append(cardOne)
}
}
Keep in mind that viewDidLoad is called every time the View is loaded into memory. In my example cardOne would be recreated every time. To avoid this you could store cardOne in a instance var, as you did initially.
class ViewController: UIViewController {
var cardButtons = [CardView]()
let cardOne = CardView()
override func viewDidLoad() {
super.viewDidLoad()
cardButtons.append(cardOne)
}
}
As I said, there several points of time in the ViewController's lifecycle where you might want to fill the cardButtons array. Other functions could be:
viewDidAppear(), to fill the area every time the view appears.
init(), if you are not using storyboard at all.
Here is what I ended up doing:
class ViewController: UIViewController {
let uiView: UI = {
var ui:UI = UI()
let cardOne = CardView()
let cardTwo = CardView()
ui.addButton(item: cardOne)
ui.addButton(item: cardTwo)
return ui
}()
...rest of viewController standard class
}
class UI {
var cardButtons = [CardView]()
func addButton(button: CardView){
cardButtons.append(button)
}
}
This should work, but I've got no clue why it doesn't. The code is self-explanatory.
class Themer {
class func applyTheme(_ object: inout NSObject) {
//do theming
}
}
And I apply theme to the button like so:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(&button)
}
The button object is a variable, yet the compiler throws an error.
Since button is an object, this syntax
Themer.applyTheme(&button)
means that you want to change the reference to that object. But this is not what you want. You want to change the referenced object so you simply need to write
Themer.applyTheme(button)
Finally you also don't need the inout annotation
class Themer {
class func applyTheme(_ object: AnyObject) {
//do theming
}
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(self.button)
}
}
But...
However, what should your applyTheme method do? It receives AnyObject and then what? You could make it a little but more specific and use a UIView as param
class Themer {
class func applyTheme(view: UIView) {
//do theming
}
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(view: button)
}
}
Now you have a chance to write meaningful code inside Themer.applyTheme.
inout is for the case that you want to change the reference, that is replace one object with another object. That's a very, very, very bad thing to do with an IBOutlet. That button is used in a view, connected up to lots of things, and if you change the variable, all hell will break lose.
Apart from that, listen to appzYourLife.
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++
}
I am new to iOS programming and I'm trying to understand and begin implementing delegates to get information between view controllers and any other use they may have.
I've used this topic to get a little further, but I can't comment as i just created this account, so i can't ask a question on the post. I copied drewag's example but this line
#IBOutlet weak var delegate: ViewControllerBDelegate?
is giving me an error "IBOutlet property cannot have non-object type SecondViewControllerDelegate"
I deleted it and it runs but the information is not being sent between the two view controllers. I thought i was beginning to understand delegates but just getting them implemented is beginning to get frustrating. I've been at this for a few days now.
FirstViewController:
class FirstViewController: UIViewController, SecondViewControllerDelegate {
#IBOutlet weak var theMap: MKMapView!
func requiredText() -> String {
return "test"
}
SecondViewcontroller:
protocol SecondViewControllerDelegate {
func requiredText() -> String
}
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var delegate: SecondViewControllerDelegate?
#IBAction func decide(sender: AnyObject) {
if let actualDelegate = self.delegate {
self.label.text = actualDelegate.requiredText()
}
}
So my question simply is what am I doing wrong? I thought i followed the example correctly.
You need to declare SecondViewControllerDelegate like this:
#objc protocol SecondViewControllerDelegate {
func requiredText() -> String
}
This is just a quirk of the Swift compiler or the runtime. The #objc directive makes the compiler emit additional information about the protocol. At runtime, the program uses that information to verify the delegate implements the protocol's methods. Since these objects are loaded from a xib (or storyboard), the compiler can't verify it at compile time.
You do not set your actualDelegate = firstViewController,so actualDelegate is always nil.
If you use storyboard,set Identifier of firstViewController as "first"
then
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil);
var firstview = storyboard.instantiateViewControllerWithIdentifier("first") as FirstViewController?;
self.delegate = firstview;
// Do any additional setup after loading the view, typically from a nib.
}