diff --git a/Example/ExampleViewController.swift b/Example/ExampleViewController.swift index 23e74becd..58b6f122e 100644 --- a/Example/ExampleViewController.swift +++ b/Example/ExampleViewController.swift @@ -89,6 +89,7 @@ class ExampleViewController: UIViewController { /* Choose what media types are available in the library. Defaults to `.photo` */ config.library.mediaType = .photoAndVideo config.library.itemOverlayType = .grid + config.library.sortingOption = .creationDate /* Enables selecting the front camera by default, useful for avatars. Defaults to false */ // config.usesFrontCamera = true @@ -133,6 +134,8 @@ class ExampleViewController: UIViewController { /* Defines the time limit for videos from the library. Defaults to 60 seconds. */ config.video.libraryTimeLimit = 500.0 + + config.video.compressionOption = [compressionOptions.AVAssetExportPreset640x480,compressionOptions.AVAssetExportPreset1920x1080, compressionOptions.AVAssetExportPresetPassthrough] /* Adds a Crop step in the photo taking process, after filters. Defaults to .none */ config.showsCrop = .rectangle(ratio: (16/9)) diff --git a/Package.resolved b/Package.resolved index 04d119d60..189a47d14 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/freshOS/Stevia", "state": { "branch": null, - "revision": "87dd17a86240f16788239a78dd8be11c4b013150", - "version": "4.8.0" + "revision": "cfb1a1d2159277bb553c3dc46f3f742c0275566d", + "version": "5.1.2" } } ] diff --git a/Source/Configuration/YPImagePickerConfiguration.swift b/Source/Configuration/YPImagePickerConfiguration.swift index 5cb79e13b..f887bd660 100644 --- a/Source/Configuration/YPImagePickerConfiguration.swift +++ b/Source/Configuration/YPImagePickerConfiguration.swift @@ -115,6 +115,12 @@ public struct YPImagePickerConfiguration { /// Defines the text colour to be shown when a bottom option is unselected public var bottomMenuItemUnSelectedTextColour: UIColor = .ypSecondaryLabel + /// Defines the text colour to be shown for selectMoreButton + public var selectMoreButtonBackgroundColour: UIColor = .ypSystemBlue + + /// Defines the text colour to be shown for seeAllPhotoButton + public var seeAllPhotosButtonBackgroundColour: UIColor = .ypSystemBlue + /// Defines the max camera zoom factor for camera. Disable camera zoom with 1. Default is 1. public var maxCameraZoomFactor: CGFloat = 1.0 @@ -227,6 +233,9 @@ public struct YPConfigLibrary { /// Set the overlay type shown on top of the selected library item public var itemOverlayType: YPItemOverlayType = .grid + + /// The library items are sorted by time created or modified + public var sortingOption: SortingOption = SortingOption.creationDate } /// Encapsulates video specific settings. @@ -246,6 +255,9 @@ public struct YPConfigVideo { */ public var compression: String = AVAssetExportPresetHighestQuality + /// Choose the different video compression option to be supported + public var compressionOption: [compressionOptions] = [compressionOptions.AVAssetExportPresetPassthrough] + /// Choose the result video extension if you trim or compress a video. Defaults to mov. public var fileType: AVFileType = .mov @@ -304,3 +316,71 @@ public enum YPlibraryMediaType { case video case photoAndVideo } + +public enum SortingOption: String { + case modificationDate = "modificationDate" + case creationDate = "creationDate" +} + +public enum compressionOptions: String { + case AVAssetExportPresetLowQuality + case AVAssetExportPreset640x480 + case AVAssetExportPresetMediumQuality + case AVAssetExportPreset1920x1080 + case AVAssetExportPreset1280x720 + case AVAssetExportPresetHighestQuality + case AVAssetExportPresetAppleM4A + case AVAssetExportPreset3840x2160 + case AVAssetExportPreset960x540 + case AVAssetExportPresetPassthrough + + func presetID() -> String { + switch self { + case .AVAssetExportPresetLowQuality: + return "AVAssetExportPresetLowQuality" + case .AVAssetExportPreset640x480: + return "AVAssetExportPreset640x480" + case .AVAssetExportPresetMediumQuality: + return "AVAssetExportPresetMediumQuality" + case .AVAssetExportPreset1920x1080: + return "AVAssetExportPreset1920x1080" + case .AVAssetExportPreset1280x720: + return "AVAssetExportPreset1280x720" + case .AVAssetExportPresetHighestQuality: + return "AVAssetExportPresetHighestQuality" + case .AVAssetExportPresetAppleM4A: + return "AVAssetExportPresetAppleM4A" + case .AVAssetExportPreset3840x2160: + return "AVAssetExportPreset3840x2160" + case .AVAssetExportPreset960x540: + return "AVAssetExportPreset960x540" + case .AVAssetExportPresetPassthrough: + return "AVAssetExportPresetPassthrough" + } + } + + func getLabel() -> String { + switch self { + case .AVAssetExportPresetLowQuality: + return YPConfig.wordings.textAVAssetExportPresetLowQuality + case .AVAssetExportPreset640x480: + return YPConfig.wordings.textAVAssetExportPreset640x480 + case .AVAssetExportPresetMediumQuality: + return YPConfig.wordings.textAVAssetExportPresetMediumQuality + case .AVAssetExportPreset1920x1080: + return YPConfig.wordings.textAVAssetExportPreset1920x1080 + case .AVAssetExportPreset1280x720: + return YPConfig.wordings.textAVAssetExportPreset1280x720 + case .AVAssetExportPresetHighestQuality: + return YPConfig.wordings.textAVAssetExportPresetHighestQuality + case .AVAssetExportPresetAppleM4A: + return YPConfig.wordings.textAVAssetExportPresetAppleM4A + case .AVAssetExportPreset3840x2160: + return YPConfig.wordings.textAVAssetExportPreset3840x2160 + case .AVAssetExportPreset960x540: + return YPConfig.wordings.textAVAssetExportPreset960x540 + case .AVAssetExportPresetPassthrough: + return YPConfig.wordings.textAVAssetExportPresetPassthrough + } + } +} diff --git a/Source/Configuration/YPWordings.swift b/Source/Configuration/YPWordings.swift index 2a4288961..eb01f614c 100644 --- a/Source/Configuration/YPWordings.swift +++ b/Source/Configuration/YPWordings.swift @@ -41,4 +41,15 @@ public struct YPWordings { public var filter = ypLocalized("YPImagePickerFilter") public var crop = ypLocalized("YPImagePickerCrop") public var warningMaxItemsLimit = ypLocalized("YPImagePickerWarningItemsLimit") + + public var textAVAssetExportPresetLowQuality = ypLocalized("Low") + public var textAVAssetExportPreset640x480 = ypLocalized("Medium (HD)") + public var textAVAssetExportPresetMediumQuality = ypLocalized("Medium") + public var textAVAssetExportPreset1920x1080 = ypLocalized("High (Full HD)") + public var textAVAssetExportPreset1280x720 = ypLocalized("1280 x 720") + public var textAVAssetExportPresetHighestQuality = ypLocalized("Highest") + public var textAVAssetExportPresetAppleM4A = ypLocalized("Apple M4A") + public var textAVAssetExportPreset3840x2160 = ypLocalized("3840 x 2160") + public var textAVAssetExportPreset960x540 = ypLocalized("960 x 540") + public var textAVAssetExportPresetPassthrough = ypLocalized("Original") } diff --git a/Source/Filters/Video/YPVideoCompressionVC.storyboard b/Source/Filters/Video/YPVideoCompressionVC.storyboard new file mode 100644 index 000000000..f84dc2f19 --- /dev/null +++ b/Source/Filters/Video/YPVideoCompressionVC.storyboard @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Filters/Video/YPVideoCompressionVC.swift b/Source/Filters/Video/YPVideoCompressionVC.swift new file mode 100644 index 000000000..7aeecb9b0 --- /dev/null +++ b/Source/Filters/Video/YPVideoCompressionVC.swift @@ -0,0 +1,149 @@ +// +// YPVideoCompressionVC.swift +// YPImagePicker +// +// Created by Nirai on 10/10/22. +// Copyright © 2022 Yummypets. All rights reserved. +// + +import UIKit + +let COMPRESSION_OPTION = "YPCompressionOption" + +class YPVideoCompressionVC: UIViewController, UITableViewDelegate, UITableViewDataSource { + + @IBOutlet weak var tableView: UITableView! + @IBOutlet weak var bkgOverlayView: UIView! + + @IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint! + + var titleArray = [String]() + var checkedIndex: Int? = nil + var headingTitle: String = "" + + var didDismiss: (Int) -> Void = { (index: Int) in + } + + required init() { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + override func viewDidLoad() { + super.viewDidLoad() + + //keep original as default vide compression option + self.checkedIndex = (UserDefaults.standard.object(forKey: COMPRESSION_OPTION) != nil) ? UserDefaults.standard.integer(forKey: COMPRESSION_OPTION) : (titleArray.count - 1) + + self.bkgOverlayView.alpha = 0.2 + self.presentAnimateTransition() + self.setTableViewHeight() + self.tableView.layer.cornerRadius = 8.0 + if #available(iOS 15.0, *) { + UITableView.appearance().sectionHeaderTopPadding = 0.0 + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + //StatusBar.statusBar.customizeStatusBar(.Translucent(0.2)) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + //StatusBar.statusBar.customizeStatusBar(.Default) + } + + func setTableViewHeight() { + tableViewHeightConstraint.constant = CGFloat((titleArray.count + 1) * 44) + } + + func presentAnimateTransition() { + tableView.center = self.view.center + tableView.transform = CGAffineTransform.init(scaleX: 0.4, y: 0.4) + tableView.alpha = 0 + UIView.animate(withDuration: 0.33) { + self.tableView.alpha = 1 + self.tableView.transform = CGAffineTransform.identity + } + } + + @IBAction func overlayViewTapped(_ tapGesture: UITapGestureRecognizer) { + //ITALogger.log.debug("check mark overview pressed") + //AnalyticsManager.manager.logNewEvent(category: "check_mark_view", action: "card_settings_sheet_dismiss", label: "card setting sheet dismiss", value: nil) + //keep original as default vide compression option + self.didDismiss(self.checkedIndex ?? (titleArray.count - 1)) + self.dismiss(animated: true, completion: nil) + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return titleArray.count + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 44.0 + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let headerCell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") as! HeaderCell + headerCell.title.text = headingTitle + return headerCell + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return 44.0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "CompressionOptionCell") as! CompressionOptionCell + cell.separatorInset = .zero + cell.configure(titleString: titleArray[indexPath.row]) + if indexPath.row == self.checkedIndex { + cell.setChecked() + } + return cell + } + + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + self.checkedIndex = indexPath.row + UserDefaults.standard.set(self.checkedIndex, forKey: COMPRESSION_OPTION) + //keep original as default vide compression option + self.didDismiss(self.checkedIndex ?? (titleArray.count - 1)) + self.dismiss(animated: true, completion: nil) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + } + +} + +class CompressionOptionCell: UITableViewCell { + @IBOutlet weak var tickMark: UIImageView! + @IBOutlet weak var title: UILabel! + + func configure(titleString: String) { + self.setUnchecked() + self.title.text = titleString + } + + func setChecked() { + self.tickMark.isHidden = false + } + + func setUnchecked() { + self.tickMark.isHidden = true + } +} + +class HeaderCell: UITableViewCell { + @IBOutlet weak var title: UILabel! +} diff --git a/Source/Filters/Video/YPVideoFiltersVC.swift b/Source/Filters/Video/YPVideoFiltersVC.swift index 2e2c8e3a9..cdc4eb7b5 100644 --- a/Source/Filters/Video/YPVideoFiltersVC.swift +++ b/Source/Filters/Video/YPVideoFiltersVC.swift @@ -200,7 +200,7 @@ public final class YPVideoFiltersVC: UIViewController, IsMediaFilterVC { let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory()) .appendingUniquePathComponent(pathExtension: YPConfig.video.fileType.fileExtension) - _ = trimmedAsset.export(to: destinationURL) { [weak self] session in + _ = trimmedAsset.export(to: destinationURL, removeOldFile: true) { [weak self] session in switch session.status { case .completed: DispatchQueue.main.async { diff --git a/Source/Helpers/Permissions/YPPermissionCheckable.swift b/Source/Helpers/Permissions/YPPermissionCheckable.swift index fb433a2c5..6f83247af 100644 --- a/Source/Helpers/Permissions/YPPermissionCheckable.swift +++ b/Source/Helpers/Permissions/YPPermissionCheckable.swift @@ -9,20 +9,20 @@ import UIKit internal protocol YPPermissionCheckable { - func doAfterLibraryPermissionCheck(block: @escaping () -> Void) + func doAfterLibraryPermissionCheck(block: @escaping (Bool) -> Void) func doAfterCameraPermissionCheck(block: @escaping () -> Void) func checkLibraryPermission() func checkCameraPermission() } internal extension YPPermissionCheckable where Self: UIViewController { - func doAfterLibraryPermissionCheck(block: @escaping () -> Void) { + func doAfterLibraryPermissionCheck(block: @escaping (Bool) -> Void) { YPPermissionManager.checkLibraryPermissionAndAskIfNeeded(sourceVC: self) { hasPermission in - if hasPermission { - block() - } else { + if !hasPermission { ypLog("Not enough permissions.") } + block(hasPermission) + } } diff --git a/Source/Pages/Gallery/YPAssetViewContainer.swift b/Source/Pages/Gallery/YPAssetViewContainer.swift index 832d49b2f..9bdfe747e 100644 --- a/Source/Pages/Gallery/YPAssetViewContainer.swift +++ b/Source/Pages/Gallery/YPAssetViewContainer.swift @@ -27,7 +27,7 @@ final class YPAssetViewContainer: UIView { public var isShown = true public var spinnerIsShown = false - private let spinner = UIActivityIndicatorView(style: .white) + public let spinner = UIActivityIndicatorView(style: .white) private var shouldCropToSquare = YPConfig.library.isSquareByDefault private var isMultipleSelectionEnabled = false diff --git a/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift b/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift index 89e6ac411..37e401f32 100644 --- a/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift +++ b/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift @@ -7,6 +7,7 @@ // import UIKit +import Photos extension YPLibraryVC { var isLimitExceeded: Bool { return selectedItems.count >= YPConfig.library.maxNumberOfItems } @@ -67,8 +68,12 @@ extension YPLibraryVC { // Replace the current selected image with the previously selected one if let previouslySelectedIndexPath = selectedIndexPaths.last { - v.collectionView.deselectItem(at: indexPath, animated: false) - v.collectionView.selectItem(at: previouslySelectedIndexPath, animated: false, scrollPosition: []) + if(v.collectionView.indexPathsForSelectedItems?.contains(indexPath) ?? false){ + v.collectionView.deselectItem(at: indexPath, animated: false) + } + if(v.collectionView.indexPathsForVisibleItems.contains(previouslySelectedIndexPath)){ + v.collectionView.selectItem(at: previouslySelectedIndexPath, animated: false, scrollPosition: []) + } currentlySelectedIndex = previouslySelectedIndexPath.row changeAsset(mediaManager.getAsset(at: previouslySelectedIndexPath.row)) } @@ -101,7 +106,19 @@ extension YPLibraryVC { /// Checks if there can be selected more items. If no - present warning. func checkLimit() { - v.maxNumberWarningView.isHidden = !isLimitExceeded || isMultipleSelectionEnabled == false + let isHidden = !isLimitExceeded || isMultipleSelectionEnabled == false + v.maxNumberWarningView.isHidden = isHidden + self.checkSelectMoreOptions(isHidden: isHidden) + } + + func checkSelectMoreOptions(isHidden: Bool = true) { + if #available(iOS 14, *) { + let status = PHPhotoLibrary.authorizationStatus(for: .readWrite) + if status == .limited { + v.selectMoreButton.isHidden = !isHidden + v.seeAllButton.isHidden = !isHidden + } + } } } diff --git a/Source/Pages/Gallery/YPLibraryVC.swift b/Source/Pages/Gallery/YPLibraryVC.swift index 53b5451f1..64ee6f41a 100644 --- a/Source/Pages/Gallery/YPLibraryVC.swift +++ b/Source/Pages/Gallery/YPLibraryVC.swift @@ -80,11 +80,14 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { } guard mediaManager.hasResultItems else { + checkSelectMoreOptions() return } if YPConfig.library.defaultMultipleSelection || selectedItems.count > 1 { toggleMultipleSelection() + } else { + checkSelectMoreOptions() } } @@ -126,6 +129,8 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { .addTarget(self, action: #selector(multipleSelectionButtonTapped), for: .touchUpInside) + v.selectMoreButton.addTarget(self,action: #selector(selectMorePhotosButtonTapped), for: .touchUpInside) + v.seeAllButton.addTarget(self,action: #selector(seeAllPhotosButtonTapped), for: .touchUpInside) // Forces assetZoomableView to have a contentSize. // otherwise 0 in first selection triggering the bug : "invalid image size 0x0" @@ -152,11 +157,32 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { @objc func squareCropButtonTapped() { - doAfterLibraryPermissionCheck { [weak self] in - self?.v.assetViewContainer.squareCropButtonTapped() + doAfterLibraryPermissionCheck { hasPermission in + if hasPermission { + self.v.assetViewContainer.squareCropButtonTapped() + } + } + } + + // MARK: - Select more photos + + @objc func selectMorePhotosButtonTapped() { + if #available(iOS 14, *) { + PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self) } } + // MARK: - See all photos + + @objc func seeAllPhotosButtonTapped() { + let alertController = UIAlertController(title: "See all photos", message: "On the next screen, please click on “Photos“ and select “All Photos“", preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: "Later", style: .default) { _ in }) + alertController.addAction(UIAlertAction(title: "Continue", style: .default) { _ in + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) + }) + self.present(alertController, animated: true) + } + // MARK: - Multiple Selection @objc @@ -170,11 +196,13 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { return } - doAfterLibraryPermissionCheck { [weak self] in - if self?.isMultipleSelectionEnabled == false { - self?.selectedItems.removeAll() + doAfterLibraryPermissionCheck { hasPermission in + if hasPermission { + if self.isMultipleSelectionEnabled == false { + self.selectedItems.removeAll() + } + self.toggleMultipleSelection() } - self?.toggleMultipleSelection() } } @@ -261,7 +289,7 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { } let options = PHFetchOptions() - options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] + options.sortDescriptors = [NSSortDescriptor(key: YPConfig.library.sortingOption.rawValue, ascending: false)] options.predicate = YPConfig.library.mediaType.predicate() return options } @@ -409,15 +437,14 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { guard fitsVideoLengthLimits(asset: asset) else { return } - if YPConfig.video.automaticTrimToTrimmerMaxDuration { - fetchVideoAndCropWithDuration(for: asset, - withCropRect: resultCropRect, - duration: YPConfig.video.trimmerMaxDuration, - callback: callback) + self.fetchVideoAndCropWithDuration(for: asset, + withCropRect: resultCropRect, + duration: YPConfig.video.trimmerMaxDuration, + callback: callback) } else { - delegate?.libraryViewDidTapNext() - mediaManager.fetchVideoUrlAndCrop(for: asset, cropRect: resultCropRect, callback: callback) + self.delegate?.libraryViewDidTapNext() + self.mediaManager.fetchVideoUrlAndCrop(for: asset, cropRect: resultCropRect, callback: callback) } } @@ -447,118 +474,155 @@ internal final class YPLibraryVC: UIViewController, YPPermissionCheckable { return (asset, $0.cropRect) } - // Multiple selection - if self.isMultipleSelectionEnabled && self.selectedItems.count > 1 { - - // Check video length - for asset in selectedAssets { - if self.fitsVideoLengthLimits(asset: asset.asset) == false { - return - } - } - - // Fill result media items array - var resultMediaItems: [YPMediaItem] = [] - let asyncGroup = DispatchGroup() - - var assetDictionary: [PHAsset?: Int] = .init() - for (index, assetPair) in selectedAssets.enumerated() { - assetDictionary[assetPair.asset] = index + var isVideoSelected = false + let asyncCompressionGroup = DispatchGroup() + asyncCompressionGroup.enter() + + for asset in selectedAssets { + if asset.asset.mediaType == .video { + isVideoSelected = true + break } - - for asset in selectedAssets { - asyncGroup.enter() + } + + if isVideoSelected { + DispatchQueue.main.async { + let compressionOptions = YPImagePickerConfiguration.shared.video.compressionOption + let storyBoard = UIStoryboard(name: "YPVideoCompressionVC", bundle: Bundle(for: YPVideoCompressionVC.self)) + let ypVideoCompressionVC = storyBoard.instantiateViewController(withIdentifier: "YPVideoCompressionVC") as! YPVideoCompressionVC - switch asset.asset.mediaType { - case .image: - self.fetchImageAndCrop(for: asset.asset, withCropRect: asset.cropRect) { image, exifMeta in - let photo = YPMediaPhoto(image: image.resizedImageIfNeeded(), - exifMeta: exifMeta, asset: asset.asset) - resultMediaItems.append(YPMediaItem.photo(p: photo)) - asyncGroup.leave() - } - - case .video: - self.fetchVideoAndApplySettings(for: asset.asset, - withCropRect: asset.cropRect) { videoURL in - if let videoURL = videoURL { - let videoItem = YPMediaVideo(thumbnail: thumbnailFromVideoPath(videoURL), - videoURL: videoURL, asset: asset.asset) - resultMediaItems.append(YPMediaItem.video(v: videoItem)) - } else { - ypLog("Problems with fetching videoURL.") - } - asyncGroup.leave() - } - default: - break + var titleArray = [String]() + for compressionOtion in compressionOptions { + titleArray.append(compressionOtion.getLabel()) + } + ypVideoCompressionVC.titleArray = titleArray + ypVideoCompressionVC.headingTitle = "Choose video quality" + ypVideoCompressionVC.modalPresentationStyle = .overFullScreen + ypVideoCompressionVC.didDismiss = {(index) in + YPImagePickerConfiguration.shared.video.compression = compressionOptions[index].presetID() + asyncCompressionGroup.leave() } + self.present(ypVideoCompressionVC, animated: true, completion: nil) } - - asyncGroup.notify(queue: .main) { - // TODO: sort the array based on the initial order of the assets in selectedAssets - resultMediaItems.sort { (first, second) -> Bool in - var firstAsset: PHAsset? - var secondAsset: PHAsset? + } else { + asyncCompressionGroup.leave() + } + + asyncCompressionGroup.notify(queue: .main) { + DispatchQueue.global(qos: .userInitiated).async { + // Multiple selection + if self.isMultipleSelectionEnabled && self.selectedItems.count > 1 { - switch first { - case .photo(let photo): - firstAsset = photo.asset - case .video(let video): - firstAsset = video.asset - } - guard let firstIndex = assetDictionary[firstAsset] else { - return false + // Check video length + for asset in selectedAssets { + if self.fitsVideoLengthLimits(asset: asset.asset) == false { + return + } } - switch second { - case .photo(let photo): - secondAsset = photo.asset - case .video(let video): - secondAsset = video.asset - } + // Fill result media items array + var resultMediaItems: [YPMediaItem] = [] + let asyncGroup = DispatchGroup() - guard let secondIndex = assetDictionary[secondAsset] else { - return false + var assetDictionary: [PHAsset?: Int] = .init() + for (index, assetPair) in selectedAssets.enumerated() { + assetDictionary[assetPair.asset] = index } - return firstIndex < secondIndex - } - multipleItemsCallback(resultMediaItems) - self.delegate?.libraryViewFinishedLoading() - } - } else { - let asset = selectedAssets.first!.asset - switch asset.mediaType { - case .audio, .unknown: - return - case .video: - self.fetchVideoAndApplySettings(for: asset, callback: { videoURL in - DispatchQueue.main.async { - if let videoURL = videoURL { - self.delegate?.libraryViewFinishedLoading() - let video = YPMediaVideo(thumbnail: thumbnailFromVideoPath(videoURL), - videoURL: videoURL, asset: asset) - videoCallback(video) - } else { - ypLog("Problems with fetching videoURL.") + for asset in selectedAssets { + asyncGroup.enter() + + switch asset.asset.mediaType { + case .image: + self.fetchImageAndCrop(for: asset.asset, withCropRect: asset.cropRect) { image, exifMeta in + let photo = YPMediaPhoto(image: image.resizedImageIfNeeded(), + exifMeta: exifMeta, asset: asset.asset) + resultMediaItems.append(YPMediaItem.photo(p: photo)) + asyncGroup.leave() + } + + case .video: + self.fetchVideoAndApplySettings(for: asset.asset, withCropRect: asset.cropRect) { videoURL in + if let videoURL = videoURL { + let videoItem = YPMediaVideo(thumbnail: thumbnailFromVideoPath(videoURL), + videoURL: videoURL, asset: asset.asset) + resultMediaItems.append(YPMediaItem.video(v: videoItem)) + } else { + ypLog("Problems with fetching videoURL.") + } + asyncGroup.leave() + } + default: + break } } - }) - case .image: - self.fetchImageAndCrop(for: asset) { image, exifMeta in - DispatchQueue.main.async { + + asyncGroup.notify(queue: .main) { + // TODO: sort the array based on the initial order of the assets in selectedAssets + resultMediaItems.sort { (first, second) -> Bool in + var firstAsset: PHAsset? + var secondAsset: PHAsset? + + switch first { + case .photo(let photo): + firstAsset = photo.asset + case .video(let video): + firstAsset = video.asset + } + guard let firstIndex = assetDictionary[firstAsset] else { + return false + } + + switch second { + case .photo(let photo): + secondAsset = photo.asset + case .video(let video): + secondAsset = video.asset + } + + guard let secondIndex = assetDictionary[secondAsset] else { + return false + } + + return firstIndex < secondIndex + } + multipleItemsCallback(resultMediaItems) self.delegate?.libraryViewFinishedLoading() - let photo = YPMediaPhoto(image: image.resizedImageIfNeeded(), - exifMeta: exifMeta, - asset: asset) - photoCallback(photo) } + } else { + let asset = selectedAssets.first!.asset + switch asset.mediaType { + case .audio, .unknown: + return + case .video: + self.fetchVideoAndApplySettings(for: asset, callback: { videoURL in + DispatchQueue.main.async { + if let videoURL = videoURL { + self.delegate?.libraryViewFinishedLoading() + let video = YPMediaVideo(thumbnail: thumbnailFromVideoPath(videoURL), + videoURL: videoURL, asset: asset) + videoCallback(video) + } else { + ypLog("Problems with fetching videoURL.") + } + } + }) + case .image: + self.fetchImageAndCrop(for: asset) { image, exifMeta in + DispatchQueue.main.async { + self.delegate?.libraryViewFinishedLoading() + let photo = YPMediaPhoto(image: image.resizedImageIfNeeded(), + exifMeta: exifMeta, + asset: asset) + photoCallback(photo) + } + } + @unknown default: + ypLog("unknown default reached. Check code.") + } + return } - @unknown default: - ypLog("unknown default reached. Check code.") } - return } } } diff --git a/Source/Pages/Gallery/YPLibraryView.swift b/Source/Pages/Gallery/YPLibraryView.swift index d42db8866..32503c92c 100644 --- a/Source/Pages/Gallery/YPLibraryView.swift +++ b/Source/Pages/Gallery/YPLibraryView.swift @@ -48,6 +48,32 @@ internal final class YPLibraryView: UIView { v.font = YPConfig.fonts.libaryWarningFont return v }() + internal let selectMoreButton: UIButton = { + let v = UIButton() + var status: PHAuthorizationStatus + v.setTitle(" Select more ", for: .normal) + v.backgroundColor = YPConfig.selectMoreButtonBackgroundColour.withAlphaComponent(0.8) + v.layer.cornerRadius = 4 + v.layer.masksToBounds = true + v.tintColor = UIColor.ypLabel + v.isHidden = true + return v + }() + internal let seeAllButton: UIButton = { + let v = UIButton() + var status: PHAuthorizationStatus + if YPConfig.library.mediaType == .video { + v.setTitle(" See all videos ", for: .normal) + } else { + v.setTitle(" See all photos ", for: .normal) + } + v.backgroundColor = YPConfig.seeAllPhotosButtonBackgroundColour.withAlphaComponent(0.8) + v.layer.cornerRadius = 4 + v.layer.masksToBounds = true + v.tintColor = UIColor.ypLabel + v.isHidden = true + return v + }() // MARK: - Private vars @@ -129,6 +155,10 @@ internal final class YPLibraryView: UIView { shouldShowLoader = false assetViewContainer.spinnerView.alpha = 0 } + + func stopSpinner() { + assetViewContainer.spinner.stopAnimating() + } func updateProgress(_ progress: Float) { progressView.isHidden = progress > 0.99 || progress == 0 @@ -178,7 +208,9 @@ internal final class YPLibraryView: UIView { progressView, maxNumberWarningView.subviews( maxNumberWarningLabel - ) + ), + selectMoreButton, + seeAllButton ) collectionContainerView.fillContainer() @@ -200,5 +232,15 @@ internal final class YPLibraryView: UIView { |maxNumberWarningView|.bottom(0) maxNumberWarningView.Top == safeAreaLayoutGuide.Bottom - 40 maxNumberWarningLabel.centerHorizontally().top(11) + + seeAllButton.Bottom == safeAreaLayoutGuide.Bottom - 8 + seeAllButton.Top == safeAreaLayoutGuide.Bottom - 48 + seeAllButton.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.50).isActive = true + seeAllButton.Left == UIScreen.main.bounds.width * 0.05 + + selectMoreButton.Bottom == safeAreaLayoutGuide.Bottom - 8 + selectMoreButton.Top == safeAreaLayoutGuide.Bottom - 48 + selectMoreButton.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.35).isActive = true + selectMoreButton.Right == UIScreen.main.bounds.width * 0.05 } } diff --git a/Source/Resources/Assets.xcassets/Contents.json b/Source/Resources/Assets.xcassets/Contents.json index da4a164c9..73c00596a 100644 --- a/Source/Resources/Assets.xcassets/Contents.json +++ b/Source/Resources/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Contents.json b/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Contents.json new file mode 100644 index 000000000..a9071d9e9 --- /dev/null +++ b/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Success Tick@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Success Tick@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Success Tick@2x.png b/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Success Tick@2x.png new file mode 100644 index 000000000..f05a6c231 Binary files /dev/null and b/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Success Tick@2x.png differ diff --git a/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Success Tick@3x.png b/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Success Tick@3x.png new file mode 100644 index 000000000..ff53dc0e9 Binary files /dev/null and b/Source/Resources/Assets.xcassets/SuccessTickSmallRed.imageset/Success Tick@3x.png differ diff --git a/Source/YPImagePicker.swift b/Source/YPImagePicker.swift index 37dc79758..9d5358f86 100644 --- a/Source/YPImagePicker.swift +++ b/Source/YPImagePicker.swift @@ -99,58 +99,59 @@ open class YPImagePicker: UINavigationController { } // One item flow - let item = items.first! - switch item { - case .photo(let photo): - let completion = { (photo: YPMediaPhoto) in - let mediaItem = YPMediaItem.photo(p: photo) - // Save new image or existing but modified, to the photo album. - if YPConfig.shouldSaveNewPicturesToAlbum { - let isModified = photo.modifiedImage != nil - if photo.fromCamera || (!photo.fromCamera && isModified) { - YPPhotoSaver.trySaveImage(photo.image, inAlbumNamed: YPConfig.albumName) + if let item = items.first { + switch item { + case .photo(let photo): + let completion = { (photo: YPMediaPhoto) in + let mediaItem = YPMediaItem.photo(p: photo) + // Save new image or existing but modified, to the photo album. + if YPConfig.shouldSaveNewPicturesToAlbum { + let isModified = photo.modifiedImage != nil + if photo.fromCamera || (!photo.fromCamera && isModified) { + YPPhotoSaver.trySaveImage(photo.image, inAlbumNamed: YPConfig.albumName) + } + } + self?.didSelect(items: [mediaItem]) } - } - self?.didSelect(items: [mediaItem]) - } - - func showCropVC(photo: YPMediaPhoto, completion: @escaping (_ aphoto: YPMediaPhoto) -> Void) { - switch YPConfig.showsCrop { - case .rectangle, .circle: - let cropVC = YPCropVC(image: photo.image) - cropVC.didFinishCropping = { croppedImage in - photo.modifiedImage = croppedImage - completion(photo) + + func showCropVC(photo: YPMediaPhoto, completion: @escaping (_ aphoto: YPMediaPhoto) -> Void) { + switch YPConfig.showsCrop { + case .rectangle, .circle: + let cropVC = YPCropVC(image: photo.image) + cropVC.didFinishCropping = { croppedImage in + photo.modifiedImage = croppedImage + completion(photo) + } + self?.pushViewController(cropVC, animated: true) + default: + completion(photo) + } } - self?.pushViewController(cropVC, animated: true) - default: - completion(photo) - } - } - - if YPConfig.showsPhotoFilters { - let filterVC = YPPhotoFiltersVC(inputPhoto: photo, - isFromSelectionVC: false) - // Show filters and then crop - filterVC.didSave = { outputMedia in - if case let YPMediaItem.photo(outputPhoto) = outputMedia { - showCropVC(photo: outputPhoto, completion: completion) + + if YPConfig.showsPhotoFilters { + let filterVC = YPPhotoFiltersVC(inputPhoto: photo, + isFromSelectionVC: false) + // Show filters and then crop + filterVC.didSave = { outputMedia in + if case let YPMediaItem.photo(outputPhoto) = outputMedia { + showCropVC(photo: outputPhoto, completion: completion) + } + } + self?.pushViewController(filterVC, animated: false) + } else { + showCropVC(photo: photo, completion: completion) + } + case .video(let video): + if YPConfig.showsVideoTrimmer { + let videoFiltersVC = YPVideoFiltersVC.initWith(video: video, + isFromSelectionVC: false) + videoFiltersVC.didSave = { [weak self] outputMedia in + self?.didSelect(items: [outputMedia]) + } + self?.pushViewController(videoFiltersVC, animated: true) + } else { + self?.didSelect(items: [YPMediaItem.video(v: video)]) } - } - self?.pushViewController(filterVC, animated: false) - } else { - showCropVC(photo: photo, completion: completion) - } - case .video(let video): - if YPConfig.showsVideoTrimmer { - let videoFiltersVC = YPVideoFiltersVC.initWith(video: video, - isFromSelectionVC: false) - videoFiltersVC.didSave = { [weak self] outputMedia in - self?.didSelect(items: [outputMedia]) - } - self?.pushViewController(videoFiltersVC, animated: true) - } else { - self?.didSelect(items: [YPMediaItem.video(v: video)]) } } } diff --git a/Source/YPPickerVC.swift b/Source/YPPickerVC.swift index 64f361ff9..b67e70c8e 100644 --- a/Source/YPPickerVC.swift +++ b/Source/YPPickerVC.swift @@ -166,8 +166,12 @@ open class YPPickerVC: YPBottomPager, YPBottomPagerDelegate { // Re-trigger permission check if let vc = vc as? YPLibraryVC { - vc.doAfterLibraryPermissionCheck { [weak vc] in - vc?.initialize() + vc.doAfterLibraryPermissionCheck { hasPermission in + if hasPermission { + vc.initialize() + } else { + vc.v.stopSpinner() + } } } else if let cameraVC = vc as? YPCameraVC { cameraVC.start() @@ -379,6 +383,7 @@ extension YPPickerVC: YPLibraryViewDelegate { public func libraryViewHaveNoItems() { pickerVCDelegate?.libraryHasNoItems() + self.libraryVC?.v.stopSpinner() } public func libraryViewShouldAddToSelection(indexPath: IndexPath, numSelections: Int) -> Bool { diff --git a/YPImagePicker.podspec b/YPImagePicker.podspec index df06a95b3..f29f89732 100644 --- a/YPImagePicker.podspec +++ b/YPImagePicker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'YPImagePicker' - s.version = "5.2.1" + s.version = "5.3.0" s.summary = "Instagram-like image picker & filters for iOS" s.homepage = "https://github.com/Yummypets/YPImagePicker" s.license = { :type => "MIT", :file => "LICENSE" } @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.source_files = 'Source/**/*.swift' s.dependency 'SteviaLayout', '= 5.1.2' s.dependency 'PryntTrimmerView', '= 4.0.2' - s.resources = ['Source/Resources/*', 'Source/**/*.xib'] + s.resources = ['Source/Resources/*', 'Source/**/*.xib', 'Source/**/*.storyboard'] s.description = "Instagram-like image picker & filters for iOS supporting videos and albums" s.swift_versions = ['5.0', '5.1', '5.2', '5.3'] end diff --git a/YPImagePicker.xcodeproj/project.pbxproj b/YPImagePicker.xcodeproj/project.pbxproj index 88b6475ec..1f60500e0 100644 --- a/YPImagePicker.xcodeproj/project.pbxproj +++ b/YPImagePicker.xcodeproj/project.pbxproj @@ -56,6 +56,10 @@ 99CF6D2B201CB96700487F77 /* YPVideoCaptureHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99CF6D2A201CB96700487F77 /* YPVideoCaptureHelper.swift */; }; 99D1DC2B1F9788930047F0E0 /* YPImagePickerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99D1DC2A1F9788930047F0E0 /* YPImagePickerConfiguration.swift */; }; AA69BCF222E601D100FBB925 /* YPDeviceOrientationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69BCF122E601D100FBB925 /* YPDeviceOrientationHelper.swift */; }; + C86AD84028F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C86AD83F28F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard */; }; + C86AD84128F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C86AD83F28F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard */; }; + C8F28E7228F34E5800E9290E /* YPVideoCompressionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F28E7128F34E5800E9290E /* YPVideoCompressionVC.swift */; }; + C8F28E7328F34E5800E9290E /* YPVideoCompressionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F28E7128F34E5800E9290E /* YPVideoCompressionVC.swift */; }; EB05F6E7214A7191002040AA /* YPVideoProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB05F6E6214A7191002040AA /* YPVideoProcessor.swift */; }; EB1AB2EA20AC1DED00BFA79F /* CGFloat+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1AB2E920AC1DED00BFA79F /* CGFloat+Extensions.swift */; }; EB473E17208E192800D16105 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB473E16208E192800D16105 /* URL+Extensions.swift */; }; @@ -282,6 +286,8 @@ A7FD716620755C2D0044A8E8 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/YPImagePickerLocalizable.strings; sourceTree = ""; }; AA69BCF122E601D100FBB925 /* YPDeviceOrientationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YPDeviceOrientationHelper.swift; sourceTree = ""; }; BEED427CB4F6E5B8C20B6F13 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; + C86AD83F28F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = YPVideoCompressionVC.storyboard; sourceTree = ""; }; + C8F28E7128F34E5800E9290E /* YPVideoCompressionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YPVideoCompressionVC.swift; sourceTree = ""; }; DC3DAC4526C6FCAA007FD95A /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/YPImagePickerLocalizable.strings; sourceTree = ""; }; E3E492BB2169012B002BA807 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/YPImagePickerLocalizable.strings; sourceTree = ""; }; E5287F5820B908720052153D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/YPImagePickerLocalizable.strings; sourceTree = ""; }; @@ -565,7 +571,9 @@ isa = PBXGroup; children = ( EB59F450208766B800811B7B /* YPVideoFiltersVC.swift */, + C8F28E7128F34E5800E9290E /* YPVideoCompressionVC.swift */, EBB0FF7920877A7500C84E25 /* YPVideoView.swift */, + C86AD83F28F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard */, ); path = Video; sourceTree = ""; @@ -723,7 +731,6 @@ }; EBA37B4E26F749C6005DAAD4 = { CreatedOnToolsVersion = 12.5.1; - DevelopmentTeam = B3U3XRF8D7; ProvisioningStyle = Automatic; }; }; @@ -783,6 +790,7 @@ files = ( 8458057421819B95004F241C /* YPImagePickerLocalizable.strings in Resources */, 99C6D6B91F1FB5C100711DB2 /* Assets.xcassets in Resources */, + C86AD84128F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard in Resources */, 99C6D6BD1F1FB5C100711DB2 /* YPLibraryView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -795,6 +803,7 @@ EBA37BC426F750DE005DAAD4 /* YPLibraryView.xib in Resources */, EBA37B5B26F749C7005DAAD4 /* Assets.xcassets in Resources */, EBA37BC726F75151005DAAD4 /* YPImagePickerLocalizable.strings in Resources */, + C86AD84028F3D2E500E2EB89 /* YPVideoCompressionVC.storyboard in Resources */, EBA37BC626F75144005DAAD4 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -879,6 +888,7 @@ EBA37BC026F74CE0005DAAD4 /* YPFiltersView.swift in Sources */, 99CF6D1B201B6A5C00487F77 /* UICollectionView+Extensions.swift in Sources */, EBA37B0A26F73080005DAAD4 /* YPAlbumVC.swift in Sources */, + C8F28E7328F34E5800E9290E /* YPVideoCompressionVC.swift in Sources */, 99C6D6C11F1FB5C100711DB2 /* YPAssetViewContainer.swift in Sources */, EB473E17208E192800D16105 /* URL+Extensions.swift in Sources */, EB473E2B208E1ECB00D16105 /* AVCaptureDevice+Extensions.swift in Sources */, @@ -957,6 +967,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C8F28E7228F34E5800E9290E /* YPVideoCompressionVC.swift in Sources */, EBA37BBB26F74CE0005DAAD4 /* YPPhotoFiltersVC.swift in Sources */, EBA37B8F26F74CBA005DAAD4 /* AVFileType+Extensions.swift in Sources */, EBA37B7026F74CBA005DAAD4 /* YPError.swift in Sources */, @@ -1241,7 +1252,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 5.2.1; + MARKETING_VERSION = 5.3.0; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.yummypets.YPImagePicker; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1278,7 +1289,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 5.2.1; + MARKETING_VERSION = 5.3.0; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.yummypets.YPImagePicker; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1301,7 +1312,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = B3U3XRF8D7; + DEVELOPMENT_TEAM = WQTCKL2Y64; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -1332,7 +1343,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = B3U3XRF8D7; + DEVELOPMENT_TEAM = WQTCKL2Y64; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/YPImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/YPImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata index c50e3a4d5..919434a62 100644 --- a/YPImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/YPImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/YPImagePicker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/YPImagePicker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..278c37f40 --- /dev/null +++ b/YPImagePicker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "prynttrimmerview", + "kind" : "remoteSourceControl", + "location" : "https://github.com/HHK1/PryntTrimmerView", + "state" : { + "revision" : "ac1b60a22c7e6a6514de7a66d2f3d5b537c956d5", + "version" : "4.0.2" + } + }, + { + "identity" : "stevia", + "kind" : "remoteSourceControl", + "location" : "https://github.com/freshOS/Stevia", + "state" : { + "revision" : "cfb1a1d2159277bb553c3dc46f3f742c0275566d", + "version" : "5.1.2" + } + } + ], + "version" : 2 +} diff --git a/YPImagePicker.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/YPImagePicker.xcodeproj/xcshareddata/xcschemes/Example.xcscheme index 2467dde78..d7a616739 100644 --- a/YPImagePicker.xcodeproj/xcshareddata/xcschemes/Example.xcscheme +++ b/YPImagePicker.xcodeproj/xcshareddata/xcschemes/Example.xcscheme @@ -1,6 +1,6 @@ + + + + PreviewsEnabled + + +