What is the difference between the following 3 declarations? - swift

var title: UILabel {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}
let title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()
lazy var title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()
If I put 'let' in the first one, compiler will complain that 'computed property do not allow let'. Ok, kind of makes sense. The only difference between the first one and second is '=' and '()'. So, does it mean that it is not a computed property anymore?

1.
var title: UILabel {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}
It is a read only computed property. Computed properties cannot be let. These are calculated using other stored/computed properties. So they don't have any backing store of their own. Hence, computed properties are always declared as var.
2.
let title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()
It is a stored property. This is assigned a closure that returns a UILabel object. This closure is executed during the instantiation process of the object and the returned UILabel object is assigned to title.
3.
lazy var title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()
It is a lazy stored property. It is also assigned a closure that returns a UILabel object. But this closure is not executed during the instantiation process. It is executed whenever this property is first used. After the execution of the closure, the UILabel object returned is assigned to title.

This is computed get-only property, it calculates every time when you try to get it value:
var title: UILabel {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}
This is regular property initialized immidiatly by in-place invoked closure (which playing default value role):
let title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()
This is lazy property, which will be initialized only on first access by in-place invoked closure:
lazy var title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()

In addition to all #PGDev has said, I would like to point out another way to write your second/third declaration:
Instead of:
let title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()
you can write:
let title: UILabel = {
$0.font = .systemFontOfSize(13)
return $0
}(UILabel())
It does exactly the same as the above, just the code is written differently :)

Related

Setting a variable to a defined lazy var within a UIView closure cause reference problems

Recently, I was working on a project of mine and I wanted to have multiple labels with the same font, text color, and properties, except their text.
This is the code I wrote:
lazy var profileLabel: UILabel = {
let label = UILabel()
label.font = .displayNameLabel
label.textColor = .profileLabel
label.numberOfLines = .numberOfLines
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var displayName: UILabel = {
let label = profileLabel
label.text = "Kevin"
return label
}()
lazy var countryLabel: UILabel = {
let label = profileLabel
label.text = "US"
return label
}()
As you can see, to remedy my issue, I created a single label that had all the properties I wanted for all my other labels. For my other labels, I thought I was creating a new label by typing let label = profileLabel. But as it turns out, I wasn't. After consecutive calls of setting the text and adding the labels to my view, only 1 label was actually displayed, and it was the last label added; so in this case, it would be the countryLabel.
It seems to me that in all my calls to let label = profileLabel, I'm just creating a reference to the same profileLabel. And if this is the case, would changing lazy var profileLabel to var profileLabel fix this issue and create a new label with the needed properties every time profileLabel is called?
You were intended to use computed property of swift. But didn’t get it right. Your profile label should have been defined as follows.
var profileLabel: UILabel {
get {
let label = UILabel()
label.font = .displayNameLabel
label.textColor = .profileLabel
label.numberOfLines = .numberOfLines
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
}

What am I doing wrong when updating my user's credits and storing it to userDefaults?

My code below has a button, pressing which should increase the number of credits my user has by 3. I have used get/set property observers for my variable to be saved to userDefaults. While the credits label reflects the change (increases by 3), the number isn't saved to userDefaults, and consequently resets back to the base number when I kill and restart the app.
var livesLeft: Int {
get {
return UserDefaults.standard.integer(forKey: "livesLeftSaved")
}
set {
UserDefaults.standard.set(newValue, forKey: "livesLeftSaved")
}
}
lazy var livesLeftLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
livesLeft = 0
label.text = String(livesLeft)
label.font = UIFont(name: "GillSans-BoldItalic", size: 25)
label.textAlignment = .center
label.numberOfLines = 2
return label
}()
let addLivesButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "icon"), for: .normal)
button.addTarget(self, action: #selector(pressedButton), for: .touchUpInside)
return button
}()
#objc func pressedButton() {
livesLeft += 3
//I think the mistake I'm making is in the next line of code//
livesLeftLabel.text = String(livesLeft)
}
While the label gets updated from 0 to 3 on pressing the button, it fails to save the increased number to userDefaults and consequently the user loses his credits once I kill the app.
I know I'm missing out on something very trivial here, I can't figure out what I'm doing wrong
Your issue is here:
lazy var livesLeftLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
livesLeft = 0 *** HERE ***
You are resetting the livesLeft value when you load the label.
Just change this line to
livesLeftLabel.text = String(livesLeft)

total of label click on two buttons

