I'm learning Swift, but I Have two "stupid" Problems.
the first, I'd like autosize my UILabel
The second, I have another UIlabel and I want to put name and surname in it's field
I tried with
#IBOutlet weak var title: UILabel!
title.text = currentPerson?.name+""+currentPerson?.surname
But I have this error
Value of optional type 'String?' not unwrapped; did you mean to use
"!" or "?" ?
Generally advised to ask 1 question per post so you get clear responses & don't mix topics, but...
In XCode storyboard "Attributes Inspector" you can change "Autoshrink" from "Fixed Font Size" to minimum font size or scale. Also change "Lines" from default 1 to 0. You'd also need to set some AutoLayout constraints to pin the label to superview or other elements in a way that will allow it to scale. Can't say more w/o seeing storyboard.
By using optional chaining to set the label text you're trying to set the label's .text property to an optional type String? instead of a String. Those aren't equivalent. An optional of type String? might contain a String, or it might be nil. The UILabel expects you to use a String instance, so it's complaining about the mismatch.
One approach is to explicitly check the optional value against nil:
if currentPerson != nil {
title.text = "\(currentPerson.name) \(currentPerson.surname)"
}
else {
title.text = ""
}
Swift's optional binding is similar to the first option, but you create a temporary constant and can reference its properties. If currentPerson is not nil, then the if block executes.
// current convention would be to use "currentPerson" on both sides, which can be confusing. The left side is a temporary constant & the right side is the optional property you've declared somewhere above
if let aPerson = currentPerson {
title.text = "\(aPerson.name) \(aPerson.surname)"
}
else {
title.text = ""
}
Alternatively, as the error message suggests, you could Force Unwrap the optional value to access the name properties:
title.text = currentPerson!.name + " " + currentPerson!.surname
This assumes that currentPerson is never nil. If it is nil, your app will crash here.
Also note you can concatenate using + and " " or with string interpolation.
Related
Check this please i need explain why texField.text! not crash and label.text! crash
And as we know UITextField has property open var text:String? and UILabel have open var text:String?
let texField = UITextField()
texField.text = nil
print(texField.text!) // not crash
let label = UILabel()
label.text = nil
print(label.text!) //crash
From the documentation of UITextField
Declaration
var text: String? { get set }
Discussion
This string is #"" by default.
From the documentation of UILabel
Declaration
var text: String? { get set }
Discussion
This property is nil by default.
Please note the subtle difference
As a general rule of thumb, you should never force-unwrap optional values like this.
You should use if-let or guard or any way provided by swift to handle optionals safely.
If a label has no text, then label.text will return nil.
However, if a text field has no text then textField will return "" (an empty string). That's why it will not crash if you force-unwrap the text property of a text field.
I'm not sure why it designed like this by Apple, but as I wrote above you should handle optionals safely.
I have an empty text field on my UI, though it has a placeholder text (whose value is foo) set in the storyboard. In my UI test, I am trying to check that its text value starts out empty, but when I query it's value, it seems to be giving me the placeholder value instead:
func testTextFieldInitiallyEmpty {
let input = XCUIApplication().textFields["My Text Field"]
XCTAssertEqual(input.value as! String, "")
}
as the test fails with this message:
XCTAssertEqual failed: ("foo") is not equal to ("")
Of course, foo is the placeholder value, but it's not the text value of that text field. I would have expected that error message if I had written:
XCTAssertEqual(input.placeholderValue as! String, "")
input is a XCUIElement, which implements XCUIElementAttributes, so I don't see anything else that would do the trick here.
How do I check (assert) that the text field is empty?
Edit
After doing some further research and trying out the suggestions below for using the input's properties of accessibilityValue, label, and title, I have not found any solution that will give me the text field's text value when there is text, and an empty string when only the placeholder is visible.
This seems like either (a) a bug, or (b) a questionable design decision from the test framework to not provide that ability. At a minimum, the documentation for XCUIElementAttributes#value seems inadequate to me, as the only detail is:
The exact type of value varies based on the type of the element.
Still looking for a better solution...
You can compare to the XCUIElementAttributes's placeholderValue variable in addition to checking for a blank string
extension XCUIElement {
func noTextEntered() -> Bool {
return self.value as? String != "" && self.value as? String != placeholderValue
}
}
Then you can run XCAssert(input.noTextEntered(), "Unexpected text entered into field")
Just make sure your placeholder is not something a user would type in. This way you don't have to hardcode placeholder values to check against
Kind of ridiculous that this is actually the case it works and that it needs a workaround.
Anyway, my solution to get the value w/o the placeholder interfering, based on #Jason's answer.
extension XCUIElement {
var valueWithoutPlaceholder: String {
if let v = value as? String, v != placeholderValue {
return v
}
return ""
}
}
Be aware, if the input is actually the placeholder this would break!
Try using accessibilityValue property of input.
func testTextFieldInitiallyEmpty {
let input = XCUIApplication().textFields["My Text Field"]
XCTAssertEqual(input.accessibilityValue, "")
}
If you command+click the property, you can see the following..
/*
Returns a localized string that represents the value of the element, such as the value
of a slider or the text in a text field. Use only when the label of the element
differs from a value. For example: A volume slider has a label of "Volume", but a value of "60%".
default == nil
default on UIKit controls == values for appropriate controls
Setting the property will change the value that is returned to the accessibility client.
*/
public var accessibilityValue: String?
I have been using optional a lot.I have declared a string variable as optional
var str: String?
Now i set some value in this variable as str = "hello".Now if i print this optional without unwrapping then it print as
Optional("hello")
But if i set text on the label as self.label.text = str then it just display value as
hello
Please explain why it does not show text as on label
Optional("hello")
The text property of UILabel is optional. UILabel is smart enough to check if the text property's value is set to nil or a non-nil value. If it's not nil, then it shows the properly unwrapped (and now non-optional) value.
Internally, I imagine the drawRect method of UILabel has code along the lines of the following:
if let str = self.text {
// render the non-optional string value in "str"
} else {
// show an empty label
}
I knew I've seen optionals printed in UILabel, UITextView, UITextField. Rmaddy's answer wasn't convincing.
It's very likely that there is an internal if let else so if the optional has a value then it will unwrap it and show. If not then it would show nothing.
However there's a catch!
let optionalString : String? = "Hello World"
label.text = "\(optionalString)" // Optional("Hello World")
label.text = optionalString // Hello World
let nilOptionalString : String?
label.text = "\(nilOptionalString)" // `nil` would be shown on the screen.
label.text = nilOptionalString // no text or anything would be shown on the screen . It would be an empty label.
The reason is that once you do "\(optionalVariable)" then it would go through a string interpolation and once its a String and not String? then the result of the interpolation would be shown!
I couldn't understand the difference between a explicitly declared String and an implicitly unwrapped optional string.
For example,
If we initialzse a String explicitly,
let assumedString:String = "Test String"
print(assumedString)
gives the output
"Test String"
"Test String\n"
in playground.
Likewise if we implicitly unwrap an optional string like this,
let assumedString:String! = "Test String"
print(assumedString)
gives the same output
"Test String"
"Test String\n"
And also once we use '!' while initializing, then its value cannot be nil. So We can use the explicit type right?
Then why do we have to use the concept of using '!' (implicitly unwrapping an optional string).
Kindly explain the difference or the use of using '!' with a code example if possible.
In your example, you're only using let constants. With let constants, you can hardly see the difference between the two. The situation changes.
As you know, all properties in a class must be initialized to some value in the initializer(s). Optional properties is an exception to this rule. They are by default, nil i.e. no value.
I most often use implictly unwrapped optionals is when I have a property that stores a view's height or anything UI related. I always declare those as implicitly unwrapped optionals. e.g.
var myViewsHeight: CGFloat!
override func viewDidLoad() {
myViewsHeight = self.view.viewWithTag(1).frame.height
}
You obviously can't initialize that variable in init because at init, the views have not been laid out yet! There is no way to know the view's height. You make it an implictly unwrapped optional because you know it will get initialized in viewDidLoad. Another advantage of this is that it makes tracking errors easier. Say your app crashed because if something happened in viewDidLoad and the line that initializes myViewsHeight is not executed, you will know immediately because your app crashes!
You will also see that all IBOutlets are implictly unwrapped optionals, for the same reason - the views can't be assigned at init.
let assumedString:String = "Test String"
Means you simply define a string constant , but when you write
let assumedString:String! = "Test String"
means you you are declaring a string of optional type . So,it may contain a value or not . But here you are initialised at the time of declaration of string . So , If you print both will give same output. For more understanding go to below link
Difference between var someString = “Some String”, var someString: String = “Some String”, var someString = “Some String” as string
When I write a simple function such as this:
#IBAction func buttonTapped(theButton:UIButton) {
println(theButton.titleLabel.text);
}
It gives me an error: UILabel doesn't have a label called text.
However, when I change it to this:
#IBAction func buttonTapped(theButton:UIButton) {
println(theButton.titleLabel?.text);
}
It works fine, but it prints out something like this:
Optional("1");
What I am doing wrong? I am expecting a value of 1. But it is printing out Optional("1") and secondly, it is working fine when println(theButton.titleLabel?.text);
You can get directly from
let title = theButton.currentTitle!
Optional chaining makes the result optional, so you are printing optional value: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/OptionalChaining.html
With optional binding you can print the value only if it exits.
if let text = theButton.titleLabel?.text {
println(text)
} else {
// text doesn't have value
}
#Kirsteins's answer shows how to obtain the button label text in a safe manner.
Remember that:
UIButton has a titleLabel, which is an optional UILabel.
UILabel has a text property, which is an optional String
so there are 2 optionals in the chain. You can use optional binding as in #Kirsteins's answer, or use forced unwrapping:
let text = theButton.titleLabel!.text!
which however I discourage using, because if any of the 2 is nil you'll have a runtime exception. But for completeness it's worth mentioning.
The buttons titleLabel property returns an optional UILabel, that means it's possible that the button doesn't have a titleLabel.
var titleLabel: UILabel? { get }
If you don't set a title to the button, then the button doesn't have a titleLabel property, the iOS framework adds the titleLabel only if the button has a title, I think this happens to reduce memory.
This is why you have to put the "?" (is called optional chaining you can read about it here http://bit.ly/1vrSOi1) in that line, but this usually get auto completed by Xcode itself.
Kirsteins answers it correctly but misses one small detail
if your object can be nil (optional) you need to check first if it exists to then access its value, like this:
if let text = theButton.titleLabel?.text {
println(text)
}
but you can also ignore the if and just call it like this:
let text : String = theButton.titleLabel?.text
// If theButton.titleLabel don't exists then text will be nil
this happen if the IBOutlet was declared with ? but if you declare with ! that means you know that it could be nil, but you never want it to be nil, for a IBOutlet i prefer this approach since if the IBOutlet is not connected then maybe something is worn with my code.
#IBOutlet var theButton : UIButton!
// and get text value as
theButton.titleLabel!.text
this will ensure theButton.titleLabel could be nil, but in this part of code it is required, hope this helps to understand the difference between optional (?) and optional required (!)