Swift why my function call only works in the viewDidLoad - ios

A conceptual question. Why my function calls only work inside the viewDidLoad?
import UIKit
class ViewController2: UITableViewController, UITableViewDataSource {
#IBOutlet var tableview2: UITableView!
var forceView2 = [Float]()
var stiffView2 = [Float]()
var springNumView2 : NSInteger = NSInteger()
override func viewDidLoad() {
super.viewDidLoad()
println(self.forceView2)
println(self.stiffView2)
println(self.springNumView2)
var Answer = calculate(forceView2, stiffView2, springNumView2)
println(Answer.self)
}
If var Answer = calculate(forceView2, stiffView2, springNumView2) is placed above the viewDidLoad, an error of 'ViewController2.type does not have a member named 'forceView2'.

If you move that line out of the method scope, you are actually turning a local variable into a property. This way the Answer property is visible to all instance methods, but also accessible from outside.
The problem in your case is that the property is initialized with the return value of an instance function, which indirectly references to self - but swift doesn't allow referencing to self during the initialization, that's the reason why you have a compilation error.

You are trying to call a function in the class body and not within a method that's why.

You can of course create a computed property. A property that returns a calculated value:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259
Something along these lines should work:
var answer : [Float] { return calculate(forceView2, stiffView2, springNumView2) }

Related

Apply property wrapper in superclass owned property

I have created my own property wrapper for the theming of UI components like UIView, UILabel etc.
class MyUIViewController: UIViewController {
#Theme private override var view: UIView! // it doesnt work!!!
#Theme private var myCustomView: UIView! // it works!!
}
in this case, i will get a compile error "Cannot override with a stored property 'view'"
I know that the view is a property of UIViewController. Do you know if there is any possible way to apply the property wrapper to a stored(superclass) property? any suggestions would be appreciated :) thanks a lot!
I found a way to do that but it's more like a hack than a good implementation (so I would not recommend it), and I haven't fully tested it (as it really on the UIViewController view loading mechanism, this can lead to some undefined behavior).
That said, in the property wrapper documentation you can find a "translation example" that explains how property wrapper works.
#Lazy var foo = 1738
// translates to:
private var _foo: Lazy<Int> = Lazy<Int>(wrappedValue: 1738)
var foo: Int {
get { return _foo.wrappedValue }
set { _foo.wrappedValue = newValue }
}
So we can imitate this to manually wrap a superclass property.
Note that doing this on the view property is a bit special as the view is not loaded during the view controller initialization, but more like a lazy var.
#propertyWrapper
struct Theme<WrappedValue: UIView> {
var wrappedValue: WrappedValue?
}
class Controller: UIViewController {
override func loadView() {
super.loadView()
_view.wrappedValue = view
}
private var _view: Theme<UIView> = .init()
override var view: UIView! {
get {
if _view.wrappedValue == nil {
// This is a trick I would not recommend using, but basically this line
// forces the UIViewController to load its view and trigger the
// loadView() method.
_ = super.view
}
return _view.wrappedValue
}
set {
_view.wrappedValue = newValue
}
}
}
I made the wrapped value in the property wrapper optional because the view property is nil during the initialization process (as the view is not yet loaded)

How to properly set a property in a protocol from a conforming class?

I am writing a custom table header view that can expand/collapse, I wrote a protocol for it like below:
protocol ExpandableHeadViewDelegate{
var expandStateReference : [String : Bool] { get set }
var tblVw : UITableView { get set }
func didTapActionFromHeadVw(_ view: ExpandableHeadView, tag: Int)
}
Here is my expandable head view class, what Im trying to achieve is when this view is tapped I will be able to call the methods that I need from the UITableView where expandableView is embedded:
class ExpandableHeadView: UIView {
var delegate : ExpandableHeadViewDelegate?
//set up tap recognizer
.
.
.
.
private func viewTapped {
if let delegate = delegate {
delegate.tblVw.reloadData()
}
}
}
My viewcontroller that utilizes this class is as below:
class PlayersViewController: UIViewController,UITableViewDelegate,
UITableViewDataSource, ExpandableHeadViewDelegate {
var expandedCells = [String : Bool]() //
#IBOutlet var tableVw: UITableView! // compile time error
}
The issue I am facing is even though I declare my 'var tableVw' in my delegate class, Xcode gives me an error:
Protocol requires property 'tableVw' with type 'UITableView'; do you
want to add stub?
Setting var expandedCells .. however sets properly. If I set my tableVw as non-IBOutlet it can compile, but I want my tableVw to be an IBOutlet property.
EDIT:
Adding a stub variable for tableVw and assigning my tableView IBOutlet to it works. But Im still curious if this can be achieved without using a stub variable
internal var tableVw: UITableView?
#IBOutlet var tableView: UITableView!
.
.
func viewDidload {
self.tableVw = self.tableView
}
Quite simply - declare the property in your protocol as UITableView!. The ! matters - a lot.
! makes a variable an optional, but an explicitly unwrapped one. This basically means, that the unwrapping is done for you, under the hood. You promise the compiler, that although this variable can contain a nil, you will make sure it will be set with a proper value before you try to access it. In case of IBOutlets this is done for you by the SDK.

