Why can't I create an array in ViewController? - ios

When I try to create an array of UIButtons in the ViewController
Instance member 'tile11' cannot be used on type 'ViewController'
It works when I create it inside a method, but I need the array in more than one method, and it's getting annoying to having to create the array again and again.
class ViewController: UIViewController {
let tileArray: Array<UIButton> = [tile11, tile12, tile13, tile21, tile22, tile23, tile31, tile32, tile33]
}

There isn't enough code to tell for sure, but I'm guessing tile11 and the other tiles are other instance variables (button outlets to be specific)? They would technically not be accessible until run-time. This is why you cannot instantiate tileArray like that, but why it works in a method. The only way to instantiate tileArray in line with the declaration is to use constants in the array. You should initialize tileArray in viewDidLoad().

Related

How to make an array of UIImageViews at class level in Swift?

When I try to make an array with UIImageView s declared as #IBAction I receive the "Cannot use instance member within property initializer, property initializers run before self is available" compiler error. How do I workaround this?
As the error explains, you can't use instance members when you initialize the property outside of a method at the class level. Leave your array initializer at the class level and add your properties to it within a method.
var imageViews = [UIImageView]()
override func viewDidLoad(){
imageViews = [pos_1,pos2]
//or
imageViews.append(pos_1)
imageViews.append(pos_2)
}
You declared imageViews as a constant (a separate issue) then redeclared it again in viewDidLoad. You shouldn't need to redeclare it (or you could just declare once in viewDidLoad)

How to use foreign variables in classes

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.

Singleton computed property in swift 3

I am trying to achieve a singleton UIView instance in my code, so whenever I create an instance of 'MyView' the nib file will load for only once and then reuse it again:
class MyView : UIView {
#IBOutlet weak var someLabel: UILabel!
static var customeView : UIView = {
let view = Bundle.main.loadNibNamed(String(describing: MyView.self), owner: self, options: nil)?[0] as! UIView
return view
}()
convenience init() {
self.init(frame: CGRect(x: 0, y: 0, width: 576, height: 30))
let viewForOnce = MyView.customeView
viewForOnce.frame = bounds
}
}
The problem is that whenever I call the MyView.customeView it get crashed saying that 'this class is not key value coding-compliant', I think this is happening because of the 'owner: self' inside the computed property.
Any help will be appreciated.
The issue is most probably with your ReceiptView.xib. You should review it first.
Look for an IBOutlet, which is there in a xib, but not in your ReceiptView class and delete it.
Edit:
I meant with the singleton is loading the nib only once whenever I
create and instance of that class.
No, this is not going to work like that. Every time init is called, your computed property is also called and a new instance is created from xib every time.
As Carien van Zyl already mentioned, you are using self in a class var which corresponds to MyView class itself (or it's subclass if its called for a subclass), not an instance. Try passing nil as owner instead.
The whole technique is looking wrong to me. You should not use singleton pattern with UIView subclasses.
There is nothing wrong in calling loadNibNamed multiple times and create exactly the same instances. If you want to use the same instance multiple times in a view hierarchy, it's not possible since every view can have only one superview. In this case you should follow MVC pattern: create multiple MyView instances -> update model whenever you change something in a view and want those changes to be reflected elsewhere -> update another view using updated model.
customeView is a type property. Therefor, self inside of it, will reference MyView.self which is a class type. The class type do not hold the instance variables, which includes someLabel.
See Apple's documentation on Types

Can a Swift initializer have a variadic parameter?

Can a Swift init have a variadic parameter at the end so you can send multiple values of that type to the init?
An example would be to create a class that has an array of UIViews. Would the following work? Is it considered "legit" to do this? (I know I could just pass an array of views, just wondering if this is an option.)
class viewsContainer {
var myViews: [UIView] = []
init(views: UIView...) {
for view in views {
myViews.append(view)
}
}
}
Yes, it's a valid approach, but you should set different frames to not overlap the subviews.
Perfectly legal. I just think it's best to pass an array of views instead (so myViews can be a constant), though. You could create a temporary array with the variadic parameter, and assign it to a private constant, but you, know...

Swift - Same protocol for two different classes

I want to use the same protocol for two different classes. It is for two UIStoryboardSegue classes, the normal one and the unwind segue. In my first class GameSegue.swift, I've declared this protocol
#objc protocol ViewControllerWithBackgroundImage {
var backgroundImage: UIImageView { set get }
}
I use this protocol to have access to the ViewControllers property backgroundImage. In the first class GameSegue.swift, the normal segue, the backgroundImage animates 10 px up. So in the second class GameSegueUnwind.swift, I want to do the same thing backwards, move the background 10 pxdown. But to get access to the backgroundImage property I need this protocol. Therefore it would be useful, to not declare another protocol, but instead use the same.
Any idea how this is possible?
In the second class just declare a new delegate variable
class GameSegueUnwind {
var secondDelegate: ViewControllerWithBackgroundImage?
}
and you will be able to access the function in any other class that conforms to the protocol. Of course, in the conforming class remember to declare it has the delegate handler in the prepare for segue method
destinatonViewController.secondDelegate = self

Resources