total of label click on two buttons - swift

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

Related

How do I iterate through storyboard elements without having to write out everything?

I have the situation where I need to change the text label based on various conditions, but I cannot figure out how to iterate through labels that are created within the Storyboard without having to write it all out such as below. In the below example, I am first checking if the array item exists, and if so, changing the label text and color. If not, I'd like just a default setting of blank text and the UIColor black. The labels were added to an XIB cell.
if let item1 = currentObjective.items[safe: 0] {
cell.item1Label.text = item1.title
cell.item1Label?.textColor = returnColor(item: item1)
} else {
cell.item1Label.text = ""
cell.item1Label?.textColor = UIColor.black
}
if let item2 = currentObjective.items[safe: 1] {
cell.item2Label.text = item2.title
cell.item2Label?.textColor = returnColor(item: item2)
} else {
cell.item2Label.text = ""
cell.item2Label?.textColor = UIColor.black
}
if let item3 = currentObjective.items[safe: 2] {
cell.item3Label.text = item3.title
cell.item3Label?.textColor = returnColor(item: item3)
} else {
cell.item3Label.text = ""
cell.item3Label?.textColor = UIColor.black
}
Edit: I have been asked to show the structure of the storyboard. Please see below. These are labels placed on a XIB file one by one via drag and drop.
These are all added to the swift file via IBOutlet:
Assuming the title label is a sibling of the item labels, you can enumerate the array of all the item labels,
let itemLabels = [
cell.item1Label!,
cell.item2Label!,
cell.item3Label!,
cell.item4Label!,
]
for (i, label) in itemLabels.enumerated() {
if let item = currentObjective.items[safe: i] {
label.text = item.title
label.textColor = returnColor(item: item)
} else {
label.text = ""
label.textColor = UIColor.black
}
}
Alternatively, you can also put the four labels as subviews of another view (perhaps a UIStackView) in the storyboard, so that the hierarchy becomes:
ObjectiveCell
UIStackView
item1Label
item2Label
item3Label
item4Label
titleLabel
Then, add an outlet for the stack view. This way, you can use cell.stackView.arrangedSubviews, instead of writing out the itemLabels array.
If you want to go one step further, don't use a fixed number of item labels! Add them dynamically to the stack view instead, based on currentObjective.items.
// remove all the existing items first (I'm guessing you're doing this in cellForRowAt or something like that)
cell.stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
for item in currentObjective.items {
let label = UILabel()
label.text = item.title
label.textColor = returnColor(item: item)
cell.stackView.addArrangedSubview(label)
}

Adding labels and textviews in a stack view programmatically in swift

How can I do to have a title, followed by a few lines of text, followed by a title again and again few lines of text constrained in the middle of a view controller programmatically?
My goal is to have bolded for the titles, and it would be nice to have the textview lines incremented also.
My idea was to create 2 labels, and 2 textviews. And adding those to a textview in this order: label1, t1, label2, t2.
But it doesn't seem to work. I try to avoid defining the same textviews and labels many times. textviews add up if I copy its definition twice but not for labels (maybe it is view related?)
I tried with UIbuttons and it worked.
This is what I tried so far:
import UIKit
class HowToSetupProIGVC: UIViewController {
deinit {print("deinit")}
let textView: UITextView = {
let textView = UITextView()
textView.backgroundColor = .blue //bkgdColor
textView.textAlignment = .left
//textView.frame = CGRect(x: 5, y: 5, width: 5, height: 5)
textView.tintColor = .black
textView.translatesAutoresizingMaskIntoConstraints = false //enable autolayout
textView.heightAnchor.constraint(equalToConstant: 100).isActive = true
textView.widthAnchor.constraint(equalToConstant: 300).isActive = true
return textView
}()
let label: UILabel = {
let l = UILabel(frame:CGRect.zero)
//l.frame = CGRect(x: 5, y: 5, width: 5, height: 5)
l.backgroundColor = .green //bkgdColor
l.font = UIFont.preferredFont(forTextStyle: .headline)
l.translatesAutoresizingMaskIntoConstraints = false //enable autolayout
l.heightAnchor.constraint(equalToConstant: 22).isActive = true
l.widthAnchor.constraint(equalToConstant: 300).isActive = true
return l
}()
override func viewDidLoad() {
super.viewDidLoad()
self.modalUI(arrowButton: false)
self.view.backgroundColor = bkgdColor
customStackHTSProIG ()
}
}
extension HowToSetupProIGVC {
func customStackHTSProIG () {
let label1 = label
let label2 = label
let t1 = textView
let t2 = textView
label1.text = "Title1:"
label2.text = "title2:"
t1.text = """
1. On your profile tap menu
2. Tap settings
3. Tap accounts
4. Tap set up professional account
"""
t2.text = """
1. On your profile tap "Edit profile"
2. Link your created page to your account
"""
//StackView
let stackHTS = UIStackView()
stackHTS.axis = NSLayoutConstraint.Axis.vertical
stackHTS.distribution = .fillEqually
stackHTS.alignment = .center
stackHTS.spacing = 5
stackHTS.backgroundColor = .red
//Add StackView + elements
stackHTS.addArrangedSubview(label1)
stackHTS.addArrangedSubview(t1)
stackHTS.addArrangedSubview(label2)
stackHTS.addArrangedSubview(t2)
self.view.addSubview(stackHTS)
//Constraints StackView
stackHTS.translatesAutoresizingMaskIntoConstraints = false
stackHTS.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
stackHTS.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
//stackHTS.heightAnchor.constraint(equalToConstant: 88).isActive = true
}
}
UILabel & UITextView are both UIKit classes written in Objective-C. They are reference types, NOT value types.
When you write following -
let label1 = label
let label2 = label
let t1 = textView
let t2 = textView
Both label1 & label2 are pointing to the one & same instance of UILabel. So is the case for t1 & t2 as well.
When you add them like this -
//Add StackView + elements
stackHTS.addArrangedSubview(label1)
stackHTS.addArrangedSubview(t1)
stackHTS.addArrangedSubview(label2)
stackHTS.addArrangedSubview(t2)
You expect 2 labels and 2 textViews to be added to the StackView. You are adding only 1 label and 1 textView though.
You expect to see all of following -
label1.text = "Title1:"
label2.text = "title2:"
t1.text = """
1. On your profile tap menu
2. Tap settings
3. Tap accounts
4. Tap set up professional account
"""
t2.text = """
1. On your profile tap "Edit profile"
2. Link your created page to your account
"""
However you are only seeing following -
label2.text = "title2:"
t2.text = """
1. On your profile tap "Edit profile"
2. Link your created page to your account
"""
Solutions -
Create two separate instances of UITextView & UILabel like you have already done for the first two and Add these new instances to stack view as well.
Use one UILabel and remove everything else. Use NSAttributedString API to stylize your text as you want for different sections / paragraphs and assign it to UILabel.attributedText.

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)

What is the difference between the following 3 declarations?

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 :)