Ypimagepicker: Error while integrating in Swift UI

Created on 9 Feb 2020  路  13Comments  路  Source: Yummypets/YPImagePicker

Below is an attempt to integrate YPImagePicker into SwiftUI. So when I call YummyViewController inside a VStack in the main body of ContentView I get the error described below

struct YummyViewController: UIViewControllerRepresentable {

    func makeUIViewController(context: UIViewControllerRepresentableContext<YummyViewController>) -> UIViewController {
        let controller = UIViewController()

        let config = YPImagePickerConfiguration()

        let picker = YPImagePicker(configuration: config)
        picker.didFinishPicking { [unowned picker] items, _ in
            if let photo = items.singlePhoto {
                print(photo.fromCamera) // Image source (camera or library)
                print(photo.image) // Final image selected by the user
                print(photo.originalImage) // original image selected by the user, unfiltered
                print(photo.modifiedImage ?? "not modified !") // Transformed image, can be nil
                print(photo.exifMeta ?? "no exif metadata") // Print exif meta data of original image."
            }
            picker.dismiss(animated: true, completion: nil)
        }
        controller.present(picker, animated: true, completion: nil)
        return controller
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<YummyViewController>) {

    }

}

CONSOLE MESSAGE

2020-02-09 13:31:33.748928+0530 MambaLegacy[8305:840740] Warning: Attempt to present on whose view is not in the window hierarchy!
Picker deinited 馃憤

Most helpful comment

This is my solution. Doesn't produce a blank VC in the background as in the other suggested answers.

struct MediaPicker: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> YPImagePicker {
        let config = YPImagePickerConfiguration()

        let picker = YPImagePicker(configuration: config)
        picker.didFinishPicking { [unowned picker] items, _ in
            if let photo = items.singlePhoto {
                print(photo.fromCamera) // Image source (camera or library)
                print(photo.image) // Final image selected by the user
                print(photo.originalImage) // original image selected by the user, unfiltered
                print(photo.modifiedImage ?? "not modified !") // Transformed image, can be nil
                print(photo.exifMeta ?? "no exif metadata") // Print exif meta data of original image."
            }
            picker.dismiss(animated: true, completion: nil)
        }

        return picker
    }

    func updateUIViewController(_ uiViewController: YPImagePicker, context: Context) {}

    typealias UIViewControllerType = YPImagePicker

}

All 13 comments

Here is the solution:

`import SwiftUI

struct ContentView : View {
    @State private var showYPImagePickerView = true

    var body: some View {
        VStack {
            Button(action: {
                self.showYPImagePickerView.toggle()
            }, label: { Text("Show Square").font(.title) })

            if showYPImagePickerView {
                YPImagePickerViewr()
            }
        }

    }
}

struct YPImagePickerView: UIViewControllerRepresentable
{
    func makeUIViewController(context: Context) -> YPImagePickerViewController {
        let vc =  YPImagePickerViewController()
        print("\nmakeUIViewController \(vc)")
        return vc
    }

    func updateUIViewController(_ uiViewController: YPImagePickerViewController, context: Context) {
        print("updateUIViewController \(uiViewController)")
    }

    static func dismantleUIViewController(_ uiViewController: YPImagePickerViewController, coordinator: Self.Coordinator) {
        print("dismantleUIViewController \(uiViewController)")
    }

}

class YPImagePickerViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.green
        print("viewDidLoad \(self)")
let picker = YPImagePicker()
picker.didFinishPicking { [unowned picker] items, _ in
    if let photo = items.singlePhoto {
        print(photo.fromCamera) // Image source (camera or library)
        print(photo.image) // Final image selected by the user
        print(photo.originalImage) // original image selected by the user, unfiltered
        print(photo.modifiedImage) // Transformed image, can be nil
        print(photo.exifMeta) // Print exif meta data of original image.
    }
    picker.dismiss(animated: true, completion: nil)
}
present(picker, animated: true, completion: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("viewWillDissapear \(self)")
    }

    deinit {
        print("DEINIT \(self)")
    }

}`

yeah but I figured out a much shorter solution. And I am not sure if what
you are saying is correct , did you actually test it ?

>

Yes, I have tested. It works.

Yes, I have tested. It works.

Doesn't work for me. I am still getting the same warning in console. How I can fix it?

Please try the following one: "to call the struct (YummyView(image: Binding#>)

