Button in Xcode doesn't change a variable - Swift - ios

I am trying to change the value of a variable in Xcode 7 with Swift. For some strange reason, it just doesn't work.
Here are the functions I used.
#IBAction func ten(sender: AnyObject) {
var percentage = 0.10
print("test")
}
#IBAction func twentyBtn(sender: AnyObject) {
var percentage = 0.20
}
#IBAction func fifteenBtn(sender: AnyObject) {
var percentage = 0.15
}
#IBAction func twentyfiveBtn(sender: AnyObject) {
var percentage = 0.25
}
#IBAction func thirtyBtn(sender: AnyObject) {
var percentage = 0.30
}
Then later in the code I use the percentage variable in this function.
#IBAction func calcTip(sender: AnyObject) {
var billAmount: Int? {
return Int(billField.text!)
}
var tipTotal = percentage * Double((billAmount)!)
toalLabel.text = String(tipTotal)
testLabel.text = String(percentage)
}
I don't understand why this shouldn't work because I also have this variable underneath my outlets where at the beggining of the code.
What's even more confusing is that if I try to see if it the button would print into the output, as you see I've tried for the one of the buttons, IT WORKS. Even trying to change the text within a label works, but for some strange reason it won't change a variable.
I'm not sure if this could be a bug with Xcode either.

Declare and initialize your percentage variable outside of your IBAction functions.
Like Mukesh already explained in the comments, if you are writing var percentage = 0.10 within one of these functions, you are not assigning 0.10 to the correct variable. Instead, you are creating a new variable within the scope of that function.
Any code written outside of that function cannot access it, and unless you are returning this value or passing it to a different function, the variable will be deleted by the garbage collector after the program finishes executing that function (so pretty much immediately).
Long story short, your code should look something like this instead:
var percentage = 0;
#IBAction func ten(sender: AnyObject) {
percentage = 0.10
print("test")
}
#IBAction func calcTip(sender: AnyObject) {
var billAmount: Int? {
return Int(billField.text!)
}
var tipTotal = percentage * Double((billAmount)!)
toalLabel.text = String(tipTotal)
testLabel.text = String(percentage)
}

Related

How can I listen for UILabel content changes?

I want to listen the content changes in the UIlabel and prevent it from changing when its length is greater than 7. What should I do? I tried set / get / willset / didset. It seems that it can't meet my needs
I wrote a simple demo ,When I press the button, add a number to display area. I don't want his length to exceed my expectations
In my real project, I'm developing a calculator
What I can think of is to judge the length of displayValue, but doing so will make my code wordy
didSet can help you better.
add this code in line number 6 in your code.
var displayValue: String = "" {
didSet {
if displayValue.count <= 7 {
customLabel.text = displayValue
}
}
}
Then in your action function, you need to just do it.
#IBAction func clickBtn( _ sender: Any) {
displayValue = displayValue + "0"
}
For counting the characters inside a label you use the count method in the text property like this
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
testLabel.text = "duck"
print(testLabel.text.count) //return 4
}
For user action you can't use the UILabel since it's not user interactive, you have to use the UITextField and connect to him an UIAction or explain us better what you want to do.

UISegmentedControl.noSegment stopped working with Xcode 11, iOS 13 [duplicate]

