attach several UILabels from storyboard to one IBOutlet - swift

I've 15 Labels in my Storyboard they are just texts, also set from storyboard, What I want to do is to style them, but programitically, Therefore I need to create 15 IBOutlets in my ViewController, I wonder if there is any other way of doing that, without 15 IBOutlets,if it's possible to create 1 IBOutlet and attach all of them to that one? because creating 15 of them is kinda stressing...

You can do this with Outlet Collections instead of an IBOutlet for all the labels you want to group together:
One way to do it is to ctrl+drag from your storyboard to your editor and select outlet collection
This will create #IBOutlet weak var labelCollection: UILabel! in your code
This works fine but then you need to add an additional check for the type when looping:
#IBOutlet weak var labelCollection: UILabel!
func setCustomLayout()
{
for label in labelCollection2.subviews
{
if let label = label as? UILabel
{
// do your custom set up here
}
}
}
What I like to do is to create the specific outlet collection in code first if I way to track the same type like so:
#IBOutlet var labelCollection: [UILabel]!
The I drag from the editor to the storyboard
Then I can work with it as follows
#IBOutlet var labelCollection: [UILabel]!
func setCustomLayout()
{
for label in labelCollection
{
// do your customization here
}
}
Then you can loop through the UIViews inside the IBOutletCollection and do the needful

Related

Why when I pass data to a UILabel directly in the Second View Controller it turns to be nil?

Why when I pass data to a UILabel directly in the Second View Controller it turns to be nil but when I pass data to a variable first and then apply it to the UILabel everything works fine?
The following code crash the app after segue performed (theResult is nil):
Second View Controller:
#IBOutlet weak var theResult: UILabel!
Main View Controler:
secondVC.theResult.text = “Nice”
But when I create a variable in the Second View Controller, I can pass the data to it easily and then apply it to the UILabel without any problem:
Second View Controller:
var calculation: String?
#IBOutlet weak var theResult: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
theResult.text = calculation
}
Main View Controler:
secondVC.calculation = “Nice”
Why is that?
Note: The UILabel has a default value already when it is created in the Storyboard.
I am almost sure that this is not working but I want to know the reason. Why you can easily change for example the background of the Second View Controller like this:
secondVC.view.backgroundColor = .green
but when you touch the IBOutlets it do not work.

how to create one iboutlet for multiple buttons in swift

So far I have only been able to create one iboutlet per button. What i want to achieve is to create one iboutlet for many buttons thus allowing me to write the code for creating the same border width, colour and radius around these buttons only once. Is this possible?
import UIKit
class GameScreenViewController: UIViewController {
#IBOutlet weak var answerTextLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var numberBtn: UIButton! //this iboutlet is what i would like to be able to connect to multiple number buttons in my app.
}
you can use IBoutlet Collection !
You can connect several same types of objects after declaring as IBoutlet Collection.
And if you want to run the same code, you can use the for statement.
for item in myButton {
item.layer.cornerRadius = 3
}
The above solution recommended is indeed correct. There is an option for connecting collection of IBOutlets. But you need to make sure all the IBOutlet it connected to the collection are of the same type. For example collection of UIButtons only.
You connect using New Referencing Outlet Collection instead of New Referencing Outlet

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
}
}

How to add a custom uiview to a view controller programmatically SWIFT

I am trying to add a custom view programmatically to my view controller. I used this snippet of code with no success of it appearing in front of my main view controller.
var DynamicView = CustomFilter()
DynamicView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(DynamicView)
CustomFilter Class:
import UIKit
class CustomFilter:
UIView {
#IBOutlet weak var party: UIButton!
#IBOutlet weak var outdoors: UIButton!
#IBOutlet weak var sports: UIButton!
#IBOutlet weak var diner: UIButton!
#IBOutlet weak var music: UIButton!
#IBOutlet weak var gaming: UIButton!
}
The custom filter is connected to a xib file.
Xib File:
Is there a possibility that the custom view maybe out of placed? I'm not sure what I'm doing wrong.
In order to use a view we designed in a xib, we most load from the xib.
if
let bundle = NSBundle(forClass: CustomFilter.self),
let nib = bundle.loadNibNamed("<#Xib File Name#>", owner: self, options: nil),
let dynamicView = nib.first as? CustomFilter {
self.view.addSubview(dynamicView)
}
An alternative approach would be to write your CustomFilter's init to load the view from the xib itself.
More clearly, the problem you're having is that none of your CustomFilter's initializers are going to care about the xib file you made unless you write them and tell them to care about it. Your current code is returning a 0x0 view with probably a white or clear background. If you modified your current code to set the CustomFilter's frame to something other than 0x0 size and set the background color to something like UIColor.greenColor(), you'd see it clear as day.
Also, you could use Xcode's visual debugger to find it.
It's probably zero height and zero width, and may be off-screen also.
You need to give it height and width constraints and x and y position constraints.
You should probably also use CustomFilter(frame: someFrameRect), since as I recall initWithFrame is the designated initializer for UIView.
As an aside, variable names should start with a lower-case letter, so DynamicView should be dynamicView

