Connect all UI Elements to one outlet at once, swift [duplicate] - ios

This question already has answers here:
Swift - IBOutletCollection equivalent
(7 answers)
Closed 6 years ago.
Using Swift 2 and XCode 7.2.1, is there a way to connect all of the UI Elements (buttons and labels) to one outlet? I feel like this would be done by doing CMD-A on all your elements then control-dragging to your code, but this only hooks up one of the selected elements to the outlet.

You can not do this.
The only things that multiple UI elements can be hooked up to are actions or outlet collections.
Importantly, an outlet is a single reference to a single object:
#IBOutlet weak var label: UILabel!
Just like with any other variable, it can't be two or more things at once. It can only be one thing. If I hook another thing up to this outlet, it will unhook whatever was previously hooked up to it.
However, I can make an outlet collection:
#IBOutlet strong var labels: [UILabel]!
I don't believe there is a short-cut to hooking up multiple elements at once, but you can hook up multiple elements (albeit it one at a time).
It's important to note that by default, Xcode will create the outlet collection as the exact type of whatever you dragged in, and you'll only be able to add elements of that type or subclasses of that type to the collection.
You can, however, manually change the type to a broader type and thereby hook up a wider variety of things:
#IBOutlet strong var labels: [UIView]!
Likewise, multiple objects of varying types can be hooked up to an #IBAction as long as the interface for the method makes sense:
#IBAction func action(sender: AnyObject) {
// write code to handle action here
}

Related

Why can't an #IBOutlet be assigned let instead of var in Swift?

I'm following a tutorial on Swift and I noticed that the author uses var instead of let when declaring an #IBOutlet variable. So I became curious as to why I can't use let instead since an object's properties are still mutable even if the object is constant or is this not the case?
The error Xcode shows when using let is
#IBOutlet attribute requires property to be mutable
but I'm confused because questionLabel is a UILabel object and not necessarily a property of an object. Or is the questionLabel object a property of the current viewController?
import UIKit
class ViewController: UIViewController {
#IBOutlet let questionLabel: UILabel!
}
Thank you in advance if I'm over analyzing.
The #IBOulet marked properties are generally properties of a ViewController that are connected using the interface builder. The view you create in the interface builder has to actually connect the interface elements in it to the properties during your actual application runtime.
For that reason it firstly creates a new ViewController using some init without connecting any interface elements. They only get connected at a later stage. For the runtime to be able to hook the properties up to the view elements after the object creation has completed they cannot be constants, they have to be mutable. Because they do not have a value after the initializer has finished they have to be optionals. And to not make using the properties cumbersome afterwards they are implicitly unwrapped optionals, so that you do not have to write label!.property but label.property suffices.
That is why your code crashes as soon as you try to do something with an IBOutlet variable which you failed to connect and that is also the reason why you cannot use / change / manipulate those fields in the initializer.
Regarding your actual var / let confusion. Yes, the object itself that is referenced using let can be changed, e.g. the text of a UILabel BUT the actual object reference cannot be changed. That would mean that if you do not give the constant a specific value in the initializer it would forever remain nil.
For the simple reason that it is not assigned during initialization (in the initXXX methods) but later, when the view is being loaded.
The compiler actually cannot be even sure that the variable is ever assigned because the view loading is comletely dynamic.
In swift, all vars and lets can be thought of as properties.
A property is immutable (a constant) if it's declared with let. It's mutable (a variable) if it's declared using the var keyword. That is the defining difference between let and var.
Outlets must be mutable because their value does not get set until after the object is initialized. (The view controller gets initialized and it's outlets don't get loaded right away.)
You are right questionLabel is an object of type UILabel but used as a property of your class ViewController. That's why you have #IBOutlet attribute requires property to be mutable. If you use var you are saying that a property is mutable. If you use let you are saying that the property is immutable.
Try to create questionLabel without #IBOutletand see what's going on. Probably, you can put let in front.
First the ViewController is created, then the view tree is built. That means that when the ViewController finished it's init these views don't exist yet. They will be added just before viewDidLoad by parsing the XML-like data of the storyboard or XIB.
I know that it's Apple's default way of doing things but I would always write my outlets like:
#IBOutlet let questionLabel: UILabel?
For the simple reason it's absolutely not proven this label will really exist at run time. For example when reusing ViewControllers over multiple screens, changing the layout after setting the connections and so on this outlet might not be set. If you would use the questionLabel defined as UILabel! and it would be nil your application will crash. I don't think applications in production should ever crash over something silly like that.
For real application safety the only way to know really for sure it exists is to build your UI's in code. But the ease of use of Storyboards for one off screens is really tempting, I still use them a lot.

Why the default type of the label is a forced unwrapped optional?

