Property-like closures for local variables - ios

I didn't found answer for my question in swiftbook.
Is this possible to create property-like closure for local variable in swift? I mean smt like further snippet:
func someFunc() {
// here goes our closure
var myRect:CGRect {
var x = 10
var y = 20
var width = 30
var heigth = 40
myRect = CGPointMake(x,y,width,heigth)
}
}
I have complexity evaluation of UI elements position. This trick should make my code much readable

This is called read-only computed property where you can omit the getter to simplify declaration:
var myRect: CGRect {
let x:CGFloat = 10
let y:CGFloat = 20
let width:CGFloat = 30
let height:CGFloat = 40
return CGRectMake(x, y, width, height)
}
Read-Only Computed Properties
A computed property with a getter but no setter is known as a
read-only computed property. A read-only computed property always
returns a value, and can be accessed through dot syntax, but cannot be
set to a different value.
NOTE
You must declare computed properties—including read-only computed
properties—as variable properties with the var keyword, because their
value is not fixed. The let keyword is only used for constant
properties, to indicate that their values cannot be changed once they
are set as part of instance initialization.
You can simplify the declaration of a read-only computed property by
removing the get keyword and its braces:
Documentation Swift Conceptual Properties

Why not try this way?
fun someFunc() {
var myRect = {() -> CGRect in
let x:CGFloat = 10.0
let y:CGFloat = 20.0
let width:CGFloat = 30.0
let height:CGFloat = 40.0
return CGRectMake(x,y,width,height)
}
myRect() //Call it
}
EDIT I think if there are some requirements to calculate some points position like maxElement use closure is good to save some small functions.

Related

Cannot convert value of type '() -> _' to specified type UIImageView

I am trying to make a UIImage view clickable, but I am having no luck. What is the best way to complete this task? The error I am getting is "Cannot convert value of type '() -> _' to specified type 'UIImageView'".
lazy var profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "ic_file_upload_white_48pt")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
imageView.isUserInteractionEnabled = true
return imageView
}
You are telling the compiler that you want to make profileImageView contain a closure. If you want profileImageView to contain the results of that closure, you need to add parens after in order to invoke the closure:
lazy var profileImageView: UIImageView = {
//your code here
return imageView
}()
Note the parentheses after the closure. That assigns the result of calling the closure to your variable profileImageView the first time you reference the variable.
Edit:
Any time you see a type of (<something>) -> type it's a closure. The -> bit separates the parameters from the return type. Swift's error messages can be hard to decipher, but that's a clue that you're returning a closure rather than whatever is expected.
Edit #2:
Note that there are 2 similar constructs related to a variable defined by a closure in Swift: Computed properties and lazy variables.
Computed properties
A computed property is declared as
var computed: type { closure_returning_result }
There is no equals sign in a computed property. Every time you ask for a value from the computed property, the closure code runs, and the returned value of the closure is the new value of the property.
Lazy vars:
A lazy var looks like this:
var lazy: type = expression_returning_result
The expression is often a closure, but it doesn't have to be. A common form of a lazy var would use a closure, like this:
var lazy: type = { closure_returning_result }()
There is an equals sign in the declaration of a lazy var. If you use a closure to assign a value to a lazy var, you need to add parentheses after the closure, so the lazy var is assigned the returned value of the closure, not the closure itself. This thread came up when #jameel forgot the closing parentheses in his code.
Consider the following code:
var counter = 1
var computed: Int {
counter += 1
return counter
}
var lazy: Int = {
counter += 1
return counter
}()
print("lazy = \(lazy)")
print("lazy = \(lazy)")
print("lazy = \(lazy)")
print("computed = \(computed)")
print("computed = \(computed)")
print("computed = \(computed)")
That prints the output:
lazy = 2
lazy = 2
lazy = 2
computed = 3
computed = 4
computed = 5
Note that the value of the lazy var doesn't change, but the value of the computed property does. That is the key difference between them.
A lazy variable gets evaluated once the first time you ask for it, and that value "sticks". The expression/closure that gives the value is not run until you ask for it, and then only once.
In contrast, a computed property always uses a closure, and every time you ask for the value of a computed property, the closure is executed.
Looks like a syntax issue, try
func profileImageView() -> UIImageView {
let imageView = UIImageView()
imageView.image = UIImage(named: "ic_file_upload_white_48pt")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
imageView.isUserInteractionEnabled = true
return imageView
}
You should add paranthesis after the end of computed property in order to execute it.
lazy var profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "ic_file_upload_white_48pt")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
imageView.isUserInteractionEnabled = true
return imageView
}()

