Is that possible to push vc over the PHPickerViewController?
I'm trying to do that like this with no luck:
var configuration = PHPickerConfiguration()
configuration.filter = .any(of: [.images, .livePhotos])
photoPickerController = PHPickerViewController(configuration: configuration)
photoPickerController.delegate = self
present(self.photoPickerController, animated: true)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
//Push segue
performSegue(withIdentifier: "showAddPost", sender: self)
}
Update - Using CocoaPods
I created a simple pod PhotoCropController that includes a basic photo crop controller to be presented from the PHPickerViewControllerDelegate. It provides transitions to push to a modally presented controller and to pop or dismiss. The aspect ratio of the crop view can be edited as well. To use conform your view controller to the PhotoCropDelegate protocol and present the PhotoCropController from your PHPickerViewControllerDelegate. Implementation would look something like the following:
extension ViewController: PHPickerViewControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, PhotoCropDelegate {
func browsePhotoLibrary() {
if #available(iOS 14, *) {
var config = PHPickerConfiguration()
config.filter = PHPickerFilter.images
config.selectionLimit = 1
config.preferredAssetRepresentationMode = .compatible
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
let nav = UINavigationController(rootViewController: picker)
nav.setNavigationBarHidden(true, animated: false)
nav.setToolbarHidden(true, animated: true)
present(nav, animated: true) } else {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let edited = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
image = edited
} else if let selected = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
image = selected
}
presentedViewController?.dismiss(animated: true)
}
@available(iOS 14, *)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
if let provider = results.last?.itemProvider,
provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { [weak self] result, error in
if let image = result as? UIImage {
DispatchQueue.main.async { self?.select(image: image) }
} else if let error = error {
NSLog("Error picking image: %@", error.localizedDescription)
DispatchQueue.main.async { picker.dismiss(animated: true) }
}
}
} else { DispatchQueue.main.async { picker.dismiss(animated: true) } }
}
func select(image: UIImage) {
let destinationSize = AVMakeRect(aspectRatio: image.size, insideRect: view.frame).integral.size
//Best Performance
let resizedImage = UIGraphicsImageRenderer(size: destinationSize).image { (context) in
image.draw(in: CGRect(origin: .zero, size: destinationSize))
}
let cropController = PhotoCropController()
cropController.delegate = self
cropController.image = resizedImage
presentedViewController?.present(cropController, animated: true)
}
@objc func cropViewDidCrop(image: UIImage?) {
self.image = image
presentedViewController?.dismiss(animated: true) { [weak self] in
self?.presentedViewController?.dismiss(animated: true)
}
}
}
To present another controller modally in front of the PHPickerViewController:
The presentedViewController property of your view controller will be your photoPickerController so you can present another controller in front of it as follows:
present(photoPickerController, animated: true)
presentedViewController?.present(yourViewController, animated: true)
If dismissing from the presentedViewController, you will need to call twice, once to dismiss yourViewController and then again to dismiss the photoPickerController, if they are both presented:
presentedViewController?.dismiss(animated: true)
presentedViewController?.dismiss(animated: true)
Pushing a custom controller on top of the navigation stack
To create the appearance of pushing to the PHPickerViewController stack you can use custom UIPresentationController and UIViewControllerAnimatedTransitioning classes to present your view. The following classes will mimic a push to a modally presented navigation controller:
import UIKit
import PhotosUI
class SlideInModalPresentationController: UIPresentationController {
var offset: CGFloat = 0.0
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
presentedView?.frame.origin.y = offset
presentedView?.frame.size.height -= offset
presentedView?.layer.cornerRadius = 10
presentedView?.clipsToBounds = true
}
}
class SlideInTransition: NSObject, UIViewControllerAnimatedTransitioning {
private let duration = 0.3
var isPresenting: Bool = true
var dismissModally: Bool = false
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toController = transitionContext.viewController(forKey: .to),
let fromController = transitionContext.viewController(forKey: .from)
else { return}
if isPresenting {
toController.view.frame.origin.x = fromController.view.frame.width
transitionContext.containerView.addSubview(toController.view)
UIView.animate(withDuration: duration, animations: {
toController.view.frame.origin.x = 0
}, completion: { _ in
transitionContext.completeTransition(true)
})
} else if dismissModally {
var stack: UIView? = nil
if #available(iOS 14, *), toController is PHPickerViewController {
stack = toController.view.superview
toController.dismiss(animated: false)
} else if toController is UIImagePickerController {
stack = toController.view.superview
toController.dismiss(animated: false)
}
UIView.animate(withDuration: duration, animations: {
stack?.frame.origin.y = fromController.view.frame.height
fromController.view.frame.origin.y = fromController.view.frame.height
}, completion: { _ in
transitionContext.completeTransition(true)
fromController.view.removeFromSuperview()
})
} else {
UIView.animate(withDuration: duration, animations: {
fromController.view.frame.origin.x = fromController.view.frame.width
}, completion: { _ in
transitionContext.completeTransition(true)
fromController.view.removeFromSuperview()
})
}
}
}
To implement in your view controller:
class ViewController: UIViewController {
let slidInTransition = SlideInTransition()
}
extension ViewController: UIViewControllerTransitioningDelegate {
private func presentYourController(_ image: UIImage) {
let yourController = YourController()
yourController.image = image
yourController.modalPresentationStyle = .custom
yourController.transitioningDelegate = self
slidInTransition.dismissModally = false
presentedViewController?.present(yourController, animated: true)
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let presentationController = SlideInModalPresentationController(presentedViewController: presented, presenting: presenting)
presentationController.offset = view.convert(source.view.frame, to: nil).origin.y + 10
return presentationController
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
slidInTransition.isPresenting = true
return slidInTransition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
slidInTransition.isPresenting = false
return slidInTransition
}
private func dismissPhotoStack() {
slidInTransition.dismissModally = true
presentedViewController?.dismiss(animated: true)
}
}
When you are ready to dismiss the whole stack you can call dismissPhotoStack.