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
Related
I have one View Controller that has three fields, UITextField, UIDatePicker and UIImageView.I select the image from the library and display it on myImage and this works fine.I think I have managed to encode the image into Data and I think I am saving it also, but when I try to choose an entry from my details View Controller tableview (decode the Data into an image and present it to my details View controller) I get this message "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value" ,next to my decompressImage function, what am I doing wrong?
Inside my didTapSave method I am trying to turn my image into data:
let data = myImage.image
let dataImage = data?.pngData()
realm.add(newItem1)
try! realm.commitWrite()
//lastly I call the complitionHandler
completionHandler1?()
lastly on the last VC I am trying to decode it into an image and present it:
func decompressImage() -> UIImage {
let imageData = UIImage().jpegData(compressionQuality: 0.2)
let imageDec = UIImage(data: imageData!)
return imageDec!
}
// //we need a formater to change our data into a UIImage
// static let dataFormatter:
override func viewDidLoad() {
super.viewDidLoad()
//we assign
itemLabel.text = asset?.titleNote
dateLabel.text = Self.dateFormatter1.string(from: asset!.date)
imagePresent.image = decompressImage()
}
I have set a placeholder image for my image views. The user can change these to a photo from their library. Once they have done this, they can then upload these images to a database. The user can upload a single image or many, either way, they are uploaded as an [UIImage]. However, I do not want the placeholder images to be uploaded.
I have managed to achieve this, but in a very ungraceful manner. I have done this by firstly subclassing UIImageView, adding a property called isSet and setting all my image views to this class:
class ItemImageViewClass: UIImageView {
//keeps track of whether the image has changed from the placeholder image.
var isSet = Bool()
}
and then when the image has been set after the user has selected an image, the isSet property is set to true.
To check if the image in the image view has been changed from the placeholder image (i.e. isSet == true) I used the following code:
var imageArray: [UIImage]? = nil
if imageViewOne.isSet {
guard let mainImage = imageViewOne.image else {return}
if (imageArray?.append(mainImage)) == nil {
imageArray = [mainImage]
}
}
if imageViewTwo.isSet {
guard let imageTwo = imageViewTwo.image else {return}
if (imageArray?.append(imageTwo)) == nil {
imageArray = [imageTwo]
}
}
guard imageArray != nil else {
print("imageArray is nil")
alertMessage("Hey!", message: "Please add some images!")
return
}
When at least one image has been selected, the array will be saved to the database.
This seems like a very messy way to do it; subclassing UIImageView and having to check that each image has changed using a series of if statements. Is there a more elegant way to achieve this? Thanks
There is a solution by extending UIImageView to avoid subclassing. And, using filter and flatMap to by pass the if-statements. This requires that you use the same reference to the placeholder image for each UIImageView.
// In this example, the placeholder image is global but it doesn't have to be.
let placeHolderImage = UIImage()
extension UIImageView {
// Check to see if the image is the same as our placeholder
func isPlaceholderImage(_ placeHolderImage: UIImage = placeHolderImage) -> Bool {
return image == placeHolderImage
}
}
Usage:
let imageViewOne = UIImageView(image: placeHolderImage)
imageViewOne.isPlaceholderImage() // true
let imageViewTwo = UIImageView()
imageViewTwo.isPlaceholderImage() // false
let imageViewThree = UIImageView()
imageViewThree.image = UIImage(named: "my-image")
imageViewThree.isPlaceholderImage() // false
Filter and map for images that are not placeholders or nil:
let imageArray = [imageViewOne, imageViewTwo, imageViewThree].filter
{ !$0.isPlaceholderImage() }.flatMap { $0.image }
print(imageArray) // imageViewThree.image
If the extension does not work for your requirements, I would definitely consider "filter and flatMap" to avoid that if-statement.
I would recommend creating a new UI component for your needs with a background UIImageView(placeholder) and an optional foreground UIImageView (user selected). Then just check for the existence of the optional foreground UIImageView as you can rely upon this being the UIImageView containing the user chosen image.
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 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
}