Skip to content

Commit 8fd8d69

Browse files
committed
limit number of image or video
1 parent 750f26a commit 8fd8d69

8 files changed

+117
-41
lines changed

Example/Base.lproj/Main.storyboard

+21-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
33
<device id="retina4_7" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
7+
<capability name="System colors in document resources" minToolsVersion="11.0"/>
78
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
89
</dependencies>
910
<scenes>
@@ -19,7 +20,7 @@
1920
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
2021
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
2122
<subviews>
22-
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GFw-cP-rmQ">
23+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GFw-cP-rmQ">
2324
<rect key="frame" x="143.5" y="318.5" width="88" height="30"/>
2425
<state key="normal" title="Image picker">
2526
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -28,7 +29,7 @@
2829
<action selector="showImagePicker:" destination="BYZ-38-t0r" eventType="touchUpInside" id="LaC-Tl-d1J"/>
2930
</connections>
3031
</button>
31-
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzO-ex-PdY">
32+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzO-ex-PdY">
3233
<rect key="frame" x="114.5" y="356.5" width="146" height="30"/>
3334
<constraints>
3435
<constraint firstAttribute="width" constant="146" id="Cd2-rJ-ekE"/>
@@ -41,7 +42,7 @@
4142
<action selector="showCustomImagePicker:" destination="BYZ-38-t0r" eventType="touchUpInside" id="GKA-nS-l2H"/>
4243
</connections>
4344
</button>
44-
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zgs-tU-5p9">
45+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zgs-tU-5p9">
4546
<rect key="frame" x="71.5" y="394.5" width="232" height="30"/>
4647
<constraints>
4748
<constraint firstAttribute="width" constant="232" id="IRj-9r-lbP"/>
@@ -52,12 +53,21 @@
5253
<action selector="showImagePickerWithSelectedAssets:" destination="BYZ-38-t0r" eventType="touchUpInside" id="m00-0a-bkf"/>
5354
</connections>
5455
</button>
56+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ywf-iF-6QR">
57+
<rect key="frame" x="66" y="432.5" width="243" height="30"/>
58+
<state key="normal" title="Image picker limit 2 images, 1 video"/>
59+
<connections>
60+
<action selector="showImagePickerWithLimit:" destination="BYZ-38-t0r" eventType="touchUpInside" id="chb-F8-vjn"/>
61+
</connections>
62+
</button>
5563
</subviews>
56-
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
64+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
5765
<constraints>
5866
<constraint firstItem="zgs-tU-5p9" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="0A6-aX-1jb"/>
5967
<constraint firstAttribute="centerX" secondItem="DzO-ex-PdY" secondAttribute="centerX" id="EZn-Ed-pHh"/>
6068
<constraint firstItem="zgs-tU-5p9" firstAttribute="top" secondItem="DzO-ex-PdY" secondAttribute="bottom" constant="8" id="GUE-MX-lLP"/>
69+
<constraint firstItem="Ywf-iF-6QR" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="JYd-Ph-njS"/>
70+
<constraint firstItem="Ywf-iF-6QR" firstAttribute="top" secondItem="zgs-tU-5p9" secondAttribute="bottom" constant="8" id="VjK-Iu-ymf"/>
6171
<constraint firstAttribute="centerY" secondItem="GFw-cP-rmQ" secondAttribute="centerY" id="XmI-RH-XSN"/>
6272
<constraint firstItem="DzO-ex-PdY" firstAttribute="top" secondItem="GFw-cP-rmQ" secondAttribute="bottom" constant="8" id="j2M-qn-wIK"/>
6373
<constraint firstAttribute="centerX" secondItem="GFw-cP-rmQ" secondAttribute="centerX" id="vJT-bW-R2W"/>
@@ -69,4 +79,9 @@
6979
<point key="canvasLocation" x="138" y="134"/>
7080
</scene>
7181
</scenes>
82+
<resources>
83+
<systemColor name="systemBackgroundColor">
84+
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
85+
</systemColor>
86+
</resources>
7287
</document>

Example/ViewController.swift

+55-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ViewController: UIViewController {
3030
let imagePicker = ImagePickerController()
3131
imagePicker.settings.selection.max = 5
3232
imagePicker.settings.theme.selectionStyle = .numbered
33-
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image, .video]
33+
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image(), .video()]
3434
imagePicker.settings.selection.unselectOnReachingMax = true
3535

