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.
Related
I have a function that I want to return an UIImage but the UIImage must be in the array of UIImages I have created.
This works but I want it to fail if the item is not a part of the array.
private let screenImages: [UIImage] = [#imageLiteral(resourceName: "screen-1"), #imageLiteral(resourceName: "screen-2"), #imageLiteral(resourceName: "screen-3")]
private let getImage() -> (UIImage) {
let random = Int(arc4random_uniform(UInt32(screenImages.count)))
let image = screenImages[random]
return image
}
So in the escaping of the function am I able to tell the function what kind of image it should return instead of UIImage.
Something like this
private let getImage() -> (UIImage in screenImages)
Shuri2060, answered my question.
What i was looking for was.
if screenImages.contains(x)
I am trying to set the image property on a UIImageView. When I use a UIImage to set the .image property it throws this error every time:
"unexpectedly found nil while unwrapping an Optional value"
The problem is that my UIImage is not nil.
Here is the code where I am setting the UIImage
func setPhotosForNewsItem(photoArray:[Photo]) {
println("Image Count: " + String(photoArray.count))
var image:UIImage = photoArray[0].photo
println(image.description)
self.newsImage.image = image
}
Here is the console output:
Image Count: 2
UIImage: 0x7fdd93c5cdd0, {1115, 1115}
fatal error:
unexpectedly found nil while unwrapping an Optional value (lldb)
I am able to use the Quick Look tool in xCode on my supposedly nil UIImage and see the photo that I am trying to use. Why would I be throwing a nil error when my UIImage is clearly not nil?
UPDATE::
It seems that I am not properly storing the UIImage in my array. Here is where I download my images and store them to my array for unpacking later.
var relatedPhotos:[PFObject] = relations as! [PFObject]
//println(relations!)
var photoArray:[Photo] = [Photo]()
for photo in relatedPhotos {
var newPhoto = Photo()
var photoFile:PFFile = photo.objectForKey("photo") as! PFFile
newPhoto.object = photo
newPhoto.objectID = photo.objectId!
photoFile.getDataInBackgroundWithBlock({(imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
newPhoto.photo = UIImage(data:imageData!)!
//println(newPhoto.photo)
photoArray.append(newPhoto)
if photoArray.count == relatedPhotos.count {
if newObject is FieldReport {
var report = newObject as! FieldReport
report.photos = photoArray
updatedReports.append(report)
//println("Report Count 1: " + String(updatedReports.count))
}
else {
var report = newObject as! Feature
report.photos = photoArray
updatedReports.append(report)
}
if updatedReports.count == objects.count {
self.delegate?.fieldReports(updatedReports)
}
}
}
})
}
I know that this works to download and display the photo as I have just used it successfully to do so. To me that means I am not storing the UIImage properly. Is there a different way I should be storing these image files?
You can prevent the crash from happening by safely unwrapping
func setPhotosForNewsItem(photoArray:[Photo]) {
println("Image Count: " + String(photoArray.count))
if var image:UIImage = photoArray[0].photo
{
println(image.description)
self.newsImage.image = image
}
}
Setup breakpoint and check what's wrong
EDIT:
The only thing i can purpose - check what happens here:
newPhoto.photo = UIImage(data:imageData!)!
seems like it's the main reason of all problems. check type of imageData, try to convert it to image via, for example UIImage(CGImage: <#CGImage!#>). You need to figure out how to deal with this image.
I'm successfully pulling in data from Parse into swift, but my images don't seem to be working the way I'm doing it.
In my cellForRowAtIndexPath method, I do the following:
var event: AnyObject? = eventContainerArray[indexPath.row]
if let unwrappedEvent: AnyObject = event {
let eventTitle = unwrappedEvent["title"] as? String
let eventDate = unwrappedEvent["date"] as? String
let eventDescription = unwrappedEvent["description"] as String
let eventImage = unwrappedEvent["image"] as? UIImage
println(eventImage)
if (eventImage != nil){
cell.loadItem(date: eventDate!, title: eventTitle!, description: eventDescription, image: eventImage!)}
else {
let testImage: UIImage = UIImage(named: "test-image.png")!
cell.loadItem(date: eventDate!, title: eventTitle!, description: eventDescription, image: testImage )
}
}
}
return cell
}
I'm using println() with my PFQuery, and I am seeing this as part of the object that's loading in: image = "<PFFile: 0x7fee62420b00>";
So I'm getting title, date, description, etc. all loading fine as part of the above eventContainerArray, but when I look at eventImage, it's nil every time. In the code above, it always defaults to loading test-image.png, being that the image is coming up nil. Am I simply handling that PFFile the improper way? Not sure why it's not working.
Thanks!
Your image is likely a PFFile object. You will need to load it to get an UIImage object from it.
let userImageFile = unwrappedEvent["image"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
let eventImage = UIImage(data:imageData)
// do something with image here
}
}
I'm not familiar with Parse, but given what you're seeing from the println() it looks like what you've received isn't a UIImage but instead some sort of data type. This makes sense; you aren't going to be receiving ObjC objects over the network. So you are getting a file, but when you use the conditional downcast (x as? UIImage) it's returning nil because you don't have a UIImage.
Instead you're going to need to cast to PFFile, and then probably create a UIImage with UIImage(data: file.getData) or something similar. I'm not familiar with exactly how Parse works.
Edit: here's a related question that might be helpful: I can't get image from PFFile
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.
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
}