iOS Swift Variable are not accessible in functions

Im doing this app and When I call a variable outside a function it won't recognize it as shown bellow
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var monsterImg: MonsterAnimation!
#IBOutlet weak var heartImg: DragImages!
#IBOutlet weak var foodImg: DragImages!
// Skulls on Top Board
#IBOutlet weak var penalty1Img: UIImageView!
#IBOutlet weak var penalty2Img: UIImageView!
#IBOutlet weak var penalty3Img: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Skulls Alpha Manipulation
let DIM_ALPHA: CGFloat = 0.2
let OPAQUE: CGFloat = 1.0
let MAX_PENALTIES = 3
var penalties = 0 // VARIABLE NOT ABLE TO BE SEEN IN FUNCTION ALSO ABOVE ^^^
foodImg.dropTarget = monsterImg
heartImg.dropTarget = monsterImg
// Skulls Alpha Initalzation
penalty1Img.alpha = DIM_ALPHA
penalty2Img.alpha = DIM_ALPHA
penalty3Img.alpha = DIM_ALPHA
NSNotificationCenter.defaultCenter().addObserver(self, selector: "itemDroppedOnCharacter:", name: "onTargetDropped", object: nil)
}
func itemDroppedOnCharacter(notif: AnyObject) {
}
func startSkullTimer() {
var skullTimer: NSTimer!
if skullTimer != nil {
skullTimer.invalidate()
}
skullTimer = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: "changeGameState", userInfo: nil, repeats: true)
}
func changeGameState() {
penalties++ // HERE IT"S NOT ABLE TO SEE THE VARIABLE ABOVE
}
But When I put it inside the function it works but then the other variables and things don't recognize it because its in the function. How do I fix this so the function will recognize the Variables out side it?
Just declare your variables outside of viewDidLoad() together with your IBOutlets.
If you want to give them initial values you can do it either direct when declaring or just give a type and set the value again in viewDidLoad()
For more information on the topic of access control, read up on the Apple docs:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html
Welcome to SO. You are facing the issue of "scope". A variable defined in an inner scope like a function is only accessible inside that inner scope (the function). Once you exit the function, the variable "goes out of scope" and is forgotten.
Think of scope as groups of circles. A variable defined in a circle (scope) is only visible inside that circle. Circles inside a larger circle have access to the variables defined in the outer circle. (Inner scope has access to enclosing scope.)
Functions have a "containing scope" in which they are defined. If a function is an instance method of a class, then it has access to instance variables from the object.
Thus, if you need two functions to both have access to the same variable, that variable needs to be in a common, outer scope. (Or if one function calls the other you can pass the value as a parameter.) Again, for instance methods, it might make sense to make the value that both functions need an instance variable of the class.
In your case, these are instance methods of a view controller, so you just need to make your penalties variable an instance variable of your view controller. You do that by moving it's declaration outside of the function and into the top part of the declaration of your ViewController class.

Swift setting variable inline or in function?

What is the Swift standard for setting a variable you already know the value for? Here are the 2 different ways I'm thinking of.
Option 1: Declaring the variable in the class and then setting it in the ViewDidLoad method
class ViewController: UIViewController {
var refreshControl: UIRefreshControl!
var sampleString: String!
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
sampleString = "Hello"
}
}
Option 2: Declaring the variable in the class and setting it inline
class ViewController2: UIViewController {
var refreshControl = UIRefreshControl()
var sampleString = "Hello"
override func viewDidLoad() {
super.viewDidLoad()
}
}
Which is the preferred way to do this in Swift? Thanks in advance!
First of all, you have two fundamentally different types in your two examples. In the first example, the type is an implicitly unwrapped optional String (i.e., String!), which means it can accept the nil value. In the second example, it is just String. If the value does not need to be nil assignable, the second option is better.
With regard to your actual question. I would say the second option is preferable, as you initialize the value earlier and there is no chance that you will use it before it is initialized. This would be equivalently good to declaring the type as String and deferring the initialization to an init method.
The viewDidLoad method is only useful for UIViewController instances, and doesn't get invoked until the view is loaded (which typically is during presentation). Waiting to initialize a value until then is probably not preferred and wouldn't be useful in objects that don't subclass UIViewController.

will create a view in code, how to delcare it in Swift 1.2

I have to fix another developer's code by adding a dimmedView. I'd like to create this all in code. I understand the ! for an optional value but how would I declare the following view. It doesn't seem like I would need to initialize it but the compiler wants it initialized. How would I deal with this?
class EKViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var mainTV: UITableView!
var dimmedView:UIView
....
func renderOverlayNav(){
println("YYY about to render outlay")
self.dimmedView=UIView(frame: CGRectMake(0.0, 0.0, self.screenWidth, self.screenHeight))
Any instance variables declared within a class have to either be optional, have an initial value, or instantiated inside the init method.
var dimmedView = UIView()
or
var dimmedView: UIView?
or
init() {
dimmedView = UIView()
}
You must initialize all properties in Swift in your init. To get around this you can make the dimmedView an optional which will set it's value to nil or you can initialize it like so var dimmedView = UIView()

Resources