Iterate through different view with array, Swift - ios

I have 4 different progressView
#IBOutlet weak var firstProgressBar: UIProgressView!
#IBOutlet weak var secondProgressBar: UIProgressView!
#IBOutlet weak var thirdProgressBar: UIProgressView!
#IBOutlet weak var fourthProgressBar: UIProgressView!
And I have an array with 4 values
[30,20,24,25]
Since this array change from the API call (I need to check if I really have this value), what is the best and safer way to:
Order this array from bigger to smaller
Iterate throught this array and set each of my progressView value to the corresponding array value
This is my current code:
func setupProgressBar(item: CustomObject) {
let array = item.array // [x,y,z,w] array
self.progressBar.setProgress( ?? , animated: false)
}
I have an animation for the value but it's not the point of my question, i just need to set values

A better approach would be to connect all the UIProgressBar in your storyboard to an #IBOutlet collection.
#IBOutlet weak var progressBars: [UIProgressView]!
Then iterate through the array to set the value of progressBars.
func setupProgressBar(item: CustomObject) {
let array = item.array // [x,y,z,w] array
for (progressBar, value) in zip(progressBars, array) {
progressBar.setProgress(value, animated: false)
}
}

func setupProgressBar(item: CustomObject) {
let valueArray = item.array.sorted // [x,y,z,w] array
let progressBars = [firstProgressBar, second….., fourthProgressBar]
for item in valueArray.enumerated() {
progressBars[item.index].setProgress(item.value)
}
}
Make sure that progress bars and values are equal. Otherwise the code will crash.

Related

Swift - How to calculate a sum of buttons in table view cell

I have two types of table cells, one that has 6 buttons, and each button has a number value, and then a second cell that has a button to calculate the total sum of the selected numbers, and a label to display this total sum in.
My issue is I cannot get the label to calculate the total. Here is what I have so far
Number Cell:
protocol ToggleNumberCellDelegate: AnyObject {
/// This method detects the selected value of the cell.
func toggleNumberCell(_ toggleNumberCell: ToggleNumberCell, selectedValue: Int)
}
class ToggleNumberCell: UITableViewCell {
static let reuseIdentifier = String(describing: ToggleNumberCell.self)
#IBOutlet private weak var titleLabel: UILabel!
#IBOutlet private weak var zeroButton: UIButton!
#IBOutlet private weak var oneButton: UIButton!
#IBOutlet private weak var twoButton: UIButton!
#IBOutlet private weak var threeButton: UIButton!
#IBOutlet private weak var fourButton: UIButton!
#IBOutlet private weak var fiveButton: UIButton!
#IBOutlet private weak var sixButton: UIButton!
private weak var delegate: ToggleNumberCellDelegate?
private var value: Int?
//...
#IBAction func buttonTapped(_ sender: UIButton) {
switch sender {
case zeroButton:
self.zeroButton.backgroundColor = UIColor.NBABlue
self.zeroButton.tintColor = UIColor.white
self.value = 0
self.delegate?.toggleNumberCell(self, selectedValue: self.value!)
print("The value you tapped is \(value)")
case oneButton:
self.oneButton.backgroundColor = UIColor.NBABlue
self.oneButton.tintColor = UIColor.white
self.value = 1
self.delegate?.toggleNumberCell(self, selectedValue: self.value!)
print("The value you tapped is \(value)")
case twoButton:
self.twoButton.backgroundColor = UIColor.NBABlue
self.twoButton.tintColor = UIColor.white
self.value = 2
self.delegate?.toggleNumberCell(self, selectedValue: self.value!)
print("The value you tapped is \(value)")
The above code just sets up the buttons, and gives each a value by using its delegate.
Label Cell
class CalculateCell: UITableViewCell, ToggleNumberCellDelegate {
var increment = 0
static let reuseIdentifier = String(describing: CalculateCell.self)
#IBOutlet private weak var calculateButton: UIButton!
#IBOutlet private weak var totalLabel: UILabel!
func configure(answer: AnswerModel) {
self.backgroundColor = UIColor.secondarySystemGroupedBackground
self.totalLabel.text = answer.text
self.totalLabel.font = .preferredFont(forTextStyle: .headline)
calculateButton.layer.cornerRadius = 10.0
}
override func prepareForReuse() {
super.prepareForReuse()
self.totalLabel.text = nil
}
#IBAction func calculateTapped(_ sender: UIButton) {
// You need to get the selected value in here somehow.
}
func toggleNumberCell(_ toggleNumberCell: ToggleNumberCell, selectedValue: Int) {
increment = selectedValue
}
}
Here I called the delegate to get the selected value, but I think I did this wrong, can someone tell me how I can calculate to total value?
Here is a screenshot of what I want to achieve.
You have a basic problem with your approach. You should not store data in table view cells. They are views, and should display data, not store data.
You should set up a model object to store the state of your UI. (it could be as simple as an array of integers, one of the value of each ToggleNumberCell in your table view.)
You should have a controller object serve as the delegate for the ToggleNumberCell. When its toggleNumberCell method gets called, it would update the count for that cell's entry in your data model, and then tell the total cell to update itself. Your table view data source would query the model and use the value for each entry in the model to calculate the totals, then install that value in the total cell.
I would recommend you make the UIViewController (than manages the UITableView) the ToggleNumberCellDelegate. Then that view controller can keep track of the total of all of the buttons. The view controller then can provide the total to
the CalculateCell. This separates the logic between the cells. Ideally the controller manages the logic, while the cells are simply views displaying the data.
Also, I would change the toggleNumberCell function to the following:
func toggleNumberCell(_ toggleNumberCell: ToggleNumberCell, selectedValue: Int) {
// Using += will result in adding to the existing value instead of overwriting it
increment += selectedValue
}

Value of optional type 'Question?' must be unwrapped to refer to member 'questionText' of wrapped base type 'Question'

I am making a small question and answer ios app. I want the first question to be randomized. The code I have is below.
let allQuestions = QuestionBank()
var pickedAnswer : Bool = false
//Place your instance variables here
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet var progressBar: UIView!
#IBOutlet weak var progressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let firstQuestion = allQuestions.list.randomElement();
questionLabel.text = firstQuestion.questionText
}
I am accessing the array with .list but I want the first question to be a random element each time the app is launched. Any help would be appreciated.
It looks like firstQuestion can be nil, so it's of type Question?. If you want to use its questionText property, you have to unwrap your question. One way is by optional-binding
if let firstQuestion = allQuestions.list.randomElement() {
questionLabel.text = firstQuestion.questionText
}
If list were empty, there would be no element to select and randomElement would return nil. This is similar to .first or .last properties.
You can handle that simply using optional chaining:
let firstQuestion: Question? = allQuestions.list.randomElement()
questionLabel.text = firstQuestion?.questionText
Since list is probably never empty, this situation will actually never happen. We just have to handle that because the compiler cannot be sure.