Swift - IBOutletCollection equivalent

I'm trying to replicate the Stanford Matchismo game from "Developing ios7 apps for iphone and ipad" in iTunesU in Swift.
On page 77 of the 3rd lecture slides, it shows using an IBOutletCollection which isn't an option on Swift. The Swift doc example shows one example that has an array of IBOutlet, but I can't figure out how to make Interface Builder connect multiple outlets to the same IBOutlet/IBOutlet Array.
Has anyone figured out how to do this yet?
I know that I can create 12 outlets and deal with it that way, but I'd like to make this work as closely as possible to the example in the lecture slides.
Update: This works properly in Xcode now - "Outlet Collection" is one of the connection options in Interface Builder, which creates something that looks like:
#IBOutlet var labelCollection: [UILabel]!
While we're waiting for a fix, you can approximate this using a computed property. Let's say my view has five UILabels that I want in a collection. I still have to declare each one, but then I also declare a computed property that collects them:
class MyViewController {
#IBOutlet var label1 : UILabel
#IBOutlet var label2 : UILabel
#IBOutlet var label3 : UILabel
#IBOutlet var label4 : UILabel
#IBOutlet var label5 : UILabel
var labels: UILabel![] { return [label1, label2, label3, label4, label5] }
Kind of annoying, but from then on we can treat the labels property as if it were an IBOutletCollection, and won't have to change the rest of our code once the bug is fixed:
override func viewDidLoad() {
super.viewDidLoad()
for (index, item) in enumerate(self.labels) {
item.text = "Label #\(index)"
}
}
Use:
#IBOutlet var lineFields: [UITextField]!
Then control-drag from UITextField elements to lineFields in order.
#IBOutlet var buttons : [UIView]!
then drag it from the connections inspector in the interface builder or whatever metod you usually use for that
EDIT
This was fixed in a later Beta release of Swift - there's now in
IBCollection option in the interface builder.
For early Beta releases of Swift:
I came across the same problem: in the release notes of Beta 2 you find the following statement:
Interface Builder does not support declaring outlet collections in Swift classes
I solved this the following way (easy to customize):
class CardGameViewController: UIViewController {
#lazy var cardButtons : UIButton[] = {
var tempBtn: UIButton[] = []
for v:AnyObject in self.view.subviews {
if v is UIButton {
tempBtn.append(v as UIButton)
}
}
return tempBtn
}()
...
Basically, it loops through all the subviews and checks if one is a UIButton. In that case it gets added to a temporary array. This temporary array is then used to lazy instantiate the cardButtons array. For all details, check: Matchismo: Objective-C to Swift
Follow steps to create an array of outlets and connect it with IB Elements:
Create an array of IBOutlets
Add multiple UIElements (Views) in your Storyboard ViewController interface (As shown in below snapshot)
Select ViewController (In storyboard) and open connection inspector
There is option 'Outlet Collections' in connection inspector (You will see an array of outlets there)
Connect if with your interface elements
-
class ViewController2: UIViewController {
#IBOutlet var collection:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
}
}
I got this working in Xcode seed 3 using this syntax
#IBOutlet strong var views: NSArray?
See my discussion here: https://stackoverflow.com/a/24686602/341994
What #machine said seems to be the current state (XCode 7.1) with iOS 9 bindings.
The key is to drag them all in order.
Use the first item to control+drag into the controller code and then change the Outlet type to collection. After the from the controller code file drag the outlet point onto each of the screen controls one by one in order (as #machine says)