`class YPImagePickerUIViewController: UIViewController {

var selectedItems = [YPMediaItem]()

let selectedImageV = UIImageView()
let pickButton = UIButton()
let resultsButton = UIButton()

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemBackground
    print("viewDidLoad \(self)")
   self.view.backgroundColor = .systemBackground

   selectedImageV.contentMode = .scaleAspectFit
   selectedImageV.frame = CGRect(x: 0,
                                 y: 0,
                                 width: UIScreen.main.bounds.width,
                                 height: UIScreen.main.bounds.height * 0.45)
   view.addSubview(selectedImageV)

   pickButton.setTitle("Pick", for: .normal)
   pickButton.setTitleColor(.label, for: .normal)
   pickButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
   pickButton.addTarget(self, action: #selector(showPicker), for: .touchUpInside)
   view.addSubview(pickButton)
   pickButton.center = view.center

   resultsButton.setTitle("Show selected", for: .normal)
   resultsButton.setTitleColor(.label, for: .normal)
   resultsButton.frame = CGRect(x: 0,
                                y: UIScreen.main.bounds.height - 100,
                                width: UIScreen.main.bounds.width,
                                height: 100)

    resultsButton.addTarget(self, action: #selector(showResults), for: .touchUpInside)
    view.addSubview(resultsButton)


}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    showPicker()
}

@objc func showResults() {
    if selectedItems.count > 0 {
        let gallery = YPSelectionsGalleryVC(items: selectedItems) { g, _ in
            g.dismiss(animated: true, completion: nil)
        }
        let navC = UINavigationController(rootViewController: gallery)
        self.present(navC, animated: true, completion: nil)
    } else {
        print("No items selected yet.")
    }
}

@objc func showPicker() {

        var config = YPImagePickerConfiguration()


        config.library.mediaType = .photoAndVideo


        config.shouldSaveNewPicturesToAlbum = false

        /* Choose the videoCompression. Defaults to AVAssetExportPresetHighestQuality */
        config.video.compression = AVAssetExportPresetMediumQuality


        config.startOnScreen = .library

        /* Defines which screens are shown at launch, and their order.
           Default value is `[.library, .photo]` */
        config.screens = [.library, .photo]


        config.video.libraryTimeLimit = 500.0

        /* Adds a Crop step in the photo taking process, after filters. Defaults to .none */
        config.showsCrop = .rectangle(ratio: (1/1))

        config.wordings.libraryTitle = "Galeri"

        /* Defines if the status bar should be hidden when showing the picker. Default is true */
        config.hidesStatusBar = false

        /* Defines if the bottom bar should be hidden when showing the picker. Default is false */
        config.hidesBottomBar = false

        config.maxCameraZoomFactor = 2.0

        config.library.maxNumberOfItems = 5
        config.gallery.hidesRemoveButton = false


        //config.library.options = options
        config.library.preselectedItems = selectedItems

        let picker = YPImagePicker(configuration: config)

        /* Change configuration directly */
        // YPImagePickerConfiguration.shared.wordings.libraryTitle = "Gallery2"




        /* Single Photo implementation. */
         picker.didFinishPicking { [unowned picker] items, _ in
             self.selectedItems = items
             self.selectedImageV.image = items.singlePhoto?.image
             picker.dismiss(animated: true, completion: nil)
         }

        present(picker, animated: true, completion: nil)
    }

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("viewWillDissapear \(self)")
}

deinit {
    print("DEINIT \(self)")
}

}

`

Thank you! It worked

Please try the following one: "to call the struct (YummyView(image: Binding#>)