This question already has answers here:
Specifying UISegmentedControlNoSegment to UISegmentedControl's selectedSegmentIndex has no Effect on iOS 13
(3 answers)
Closed 3 years ago.
I've had two segmented controls stacked on top of each other, each with two options, so there's a 2x2 grid of filtering options for a search field. This has worked fine, but I just updated to Xcode 11 and UISegmentedControl.noSegment has stopped working when I try to update it in response to user selection. However, it works when I set the initial value to .noSegment in the property observer. isMomentary is set to false. The outlets are all set up correctly. Is there some update to UISegmentedControl behavior I'm missing, or is this a bug?
New, incorrect behavior shown here.
Current code that was working before, and stopped working after update:
#IBOutlet private weak var segmentedControlOne: UISegmentedControl!
#IBOutlet private weak var segmentedControlTwo: UISegmentedControl! {
didSet {
// Start with no segment selected on this control. This works!
segmentedControlTwo.selectedSegmentIndex = -1
}
}
#IBAction private func oneIndexChanged(_ sender: UISegmentedControl) {
//Turn off selection on second control while first is selected
segmentedControlTwo.selectedSegmentIndex = UISegmentedControl.noSegment
let i = sender.selectedSegmentIndex
if i == 0 {
searchType = .users
} else {
searchType = .contributors
}
}
#IBAction private func twoIndexChanged(_ sender: UISegmentedControl) {
//Turn off selection on first control while second is selected
segmentedControlOne.selectedSegmentIndex = UISegmentedControl.noSegment
let i = sender.selectedSegmentIndex
if i == 0 {
searchType = .articles
} else {
searchType = .categories
}
}
Thanks for asking this question. I ran into the same issue, so was great to get some confirmation it wasn't just something I was missing.
While Apple hopefully fixes this bug soon, I implemented the following workaround by recreating the segments. This code sample is based on a UISegmentedControl with images as segments, you could of course implement the same approach for title strings:
public func resetSegmentedControl(_ control: UISegmentedControl) {
if #available(iOS 13, *) {
// workaround: recreate the segments
let numSegments = control.numberOfSegments
let segmentImages = (0..<numSegments).compactMap { control.imageForSegment(at: $0) }
control.removeAllSegments()
for (index, image) in segmentImages.enumerated() {
control.insertSegment(with: image, at: index, animated: false)
}
} else {
// for earlier versions of iOS, just reset the selectedSegmentIndex
control.selectedSegmentIndex = UISegmentedControl.noSegment
}
}
There's a slight flicker when removing and re-inserting the segments, but for me that's preferable to the broken state.
EDIT
As pointed out by #matt in the comment below, all that's needed is a call to setNeedsLayout,i.e.:
control.selectSegmentIndex = .noSegment
control.setNeedsLayout()

accessibilityIncrement and accessibilityDecrement not called with a slider

I'm trying to add accessibility to a simple UISlider. I read the Apples adjustable document and saw that I need to implement two functions from the UIAccessibilityAction protocol; accessibilityIncrement() and accessibilityDecrement().
The problem I'm having is that even if I set the slider to be an accessibility element in viewDidLoad and setting slider.accessibilityTraits = .adjustable, the two override functions aren't called even if I change the values.
I also tried to set slider.accessibilityLabel = "test", but it's still not reading the label. Only how far the slider has come. For instance "80%".
Any idea on how I can make this work? I also read these two posts on stackOverflow, but none of them worked for me. accessibilityIncrement / Decrement not called and Accessibility accessibilityDecrement() not getting called
I can also mention that I also tried setting breakpoints at the accessibilityIncrement() and accessibilityDecrement(), but nothing happened.
My code
override func viewDidLoad() {
super.viewDidLoad()
slider.isAccessibilityElement = true
slider.accessibilityLabel = "test"
slider.accessibilityTraits = .adjustable
}
override func accessibilityIncrement() {
slider.accessibilityValue = textField.text!
}
override func accessibilityDecrement() {
slider.accessibilityValue = textField.text!
}
#IBAction func sliderValueChanged(_ sender: UISlider) {
guard let unit = question?.unit else { return }
let currentValue = Int(sender.value + 0.5)
textField.text = "\(currentValue) \(unit)"
slider.accessibilityLabel = textField.text!
}
You implement the accessibilityIncrement() and accessibilityDecrement() methods in your view controller but they should belong to the created slider whose trait should be .adjustable.
I suggest you take a look at this accessibility site where a complete example about adjustable values with code snippets and illustrations is provided for both ObjC and Swift.
Following this example will allow to call the accessibilityIncrement() and accessibilityDecrement() methods with your slider.

Adding Model file to view controller, help getting UI working

