| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- //
- // LNMediaPicker.swift
- // Lanu
- //
- // Created by OneeChan on 2025/11/26.
- //
- import Foundation
- import UIKit
- import Photos
- enum LNMediaPickerType: CaseIterable {
- case camera
- case photo
-
- var title: String {
- switch self {
- case .camera: .init(key: "A00011")
- case .photo: .init(key: "A00012")
- }
- }
- }
- extension LNBottomSheetMenu {
- static func showImageSelectMenu(view: UIView? = nil,
- options: LNMediaPickerOptions = .init(),
- handler: @escaping LNMediaPickerHandler) {
- let panel = LNBottomSheetMenu()
- let types: [LNMediaPickerType] = [.camera, .photo]
- panel.update([
- LNMediaPickerType.camera.title,
- LNMediaPickerType.photo.title,
- .init(key: "A00003")
- ]) { index, _ in
- guard index < types.count else { return }
- let type = types[index]
- LNMediaPicker.shared.pick(from: view, type: type, options: options, handler: handler)
- }
- panel.popup()
- }
- }
- extension LNCommonAlertView {
- static func showPhotoLibraryPermissionAlert() {
- let alert = LNCommonAlertView()
- alert.titleLabel.text = .init(key: "A00287")
- alert.showConfirm(.init(key: "A00289")) {
- openAppSetting()
- }
- alert.showCancel()
- alert.popup()
- }
-
- static func showCameraPermissionAlert() {
- let alert = LNCommonAlertView()
- alert.titleLabel.text = .init(key: "A00288")
- alert.showConfirm(.init(key: "A00289")) {
- openAppSetting()
- }
- alert.showCancel()
- alert.popup()
- }
- }
- typealias LNMediaPickerHandler = (_ image: UIImage?, _ videoUrl: URL?) -> Void
- struct LNMediaPickerOptions {
- let allowEdit: Bool
- let allowVideo: Bool
- let videoMaximumDuration: TimeInterval
- init(allowEdit: Bool = false,
- allowVideo: Bool = false,
- videoMaximumDuration: TimeInterval = 60) {
- self.allowEdit = allowEdit
- self.allowVideo = allowVideo
- self.videoMaximumDuration = videoMaximumDuration
- }
- }
- private var mediaHandlerKey: UInt8 = 0
- extension UIViewController {
- fileprivate var mediaPickerHandler: LNMediaPickerHandler? {
- get {
- objc_getAssociatedObject(self, &mediaHandlerKey) as? LNMediaPickerHandler ?? nil
- }
- set {
- objc_setAssociatedObject(self, &mediaHandlerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- }
- }
- }
- class LNMediaPicker: NSObject {
- static let shared = LNMediaPicker()
-
- private override init() {
- super.init()
- }
- }
- extension LNMediaPicker {
- func pick(from view: UIView? = nil,
- type: LNMediaPickerType,
- options: LNMediaPickerOptions,
- handler: @escaping LNMediaPickerHandler) {
- switch type {
- case .photo:
- handlePhotoAuthorization(view: view, options: options, handler: handler)
- case .camera:
- handleCameraAuthorization(view: view, options: options, handler: handler)
- }
- }
- private func handlePhotoAuthorization(view: UIView?,
- options: LNMediaPickerOptions,
- handler: @escaping LNMediaPickerHandler) {
- // 申请相册权限(iOS 10+ 需授权)
- LNPermissionHelper.requestPhotoLibraryAuthorization { [weak self] status in
- guard let self else { return }
- switch status {
- case .authorized: // 已授权,打开相册
- self.presentPicker(from: view, sourceType: .photoLibrary, options: options, handler: handler)
- case .denied, .restricted: // 拒绝授权或受限制
- LNCommonAlertView.showPhotoLibraryPermissionAlert()
- handler(nil, nil)
- case .notDetermined: // 首次请求,系统会自动弹出授权框(无需额外处理)
- break
- case .limited:
- break
- @unknown default:
- break
- }
- }
- }
- private func handleCameraAuthorization(view: UIView?,
- options: LNMediaPickerOptions,
- handler: @escaping LNMediaPickerHandler) {
- let status = LNPermissionHelper.cameraAuthorizationStatus()
- switch status {
- case .authorized: // 已授权
- presentPicker(from: view, sourceType: .camera, options: options, handler: handler)
- case .notDetermined: // 未决定,请求权限
- LNPermissionHelper.requestCameraAccess { [weak self] granted in
- guard let self else { return }
- guard granted else {
- handler(nil, nil)
- return
- }
- self.presentPicker(from: view, sourceType: .camera, options: options, handler: handler)
- }
- case .denied, .restricted: // 拒绝/受限
- LNCommonAlertView.showCameraPermissionAlert()
- handler(nil, nil)
- @unknown default:
- handler(nil, nil)
- }
- }
- }
- extension LNMediaPicker {
- private func presentPicker(from view: UIView?,
- sourceType: UIImagePickerController.SourceType,
- options: LNMediaPickerOptions,
- handler: @escaping LNMediaPickerHandler) {
- let vc = view?.viewController ?? UIView.appKeyWindow?.rootViewController
- let picker = UIImagePickerController()
- picker.delegate = self
- picker.sourceType = sourceType
- picker.allowsEditing = options.allowEdit
- picker.mediaTypes = options.allowVideo
- ? [UTType.image.identifier, UTType.movie.identifier]
- : [UTType.image.identifier]
- if sourceType == .camera {
- picker.videoMaximumDuration = options.videoMaximumDuration
- }
- picker.mediaPickerHandler = handler
- vc?.present(picker, animated: true)
- }
- }
- extension LNMediaPicker: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
- func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
- // 关闭相册
- picker.dismiss(animated: true)
-
- if let videoURL = info[.mediaURL] as? URL {
- picker.mediaPickerHandler?(nil, videoURL)
- return
- }
-
- // 获取选择的图片(editedImage:编辑后的图片,originalImage:原始图片)
- let image: UIImage?
- if let editedImage = info[.editedImage] as? UIImage {
- image = editedImage
- } else if let originalImage = info[.originalImage] as? UIImage {
- image = originalImage
- } else {
- image = nil
- }
- picker.mediaPickerHandler?(image, nil)
- }
-
- // 取消选择
- func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
- picker.dismiss(animated: true)
- picker.mediaPickerHandler?(nil, nil)
- }
- }
|