chipbk10
chipbk10

Reputation: 5955

Why using ? in this case is not working? (swift)

My purpose is to assign the color of UIView from an existing image:


self.myView.backgroundColor = UIColor(patternImage: UIImage(named: "04_white_back_page2")?)

As I understand, I don’t know whether this UIImage(named: "04_white_back_page2") contains nil or a value, so: if it has value use it, and otherwise just consider the whole expression nil, and do nothing.

Why do I always get the error:

"Value of optional type 'UIImage' not unwrapped; Did you mean to use '!' or '?'"

Upvotes: 0

Views: 1743

Answers (4)

Justin Rose
Justin Rose

Reputation: 1041

If you don't know whether your image exists or not, you should use optional binding, try this:

func setBGImage(myImageName: String) {
  if let bgColor = UIImage(named: myImageName) {
    self.view.backgroundColor = UIColor(patternImage: bgColor)
  }
}

If myImage exists, it will assign it to bgColor, in which is assigned to self.view.backgroundColor.

or, as stated before, if you know the image exists, just forcibly unwrap it by:

self.myView.backgroundColor = UIColor(patternImage: UIImage(named: "04_white_back_page2")!)

Good luck!

Upvotes: 1

vacawama
vacawama

Reputation: 154603

so: if it has value use it, and otherwise just consider the whole expression nil, and do nothing

You are describing optional chaining. In an optional chain, if a variable or expression might be nil, following it by a ? allows you to unwrap it for the purposes of accessing members, methods, or an array access or dictionary look up. If the variable or expression is nil, the whole expression is nil.

Optional chaining only works if the ? is followed by a member name (property), a method call, or array/dictionary access []. It does nothing to just end with a ?. Furthermore, the result of an optional chain is always an optional value, so it wouldn't help you here anyway because the constructor for UIColor doesn't take an optional. Unfortunately, you can't use optional chaining in the way you are attempting. It would be nice if it just skipped the entire line if your UIImage were nil, but it doesn't work that way.

The proper way to handle this if you don't know for sure if the image exists is to use optional binding when you create the image:

if let image = UIImage(named: "04_white_back_page2") {
    self.myView.backgroundColor = UIColor(patternImage: image)
}

Upvotes: 0

Wonjung Kim
Wonjung Kim

Reputation: 1933

Short answer: Change the code to

self.myView.backgroundColor = UIColor(patternImage: UIImage(named: "04_white_back_page2")!)

Long answer

First, UIImage(named:) is a 'failable initializer' .

If your filename 04_white_back_page2 does not exists in your project's resources, then the initializer UIImage(named: "04_white_back_page2") fails and it becomes nil.

Since UIColor(patternImage:) only accepts UIImage, the swift compiler complained that your UIImage(named: "04_white_back_page2")'s type is UIImage?.

You cans see the detail description about failable initializer on (https://developer.apple.com/swift/blog/?id=17)

However, you know 04_white_back_page2 is in your project or not(right?). Therefore, it's safe to 'unwrap' your expression with !. Use ! to say the compiler that you are quite confident that the expression is not nil. Then UIImage(named: "04_white_back_page2")! 's type is UIImage and the compiler is happy. If the trust is wrong, your application will die in runtime with the message fatal error: unexpectedly found nil while unwrapping an Optional value.

So, use ! only when you are confident.

In general, you don't know your expression is nil or not. In the case, you should handle the both cases in this way.

if let img = UIImage(named: "04_white_back_page2") {
  self.myView.backgroundColor = UIColor(patternImage: img)
}

Upvotes: 3

giorashc
giorashc

Reputation: 13713

the init method of UIColor that you are using (patternImage) does not expect an optional but an actual value (so a nill value is not allowed)

So either force unwrapping with '!'

self.myView.backgroundColor = UIColor(patternImage: UIImage(named: "04_white_back_page2")!)

or make sure the image exists before creating the color.

The former is not safe as it will cause a runtime error if the image fails to load for whatever reason so I'll suggest you use the second option

Upvotes: 1

Related Questions