Found nil when unwrapping optional value in core data

I've been trying to address this issue for a few days and it seems I'm only going in circles.
I'm adding data to core data by tapping on tableView cells and then displaying the items on another tableView.
That works great, the problem is this:
When I click on the other table view rows, to be able to see the item with all the other values stored in core data, I get
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value on the showMeTheGoodiesTwo(entry: myCart) line
...Inside viewDidLoad. But I do not know why that's happening, as the entity is not empty.
Here's the code of the view controller where I want to display the item's details.
Hope somebody can give me a hand.
Cheers!
import UIKit
class productQuantityViewController: UIViewController {
var myCart: Cart!
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var productNameLabel: UILabel!
#IBOutlet weak var productDescriptionLabel: UILabel!
#IBOutlet weak var productAmountLabel: UITextField!
#IBOutlet weak var minusButton: UIButton!
#IBOutlet weak var plusButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
productImage.layer.cornerRadius = 10
minusButton.layer.cornerRadius = minusButton.frame.size.width/2
plusButton.layer.cornerRadius = plusButton.frame.size.width/2
showMeTheGoodiesTwo(entry: myCart)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func showMeTheGoodiesTwo(entry: Cart) {
let name = entry.product
let quantity = entry.inventory
let description = entry.productDescription
let image = entry.productImage as Data?
let xNSNumber = quantity as NSNumber
productNameLabel!.text = name
productDescriptionLabel!.text = description
productAmountLabel!.text = xNSNumber.stringValue
productImage!.image = UIImage(data:image!)
print(productNameLabel.text as Any)
}
}
You need to change
var myCart: Cart!
To
var myCart: Cart?
? is use to make it optional.
And in viewDidLoad use
if (myCart != nil){
showMeTheGoodiesTwo(entry: myCart)
} else {
//do something.
}
instead of
showMeTheGoodiesTwo(entry: myCart)

How to reload view after data changes in Swift

I have a view that contains labels, and have a button to change the values of the labels. Instead of changing the values one by one in the button, how can I reload the whole view to update the labels.
#IBOutlet weak var one: UILabel!
#IBOutlet weak var two: UILabel!
#IBOutlet weak var three: UILabel!
....
#IBOutlet weak var updateLabels: UIButton!{
//doing something to change the value of the labels
//then wanna reload the whole view
viewDidLoad()
}
I had called the viewDidLoad() method, but didn't work.
You should never call viewDidLoad yourself. It's a framework function that the OS calls, as an indication that your views are ready to be setup.
It would serve better if you separated your function
func updateLabels() {
one.text = "one"
two.text = "two"
three.text = "three"
}
and now you can call the updateLabels function when you want.
Why dont you put all labels on a method. and fire it when ever you need to reload.
override func viewDidLoad() {
super.viewDidLoad()
updateLabels()
}
func updateLabels() {
one.text = "one"
two.text = "two"
three.text = "three"
}
#IBAction func updateLabels(_ sender: Any) {
updateLabels()
}
Your method of updating your labels is incorrect. What you need to do is as follows:
Declare your labels like you did ensuring they are linked in Interface Builder:
//Declare The Labels
#IBOutlet var one: UILabel!
#IBOutlet var two: UILabel!
#IBOutlet var three: UILabel!
Then create an IBAction function which is triggered by a UIButton:
/// Set The Text Labels Text
#IBAction func updateLabelText(){
//Set Label Text
one.text = "one"
two.text = "two"
three.text = "three"
}
Of course remembering to link this to the UIButton instance in Interface Builder.
Hope this helps.