`class YPImagePickerUIViewController: UIViewController {

var selectedItems = [YPMediaItem]()

let selectedImageV = UIImageView()
let pickButton = UIButton()
let resultsButton = UIButton()

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemBackground
    print("viewDidLoad \(self)")
   self.view.backgroundColor = .systemBackground

   selectedImageV.contentMode = .scaleAspectFit
   selectedImageV.frame = CGRect(x: 0,
                                 y: 0,
                                 width: UIScreen.main.bounds.width,
                                 height: UIScreen.main.bounds.height * 0.45)
   view.addSubview(selectedImageV)

   pickButton.setTitle("Pick", for: .normal)
   pickButton.setTitleColor(.label, for: .normal)
   pickButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
   pickButton.addTarget(self, action: #selector(showPicker), for: .touchUpInside)
   view.addSubview(pickButton)
   pickButton.center = view.center

   resultsButton.setTitle("Show selected", for: .normal)
   resultsButton.setTitleColor(.label, for: .normal)
   resultsButton.frame = CGRect(x: 0,
                                y: UIScreen.main.bounds.height - 100,
                                width: UIScreen.main.bounds.width,
                                height: 100)

    resultsButton.addTarget(self, action: #selector(showResults), for: .touchUpInside)
    view.addSubview(resultsButton)


}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    showPicker()
}

@objc func showResults() {
    if selectedItems.count > 0 {
        let gallery = YPSelectionsGalleryVC(items: selectedItems) { g, _ in
            g.dismiss(animated: true, completion: nil)
        }
        let navC = UINavigationController(rootViewController: gallery)
        self.present(navC, animated: true, completion: nil)
    } else {
        print("No items selected yet.")
    }
}

@objc func showPicker() {

        var config = YPImagePickerConfiguration()


        config.library.mediaType = .photoAndVideo


        config.shouldSaveNewPicturesToAlbum = false

        /* Choose the videoCompression. Defaults to AVAssetExportPresetHighestQuality */
        config.video.compression = AVAssetExportPresetMediumQuality


        config.startOnScreen = .library

        /* Defines which screens are shown at launch, and their order.
           Default value is `[.library, .photo]` */
        config.screens = [.library, .photo]


        config.video.libraryTimeLimit = 500.0

        /* Adds a Crop step in the photo taking process, after filters. Defaults to .none */
        config.showsCrop = .rectangle(ratio: (1/1))

        config.wordings.libraryTitle = "Galeri"

        /* Defines if the status bar should be hidden when showing the picker. Default is true */
        config.hidesStatusBar = false

        /* Defines if the bottom bar should be hidden when showing the picker. Default is false */
        config.hidesBottomBar = false

        config.maxCameraZoomFactor = 2.0

        config.library.maxNumberOfItems = 5
        config.gallery.hidesRemoveButton = false


        //config.library.options = options
        config.library.preselectedItems = selectedItems

        let picker = YPImagePicker(configuration: config)

        /* Change configuration directly */
        // YPImagePickerConfiguration.shared.wordings.libraryTitle = "Gallery2"




        /* Single Photo implementation. */
         picker.didFinishPicking { [unowned picker] items, _ in
             self.selectedItems = items
             self.selectedImageV.image = items.singlePhoto?.image
             picker.dismiss(animated: true, completion: nil)
         }

        present(picker, animated: true, completion: nil)
    }

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("viewWillDissapear \(self)")
}

deinit {
    print("DEINIT \(self)")
}

}

`

Without use of a coordinator how do you go about passing back data to the swiftUI View/Navigate to other swiftUI views on completion?

Please try the following one: "to call the struct (YummyView(image: Binding#>)
`class YPImagePickerUIViewController: UIViewController {

var selectedItems = [YPMediaItem]()

let selectedImageV = UIImageView()
let pickButton = UIButton()
let resultsButton = UIButton()

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemBackground
    print("viewDidLoad \(self)")
   self.view.backgroundColor = .systemBackground

   selectedImageV.contentMode = .scaleAspectFit
   selectedImageV.frame = CGRect(x: 0,
                                 y: 0,
                                 width: UIScreen.main.bounds.width,
                                 height: UIScreen.main.bounds.height * 0.45)
   view.addSubview(selectedImageV)

   pickButton.setTitle("Pick", for: .normal)
   pickButton.setTitleColor(.label, for: .normal)
   pickButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
   pickButton.addTarget(self, action: #selector(showPicker), for: .touchUpInside)
   view.addSubview(pickButton)
   pickButton.center = view.center

   resultsButton.setTitle("Show selected", for: .normal)
   resultsButton.setTitleColor(.label, for: .normal)
   resultsButton.frame = CGRect(x: 0,
                                y: UIScreen.main.bounds.height - 100,
                                width: UIScreen.main.bounds.width,
                                height: 100)

    resultsButton.addTarget(self, action: #selector(showResults), for: .touchUpInside)
    view.addSubview(resultsButton)


}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    showPicker()
}

@objc func showResults() {
    if selectedItems.count > 0 {
        let gallery = YPSelectionsGalleryVC(items: selectedItems) { g, _ in
            g.dismiss(animated: true, completion: nil)
        }
        let navC = UINavigationController(rootViewController: gallery)
        self.present(navC, animated: true, completion: nil)
    } else {
        print("No items selected yet.")
    }
}

@objc func showPicker() {

        var config = YPImagePickerConfiguration()


        config.library.mediaType = .photoAndVideo


        config.shouldSaveNewPicturesToAlbum = false

        /* Choose the videoCompression. Defaults to AVAssetExportPresetHighestQuality */
        config.video.compression = AVAssetExportPresetMediumQuality


        config.startOnScreen = .library

        /* Defines which screens are shown at launch, and their order.
           Default value is `[.library, .photo]` */
        config.screens = [.library, .photo]


        config.video.libraryTimeLimit = 500.0

        /* Adds a Crop step in the photo taking process, after filters. Defaults to .none */
        config.showsCrop = .rectangle(ratio: (1/1))

        config.wordings.libraryTitle = "Galeri"

        /* Defines if the status bar should be hidden when showing the picker. Default is true */
        config.hidesStatusBar = false

        /* Defines if the bottom bar should be hidden when showing the picker. Default is false */
        config.hidesBottomBar = false

        config.maxCameraZoomFactor = 2.0

        config.library.maxNumberOfItems = 5
        config.gallery.hidesRemoveButton = false


        //config.library.options = options
        config.library.preselectedItems = selectedItems

        let picker = YPImagePicker(configuration: config)

        /* Change configuration directly */
        // YPImagePickerConfiguration.shared.wordings.libraryTitle = "Gallery2"




        /* Single Photo implementation. */
         picker.didFinishPicking { [unowned picker] items, _ in
             self.selectedItems = items
             self.selectedImageV.image = items.singlePhoto?.image
             picker.dismiss(animated: true, completion: nil)
         }

        present(picker, animated: true, completion: nil)
    }

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("viewWillDissapear \(self)")
}

deinit {
    print("DEINIT \(self)")
}

}
`

