My view controller:
class myViewController: UIViewController {
var myvar = 0
func count() {
print(myvar)
myvar+=1
}
}
Other view controller:
Interval(1) {
myViewController().count()
}
The code above is the code I got so far. But there seems to be an big issue:
The var called myvar gets kind of new initialized and so always gets it default value "0".
Why doesmyViewController get reset after calling myViewController().count()? How can I get rid of this behavior?
EDIT: The "myViewController" was never loaded before, maybe this causes the issue?!
EDIT: There must be some other way then just generating instances. Because multiple files are using multiple instances and this will cause error again! (cause a reset)
Any help would be VERY appreciated :)
Answering two of your questions:
Why:
myViewController is your Class. myViewController() is an instance of a class. You may have an unlimited quantity of instances of one class. And all of them by default will have a value 0. If you expect to see something else but not a 0 then you should get that particular instance of a class where you increased the myvar variable.
How to get rid:
There is no way to get rid of this. This is how it works.
Solution: please share with us your business logic / goal / explain what are you trying to achieve / what are you building / what's the final result?
But my guess is you want to do this:
let myControllerInstance = myViewController()
myControllerInstance.count()
myControllerInstance.count()
print(myControllerInstance.myvar) // expect 2 here
UPDATE: if it doesn't get called then call it:) maybe set an "initial view controller".
myViewController is the name of your view controller implementation. myViewController() is a call to initializer of that view controller implementation without any parameters. That means, myViewController() create a new instance. You call myViewController().count(), which increments the value, but for that newly created instance.
What you need here is to get a reference to a single myViewController instance that you present on the screen. Without knowing more about hierarchy of your view controllers, it is impossible to tell you how to get it (although you should have now an idea of what is a problem and how to approach it).
You are initializing a new instance of myViewController every time you call myViewController().count().
I guess instead you should have something like:
let myVC = myViewController()
Interval(1) {
myVC.count()
}
The var called myvar gets kind of new initialized
Not kind of, it does get new initialized.
There is a common mistake / misunderstanding: The default initializer myViewController() creates always a brand new instance of the controller which is different from all other instances and – more important – from the instance designed in the storyboard.
The way you can get rid of the issue depends on your design which is unclear.
Problem
myViewController()
This line of code right here creates a new instance of myViewController. myvar is set to zero upon creation of a myViewController. This is why it appears myVar is "being reset to zero" each time your interval function is called.
var myvar = 0 //Called whenever you use myViewController()
A more explicit way to write your class to make it more obvious would be:
class myViewController: UIViewController {
var myvar: Int
init() {
myvar = 0
}
...
Solution
let vc: myViewController = myViewController()
Interval(1) {
vc.count()
}
Pro Tips
Welcome to the world of programming! I'm very excited for you!
Stick with standard naming conventions: rename myViewController to MyViewController.
Don't increment a variable in a function named count. Logically, it makes no sense. Somebody, even you, later on may use this function, and forget that count() increments that internal variable. Instead, change count to do just that, get the count.
Pro Revision
class MyViewController: UIViewController {
var myvar = 0
func count() -> Int {
return myvar
}
func incrementCount() {
myvar += 1
}
}
...
let vc = MyViewController()
Interval(1) {
print(vc.count())
vc.incrementCount()
}
Suggested Reading
Pure Functions
Swift style guide
Related
The value of the variable 'switcheroo' in the view controller below is always the same when I attempt to access it via a singleton. I am trying to access its value from a custom label class that prints the characters of the label one by one. When the label is set, I try to get the updated value of switcheroo in the Viewcontroller singleton. However it always returns the initial value of switcheroo, not the updated value (which I can trace in the viewcontroller). Am I doing something wrong?
class TheViewController: UITableViewController, UIGestureRecognizerDelegate, UITabBarControllerDelegate {
static let shared = TheViewController()
var switcheroo = 0
... various operations that change the value of switcheroo...
}
class CustomLabel: UILabel {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var attributedText: NSAttributedString? {
didSet {
DispatchQueue.main.async {
let characterDelay = TimeInterval(0.01 + Float(arc4random()) / Float(UInt32.max)) / 100
for (index, _) in attributedText.string.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay * Double(index)) {
print("switcheroo value in TheViewController is now: \(TheViewController.shared.switcheroo)")
super.attributedText = attributedText.attributedSubstring(from: NSRange(location: 0, length: index+1))
}
}
}
}
I would not suggest making a view controller a singleton solely for the purpose of some shared state or model property. View controllers have their own life cycle patterns (e.g. instantiated from storyboard scenes, released when they are dismissed, recreated when presented again, etc.), and you’re likely to have issues arising from dealing with separate instances of your view controller(s).
Instead, don’t try to fight the standard view controller life cycle, but rather just move this property into a separate object, e.g.
final class StateManager {
static let shared = StateManager()
var switcheroo = 0
private init() { }
}
Then your view controllers can use that:
class ViewController: UIViewController {
...
func examineSwitcheroo() {
print(StateManager.shared.switcheroo)
}
func updateSwitcheroo(to value: Int) {
StateManager.shared.switcheroo = value
}
}
This way, you enjoy this shared state, without entangling normal view controller life cycles in this process.
Now, what the right name for this singleton, StateManager in my example, depends entirely upon what this shared property is. But there’s not enough information about what this switcheroo object really is to offer better counsel on this matter.
And, probably needless to say, it’s a separate question as to whether you really should be using singletons at all for state variables and model objects, but that’s beyond the scope of this question.
If you have determined that having a ViewController singleton is the right decision, the likely answer is that you are not using that shared instance every time, instead accidentally calling the initializer at some point in your project (possibly Xcode is doing it automatically through interfaces).
To search through your entire project, you can use cmd + shift + F and then type TheViewController(). There should only be one occurrence (the shared instance). Be sure to also check for TheViewController.init(). That will find any time you do it.
If the issue persists, perhaps try setting the shared instance to self in the viewDidLoad method of TheViewController?
Hope this helps!
Don't manage your application's data in your view controller(s). The Cocoa and Cocoa Touch frameworks use the MVC paradigm, where the M is meant to stand for model, i.e. the application's data model. Any data that needs to be preserved, or that's relevant beyond the scope of the view controller, should be stored and managed in a model object. If you give your view controller's a reference to the model when you create them, you never need to worry about passing data from one view controller to another; instead, they each operate on the model, and any data they need comes from the model.
I'm new to programming and I'm following a tutorial for fun to understand things in more detail. The tutorial I'm reading creates code that takes a Double and a Int type and multiply them. As we all know, they converted the Int to a Double to perform the operation. Here is the example code:
let hourlyRate: Double = 19.5
let hoursWorked: Int = 10
let totalCost: Double = hourlyRate * Double(hoursWorked)
After seeing this I said okay cool so let me see if I can do that in a passion project I'm working on. In the passion project code I use a UIStoryboard method that returns a UIViewController so I thought it would be interesting if I could do the same as the tutorial with my variable called controller in the following code:
class ItemListDataProviderTests: XCTestCase {
var controller: ItemListViewController!
var sut: ItemListDataProvider!
var tableView: UITableView!
override func setUp() {
super.setUp()
sut = ItemListDataProvider()
sut.itemManager = ItemManager()
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
controller = storyboard.instantiateViewController(withIdentifier: "ItemListViewController") as! ItemListViewController //compiles
controller = ItemListViewController(storyboard.instantiateViewController(withIdentifier: "ItemListViewController")) //causes error and code doesn't compile
_ = controller.view
tableView = controller.tableView
tableView.dataSource = sut
}
As you can see it didn't work out the way I hoped. I felt type casting was an extra step but I see that its necessary to make the code compile but I don't understand WHY that wouldn't work. What does the computer think I'm asking it to do which makes it not understand? I really want to know and understand why so any help is greatly greatly appreciated.
Double has an init method that takes an Int as an argument so you can write Double(intValue). You cannot do that with the ViewController classes above because there is no init that takes the other type, and in fact you do not want to create a new instance, you want to say 'this instance is actually this type', and you use 'as' for that.
With this line of code :
ItemListViewController(storyboard.instantiateViewController(withIdentifier: "ItemListViewController"))
you're trying to pass an argument of UIViewController into a constructor of ItemListViewController. No such constructor exists.
Also, by instantiating the view controller from the storyboard, you are given an instance of that view controller that's already loaded into memory. So there would be no need to instantiate another as you are doing
The correct way instead is to use the as! operator as you are doing on the line before.
I'm not sure what you're trying to do with controller = ItemListViewController(storyboard.instantiateViewController(withIdentifier: "ItemListViewController")), but this is both wrong and unnecessary. In the line just above you correctly instantiate the viewController and assign it to the controller variable. On this line you appear to be trying to do the exact same thing again, but in a different way. The reason it's failing is that I assume you don't have an initializer for ItemListViewController that takes a UIViewController as the only argument, which is what you're asking it to do. If I'm wrong, please post the code for that initializer so we can analyze. Either way, that line is completely unnecessary. Delete it and your code should compile.
I have two .swift files so I want one of them to modify the text of an IBoutlet label that is the other class.
Class 1:
class class1: UIViewController {
#IBOutlet var label1: UILabel!
}
Class 2:
class class2: SKScene {
var cool_variable = 1
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
}
by doing this I'm getting the following error message:
Instance member "label1" cannot be used on type "class2"
Thanks in advance!
The distinction and relationship between classes and instances is absolutely crucial to object-oriented programming. Thus, consider your proposed code:
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
The problem here is that class1 is the name of a class. (That would be more obvious if you just, please, follow the elementary rule that class names start with a capital letter.) But label1 is an instance property. Thus what you need here is the name of an instance of the class1 class — presumably a reference to the actual existing instance that is already part of the view hierarchy.
You never create an instance of class1 in class2 to access the variables.
My guess is that you are using Storyboards. In this case you wouldn't want to create an instance of class1. Instead you would use delegation (This would also be a good idea if you are not using Storyboards).
Delegation can be a complicated topic, so I will try to keep this simple.
First, you start with a protocol. Usually you name it something like <CLASS-NAME>DataSource, so you would do something like:
protocol class2DataSource: class {}
The class keyword is required for delegation protocols.
Then you would add the methods to it that you want called in other classes when you call a method in the class the protocol delegates for, so, for example, willCoolFunc:
protocol class2DataSource: class {
func willCoolFunc(with variable: Int)
}
You have the parameter so you can access the variable cool_variable as you are trying to.
Next, you need to create a a variable in class2 that is of type class2DataSource:
weak var dataSource: class2DataSource?
Make sure the variable is weak and an optional.
Next, call the method, you would do it in coolFunc:
func coolFunc () {
dataSource?.willCoolFunc(with: cool_variable)
}
Now you, to access cool_variable when the function is called, you need to implement class2DataSource on class1. Create an extension of class1 that implements class2DataSource and add the function willCoolFunc:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
}
}
Now you can access the variable cool_variable in class1! The reason why is because when you call class2.coolFunc(), the willCoolFunc method is called with cool_variable passed in. Any class that implements the class2DataSource can access cool_variable with the willCoolFunc method.
To finish of the method, here is what the extension would look like:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
self.label1.text = "\(variable)"
}
}
We are almost done, but not quite. We still have to set the class1 as the data source for class2DataSource. To do this, I will reference Nikolay Mamaev from this post:
Go to the Interface Builder.
Type "Object" in the search text field of the Objects Library and drag an 'Object' to your view controller containing that connects to
class1 (i.e. do the same as you add any view or view controller to
storyboard scene, with the exception you add not a view or view
controller but an abstract object).
In the left-side 'Scenes' panel of your storyboard, highlight just added Object; in right-side panel go to the 'Identity Inspector' and
type class2DataSource instead of pre-defined NSObject. In left side
panel ('Scenes'), 'Object' will be renamed to 'Class2 Data Source'
automatically.
In the left-side 'Scenes' panel of your storyboard, control-drag from your UIView [*not the controller*] to the 'Class2 Data Source';
in pop-up appeared, select dataSource outlet.
There! You now set class1's label1's text to the value of cool_variable when you call class2.coolFunc()!
I do not know the exact problem you're trying to solve, but I'm just going to consider the part that you want to access the variable in class1 in class2. There are two basic ways to go about this, one is Inheritance and the other one is by creating an object. These are basic Object Oriented Programming concepts which you need to be familiar with.
Swift does not support multiple inheritance. So that rules out inheritance for solving you problem, since you are inheriting two classes SKScene and UIViewController.
Create an object in the class1 and call the function coolFunc
class class1: UIViewController {
#IBOutlet var label1: UILabel!
func modifyLabel(){
let someObject = class2()
someObject.coolFunc()
}
}
Of course this solution might not be the one you're looking for. You will have to explain more about the problem you're facing if you need a solution that will work for you.
First q here, so trying to get protocol right...what I've done works, in that the data and views display correctly, but memory is not deallocating (still getting used to ARC after many years of allocating/deallocating), and I'm trying to figure out the right strategy. Document based app. When doc is created, view controller is instantiated, which creates several views which need to refer to each other for size/position/methods, and all of which need access to the doc data.
class MyDoc: UIDocument {
var data: Int
etc...
}
class MyController: UIViewController {
var doc: myDoc! // code which creates MyDoc instance assigns self to this property
var thisView1: MyView1!
var thisView2: MyView2!
thisView1 = MyView1(...)
thisView2 = MyView2(...)
thisView1.theOtherView2 = thisView2
thisView2.theOtherView1 = thisView1
thisView1.doc = self.doc
thisView2.doc = self.doc
}
class MyView1: UIView {
var theOtherView2: MyView2!
var doc: MyDoc!
}
class MyView2: UIView {
var theOtherView1: MyView1!
var doc: MyDoc!
}
I don't think I excluded anything meaningful. Assigning thisView1 and thisView2 to each other creates a strong reference cycle, right? I tried combining an unowned property on one with implicitly unwrapped on the other, per The Swift Programming Language, but kept having trouble with the init() methods of the views. Is using weak references and then unwrapping the optional all the time (though I make sure there's a valid value before proceeding from viewController) the only thing that'll work? I've read so many explanations that each new one now confuses me more.
Thank you!
For any object created I generally use two two scopes 1) Singleton 2) {local scope}. I am looking for something in between.
Say I have one object that 5 view controllers are editing. I want to share an object between view controllers without having to pass it between view controllers. But it should not also live throughout application since once I am done editing the object i don't need it anymore.
I don't want to inherit all view controller from another class an create a variable there. Since view controller are reusable for different objects. I want to create an object that comes to life before launch of first view controller, lives throughout the scope of 5 view controllers and then dies after I have saved it someway. Is there anyways I could do this in iOS.
An alternative is to use your AppDelegate. Within it you can declare a global var than 2 functions, a first one to get the current value and another one to set the value.
It might give something like this:
// Get AppDelegate instance
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate;
// Use your getter to get the current value
var something = appDelegate.getYourStuff();
// Or use a setter to set it, a modifier to modify oit
appDelegate.setYourStuff(yourStuff);
appDelegate.modifiyYourStuffAttribute(newAttributeValue);
Don't realize if such a method is a bad practice or not, but it works for me.
Open to other suggestions!
As Mat said you can do is in that what. For me better is to create specific class for that that will do one particular job.
class EditingSession {
class Factory {
private static let session = EditingSession() //do it lazy
static func create() -> EditingSession {
return session
}
}
func openSession() {
}
func endSession {
}
func getData () -> AnyObject {
...
}
}
In editing session create private initializer. Factory should give the shared instance.
You can pass this class to your ViewControllers and manipulate with the data. You can inject it or just set as property in your VC or VM.
If you are done with editing you should end session and clear data or other stuff.