13 Replies
      Latest reply on Mar 22, 2020 7:43 AM by Onur Sd
      Onur Sd Level 1 Level 1 (0 points)

        The function is called from the previous viewController, the code compiles succesfully. When I run it and click the tableView row that calls this function, I receive "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value", underlining the "targetSize" causing the error. After I set a debug breakpoint on the "targetSize" and step over the error, it takes me back to the "photoImageView" outlet(which is properly connected to the storyboardy) then back to the "targetSize"

         

        Any help please ?

         

        public func chooseLastImage(_ sender: Any) {

         

            let scale = UIScreen.main.scale

            let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width * scale, height: photoImageView.bounds.height * scale)

         

            let options = PHImageRequestOptions()

            options.isNetworkAccessAllowed = true

            options.deliveryMode = .highQualityFormat

         

         

            let imgManager = PHImageManager.default()

            let fetchOptions = PHFetchOptions()

            fetchOptions.sortDescriptors = [NSSortDescriptor(key:"lastCreationDate", ascending: true)]

            let fetchResult: PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: (.image), options: fetchOptions)

         

            imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in

              guard let result = result else { return }

           

              DispatchQueue.main.async(execute: {

                self.photoImageView.image = result

              })

         

            }

         

          }

        }

        • Re: PHImageManager Error
          Claude31 Level 8 Level 8 (9,145 points)

          Question is: why is targetSize nil ?

          In addition, why do you make it optional ?

           

          Could you instrument your code, as proposed below:

           

           

          public func chooseLastImage(_ sender: Any) {
          
              let scale = UIScreen.main.scale
              let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width * scale, height: photoImageView.bounds.height * scale)
              print("targetSize", targetSize)          // Is it nil ? If not, what is its value
          
              let options = PHImageRequestOptions()
              options.isNetworkAccessAllowed = true
              options.deliveryMode = .highQualityFormat
          
              let imgManager = PHImageManager.default()
              let fetchOptions = PHFetchOptions()
              fetchOptions.sortDescriptors = [NSSortDescriptor(key:"lastCreationDate", ascending: true)]
              let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: (.image), options: fetchOptions)
          
              print("targetSize for request", targetSize)          //What is its value 
              imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in 
                       
                    guard let result = result else { return }
          
                    DispatchQueue.main.async(execute: {
                      self.photoImageView.image = result
                    })
          
              }
            }
          }

           

          Please confirm error is on line 17.

           

          And tell what you get as print log.

            • Re: PHImageManager Error
              Onur Sd Level 1 Level 1 (0 points)

              I appreciate your revert Claudia, I set targetSize constant as an optional to solve the error but it didn't work. I ran your proposed code, I got the same error message on line 04 "targetSize". Then, I set a breakpoint on line 04 "targetSize" and after I stepped into it, the error pointed out the "photoImageView" outlet.

              • Re: PHImageManager Error
                Onur Sd Level 1 Level 1 (0 points)

                Btw., I set the constraints of "photoImageView" on the storyboard. The "targetSize" constant can be revoked but as far as I see, the error is caused by the photoImageView.

                  • Re: PHImageManager Error
                    Claude31 Level 8 Level 8 (9,145 points)

                    As error is line 4, please instrument more, adding lines 04 and 05.

                    Please report exactly the logs you get

                     

                     

                     

                    public func chooseLastImage(_ sender: Any) {
                    
                        let scale = UIScreen.main.scale
                        print("photoImageView", photoImageView)
                        print("photoImageView.bounds", photoImageView.bounds)
                    
                        let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width * scale, height: photoImageView.bounds.height * scale)
                        print("targetSize", targetSize)          // Is it nil ? If not, what is its value
                    
                        let options = PHImageRequestOptions()
                        options.isNetworkAccessAllowed = true
                        options.deliveryMode = .highQualityFormat
                    
                        let imgManager = PHImageManager.default()
                        let fetchOptions = PHFetchOptions()
                        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"lastCreationDate", ascending: true)]
                        let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: (.image), options: fetchOptions)
                    
                        print("targetSize for request", targetSize)          //What is its value
                        imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in
                            
                              guard let result = result else { return }
                    
                              DispatchQueue.main.async(execute: {
                                self.photoImageView.image = result
                              })
                    
                        }
                      }
                    }
                      • Re: PHImageManager Error
                        Onur Sd Level 1 Level 1 (0 points)

                        Sure, here are the logs; (this time the error is on "print("photoImageView", photoImageView)" Regarding this viewController, on the same photoImageView outlet, I also access my photo library from the previous viewController without any error. I've been having trouble with accessing the latest photo in the library as the log prints photoImageView as nil.

                         

                        2020-03-22 17:34:31.197852+0300 Stitch[60446:1861363] [Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a table view cell's content view. We're considering the collapse unintentional and using standard height instead. Cell: <Stitch.PhotoPopUpCell: 0x7ff73c89c5a0; baseClass = UITableViewCell; frame = (0 0; 341.667 44); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x6000029609c0>>

                        photoImageView nil

                        Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/sedatonurduzova/Desktop/Xcode Projects/iOS Photos Framework/Manipulate Assets to Generate Collages/P02E04 Observe Photo Library Changes to Update UI/end/Stitch/Effect View Controllers/PhotoViewController.swift, line 67

                        2020-03-22 17:34:39.737915+0300 Stitch[60446:1861363] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/sedatonurduzova/Desktop/Xcode Projects/iOS Photos Framework/Manipulate Assets to Generate Collages/P02E04 Observe Photo Library Changes to Update UI/end/Stitch/Effect View Controllers/PhotoViewController.swift, line 67

                        (lldb)

                        • Re: PHImageManager Error
                          Onur Sd Level 1 Level 1 (0 points)

                          My previous message with the full logs is being moderated. This time the error is on "print("photoImageView", photoImageView)" Regarding this viewController, on the same photoImageView outlet, I also access my photo library from the previous viewController without any error. I've been having trouble with accessing the latest photo in the library as the log prints photoImageView as nil.

                           

                          photoImageView nil

                          Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file

                            • Re: PHImageManager Error
                              Claude31 Level 8 Level 8 (9,145 points)

                              What is exactly the error message you get ?

                              Are you sure it is on line 04

                                  print("photoImageView", photoImageView)

                              and not on line 05

                                  print("photoImageView.bounds", photoImageView.bounds)

                               

                              Please show more code (the full code of the class where photoImageView is declared, so that we can understand:

                              - where and how photoImageView is declared

                              - the cause of error

                                • Re: PHImageManager Error
                                  Onur Sd Level 1 Level 1 (0 points)

                                  I apologise, you are right the error is on line 05; print("photoImageView.bounds", photoImageView.bounds)

                                   

                                  The complete PhotoViewController;

                                   

                                  import Foundation

                                  import UIKit

                                  import Photos

                                   

                                  class PhotoViewController: UIViewController {

                                   

                                    @IBOutlet weak var photoImageView: UIImageView!

                                   

                                    var imagePicker = UIImagePickerController()

                                   

                                    override func viewDidLoad() {

                                      super.viewDidLoad()

                                   

                                      imagePicker.delegate = self

                                    }

                                   

                                    func chooseImage(_ sender: Any) {

                                   

                                      if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {

                                        imagePicker.sourceType = .photoLibrary

                                        imagePicker.allowsEditing = true

                                        self.present(imagePicker,animated: true, completion: nil)

                                      }

                                    }

                                   

                                    func accessVideo(_ sender: Any) {

                                   

                                      if UIImagePickerController.isSourceTypeAvailable(.camera) {

                                        imagePicker.sourceType = .camera

                                        self.present(imagePicker, animated: true, completion: nil)

                                      } else {

                                        print("Camera not available")

                                      }

                                    }

                                   

                                    public func chooseLastImage(_ sender: Any) {

                                   

                                        let scale = UIScreen.main.scale

                                        print("photoImageView", photoImageView)

                                        print("photoImageView.bounds", photoImageView.bounds)

                                   

                                        let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width * scale, height: photoImageView.bounds.height * scale)

                                        print("targetSize", targetSize)          // Is it nil ? If not, what is its value

                                   

                                        let options = PHImageRequestOptions()

                                        options.isNetworkAccessAllowed = true

                                        options.deliveryMode = .highQualityFormat

                                   

                                        let imgManager = PHImageManager.default()

                                        let fetchOptions = PHFetchOptions()

                                        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"lastCreationDate", ascending: true)]

                                        let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: (.image), options: fetchOptions)

                                   

                                        print("targetSize for request", targetSize)          //What is its value

                                        imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in

                                           

                                              guard let result = result else { return }

                                   

                                              DispatchQueue.main.async(execute: {

                                                self.photoImageView.image = result

                                              })

                                   

                                        }

                                      }

                                    }

                                   

                                  extension PhotoViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

                                   

                                    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

                                    

                                      if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {

                                      

                                        photoImageView.image = image

                                      }

                                      dismiss(animated: true, completion: nil)

                                    }

                                   

                                    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {

                                      dismiss(animated: true, completion: nil)

                                    }

                                   

                                  }

                                    • Re: PHImageManager Error
                                      Claude31 Level 8 Level 8 (9,145 points)

                                      Pretty likely, photoImageView IBOutlet connection is wrong, or has been damaged.

                                       

                                      To correct :

                                      - In Interface Builder, select photoImageView and open Connection inspector (or right click on the object)

                                      - You should see a section named Referencing Outlets, with photoImageView on the left part and PhotoViewController on the right

                                      - click the small x close to PhotoViewController to remove the connection

                                      - You should see in PhotoViewController code that the circle on the left of photoImageView is white

                                      - recreate the connection by usual control drag

                                      - do an option clean build Folder from Product menu in XCode

                                       

                                      Then run again

                                        • Re: PHImageManager Error
                                          Onur Sd Level 1 Level 1 (0 points)

                                          I've taken the steps you have suggested, the same error continues on the same line. The viewController calling the function;

                                           

                                          import Foundation

                                          import UIKit

                                          import Photos

                                           

                                           

                                          class PhotoPopUpViewController: UIViewController {

                                           

                                            let photoViewController = PhotoViewController()

                                           

                                            override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

                                            

                                              dismiss(animated: true, completion: nil)

                                            

                                              }

                                           

                                            @IBOutlet private var photoCollectionView: UICollectionView!

                                            @IBOutlet weak var tableView: UITableView!

                                           

                                            var imageArray = [UIImage]()

                                           

                                            var tableImages: [PhotoPopUpCellData] = []

                                           

                                            var selectedTableImage: PhotoPopUpCellData?

                                           

                                            override func viewDidLoad() {

                                              super.viewDidLoad()

                                              grabPhotos()

                                              tableImages = createArray()

                                            

                                              tableView.delegate = self

                                              tableView.dataSource = self

                                            }

                                           

                                            func grabPhotos() {

                                            

                                              let imgManager = PHImageManager.default()

                                            

                                              let requestOptions = PHImageRequestOptions()

                                              requestOptions.isSynchronous = true

                                              requestOptions.deliveryMode = .highQualityFormat

                                            

                                              let fetchOptions = PHFetchOptions()

                                              fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

                                            

                                              let fetchResult: PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: .image, options: fetchOptions)

                                              

                                                if fetchResult.count > 0 {

                                                  for i in 0..<fetchResult.count {

                                                    imgManager.requestImage(for: fetchResult.object(at: i), targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: requestOptions, resultHandler: {

                                                      image, error in

                                                    

                                                      self.imageArray.append(image!)

                                                    })

                                                  }

                                                } else {

                                                  print("You got no photos!")

                                                

                                                }

                                              }

                                          }

                                           

                                          extension PhotoPopUpViewController: UICollectionViewDelegate, UICollectionViewDataSource {

                                           

                                            func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

                                            return imageArray.count

                                            }

                                           

                                            func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

                                            

                                              let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoPopUpCell", for: indexPath) as? DataCollectionViewCell

                                            

                                              cell?.img.image = imageArray[indexPath.row]

                                            

                                              return cell!

                                            }

                                           

                                          }

                                           

                                          extension PhotoPopUpViewController: UICollectionViewDelegateFlowLayout {

                                           

                                            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

                                            

                                              let width = collectionView.bounds.width / 3.0

                                              let height = collectionView.bounds.height

                                            

                                              return CGSize(width: width, height: height)

                                            }

                                           

                                            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

                                              return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

                                            }

                                           

                                            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {

                                              return 1

                                            }

                                           

                                            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {

                                              return 1

                                            }

                                          }

                                           

                                          extension PhotoPopUpViewController {

                                           

                                            func createArray() -> [PhotoPopUpCellData] {

                                            

                                              var tempData: [PhotoPopUpCellData] = []

                                            

                                              let tableImages1 = PhotoPopUpCellData(image: UIImage(systemName: "photo")!, title: "Open from Albums")

                                              let tableImages2 = PhotoPopUpCellData(image: UIImage(systemName: "camera.fill")!, title: "Take a Photo")

                                              let tableImages3 = PhotoPopUpCellData(image: UIImage(systemName: "tray.and.arrow.up.fill")!, title: "Open last Image")

                                            

                                              tempData.append(tableImages1)

                                              tempData.append(tableImages2)

                                              tempData.append(tableImages3)

                                            

                                              return tempData

                                            }

                                          }

                                           

                                          extension PhotoPopUpViewController: UITableViewDataSource, UITableViewDelegate {

                                           

                                            func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

                                              return tableImages.count

                                            }

                                           

                                            func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

                                              let tableImage = tableImages[indexPath.row]

                                            

                                              let cell = tableView.dequeueReusableCell(withIdentifier: "PhotoPopUpCell") as! PhotoPopUpCell

                                            

                                              cell.setPhotoPopUp(photoPopUpCellData: tableImage)

                                            

                                              return cell

                                            }

                                           

                                            func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

                                            

                                              let tableImage = tableImages[indexPath.row]

                                            

                                              selectedTableImage = tableImage

                                            

                                              if indexPath.row == 0 {

                                           

                                              present(photoViewController, animated: false, completion: {

                                                self.photoViewController.chooseImage(self)

                                              })

                                            

                                              }

                                            

                                              if indexPath.row == 1 {

                                              

                                                present(photoViewController, animated: false, completion: {

                                                  self.photoViewController.accessVideo(self)

                                                })

                                              

                                              }

                                            

                                              if indexPath.row == 2 {

                                                present(photoViewController, animated: false, completion: {

                                                

                                                  self.photoViewController.chooseLastImage(self)

                                                })

                                              }

                                            }

                                          }

                                            • Re: PHImageManager Error
                                              OOPer Level 8 Level 8 (6,115 points)

                                              The worst thing in your code is this line:

                                                let photoViewController = PhotoViewController()
                                              
                                              

                                              With this line, you instatiate your `PhotoViewController` with initializer `init()`, which is not documented clearly,

                                              but as in the current implementation of iOS,

                                              - it ignores any settings you have defined in your storyboard

                                              - so, any of the IBActions or IBOutlets are not connected

                                              - thus, `photoImageView` is still nil, even if you successfully connected the IBOutlet in the storyboard

                                              Causes Fatal Error: Unexpectedly found nil while implicitly unwrapping an Optional value...

                                               

                                              If you want to make iOS create an instance according to your storyboard settings, you need to use an appropriate method for storyboard.

                                              (Or you can use segue, with a little more settings in the storyboard and a little bit different Swift code. I assume you do not use segues.)

                                               

                                              Remove the line shown above, and modify your `tableView(_:didSelectRowAt:)` method as follows:

                                                  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                                                      
                                                      let tableImage = tableImages[indexPath.row]
                                                      
                                                      selectedTableImage = tableImage
                                                      
                                                      //### Instantiate your view controller using storyboard
                                                      //(You need to give "PhotoViewController" as the Storyboard ID of your PhotoViewController in the storyboard.
                                                      let photoViewController = self.storyboard!.instantiateViewController(identifier: "PhotoViewController") as! PhotoViewController
                                                      _ = photoViewController.view //Make iOS construct views baased on the storyboard settings.
                                                      
                                                      if indexPath.row == 0 {
                                                          
                                                          present(photoViewController, animated: false, completion: {
                                                              photoViewController.chooseImage(self) //###
                                                          })
                                                          
                                                      }
                                                      
                                                      if indexPath.row == 1 {
                                                          
                                                          present(photoViewController, animated: false, completion: {
                                                              photoViewController.accessVideo(self) //###
                                                          })
                                                          
                                                      }
                                                      
                                                      if indexPath.row == 2 {
                                                          present(photoViewController, animated: false, completion: {
                                                              
                                                              photoViewController.chooseLastImage(self) //###
                                                          })
                                                      }
                                                  }
                                              
                                              

                                               

                                              I have not checked other parts of your code yet, so you may need to modify some other parts,

                                              but, at least, you need to change the way instantiating the next view controller.

                              • Re: PHImageManager Error
                                OOPer Level 8 Level 8 (6,115 points)

                                The function is called from the previous viewController

                                Please show the code of the previous viewController. You may be doing something wrong there.