Without use of a coordinator how do you go about passing back data to the swiftUI View/Navigate to other swiftUI views on completion?

@Binding is one of SwiftUI鈥檚 less used property wrappers, it lets us declare that one value actually comes from elsewhere, and should be shared in both places.

Hello,
Thank you all for the informations. I managed to get it working, but when I click on cancel button, the YPImagepicker always reappears as I suppose the Button toggle is still on true. How can I change the toggle state back in my contentView? Thanks a lot!

@TeeAche You have to remove the showPicker() from viewDidAppear
I believe it is there for testing purposes.

This is my solution. Doesn't produce a blank VC in the background as in the other suggested answers.

struct MediaPicker: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> YPImagePicker {
        let config = YPImagePickerConfiguration()

        let picker = YPImagePicker(configuration: config)
        picker.didFinishPicking { [unowned picker] items, _ in
            if let photo = items.singlePhoto {
                print(photo.fromCamera) // Image source (camera or library)
                print(photo.image) // Final image selected by the user
                print(photo.originalImage) // original image selected by the user, unfiltered
                print(photo.modifiedImage ?? "not modified !") // Transformed image, can be nil
                print(photo.exifMeta ?? "no exif metadata") // Print exif meta data of original image."
            }
            picker.dismiss(animated: true, completion: nil)
        }

        return picker
    }

    func updateUIViewController(_ uiViewController: YPImagePicker, context: Context) {}

    typealias UIViewControllerType = YPImagePicker

}

This is my solution. Doesn't produce a blank VC in the background as in the other suggested answers.

struct MediaPicker: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> YPImagePicker {
        let config = YPImagePickerConfiguration()

        let picker = YPImagePicker(configuration: config)
        picker.didFinishPicking { [unowned picker] items, _ in
            if let photo = items.singlePhoto {
                print(photo.fromCamera) // Image source (camera or library)
                print(photo.image) // Final image selected by the user
                print(photo.originalImage) // original image selected by the user, unfiltered
                print(photo.modifiedImage ?? "not modified !") // Transformed image, can be nil
                print(photo.exifMeta ?? "no exif metadata") // Print exif meta data of original image."
            }
            picker.dismiss(animated: true, completion: nil)
        }

        return picker
    }

    func updateUIViewController(_ uiViewController: YPImagePicker, context: Context) {}

    typealias UIViewControllerType = YPImagePicker

}

Good solution, thanks! Do you know how to show my custom 'next' screen after user taps "Next" for MediaPicker (like in Instagram, create new post screen is shown after image selected)?

This is my solution. Doesn't produce a blank VC in the background as in the other suggested answers.

struct MediaPicker: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> YPImagePicker {
        let config = YPImagePickerConfiguration()

        let picker = YPImagePicker(configuration: config)
        picker.didFinishPicking { [unowned picker] items, _ in
            if let photo = items.singlePhoto {
                print(photo.fromCamera) // Image source (camera or library)
                print(photo.image) // Final image selected by the user
                print(photo.originalImage) // original image selected by the user, unfiltered
                print(photo.modifiedImage ?? "not modified !") // Transformed image, can be nil
                print(photo.exifMeta ?? "no exif metadata") // Print exif meta data of original image."
            }
            picker.dismiss(animated: true, completion: nil)
        }

        return picker
    }

    func updateUIViewController(_ uiViewController: YPImagePicker, context: Context) {}

    typealias UIViewControllerType = YPImagePicker

}

Good solution, thanks! Do you know how to show my custom 'next' screen after user taps "Next" for MediaPicker (like in Instagram, create new post screen is shown after image selected)?

Do you mind sharing the rest of your SwiftUI code. I am trying to figure out the following:

  1. How do I get the image back into SwiftUI
  2. How can I use SwiftUI to manage the "Select Image" button instead of having it in the view controller
  3. How can I use SwiftUI to display the selected image and not show it in the view controller

Thank you

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nickeyzzz picture nickeyzzz  路  6Comments

fukemy picture fukemy  路  6Comments

Sieniek picture Sieniek  路  5Comments

roarapps picture roarapps  路  4Comments

esabunor picture esabunor  路  5Comments