Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error while integrating in Swift UI #455

Closed
TilakMaddy opened this issue Feb 9, 2020 · 14 comments
Closed

Error while integrating in Swift UI #455

TilakMaddy opened this issue Feb 9, 2020 · 14 comments

Comments

@TilakMaddy
Copy link

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 <YPImagePicker.YPImagePicker: 0x7fb4f5122a00> on <UIViewController: 0x7fb4f8949ec0> whose view is not in the window hierarchy!
Picker deinited 👍

@anoels
Copy link

anoels commented Feb 10, 2020

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)")
    }

}`

@TilakMaddy
Copy link
Author

TilakMaddy commented Feb 28, 2020 via email

@anoels
Copy link

anoels commented Feb 28, 2020

Yes, I have tested. It works.

@KaufmannTrofim
Copy link

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?

@anoels
Copy link

anoels commented Mar 1, 2020

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

`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)")
}

}

`

@KaufmannTrofim
Copy link

Thank you! It worked

@bahampton
Copy link

bahampton commented Mar 26, 2020

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

`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?

@anoels
Copy link

anoels commented Mar 26, 2020

Please try the following one: "to call the struct (YummyView(image: Binding<Image?>#>)
`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’s less used property wrappers, it lets us declare that one value actually comes from elsewhere, and should be shared in both places.

@TeeAche
Copy link

TeeAche commented May 4, 2020

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!

@lachezartodorov
Copy link

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

@Claeysson
Copy link
Contributor

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
    
}

@aksonov
Copy link

aksonov commented Feb 18, 2021

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)?

@ramluro1
Copy link

ramluro1 commented Mar 6, 2021

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

@kamrankhan07
Copy link

SwiftUI

struct SampleView: View {
    
    @State private var showMediaPicker: Bool = false
    @State private var image: UIImage?
    
    var body: some View {
        ZStack {
            Button {
                showMediaPicker = true
            } label: {
                Text("Select Picture")
            }
            .sheet(isPresented: $showMediaPicker) {
                MediaPicker(image: $image)
                
            }
            
            if let image = self.image {
                Image(uiImage: image)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(maxHeight: 100)
            }
        }
    }   
}

MediaPicker.swift

import SwiftUI
import YPImagePicker

struct MediaPicker: UIViewControllerRepresentable {
  
    class Coordinator: NSObject, UINavigationControllerDelegate {
        let parent: MediaPicker
        
        init(_ parent: MediaPicker) {
            self.parent = parent
        }
    }

    typealias UIViewControllerType = YPImagePicker
    @Binding var image: UIImage?
    
    func makeUIViewController(context: Context) -> YPImagePicker {
        var config = YPImagePickerConfiguration()
       
        //Common
        config.shouldSaveNewPicturesToAlbum = true
        config.albumName = "Coding Challenge"
        config.showsPhotoFilters = false
        config.showsCrop = .none
        config.screens = [.library]
        config.startOnScreen = .library
        config.hidesStatusBar = true
        config.hidesBottomBar = true
        
        //library
        config.library.mediaType = .photo
        config.library.maxNumberOfItems = 1
    
        config.wordings.libraryTitle = "Gallery"
        config.library.skipSelectionsGallery = true
        config.targetImageSize = .cappedTo(size: 1080)
        
        let picker = YPImagePicker(configuration: config)
        picker.didFinishPicking { [unowned picker] items, _ in
            if let photo = items.singlePhoto {
                self.image = photo.image
            }
            picker.dismiss(animated: true, completion: nil)
        }
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: YPImagePicker, context: Context) {
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
}

References:
https://www.hackingwithswift.com/books/ios-swiftui/using-coordinators-to-manage-swiftui-view-controllers
https://stackoverflow.com/questions/70962237/use-ypimagepikcer-in-swiftui

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants