How can I create a function for multiple labels in a stack? - swift

I have 3 labels and I want to generate random numbers for each of them. I use GKRandomSource class in function, thats ok. The problem is, if I want to have much more labels (ie. 30) and all with same action, I need to reference all labels one by one to IBAction, I need to state all labels one by one in func code… I’ve been searching for a shorter way, maybe put them all in 3 stacks (10 labels for each stacks) and trigger, but I got nothing. I tried outlet collections (as we use in UIButtons) but it doesn’t let me to change label text.
How can I use a function for multiple labels with no-repeat?
Example;
let allNumbers = [Int](1...99)
var shuffledNum = [Int]()
#IBOutlet weak var labelOne: UILabel!
#IBOutlet weak var labelTwo: UILabel!
#IBOutlet weak var labelThree: UILabel!
func generateNumbers() {
shuffledNum = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: allNumbers) as! [Int]
let threeNumbers = shuffledNum.prefix(3).sorted()
labelOne.text = String(threeNumbers[0])
labelTwo.text = String(threeNumbers[1])
labelThree.text = String(threeNumbers[2])
}

you can make the array of UILabel's and put all the outlets in the same array then you can use for loop to do operations on each of them.
for example:
#IBOutlet var formLabels: [UILabel]!
and can do as:
formLabels.forEach { label in
label.text = ""//put your random number function here
}
see its working after adding both outlets I have also shown the connection in the storyboard the connection exists

Related

Enabling access to data and displaying that data on two different types of Views

I am accessing a Realm database to load values in my iOS app. The first one works fine. I have a UIViewController with UITextFields. The big picture is that i:
Define the outlets in the ViewController and connect them to the mainstoryboard UITextFields. There are three fields
I set up an array and load the results of the Realm query from a PersonData.swift data model. Three properties defined in the swift file.
In viewDidLoad I call the function to loadPersonData.
The first thing in the loadPersonData func is to put the realm.objects including three properties into the three fields that are defined by the Outlets at the top of the ViewController.
They load perfectly, are editable, and I can save the values of the data entry from the screen to the Realm database with a Realm modify. I can show you this code if you want to see it.
This is all working fine. Now my problem. My next View is a UITableViewController, with prototype cell, and three UILabels in each single cell row. This code has in the past used an Array for testing and that worked fine to load sample data back to the rows. I want to now read Realm data and scroll up and down with the three fields in each row. Later, when I select one,I will expose in another View all of the properties of that object called Year which is approximately 10 properties or UITextFields that can be edited.
I was told to build a subclass for the protype cell that will be a sub to the UITableViewCell.
class BYMyCell: UITableViewCell {
I put the Outlets for the three UILabels from the prototype cell into the BYMyCell.swift subclass. These outlets are connected to the UILabels defined in the prototype cell.
I saw one suggestion about putting the same outlets in the first businessYearViewController where all the row support functions were located. I was told to add the = nil to these outlets.
#IBOutlet weak var yearLabel: UILabel! = nil
#IBOutlet weak var descriptionLabel: UILabel! = nil
#IBOutlet weak var startingDateLabel: UILabel! = nil
Now when I try to move the Results of the Realm query from the three properties to the three Labels there appears to be nil in all the fields. I can see the data come into my businessYearViewController where I make the query, but I am having trouble putting the data into the Outlets in the BYMyCell.swift data model for exposure on the view/screen.
If I should post my two files, I will try to put them in a response. Thanks for your help. I'm pretty sure this has something to do with my lack of knowledge of processing TableViewController and prototype cell coordination. My rows are simple. One row, three labels. I will also have an add button at the top right corner and the ability to edit the fields in another View for data entry and changes.
Edited one hour later with information. This is my loadBusinessYear func.
func loadBusinessYearData() {
businessYearArray = realm.objects(YrData.self)
print(businessYearArray!.count)
print(businessYearArray?[0])
if businessYearArray!.count > 0 {
yearLabel.text = businessYearArray?[0].year
descriptionLabel.text = businessYearArray?[0].yearType
startingDateLabel.text = businessYearArray?[0].startingDate
} else {
yearLabel.text = "Sample Name"
descriptionLabel.text = "Sample .com"
startingDateLabel.text = "Sample Date"
}
}
The two print statements at the top give you the 1 for the count and the object properties as Optionals.
1
Optional(YrData {
ydID = 0;
year = 2020;
yearType = Business Open;
startingDate = 12/28/2019;
firstWeekEndDate = 01/03/2020;
lastWeekEndDate = 01/03/2021;
firstQtrTotal = 520;
secondQtrTotal = 520;
thirdQtrTotal = 520;
fourthQtrTotal = 520;
leapYearExtraWeek = 40;
yearContains53Weeks = 1;
laborReporting = List<LaborReportData> <0x600000a15290> (
);
expenseAccounting = List<ExpenseAccountData> <0x600000a15440> (
);
})
The data subclass is BYMyCell swift file.
import UIKit
class BYMyCell: UITableViewCell {
#IBOutlet weak var yearLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var startingDateLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

In swift, how do you add button using a for loop in stack view in scroll view?

I have added two scroll views, and two vertical stack views inside them, respectively. The constraints have been set already. I have connect the two stack views as outlet collections in ViewController.swift. Suppose I have an array of strings, say
I wanted to add a few buttons (same as length of labels) in the top stack view using a for loop. How do I do this? And since the stack view is in scroll view, do I need to specify the distance between two buttons?
My code is below.
#IBOutlet var topStackView: [UIStackView]!
#IBOutlet var bottomStackView: [UIStackView]!
#IBOutlet weak var enterItemTextField: UITextField!
let labels = ["a", "b", "c", "d", "e"]
override func viewDidLoad() {
super.viewDidLoad()
enterItemTextField.delegate = self
var buttons = [UIButton()]
for _ in 0..<labels.count {
let button = UIButton(type: UIButton.ButtonType.system)
# some operations on button
buttons.append(button)
}
# what do I do here?
}
The stackview has less freedom to adjust the locations. I have removed the stackview and am putting buttons on the scroll view directly.
You need it after
buttons.append(button)
topStackView.addArrangedView(button)
BTW for single item you don't need a collection so to be
#IBOutlet var topStackView: UIStackView!
#IBOutlet var bottomStackView: UIStackView!
instead of
#IBOutlet var topStackView: [UIStackView]!
#IBOutlet var bottomStackView: [UIStackView]!
Do I need to specify the distance between two buttons?
no you don't have to but you can do this
topStackView.spacing = 20

How to generate Labelname from a variable? (how to cast a string to UILabel)

I want to refer to a label based on the title of the button pressed.
I have a plenty of buttons, that I connected together in a single action. I want to be able to refer to only one label, that corresponds to the name of button pressed.
The Label names are: aLabel, bLabel, cLabel ...
The buttons pressed are titled "a", "b", "c"...
I ame creating the string with name of the label i want to refer to, but I can't use it as label name to change this particular label values.
I want to use this string to refer to the corresponding label, to change it title, color, and so on.
I tried, casting, looking for a function that changes strings into UILabels. I was also thinking about an array with pointers to the Labels, but I didn't succed even with establishing a pointer to a single Label...
//My code
#IBOutlet weak var a: UIButton!
#IBOutlet weak var b: UIButton!
#IBOutlet weak var c: UIButton!
#IBOutlet weak var aLabel: UILabel!
#IBOutlet weak var bLabel: UILabel!
#IBOutlet weak var cLabel: UILabel!
var currentLabel : String
#IBAction func FieldDisplay(_ sender: UIButton) {
currentLabel = sender.currentTitle! + "Label"
currentLabel.text = "OK"
}
//what I tried
(UILabel)currentLabel.text = "OK"
currentLabel = currentLabel.to.UIlabel
When interface objects are associated in multiple pairs like this — a series of button–label pairs, as you have it — there are two approaches commonly used. One is to assign each pair a tag. For example, in the storyboard, the first button would have tag 1, and the first label would have tag 101. Then the second button would have tag 2, and the second label would have tag 102. And so on.
So now in your IBAction function you look at the tag of the sender, add 100 to it, and call viewWithTag on your view to find the corresponding label.
The other possibility is to use outlet collections. Instead of three button outlets you have one array-of-button outlet; so too for the labels. Now in your IBAction function you get the firstIndex of the button in its array; that, if you've set this up correctly, is the index of the corresponding label in its array.
Remove all outlets and create 2 collections
#IBOutlet var allBts: [UIButton]!
#IBOutlet var allLbls: [UILabel]!
then hook all labels / btns to each , and set a tag for each group ( lbl1 && btn1 tag = 0 , lbl2 && btn2 tag = 1 and so on)
#IBAction func FieldDisplay(_ sender: UIButton) {
allLbls[sender.tag].text = sender.currentTitle! + "Label"
}
You can use value(forKey:) method
#IBAction func buttonTapped(_ sender: UIButton) {
if let currentLabel = value(forKey: sender.currentTitle! + "Label") as? UILabel {
currentLabel.text = "OK"
}
}
https://developer.apple.com/documentation/objectivec/nsobject/1412591-value