I wrote a Model file for my basic app and I'm in the process of applying the model to the UI. It has 3 manual inputs and a text area for the "results" once the calculate button has been pressed.
I'm really struggling with making the UI do what I want it to do, I just want the results displaying, I tried print() but get unresolved identifiers in view controller.
How do I get the .value of my results from the Model file in the view controller? Or have to initialize those again?
Model File (Struct)
struct FuelMaths {
var rate: Double = 3.7 //set initialValvue
var tank: Int = 110
var laps: Int = 13
var totalFuel: Double
var totalStops: Double
init (rate: Double, tank: Int, laps: Int) {
self.rate = rate // declare an instance
self.tank = tank
self.laps = laps
totalFuel = rate * Double(laps)
totalStops = totalFuel / Double(tank)
}
func result () {
print(totalFuel.value)
print(totalStops.value)
}
}
View Controller
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var rate: UITextField!
#IBOutlet weak var laps: UITextField!
#IBOutlet weak var tank: UITextField!
let calculate = FuelMaths(rate: 3.7, tank: 110, laps: 13)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func calculate(sender: UIButton) {
}
#IBOutlet weak var display: UITextView!
}
Thanks all.
(Edit: I originally said you didn't instantiate the model at class scope; you did, but you misnamed it as a verb calculate rather than a good noun like fuelCalculationModel. But anyway, I'm using it the way you've written it: which is as sort of a one-off where the values are passed in at construction time and those values are assumed final so that the result is calculated immediately. It is possible to write a model that is designed to persist and adjust to changes in its source value over time, but you need to understand property observers or computed properties for that.)
See if the solution is helpful:
#IBAction func calculate(sender: UIButton) {
if let rateVal = Double(rate.text!),
tankVal = Int(tank.text!),
lapVal = Int(laps.text!) {
let fuelModel = FuelMaths(rate: rateVal, tank: tankVal, laps: lapVal)
display.text = "Total fuel: \(fuelModel.totalFuel) Total Stops: \(fuelModel.totalStops)"
}
else {
display.text = "One of the text fields did not have a valid number."
}
}
Ultimately you want the model to be a persistent object at class-level scope, not function scope, and you want it to recalculate its results whenever its source values are changed, but wait on that until you understand this, especially if-let when constructing numbers out of parsed Strings.

Incrementing Array Element By One In Swift

I am Currently working on a swift project in XCode, and I've encountered an issue where I cannot increment an array's one element by through various endeavours.
My ViewController.Swift
import UIKit
class FailureViewController: UIViewController {
#IBOutlet weak var failFactLabel: UILabel!
let failFact = Failure()
override func viewDidLoad() {
super.viewDidLoad()
failFactLabel.text = failFact.failArray[0]
}
#IBAction func showFunFact() {
//this is where the issue is
}
}
For the function showFunFact I have previously tried
for var x = 0; x < failArray.cout; x++ {
failFactLabel.text = failFact.failArray[x]
}
but encountered the error: "Use of Unresolved Identifier".
Putting this aside, I decided to use
for var x = 0; x < 10; {
x+=1
}
Although this does not generate an error however, I see it stops at the first element in my array. I tried +=5 and it displays the 5th element in my array once, I believe this code runs through once. I need it to be a consistently working piece so it continuously displays the next element, I am stumped because this should theoretically work since it is called a "Loop" Any help or suggestion is Appreciated!
Objective Redefined:
I am trying to show one fact at a time, before this I have used a random function to generate a random fact every time however things tend to get repetitive and therefore I decided to start on the initial array index is 0 and grab the next fact (next element in array) every time the button is pressed (Thanks to Luk's Question)
You need to create an instance variable to hold the current factIndex, if that is what you actually are trying:
import UIKit
class FailureViewController: UIViewController {
#IBOutlet weak var failFactLabel: UILabel!
var factIndex = 0
let failFact = Failure()
override func viewDidLoad() {
super.viewDidLoad()
failFactLabel.text = failFact.failArray[factIndex]
}
#IBAction func showFunFact() {
factIndex++
if factIndex >= failFact.failArray.count then {
factIndex = 0
}
failFactLabel.text = failFact.failArray[factIndex]
}
}

Resources