Is this Swift syntax efficient?

Would this for loop fire each time the getter is accessed ? Or does Swift cache it 'behind the scenes' ?
var colors: [UIColor] {
get {
var colors = [UIColor]()
let palette = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), UIColor.orangeColor(), UIColor.purpleColor(), UIColor.yellowColor()]
var paletteIndex = 0
for _ in 0..<photos.count {
colors.append(palette[paletteIndex])
paletteIndex = paletteIndex == (palette.count - 1) ? 0 : ++paletteIndex
}
return colors
}
}
In objective-c a getter like this would be positioned behind a check on the private ivar, so that the ivar is set once and then the ivar returned on subsequent calls.
It fires everytime the getter is called. No way this could be optimized away.
You might want to keep a preset colors array in sync with the photos property, i.e. change it directly when setting the photos.
I also dislike the tight coupling in that place which seems unnecessary. I think it might be worthwhile to refactor this and just return a particular color for a given photo index (which is trivial using the modulo operator).
So my suggestion is to keep the palette as an instance variable, and just make your method return palette[index % palette.count], which will give the correct color immediately.
I will be doing this, as #Eiko suggested
let palette = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), UIColor.orangeColor(), UIColor.purpleColor(), UIColor.yellowColor()]
override func viewDidLoad() {
super.viewDidLoad()
...
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AnnotatedPhotoCell", forIndexPath: indexPath) as UICollectionViewCell
cell.contentView.backgroundColor = palette[indexPath.item % palette.count]
return cell
}
You are using computed property which do not actually store a value and the value of a computed named value or a computed property is not stored in memory. It's in the swift programming book
You can use lazy initialisation:
lazy var colors : [UIColor] = {
var colors = [UIColor.redColor]
// blablabla
return colors
}()
Which will be run just once when you try to access the colors the first time. However if the colors need to update throughout your class lifetime i suggest using a function to recapculate them:
func recalcColors() -> [UIColor] {
var colors = [UIColor.redColor]
// blablabla
return colors
}
lazy var colors = recalcColors()
And when you need to update them you can call colors = recalcColors()
From a Swift logic perspective, it will run every time, but that said, there are some circumstances when the optimizer could turn the entire calculation into a constant, in which case it wouldn’t matter.
There are two things standing in the way of that: the palette array, and the photos.count property. In theory, the palette array never changes, but the compiler can’t know this because it doesn’t know that a function like redColor() always returns the same value. So hoist that array creation out of the property.
photos.count there is probably no fixing, assuming photos changes in size dynamically during the course of the program.
But this will give you the same effect and not require the creation of any arrays at all:
struct PaletteCycler {
let palette = [
UIColor.redColor(), UIColor.greenColor(),
UIColor.blueColor(), UIColor.orangeColor(),
UIColor.purpleColor(), UIColor.yellowColor(),
]
subscript(idx: Int)->UIColor {
return palette[idx%palette.count]
}
}
let colors = PaletteCycler()
Since everything is constant there is very little runtime cost to fetching a color. Unlike the original version, this does not create an array every time. Since mod and fetch is pretty efficient, and the palette variable is constant, it should be fast.
By the way, if things weren’t so constant and you really did want an array, you could rewrite your loop using map like so:
let palette = [
UIColor.redColor(), UIColor.greenColor(),
UIColor.blueColor(), UIColor.orangeColor(),
UIColor.purpleColor(), UIColor.yellowColor(),
]
var colors: [UIColor] = {
// Swift 2.0 syntax. For 1.2, write
// indices(photos).map
return photos.indices.map {
palette[$0 % palette.count]
}
}

Updating Random Integers (swift, spritekit)

I have a random integer that needs to update every second, but my issue is that the random int cannot be defined outside of a function because it is an random int with the range of 0 to the screen size
var randomX = UInt32(self.size.width)
I cannot use self.size.width outside of a function. At first I thought maybe its not updating because its 1. Declared in the function didMoveToView() and 2. I declared it using "let" instead of "var". If I declare it in my function that updates every second, the variable cannot be used outside of that function which is a huge problem.
You can declare your var as an instance variable of your class (outside a method), update it in a method, and use it anywhere else in an instance of that class.
class RTest {
var randomX: UInt32
init() {
self.randomX = 2;
}
func rx5()->UInt32 {
return self.randomX * 5
}
}
Create a global class variable so you could access it anywhere in the class
class MyClass: UIViewController {
var screenWidth: CGFloat = 0.0; //Global variable for this class
func getRand() -> UInt32 {
//generate your random number
return UInt32(self.screenWidth)
}
func useRandom() {
//call getRand anytime you want to generatea a new random num
var myRandNum = getRand()
}
func override viewWillAppear(animated: Bool) {
self.screenWidth = self.view.frame.size.width
}
}
Code not tested but should give you an idea on how to go about it.
hope that helps.