How do you make a function which sets properties of other instances in swift?

I am trying to write the following code in a shorter way.
func colourChangeIfDeletionCancelled(word:Int){
for i in 0...selectedWords[word].count - 1 {
let square = selectedWords[word][i]
arrayOfRows[square.0][square.1].topToRight.backgroundColor = nil
arrayOfRows[square.0][square.1].topToLeft.backgroundColor = nil
arrayOfRows[square.0][square.1].bottomToRight.backgroundColor = nil
arrayOfRows[square.0][square.1].bottomToLeft.backgroundColor = nil
arrayOfRows[square.0][square.1].horizontalTube.backgroundColor = nil
arrayOfRows[square.0][square.1].verticalTube.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromLeft.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromRight.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromTop.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromBottom.backgroundColor = nil
}
}
This code works but I am sure there is a better (shorter) way to do write it but I am unsure how. I have tried to make a function that takes the subviews as a variable but am lost on how to do that. I'm sure this isn't the hardest problem but I am stuck on it and any help is appreciated.
Edit:
arrayOfRows is just an array of arrays of a class I have created called LetterSquareView
class LetterSquareView: UIView {
var letter:String!
#IBOutlet weak var topToLeft: UIView!
#IBOutlet weak var topToRight: UIView!
#IBOutlet weak var bottomToRight: UIView!
#IBOutlet weak var bottomToLeft: UIView!
#IBOutlet weak var horizontalTube: UIView!
#IBOutlet weak var verticalTube: UIView!
#IBOutlet weak var endFromLeft: UIView!
#IBOutlet weak var endFromRight: UIView!
#IBOutlet weak var endFromTop: UIView!
#IBOutlet weak var endFromBottom: UIView!
#IBOutlet weak var topToLeftWhiteSpace: UIView!
#IBOutlet weak var topToRightWhiteSpace: UIView!
#IBOutlet weak var bottomToRightWhiteSpace: UIView!
#IBOutlet weak var bottomToLeftWhiteSpace: UIView!
#IBOutlet weak var letterSquareViewView: LetterSquareViewView!
#IBOutlet var letterSquareView: UIView!
#IBOutlet weak var letterLbl: UILabel!
init(frame: CGRect, letter: String) {
super.init(frame: frame)
NSBundle.mainBundle().loadNibNamed("LetterSquareView", owner: self, options: nil)
letterLbl.text = letter.capitalizedString
self.letter = letter
self.addSubview(letterSquareView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The subviews that I am trying to change the background colour of have a background colour set when they are created. I am trying to delete that colour when they are deleted. I thought there would be a function where you could input an array of the views and input a property of there's to set and it'd just be set but I can't find anything of the sort.
I guess the most straight forward way is to have:
func clearBackgroundColors(e: WhateverViewThisIs) {
for view in [e.topToRight, e.topToLeft, etc. ] {
view.backgroundColor = nil
}
}
clearBackgroundColors(arrayOfRows[square.0][square.1])
A good question to think is about responsibilities of the classes. I would argue the view in arrayOfRows[][] should have it's subviews private, knowledge of them is likely an implementation detail and not of public knowledge. So the clearBackgroundColors() method should be placed there instead of setting all colors from a more global place.
import UIKit
var arrayOfRows: [[UIView]] = [[]]
let square: (Int, Int)!
A way to make your touple code cleaner:
//extracting touple
let (row, col) = square
arrayOfRows[row][col].topToRight.backgroundColor = nil
Only use this is you are sure all the subviews of this view you want to make the background color nil.
//for all views
for arrayOfColumns in arrayOfRows {
for views in arrayOfColumns {
for subview in views.subviews {
subview.backgroundColor = nil
}
}
}
This is how I recommend to handle it. This also allows you to customize those views further with OOP design. Then you can
//with class type
//use this if there are other subviews
//make all your views in your list a CustomViewToMakeNil
class CustomViewToMakeNil: UIView {
//set the views content
}
for arrayOfColumns in arrayOfRows {
for views in arrayOfColumns {
for subview in views.subviews {
//check if they are of the type you want to make the color nil
if subview is CustomViewToMakeNil {
subview.backgroundColor = nil
}
}
}
}

Resources