Skip to content
12 changes: 12 additions & 0 deletions BSImagePicker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
2F0FADE526669B0F005747B4 /* Size+BSImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0FADE426669B0F005747B4 /* Size+BSImagePicker.swift */; };
2F0FADE826669B3C005747B4 /* AutorizationStatusHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0FADE726669B3C005747B4 /* AutorizationStatusHeaderView.swift */; };
2F0FADEC2666A140005747B4 /* Alert+BSImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0FADEB2666A140005747B4 /* Alert+BSImagePicker.swift */; };
531D8C532249C81A00281681 /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531D8C522249C81A00281681 /* SettingsTests.swift */; };
53FEDA162247FEB80098E34A /* CGSize+Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FEDA152247FEB80098E34A /* CGSize+Scale.swift */; };
53FEDA18224805BA0098E34A /* CGSizeExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FEDA17224805BA0098E34A /* CGSizeExtensionTests.swift */; };
Expand Down Expand Up @@ -78,6 +81,9 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
2F0FADE426669B0F005747B4 /* Size+BSImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Size+BSImagePicker.swift"; sourceTree = "<group>"; };
2F0FADE726669B3C005747B4 /* AutorizationStatusHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutorizationStatusHeaderView.swift; sourceTree = "<group>"; };
2F0FADEB2666A140005747B4 /* Alert+BSImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Alert+BSImagePicker.swift"; sourceTree = "<group>"; };
531D8C522249C81A00281681 /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
53FEDA152247FEB80098E34A /* CGSize+Scale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGSize+Scale.swift"; sourceTree = "<group>"; };
53FEDA17224805BA0098E34A /* CGSizeExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSizeExtensionTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -222,6 +228,7 @@
C2DC13C923F75BDA0035FD13 /* NumberView.swift */,
55BCF8C521D52C1000386752 /* CameraCollectionViewCell.swift */,
55F67B7A222F088500805134 /* GradientView.swift */,
2F0FADE726669B3C005747B4 /* AutorizationStatusHeaderView.swift */,
);
path = Assets;
sourceTree = "<group>";
Expand Down Expand Up @@ -337,6 +344,8 @@
isa = PBXGroup;
children = (
94DA686F247BDE5900CD5251 /* UIColor+BSImagePicker.swift */,
2F0FADE426669B0F005747B4 /* Size+BSImagePicker.swift */,
2F0FADEB2666A140005747B4 /* Alert+BSImagePicker.swift */,
);
path = Extension;
sourceTree = "<group>";
Expand Down Expand Up @@ -462,6 +471,7 @@
550CB425230FD01200543217 /* ImageView.swift in Sources */,
55BCF8D521D52C1000386752 /* AssetCollectionViewCell.swift in Sources */,
559DB81B21E6B43400CD58B4 /* ImagePickerController+ButtonActions.swift in Sources */,
2F0FADEC2666A140005747B4 /* Alert+BSImagePicker.swift in Sources */,
559DB81121E6561300CD58B4 /* ImagePickerController+Closure.swift in Sources */,
55C5B802222BD529003CF3F1 /* DropdownPresentationController.swift in Sources */,
55BCF8E021D52C1000386752 /* ZoomAnimator.swift in Sources */,
Expand All @@ -471,11 +481,13 @@
94DA6870247BDE5900CD5251 /* UIColor+BSImagePicker.swift in Sources */,
5554729D21E5248400B90CA5 /* ImagePickerController.swift in Sources */,
55C5B800222BD445003CF3F1 /* DropdownTransitionDelegate.swift in Sources */,
2F0FADE526669B0F005747B4 /* Size+BSImagePicker.swift in Sources */,
55BCF8D321D52C1000386752 /* AssetsCollectionViewDataSource.swift in Sources */,
550CB427230FD08200543217 /* ImageViewLayout.swift in Sources */,
55BCF8D621D52C1000386752 /* AlbumCell.swift in Sources */,
55BCF8D021D52C1000386752 /* AlbumsTableViewDataSource.swift in Sources */,
55F67B77222EEB2500805134 /* VideoCollectionViewCell.swift in Sources */,
2F0FADE826669B3C005747B4 /* AutorizationStatusHeaderView.swift in Sources */,
559DB80F21E655D000CD58B4 /* ImagePickerControllerDelegate.swift in Sources */,
559DB81721E6AFD800CD58B4 /* ImagePickerController+Assets.swift in Sources */,
55CDB45B223435420050D572 /* PlayerView.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = Y2NHS7RD78;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
Expand Down Expand Up @@ -407,6 +408,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Y2NHS7RD78;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
Expand Down
45 changes: 41 additions & 4 deletions Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ViewController: UIViewController {
imagePicker.settings.theme.selectionStyle = .numbered
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image, .video]
imagePicker.settings.selection.unselectOnReachingMax = true
imagePicker.settings.permission.enabled = true

let start = Date()
self.presentImagePicker(imagePicker, select: { (asset) in
Expand Down Expand Up @@ -76,7 +77,7 @@ class ViewController: UIViewController {
return 2
}
}

self.presentImagePicker(imagePicker, select: { (asset) in
print("Selected: \(asset)")
}, deselect: { (asset) in
Expand All @@ -91,16 +92,17 @@ class ViewController: UIViewController {
@IBAction func showImagePickerWithSelectedAssets(_ sender: UIButton) {
let allAssets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
var evenAssets = [PHAsset]()

allAssets.enumerateObjects({ (asset, idx, stop) -> Void in
if idx % 2 == 0 {
evenAssets.append(asset)
}
})

let imagePicker = ImagePickerController(selectedAssets: evenAssets)
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image]



self.presentImagePicker(imagePicker, select: { (asset) in
print("Selected: \(asset)")
}, deselect: { (asset) in
Expand All @@ -111,5 +113,40 @@ class ViewController: UIViewController {
print("Finished with selections: \(assets)")
})
}

func showImagePickerWithPermissionHandling() {
let imagePicker = ImagePickerController()
imagePicker.settings.selection.max = 5
imagePicker.settings.theme.selectionStyle = .numbered
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image, .video]
imagePicker.settings.selection.unselectOnReachingMax = true
imagePicker.settings.permission.enabled = true

imagePicker.imagePickerDelegate = self
self.present(imagePicker, animated: true, completion: nil)
}

}


extension ViewController: ImagePickerControllerDelegate {
func imagePicker(_ imagePicker: ImagePickerController, didSelectAsset asset: PHAsset) {
print("Selected: \(asset)")
}

func imagePicker(_ imagePicker: ImagePickerController, didDeselectAsset asset: PHAsset) {
print("Deselected: \(asset)")
}

func imagePicker(_ imagePicker: ImagePickerController, didFinishWithAssets assets: [PHAsset]) {
print("Finished with selections: \(assets)")
}

func imagePicker(_ imagePicker: ImagePickerController, didCancelWithAssets assets: [PHAsset]) {
print("Canceled with selections: \(assets)")
}

func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimit count: Int) {

}
}
2 changes: 2 additions & 0 deletions Sources/Controller/ImagePickerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Photos
@objcMembers open class ImagePickerController: UINavigationController {
// MARK: Public properties
public weak var imagePickerDelegate: ImagePickerControllerDelegate?

public var settings: Settings = Settings()
public var doneButton: UIBarButtonItem = UIBarButtonItem(title: "", style: .done, target: nil, action: nil)
public var cancelButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: nil, action: nil)
Expand Down Expand Up @@ -93,6 +94,7 @@ import Photos
// Setup view controllers
albumsViewController.delegate = self
assetsViewController.delegate = self


viewControllers = [assetsViewController]
view.backgroundColor = settings.theme.backgroundColor
Expand Down
2 changes: 2 additions & 0 deletions Sources/Controller/ImagePickerControllerDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,6 @@ public protocol ImagePickerControllerDelegate: class {
/// - Parameter imagePicker: The image picker that selection limit was reached in.
/// - Parameter count: Number of selected assets.
func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimit count: Int)

}

112 changes: 112 additions & 0 deletions Sources/Extension/Alert+BSImagePicker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// The MIT License (MIT)
//
// Copyright (c) 2021 Mithilesh Parmar
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import UIKit
import Photos

extension AssetsViewController {
internal func showAlertForRestricedOrNotDeterminedAccess(){

let localizedTitle = NSLocalizedString("bsimagepicker.restrictedAccess.alert.title",
value: "Allow access to your photos",
comment: "Title of alert controller")

let localizedMessage = NSLocalizedString("bsimagepicker.restrictedAccess.alert.message",
value: "This lets you share from your camera roll and enables other features for photos. Go to your settings and tap \"Photos\".",
comment: "Message of alert controller")

let notNowLocalizedText = NSLocalizedString("bsimagepicker.restrictedAccess.alert.secondaryButton.title",
value: "Not now",
comment: "Secondary button title")

let openSettinsgLocalizedText = NSLocalizedString("bsimagepicker.restrictedAccess.alert.openSettings.title",
value: "Open Settings",
comment: "Primart button title")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Primart - typo


let alert = UIAlertController(title: localizedTitle, message: localizedMessage, preferredStyle: .alert)

let notNowAction = UIAlertAction(title: notNowLocalizedText, style: .cancel, handler: nil)
alert.addAction(notNowAction)

let openSettingsAction = UIAlertAction(title: openSettinsgLocalizedText, style: .default) { [unowned self] (_) in
// Open app privacy settings
gotoAppPrivacySettings()
}
alert.addAction(openSettingsAction)
present(alert, animated: true, completion: nil)
}

internal func showAlerForLimitedAccess(){

let localizedTitle = NSLocalizedString("bsimagepicker.limitedAccess.alert.title",
value: " ",
comment: "Title of Alert controller")

let localizedMessage = NSLocalizedString("bsimagepicker.limitedAccess.alert.message",
value: "Select more photos or go to Settings to allow access to all photos.",
comment: "Message of Alert controller")

let selectMorePhotosLocalizedText = NSLocalizedString("bsimagepicker.limitedAccess.alert.selectMorePhotos",
value: "Select more photos",
comment: "Select more photos action title")

let allowAccessToAllPhotosLocalizedText = NSLocalizedString("bsimagepicker.limitedAccess.alert.allowAccessToAllPhotos",
value: "Allow access to all photos",
comment: "Allow access to all photos action title")

let cancelLocalizedText = NSLocalizedString("bsimagepicker.cancel",
value: "Cancel",
comment: "Cancel action title")

let actionSheet = UIAlertController(title: localizedTitle, message: localizedMessage, preferredStyle: .actionSheet)

let selectPhotosAction = UIAlertAction(title: selectMorePhotosLocalizedText, style: .default) { [unowned self] (_) in
// Show limited library picker
if #available(iOS 14, *) {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
}
}
actionSheet.addAction(selectPhotosAction)

let allowFullAccessAction = UIAlertAction(title: allowAccessToAllPhotosLocalizedText, style: .default) { [unowned self] (_) in
// Open app privacy settings
self.gotoAppPrivacySettings()
}
actionSheet.addAction(allowFullAccessAction)

let cancelAction = UIAlertAction(title: cancelLocalizedText, style: .cancel, handler: nil)
actionSheet.addAction(cancelAction)

present(actionSheet, animated: true, completion: nil)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get crash on iPad because the popoverPresentationController?.sourceView is not set. Send here the "manage" button so sheet is displayed properly


}

internal func gotoAppPrivacySettings() {
guard let url = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(url) else {
assertionFailure("Not able to open App privacy settings")
return
}

UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
34 changes: 34 additions & 0 deletions Sources/Extension/Size+BSImagePicker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// The MIT License (MIT)
//
// Copyright (c) 2021 Mithilesh Parmar
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import UIKit

extension UIView {
func runtimeSize()-> CGSize {
let size: CGSize = self.systemLayoutSizeFitting(
CGSize(width: UIScreen.main.bounds.width,
height: UIView.layoutFittingExpandedSize.height),
withHorizontalFittingPriority: .fittingSizeLevel,
verticalFittingPriority: .fittingSizeLevel)
return size
}
}
Loading