When I create a label in the view (dragged from the Object library) and make the IBOutlet to connect the label and viewController (Ctrl and drag), The default code generated by Xcode is, for example, #IBOutlet weak var displayColumn: UILabel!
I can delete the exclamation mark and set it as the UILable only. But there must be a good reason the Xcode want it to be a forced unwrapped optional type.
Question: I don't think I understand the reason to use the optional type here. Because I already created a label in my view and connected it to my controller, it is firmly out there, it exists, I created it. Why Xcode want an option as the default type? If it says the text inside the label is an option, that sounds reasonable to me, because the label may be empty at some point in run time.
Many Thanks
You have created a label in Xcode and indicated to Xcode that you want that label connected to that #IBOutlet. But that connection doesn't happen until later.
This is the order of events at runtime.
The viewController is created (instantiated).
The Storyboard or .xib is unarchived. The label is created at this time.
The #IBOutlets and #IBActions are connected.
In Swift, when a class is instantiated, all properties must be initialized. In step 1, the outlets are nil because they haven't been connected yet. In order to allow for this, they must be declared as Optionals.
It is perfectly valid to use a normal Optional, such as:
#IBOutlet weak var displayColumn: UILabel?
but then you'd have to deal with unwrapping it each time you access it. The only time an #IBOutlet will be nil is when step 3 hasn't happened yet (for example, in prepareForSegue) or if you forgot to connect the outlet or renamed it. In those cases, the crash caused by accessing nil alerts you to the problem.

Retain Cycle in Swift delegate [duplicate]

This question already has answers here:
How can I make a weak protocol reference in 'pure' Swift (without #objc)
(8 answers)
Closed 7 years ago.
I have a UIViewController and in it a UIToolbar. They get instantiated from a storyboard.
I made a custom class for my UIToolbar. Based on some logic I do or do not show buttons on it.
The UIViewController needs to take action when some of the buttons are tapped.
For this I created a delegate protocol in the UIToolbar.
Currently, when I dismiss the view, it is kept in memory. Further investigation revealed my delegate created a retain cycle.
In Objective-C, we would simply define delegates as weak. However, I am using Swift, and it does not allow me to define delegate variable as weak:
weak var navigationDelegate: MainToolBarDelegate?
// 'weak' cannot be applied to non-class type 'MainToolBarDelegate'
When I dismiss the view controller, I set self.toolBar.navigationDelegate = nil and the memory gets cleared. But it feels wrong!
Why do I get the retain cycle and why can I not simply define the delegate as weak?
weak references only apply to classes, not structs or enums, which are value types. But protocols by default can apply to any of those types.
Define your MainToolBarDelegate as a class-only protocol:
protocol MainToolBarDelegate: AnyObject {
}
Then you'll be able to declare your delegate as weak.

Design & Custom Viewcontroller

have a longer question than usual.
I am attempting to make a card game and I am confused as to whether I am designing it awfully.
The player's hand consists of 10 cards, each of which is a button and 4 labels that indicate the stats of the card.
What I had in mind was creating an array of objects that have these UILabel and button properties. For example in a "Card" class I would have...
#IBOutlet var cardName: UILabel
#IBOutlet var cardStat1: UILabel
#IBOutlet var cardStat2: UILabel
#IBOutlet var cardStat3: UILabel
#IBOutlet var cardStat4: UILabel
I would then have an array that contains ten objects of this Card class. This is my design portion of my question.
My View Controller question is like I mentioned above - I have a Card class but I am having trouble connecting outlets to it. It inherits from UIViewController but I get the error...
Must call a designated initializer of the superclass 'UIViewController'
...even though I call
super.init()
Feel free to aid me with any portion of my issues, thanks a lot!
This is my design portion of my question.
Having 10 view controllers, one for each card, sounds like a solution that's far more complicated than it needs to be. I think you'd be better off with something like a collection view, with one collection view cell for each card. You'd also need some kind of data model for storing the information about each card -- an array of dictionaries, for example, or an array of your Card objects (but not derived from UIViewController).
I get the error...
The designated initializers for UIViewController are -initWithNibName:bundle: and -initWithCoder:. There's an -init method defined, but it sounds like Swift wants you to call a designated initializer, and that's not -init.

Outlet collection in Swift causes Error 254

I'm working on Stanford's Matchismo game and I'm trying to create an array of buttons that I can reference in the controller. it's a card game, each card a button.
I drag/drop a UIButton to the controller and then set it up in an outlet collection. Not only does it set it up but lets me link multiple outlets to that collection. So far so good.
However when I compile my code (target iOS 7.1) I get a 254 error. I've identified that it's the outlet collection that causes the error.
class MainController: UIViewController{
let cardDeck = PlayingCardDeck()
#IBOutlet var cardButtons: UIButton[]
}
When I replace the UIButton[] mention with NSArray the compiler loads without error. But this is not at all what the exercise is all about - I'm expecting Swift to register the outlets that have been linked to the UIButton[] array and allow me to manipulate that array.
That is, as far as I can tell, the expectation set by the "link to outlet collection" feature in XCode6 Beta 3.
I uploaded a screenshot of the error report. Apparently this has to do with unwrapping the weakly referenced optional array. This is beyond my powers to fix on my own.
You need to define it properly, currently your declaration is not proper -
If you are defining outlet of array of buttons then use -
#IBOutlet var cardButtons: Array <UIButton>
If you are defining outlet of single button then use -
#IBOutlet var cardButton: UIButton
This is still not working in Xcode seed 6.3, however for possible workaround check this link - link
Try This
#IBOutlet var cardButtons: Array<UIButton>

Resources