I'm creating a UIButton with an image,
I have written the below code for that:
let btnImg=UIButton.buttonWithType(UIButtonType.Custom) as UIButton
let img = UIImage(named: "turn_left") as UIImage
btnImg.setTitle("Turn left", forState: UIControlState.Normal)
btnImg.setImage(img, forState: UIControlState.Normal)
btnImg.frame = CGRectMake(10, 150, 200, 45)
self.view.addSubview(btnImg)
But I got the error below at let img = UIImage(named: "turn_left") as UIImage:
Swift Compiler error:
Downcast from 'UIImage?' to 'UIImage' only unwraps optionals; did you mean to use '!'?
As error Says You have to use '!' ,
Try Below code,
let img = UIImage(named: "turn_left") as UIImage! // implicitly unwrapped
OR
let img : UIImage? = UIImage(named: "turn_left") //optional
Edit
After creating img you need to check it for nil before using it.
You can always do it in an 'if let' but note the difference in syntax depending on the version of swift.
Swift < 2.0:
if let img = img as? UIImage {
Swift 2.0:
if let img = img as UIImage! {
Note the position of the exclamation
If the UIImage initialiser cannot find the file specified (or some other error happened), it will return nil, as per Apple's documentation
Return Value
The image object for the specified file, or nil if the method could not find the specified image.
So you need to put checks in:
let img = UIImage(named: "turn_left")
if(img != nil) {
// Do some stuff with it
}
You don't need to cast a UIImage to a UIImage, that's a waste.
Edit: Full code
let img = UIImage(named: "turn_left")
if(img != nil) {
let btnImg = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
btnImg.setTitle("Turn left", forState: UIControlState.Normal)
btnImg.setImage(img, forState: UIControlState.Normal)
btnImg.frame = CGRectMake(10, 150, 200, 45)
self.view.addSubview(btnImg)
}
You may want to put an else in to set the button to a shape or something else that is more likely to work in the case "turn_left" doesn't exist.
Since UIImage(named:"something") returns an optional (because it could be nil if the methods doesn't find an appropriate image the best this is not to explicitly unwrap the result (otherwise your app will crash) but to check for the result immediately with something like that:
if let image = UIImage(named: "something"){
// now you can use image without ? because you are sure to have an image here!
image.description
}
// continue your code here using the optional power of swift's vars! :)
The approach is the following: If the image is optional means that it can be null. Functions that takes in input an optional can handle that, otherwise they usually have unexpected behavior. The UIImage(named:) can return nil and you HAVE to handle this. If you explicitly unwrap it you could have problems later.
With if let something = ... something will be automatically unwrapped in runtime and you can use it safely.
Related
I am working on a image quiz. I have a button that will display either the correct image or the wrong image if there is no correct image. The unique question number comes from an array.
if (questionlist[1]correct = [UIImage imageNamed:#"questionlist[1]correct.png"]) {
Answer4.setImage(UIImage(named: "\(questionlist[1])correct.png"), forState: UIControlState.Normal)
} else {
Answer4.setImage(UIImage(named: "\(questionlist[1])fourthwrong.png"), forState: UIControlState.Normal)
}
Okay so if I'm understanding your questionList array is simply an array of numbers? So questionList = [1,2,3,etc]? In that case, you may want to try something like:
let imageName = "\(questionList[1])wrong.png" //for instance 2wrong.png
if (UIImage(named: imageName) == nil) { // no wrong image exists
imageView.image = UIImage(named: "\(questionList[1])correct.png")
} else {
imageView.image = UIImage(named: "\(questionList[1])wrong.png")
}
Of course this is assuming a correct image exists. It's unclear what you're after, some more information/more code would help.
I have faced a problem with setting uiimageview from other viewcontroller.
DiaryDetailController.swift
let imageViewPageController = (segue.destinationViewController as! ImageViewPageController)
if (imageCount > 0) {
imageViewPageController.image1 = tempImage1
}
ImageViewPageController.swift
let page: ImageViewDetailController! = storyboard?.instantiateViewControllerWithIdentifier("imageViewDetailController") as! ImageViewDetailController
page.setImage(image1)
pages.append(page)
ImageViewDetailController.swift
func setImage(image: UIImage) {
imageView.image = image
}
And I got following error on "imageView.image = image".
fatal error: unexpectedly found nil while unwrapping an Optional value
How should I solve this problem?
You're probably trying to set the image before the IBOutlets are set.
Create a UIImage property in ImageViewDetailController (let's call it image), and instead of saying:
page.setImage(image1)
You can do this
page.image = image1
Then let the ImageViewDetailController deal with it once it has loaded. So in the viewDidLoad, just say
imageView.image = self.image
I'm having issues with my project. I'm new to coding. I keep getting the error fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb). It seems that the variable "image" is not producing an image and instead producing "nil". the variable "photo" is producing a name that corresponds with my JPG image name. For some the variable "image" is not able to produce that UIImage and rather is producing nil. Hopefully you can help me.
import UIKit
class Photo {
class func allPhotos() -> [Photo] {
var photos = [Photo]()
if let URL = NSBundle.mainBundle().URLForResource("Photos", withExtension: "plist") {
if let photosFromPlist = NSArray(contentsOfURL: URL) {
for dictionary in photosFromPlist {
let photo = Photo(dictionary: dictionary as! NSDictionary)
photos.append(photo)
}
}
}
return photos
}
var caption: String
var comment: String
var image: UIImage
init(caption: String, comment: String, image: UIImage) {
self.caption = caption
self.comment = comment
self.image = image
}
convenience init(dictionary: NSDictionary) {
let caption = dictionary["Caption"] as? String
let comment = dictionary["Comment"] as? String
let photo = dictionary["Photo"] as? String
let image = UIImage(named: photo!)?.decompressedImage
self.init(caption: caption!, comment: comment!, image: image!)
}
func heightForComment(font: UIFont, width: CGFloat) -> CGFloat {
let rect = NSString(string: comment).boundingRectWithSize(CGSize(width: width, height: CGFloat(MAXFLOAT)), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(rect.height)
}
}
I have a feeling that it has to do with the decompression of the image:
import UIKit
extension UIImage {
var decompressedImage: UIImage {
UIGraphicsBeginImageContextWithOptions(size, true, 0)
drawAtPoint(CGPointZero)
let decompressedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return decompressedImage
}
}
Most likely it's these two lines:
let image = UIImage(named: photo!)?.decompressedImage
self.init(caption: caption!, comment: comment!, image: image!)
The first line assigns a value to image but specifically allows for a nil value. That's what the ? implies. Also, in decompressedImage (the purpose of which is... unclear) the call to UIGraphicsGetImageFromCurrentImageContext is allowed to return nil.
Then in the second line you use image! without checking whether it's nil. It is nil, according to your crash, which implies that one of the following is true:
photo doesn't actually contain the name of an image in your app bundle, or
photo has a valid name but the file is somehow corrupt to the point where UIImage can't handle it, or
decompressedImage is returning nil, because UIGraphicsGetImageFromCurrentImageContext for some reason.
There are many things going on here that might go wrong at runtime. So I'll focus on one, and advice you to be careful with force unwrapping (optionalProperty!) unless you know your optionals and in which situations it's ok to use force unwrapping. If you don't yet know the latter, you should avoid !.
Let's look at this segment of code
let image = UIImage(named: photo!)?.decompressedImage
As you mention, photo is an optional string that might be nil. But if we look at the class reference or UIImage, specifically the initialiser UIImage(named: ...), it specifies
init?(named name: String)
I.e., the parameter named should be a string. nil is not a string and hence your error.
If we proceed to assume that photo actually forcefully unwrapped to a string (path), we have our next problem; "Value of type UIImage has no member 'decompressedImage'". Looking back at the class reference for UIImage, we can't find this method here.
Anyway, I suggest read up on optionals and optional chaining in swift (if let, guard let, error handling), e.g.
Optional chaining.
In your specific case, you need to handle, at the very least, the case where expression dictionary["Photo"] as? String returns nil.
I am trying to figure out how to translate this in Swift and I am also having this error: "Could not find an overload for “init” that accepts the supplied arguments". Any suggestion appreciated. Thanks.
var pageImages:[UIImage] = [UIImage]()
pageImages = [UIImage(named: "example.png"), UIImage(named: "example2.png")]
Confirming what matt says:
in xCode 6.0 this does work:
images = [UIImage(named: "steps_normal"), UIImage(named: "steps_big")]
but in xCode6.1 values should be unwrapped:
images = [UIImage(named: "steps_normal")!, UIImage(named: "steps_big")!]
Unwrap those optionals. A UIImage is not the same as a UIImage?, which is what the named: initializer returns. Thus:
var pageImages = [UIImage(named: "example.png")!, UIImage(named: "example2.png")!]
(Unless, of course, you actually want an array of optional UIImages.)
UIImage(named:) changed to be a failable initializer in Xcode 6.1, which means that it will return nil if any of the images you've listed are missing from your bundle. To safely load the images, try something like this instead:
var pageImages = [UIImage]()
for name in ["example.png", "example2.png"] {
if let image = UIImage(named: name) {
pageImages.append(image)
}
}
var image = self.imageData[index] as NSString
if let derp = image as NSString? {
println(" \(image)")
} else {
println("is nil")
}
dataViewController.dataImage.image = UIImage(named: image) as UIImage
That last line:
dataViewController.dataImage.image = UIImage(named: image) as UIImage
gives me "Can't unwrap Optional.None", despite the image object successfully passing the optional binding test as shown here https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_428 . The image string does print at the bottom of Xcode.
It is possible you are getting that error because an image with that name does not exist. However, you also have some issues with your optional binding.
Optional binding is different from casting. You don't provide a new type. Also, even if casting was the way to do it, you are casting to an optional, which doesn't prove that image is not nil.
You have already asserted to the compiler that image is not nil with your as NSString on the first line. If this conversion is not successful at runtime, your whole app will crash.
After binding an optional, you should use the local variable, not use the optional later
This means your code should look like this:
var possibleImageName = self.imageData[index] as? NSString
if let imageName = possibleImageName {
var possibleImage : UIImage? = UIImage(named: imageName)
if let image = possibleImage {
dataViewController.dataImage.image = image
}
} else {
println("is nil")
}
After you understand the optional binding process, and the difference from casting, you can shorten the code to this:
if let imageName = self.imageData[index] as? NSString {
if let image = UIImage(named: imageName) as UIImage? {
dataViewController.dataImage.image = image
}
} else {
println("is nil")
}
Note: The check for nil from initializers is strange. You have to cast it to be an optional type so that you can actually test it because initializers from Objective-C actually return Implicitly Unwrapped Optionals.
UIImage(named:) may return nil in case the image with given name cannot be found. You need to check it did not nil.
var img : UIImage? = UIImage(named: image)
if img != nil {
dataViewController.dataImage.image = img!
}
or
if let img = UIImage(named: image) as UIImage! {
dataViewController.dataImage.image = img
}