swift global constants: cannot use another constant for initialization

Here is what I am trying to do:
class ViewController: UIViewController {
let screenRect: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenRect.width;
let screenHeight = screenRect.height;
let screenX = screenRect.origin.x
let screenY = screenRect.origin.y
override func viewDidLoad() {
...and so on
Swift allows me to declare screenRect.
However, it does not allow me to declare any other constants using this. It shows me the error: 'ViewController.Type' does not have a member named 'screenRect'
How do I define these constants and why does not swift allow me to use another constnt to define them.
The consts do not know about the other global consts because they are not initialized at this point. What you could do though is to use computed properties:
class ViewController: UIViewController {
var screenRect: CGRect { return UIScreen.mainScreen().bounds }
var screenWidth: CGFloat { return self.screenRect.origin.x }
}
This only works with vars, due to the nature of it. This should be considered depending on the use case. If you have to call it often, it might not be the wisest way in regards to performance.
Its not possible to access self before initialisation process gets completed, therefore its giving error.
What probably happens is that during instantiation and initialization it's not guaranteed that properties are initialized in the same order as you have defined them in the code, so you cannot initialize a property with a value retrieved from another property.
My suggestion is to move the property initializations in the init() method.
Addendum 1: as suggested by #Yatheesha, it's correct to say that self is not available until all properties have been initialized - see "Two-Phase Initialization" paragraph in the swift book, under "Initialization"
In this line (as well as in the ones after it):
let screenWidth = screenRect.width;
you are implicitly using self.
So the correct way is to use init() as follows:
let screenRect: CGRect
let screenWidth: NSNumber
let screenHeight: NSNumber
let screenX: NSNumber
let screenY: NSNumber
init(coder aDecoder: NSCoder!) {
let bounds = UIScreen.mainScreen().bounds
screenRect = bounds
screenWidth = bounds.width
screenHeight = bounds.height
screenX = bounds.origin.x
screenY = bounds.origin.y
super.init(coder:aDecoder)
}
Of course if there is more than one designated init(), you have to replicate that code on each. In that case it might be better to use #Andy's approach, using computed properties, if you prefer to avoid code duplication.
Addendum 2
Another obvious way (and probably the best one) is to avoid using self at all:
let screenRect: CGRect = UIScreen.mainScreen().bounds
let screenWidth = UIScreen.mainScreen().bounds.width;
let screenHeight = UIScreen.mainScreen().bounds.height;
let screenX = UIScreen.mainScreen().bounds.origin.x
let screenY = UIScreen.mainScreen().bounds.origin.y

Problems with Swift Declaration

I just ask myself why I can't do something like this directly under my Class Declaration in Swift:
let width = 200.0
let height = 30.0
let widthheight = width-height
I can not create a constant with 2 other constants. If I use this inside a function/method everything works fine.
Thanks
When you write let widthheight = width - height, this implicitly means let widthheight = self.width - self.height. In Swift, you're simply not allowed to use self until all its members have been initialised — here, including widthheight.
You have a little bit more flexibility in an init method, though, where you can write things like this:
class Rect {
let width = 200.0
let height = 30.0
let widthheight: Double
let widthheightInverse: Double
init() {
// widthheightInverse = 1.0 / widthheight // widthheight not usable yet
widthheight = width - height
widthheightInverse = 1.0 / widthheight // works
}
}
This is a candidate for a computed property as such:
class Foo {
let width = 200.0
let height = 30.0
var widthheight : Double { return width - height }
}
You might raise an issue of 'but it is computed each time'; perhaps your application will depend on a single subtraction done repeatedly - but not likely. If the subtraction is an issue, set widthheight in init()
For things like that, you could make use of class variables. The code would look like this:
class var width = 200.0
class var height = 30.0
class var widthheight = width - height
But when you try it, you will see a compiler error:
Class variables are not yet supported
I guess they haven't implemented that feature yet. But there is a solution for now. Just move your declarations outside the class declaration, like following:
let width = 200.0
let height = 30.0
let widthheight = width - height
class YourClass { ...

Resources