3636
let start = Date()
@@ -52,7 +52,7 @@ class ViewController: UIViewController {
5252
let imagePicker = ImagePickerController()
5353
imagePicker.settings.selection.max = 1
5454
imagePicker.settings.selection.unselectOnReachingMax = true
55-
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image, .video]
55+
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image(), .video()]
5656
imagePicker.albumButton.tintColor = UIColor.green
5757
imagePicker.cancelButton.tintColor = UIColor.red
5858
imagePicker.doneButton.tintColor = UIColor.purple
@@ -99,7 +99,7 @@ class ViewController: UIViewController {
9999
})
100100

101101
let imagePicker = ImagePickerController(selectedAssets: evenAssets)
102-
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image]
102+
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image()]
103103

104104
self.presentImagePicker(imagePicker, select: { (asset) in
105105
print("Selected: \(asset)")
@@ -111,5 +111,57 @@ class ViewController: UIViewController {
111111
print("Finished with selections: \(assets)")
112112
})
113113
}
114+
115+
@IBAction func showImagePickerWithLimit(_ sender: Any) {
116+
let imagePicker = ImagePickerController()
117+
imagePicker.settings.selection.max = 5
118+
imagePicker.settings.theme.selectionStyle = .numbered
119+
imagePicker.settings.fetch.assets.supportedMediaTypes = [
120+
.image(max: 2),
121+
.video(max: 1)
122+
]
123+
124+
let start = Date()
125+
imagePicker.imagePickerDelegate = self
126+
self.presentImagePicker(imagePicker, select: { (asset) in
127+
print("Selected: \(asset)")
128+
}, deselect: { (asset) in
129+
print("Deselected: \(asset)")
130+
}, cancel: { (assets) in
131+
print("Canceled with selections: \(assets)")
132+
}, finish: { (assets) in
133+
print("Finished with selections: \(assets)")
134+
}, completion: {
135+
let finish = Date()
136+
print(finish.timeIntervalSince(start))
137+
})
138+
139+
}
140+
}
141+
142+
extension ViewController: ImagePickerControllerDelegate {
143+
func imagePicker(_ imagePicker: ImagePickerController, didSelectAsset asset: PHAsset) {
144+
print("Selected: \(asset)")
145+
}
146+
147+
func imagePicker(_ imagePicker: ImagePickerController, didDeselectAsset asset: PHAsset) {
148+
print("Deselected: \(asset)")
149+
}
150+
151+
func imagePicker(_ imagePicker: ImagePickerController, didFinishWithAssets assets: [PHAsset]) {
152+
print("Canceled with selections: \(assets)")
153+
}
154+
155+
func imagePicker(_ imagePicker: ImagePickerController, didCancelWithAssets assets: [PHAsset]) {
156+
print("Finished with selections: \(assets)")
157+
}
158+
159+
func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimit count: Int) {
160+
print("didReachSelectionLimit with count: ", count)
161+
}
162+
163+
func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimitForType type: PHAssetMediaType) {
164+
print("didReachSelectionLimitForType: ", type.rawValue)
165+
}
114166
}
115167

Sources/Controller/ImagePickerController+Assets.swift

+4
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ extension ImagePickerController: AssetsViewControllerDelegate {
5252

5353
pushViewController(previewViewController, animated: true)
5454
}
55+
56+
func assetsViewController(_ assetsViewController: AssetsViewController, didReachSelectionLimit asset: PHAsset) {
57+
imagePickerDelegate?.imagePicker(self, didReachSelectionLimitForType: asset.mediaType)
58+
}
5559
}

Sources/Controller/ImagePickerController+Closure.swift

-26
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ import Photos
4545
imagePicker.onCancel = cancel
4646
imagePicker.onFinish = finish
4747

48-
// And since we are using the blocks api. Set ourselfs as delegate
49-
imagePicker.imagePickerDelegate = imagePicker
50-
5148
// Present
5249
self.present(imagePicker, animated: animated, completion: completion)
5350
}
@@ -70,26 +67,3 @@ extension ImagePickerController {
7067
return PHPhotoLibrary.authorizationStatus()
7168
}
7269
}
73-
74-
/// ImagePickerControllerDelegate closure wrapper
75-
extension ImagePickerController: ImagePickerControllerDelegate {
76-
public func imagePicker(_ imagePicker: ImagePickerController, didSelectAsset asset: PHAsset) {
77-
onSelection?(asset)
78-
}
79-
80-
public func imagePicker(_ imagePicker: ImagePickerController, didDeselectAsset asset: PHAsset) {
81-
onDeselection?(asset)
82-
}
83-
84-
public func imagePicker(_ imagePicker: ImagePickerController, didFinishWithAssets assets: [PHAsset]) {
85-
onFinish?(assets)
86-
}
87-
88-
public func imagePicker(_ imagePicker: ImagePickerController, didCancelWithAssets assets: [PHAsset]) {
89-
onCancel?(assets)
90-
}
91-
92-
public func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimit count: Int) {
93-
94-
}
95-
}