I have two UIButton if I click onto button1 the value of label1 is 250, if I click again onto button1 the value of label1 should be 0. The same logic is applied to my button2 and label2. I want to store the addition of label1 and label2 into label3. . My code is:
func PickUpCarCost() {
if (!isclick){
imgCheck.image = UIImage(named: "check.png")
isclick=true
//Pickup fare conver into integer
let PickUp = String(self.PickUpPrice)
PickUpFare.text = PickUp
self.pickCost = Int( PickUpFare.text!)
self.PAyAmount = ((self.PayFareWithSecurity)+(self.pickCost))
print("PaybleAmount: \(self.PAyAmount)")
self.AmountPay1 = ((self.PAyAmount)+(self.DeliverCost))
PaybleAmount.text=String(self.AmountPay1!)
}
else
{
imgCheck.image = UIImage(named: "uncheck.png")
PickUpFare.text = "0"
self.PickUpElse=Int(PickUpFare.text!)
print("PickUpElse: \(self.PickUpElse)")
self.PAyAmount = (self.PayFareWithSecurity)+(self.PickUpElse)
PaybleAmount.text=String(self.PAyAmount!)
isclick=false
}
}
func CarDeliverCost() {
if (!isclick){
imgUnCheck.image = UIImage(named: "check.png")
isclick=true
let DeliverPrice = String(self.deliveryPrice)
DeliverFare.text = DeliverPrice
self.DeliverCost = Int(DeliverFare.text!)
self.PAyAmount = ((self.PayFareWithSecurity)+(self.DeliverCost))
print("PaybleAmount: \(self.PAyAmount)")
PaybleAmount.text=String(self.PAyAmount!)
}
else
{
imgUnCheck.image = UIImage(named: "uncheck.png")
let deliveryelse = String(0)
DeliverFare.text = deliveryelse
self.deliver = Int(DeliverFare.text!)
PaybleAmount.text=String(self.PAyAmount!)
isclick=false
}
}
The first issue that you're facing is that you are using the same boolean isClick for two different scenarios. Your logic is that the user could decide to click onto both buttons or a single one. So if I click onto the first button then your flag is turn on, since the second button also uses the same boolean then you'll get that uncheck behavior automatically.
Thus you should use two different booleans such as hasPressOntoFirstButton and hasPressOntoSecondButton, where both set to false at the beginning, and each variable is used in their respective scene.
You mentioned that you want to add the label1 to label2, the easier way would be add the variable that set these labels. i.e
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
let data1: Double = 83.0
let data2: Double = 82.0
var total: Double { // have a computed variable to adds automatically
return data1 + data2
}
label1.text = "\(data1)"
label2.text = "\(data2)"
label3.text = "\(data1 + data2)" // or "\(total)"
Remarks:
(1) your naming convention are really misleading, you should separate UI object naming from data. i.e you can have deliveryFareLabel.text instead of DeliverFare.text
(2) avoid using !, unwrapped all optional using either nil coalescing ??, or if let or guard statements

Getting current value and start observing on MutableProperty

I have MutableProperty. When I create new object I want to get current value of MutableProperty and start observing.
Such as:
let mutableProperty = MutableProperty<Driver?>(Driver(id: 1, name: "John"))
let label = UILabel()
label.text = mutableProperty.value?.name
mutableProperty.signal.observeNext{driver in
label.text = driver?.name
}
Is it possible write it better ?
If you want to use the value immediately:
let mutableProperty = MutableProperty<Driver?>(Driver(id: 1, name: "John"))
let label = UILabel()
mutableProperty.producer.startWithNext{ driver in
label.text = driver?.name
}

Property with '= {return}()' or '{return}'?

There are two ways in Swift to declare complicated property:
option1:
var label: UILabel {
var label = UILabel()
label.font = UIFont(name: "ArialRoundedMTBold", size: 18.0)
return label
}
option2:
var label: UILabel = {
var label = UILabel()
label.font = UIFont(name: "ArialRoundedMTBold", size: 18.0)
return label
}()
What's the difference?
option1 declares a computed property, every time you invoke the property, the result would be re-computed. Computed property is often used to replace computing function. And computed property cannot be declared by let
option2 declares a label and customizes it. It is not a computed property which means it can also be declared as a constant. It can be used as a normal property.
The asnwer provided by #Carrl is good, but I would clarify some things with option2:
What you assign to label in option2, it is actually a closure, and the () means at the and of the curly brackets, that you execute the closure right away.
So imagine:
let labelClosure: () -> UILabel = {
var label = UILabel()
label.font = UIFont(name: "ArialRoundedMTBold", size: 18.0)
println("here2")
return label
}
This is the closure that you assign to label in option2, but this is just a function, without executed (see, there are no () at the end of the curly brackets). So if you want to create a label from that, you should write:
labelClosure()
So wrap things up: in option2 you assign the closures return value to label, in opposition to option1, which is a computed property. And what does a computed property mean? Actually, that doesn't store any value, it just computes and returns its value every time you call it.