通过 PHPickerViewController 推送视图控制器

Push view controller over PHPickerViewController

是否可以将 vc 推送到 PHPickerViewController 上?

我正在尝试这样做,但运气不佳:

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

更新 - 使用 CocoaPods

我创建了一个简单的 pod PhotoCropController,其中包含要从 PHPickerViewControllerDelegate 呈现的基本照片裁剪控制器。它提供转换以推送到模态呈现的控制器以及弹出或关闭。裁剪视图的纵横比也可以编辑。使用使您的视图控制器符合 PhotoCropDelegate 协议并从您的 PHPickerViewControllerDelegate 呈现 PhotoCropController。实施将类似于以下内容:

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

在 PHPickerViewController 前面模态显示另一个控制器:

您的视图控制器的 presentedViewController 属性 将成为您的 photoPickerController,因此您可以在其前面呈现另一个控制器,如下所示:

present(photoPickerController, animated: true)
presentedViewController?.present(yourViewController, animated: true)

如果从 presentedViewController 中关闭,您将需要调用两次,一次关闭您的 ViewController,然后再次关闭 photoPickerController,如果它们都显示的话:

presentedViewController?.dismiss(animated: true)
presentedViewController?.dismiss(animated: true)

将自定义控制器推送到导航堆栈顶部

要创建推送到 PHPickerViewController 堆栈的外观,您可以使用自定义 UIPresentationController 和 UIViewControllerAnimatedTransitioning 类 来呈现您的视图。以下 类 将模拟对模态呈现的导航控制器的推送:

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

要在您的视图控制器中实现:

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

当您准备好关闭整个堆栈时,您可以调用 dismissPhotoStack。