How to use random variable to choose correct button in swift

The title might not be the best explanation of what I want but I couldn't think of a better way to describe it.
#IBOutlet var button0: UIButton!
#IBOutlet var button1: UIButton!
#IBOutlet var button2: UIButton!
var num = (arc4random()%3);
basically I want to use the variable 'num' to choose which button and make it hidden based off the random number. Is there a way to use the variable in a a simple line like "button(num).hidden = true" or something like that?
[button0, button1, button2][num]
Basically, all you want to do is create an array of UIButtons!, and then set array[num].enable = false.
you can basically store your buttons into a array of UIButton, and then use this array randomly.
Code
self.buttons.append(button0)
self.buttons.append(button1)
self.buttons.append(button2)
self.buttons[Int(num)].hidden = true
var num = Int(arc4random_uniform(3))
let buttons:[UIButton] = [button0,button1,button2]
buttons[num].hidden = !buttons[num].hidden
Create an array that stores all your UIButtons
var buttonsArray: [UIButton]!
Access one of the UIButtons from the Array randomly and hide the accessed UIButton
var randomNum: Int = Int(arc4random()%3)
var button = buttonsArray[randomNum]
button.hidden = true

Swift: Generate Identifier for Variable

I have a large list of NSButtons (80+) that I assign to an array. I've used a numbering scheme to name them and I'd like to generate the identifiers for them instead of calling them directly by name one at a time.
#IBOutlet weak var button120: NSButton!
#IBOutlet weak var button121: NSButton!
var buttons [NSButton]()
Currently (in ViewDidLoad()) I'm doing the equivalent of:
buttons = [ button120, button121 ]
I'd prefer to do something like:
for index in 120...121 {
buttons.append("button\(index)".toIdentifier)
}
Can this be done in Swift?
This might be an easier solution, view is the parent view of the buttons
let buttons = view.subviews.filter { $0 is NSButton}