Sources/Controller/ImagePickerControllerDelegate.swift

+5
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,9 @@ public protocol ImagePickerControllerDelegate: class {
4949
/// - Parameter imagePicker: The image picker that selection limit was reached in.
5050
/// - Parameter count: Number of selected assets.
5151
func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimit count: Int)
52+
53+
/// Selection limit reach by type
54+
/// - Parameter imagePicker: The image picker that selection limit was reached in.
55+
/// - Parameter count: Number of selected assets.
56+
func imagePicker(_ imagePicker: ImagePickerController, didReachSelectionLimitForType type: PHAssetMediaType)
5257
}

Sources/Model/AssetStore.swift

+4
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@ import Photos
5555
func index(of asset: PHAsset) -> Int? {
5656
return assets.firstIndex(of: asset)
5757
}
58+
59+
func getCountByType(_ type: PHAssetMediaType) -> Int {
60+
return assets.filter { $0.mediaType == type }.count
61+
}
5862
}

Sources/Model/Settings.swift

+11-5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ import Photos
7474

7575
/// If it reaches the max limit, unselect the first selection, and allow the new selection
7676
@objc public lazy var unselectOnReachingMax : Bool = false
77+
78+
public lazy var maxImage: Int = Int.max
79+
public lazy var minImage: Int = 0
80+
81+
public lazy var maxVideo: Int = Int.max
82+
public lazy var minVideo: Int = 0
7783
}
7884

7985
public class List : NSObject {
@@ -124,11 +130,11 @@ import Photos
124130
/// Fetch options for assets
125131

126132
/// Simple wrapper around PHAssetMediaType to ensure we only expose the supported types.
127-
public enum MediaTypes {
128-
case image
129-
case video
133+
public enum MediaTypes: Hashable {
134+
case image(max: Int = Int.max)
135+
case video(max: Int = Int.max)
130136

131-
fileprivate var assetMediaType: PHAssetMediaType {
137+
var assetMediaType: PHAssetMediaType {
132138
switch self {
133139
case .image:
134140
return .image
@@ -137,7 +143,7 @@ import Photos
137143
}
138144
}
139145
}
140-
public lazy var supportedMediaTypes: Set<MediaTypes> = [.image]
146+
public lazy var supportedMediaTypes: Set<MediaTypes> = [.image()]
141147

142148
public lazy var options: PHFetchOptions = {
143149
let fetchOptions = PHFetchOptions()

Sources/Scene/Assets/AssetsViewController.swift

+17-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ protocol AssetsViewControllerDelegate: class {
2727
func assetsViewController(_ assetsViewController: AssetsViewController, didSelectAsset asset: PHAsset)
2828
func assetsViewController(_ assetsViewController: AssetsViewController, didDeselectAsset asset: PHAsset)
2929
func assetsViewController(_ assetsViewController: AssetsViewController, didLongPressCell cell: AssetCollectionViewCell, displayingAsset asset: PHAsset)
30+
func assetsViewController(_ assetsViewController: AssetsViewController, didReachSelectionLimit asset: PHAsset)
3031
}
3132

3233
class AssetsViewController: UIViewController {
@@ -195,7 +196,22 @@ extension AssetsViewController: UICollectionViewDelegate {
195196
}
196197

197198
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
198-
guard store.count < settings.selection.max || settings.selection.unselectOnReachingMax else { return false }
199+
let asset = fetchResult.object(at: indexPath.row)
200+
for type in settings.fetch.assets.supportedMediaTypes {
201+
switch type {
202+
case .image(let max), .video(let max):
203+
if asset.mediaType == type.assetMediaType {
204+
if max <= store.getCountByType(type.assetMediaType) {
205+
self.delegate?.assetsViewController(self, didReachSelectionLimit: asset)
206+
return false
207+
}
208+
}
209+
}
210+
}
211+
212+
guard store.count < settings.selection.max || settings.selection.unselectOnReachingMax else {
213+
return false
214+
}
199215
selectionFeedback.prepare()
200216

201217
return true

0 commit comments

Comments
 (0)