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>
Related
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.
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.
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
}
I have a ViewController with a ScrollView in it, which has 2 ContainerViews in it. Now, every ContainerView loads a ViewController.
And in this ViewControllers (SubController from the ContainerViews) its impossible to do Outlets.
I get the little circles at the outlets, so the reference exists, but setting the text of a label for example, always results in the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I've done the connection 10 times, with all possible names. Restarted xcode multiple times, still not working.
This answer could help: https://stackoverflow.com/a/28479934/2414069
EDIT: (below code is pseudo, everything is in the place it should..)
#IBOutlet weak var pinInfo_name: UILabel!
#IBOutlet weak var pinInfo_address: UITextView!
#IBOutlet weak var pinInfo_comment: UITextView!
self.pinInfo_name.text = defaults.stringForKey("activePin_name")!
self.pinInfo_address.text = defaults.stringForKey("activePin_address")!
self.pinInfo_comment.text = defaults.stringForKey("activePin_comment")!
SOULUTION:
Ok I slept about it, and found my error within 10 minutes: I wanted to change the .text properties from a object I created in another ViewController. I still have no Idea how to do that, buts thats a story for another time.
tl dr
Access right
As the link in your question points out, having multiple view controllers in your storyboard with the same class could cause this. Did you check that:
The child view controllers do not have the same class as each other
The child view controllers do not have the same class as the parent view controller
I made a test project and the I could connect the outlets of the UILabels in the child view controller to their respective UIViewController subclasses without any problem.
Here's my example:
IBOutlets are weak by default in Swift. I have an object in the viewController Created in the storyboard which is not in the view hierarchy , So I need it to be a strong reference in ViewController , How can I Change #IBoutlet property to strong.
You can make an IBOutlet strong by either selecting strong when connecting the outlet:
Or just remove the weak keyword from the declaration:
#IBOutlet var label: UILabel!
Change the name of the outlet, it might be a reserved name so you are trying to override it.
As of Xcode 6 beta 2, Swift does not have a way to designate strong outlets. The workaround is to connect the outlet in IB, then remove the #IBOutlet attribute from your source file.
Update: This has been added in Xcode 6 beta 3.
Its now an option when creating the outlet from a drop down.
Here is why we might want to start making them strong from WWDC 2015 Session 407
http://asciiwwdc.com/2015/sessions/407
And the last option I want to point out is the storage type, which can
either be strong or weak.
In general you should make your outlet strong, especially if you are
connecting an outlet to a sub view or to a constraint that's not
always going to be retained by the view hierarchy.
The only time you really need to make an outlet weak is if you have a
custom view that references something back up the view hierarchy and
in general that's not recommended.
So I'm going to choose strong and I will click connect which will
generate my outlet.
The strong keyword is gone again and raises a syntax error in Xcode 6.1.1. It appears like outlets are now strong by default, which previously was the opposite. So simply define the outlet w/o additional declaration.
#IBOutlet var nameOfOutlet: type = Whatever();
Another reason for that error is because a stupid bug. For example when I initialize a UIImageView object called "imageView" in the view controller an error occurred called "cannot override strong property with weak property".
But when I change the object name for example "pictureView", the error is gone.
Best regards...
As of Xcode 6 beta 3, Swift now allows marking #IBOutlets as strong.
From the release notes:
• #IBOutlets may be explicitly marked strong to override their
implicitly-weak behavior. (16954464)