├── .gitignore
├── .swift-version
├── LICENSE
├── README.md
├── XYPhotoKit.podspec
├── XYPhotoKit
├── DataSources
│ ├── XYPhotoCollectionDataSource.h
│ ├── XYPhotoCollectionDataSource.m
│ ├── XYPhotoTableDataSource.h
│ └── XYPhotoTableDataSource.m
├── ViewControllers
│ ├── XYPhotoAlbumDetailController.h
│ ├── XYPhotoAlbumDetailController.m
│ ├── XYPhotoAlbumListController.h
│ ├── XYPhotoAlbumListController.m
│ ├── XYPhotoCameraController.h
│ ├── XYPhotoCameraController.m
│ ├── XYPhotoMultiCameraPicker.h
│ ├── XYPhotoMultiCameraPicker.m
│ ├── XYPhotoMultiImagePicker.h
│ ├── XYPhotoMultiImagePicker.m
│ ├── XYPhotoPreviewViewController.h
│ └── XYPhotoPreviewViewController.m
├── Views
│ ├── XYPhotoBottomPreviewCell.h
│ ├── XYPhotoBottomPreviewCell.m
│ ├── XYPhotoCameraOverlayView.h
│ ├── XYPhotoCameraOverlayView.m
│ ├── XYPhotoCollectionViewCell.h
│ ├── XYPhotoCollectionViewCell.m
│ ├── XYPhotoHorizontalScrollItemView.h
│ ├── XYPhotoHorizontalScrollItemView.m
│ ├── XYPhotoHorizontalScrollView.h
│ ├── XYPhotoHorizontalScrollView.m
│ ├── XYPhotoPreviewOverlayView.h
│ ├── XYPhotoPreviewOverlayView.m
│ ├── XYPhotoSelectedAssetPreviewView.h
│ ├── XYPhotoSelectedAssetPreviewView.m
│ ├── XYPhotoTableCell.h
│ └── XYPhotoTableCell.m
├── XYPhotoCollectionFlowLayout.h
├── XYPhotoCollectionFlowLayout.m
├── XYPhotoDataCategory.h
├── XYPhotoDataCategory.m
├── XYPhotoKit.bundle
│ └── image
│ │ ├── cl_photo_picker_camera_devicemode@2x.png
│ │ ├── cl_photo_picker_camera_flash@2x.png
│ │ ├── cl_photo_picker_inaccessible@2x.png
│ │ ├── cl_photo_picker_picked@2x.png
│ │ ├── cl_photo_picker_shutter@2x.png
│ │ ├── cl_photo_picker_shutter_h@2x.png
│ │ ├── cl_photo_picker_unpicked@2x.png
│ │ ├── cl_photokit_placeholder@2x.png
│ │ ├── cl_photokit_play@2x.png
│ │ └── cl_photokit_play@3x.png
├── XYPhotoKitHelper.h
├── XYPhotoKitHelper.m
├── XYPhotoSelectedAssetManager.h
└── XYPhotoSelectedAssetManager.m
└── XYPhotoKitDemo
├── XYPhotoKitDemo.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── XYPhotoKitDemo
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
├── AppIcon.appiconset
│ └── Contents.json
├── Contents.json
└── add.imageset
│ ├── Contents.json
│ └── add@2x.png
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Info.plist
├── RootViewController.h
├── RootViewController.m
├── XYDemoCollectionViewCell.h
├── XYDemoCollectionViewCell.m
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 | #
37 | # Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | # Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
52 |
53 | fastlane/report.xml
54 | fastlane/Preview.html
55 | fastlane/screenshots
56 | fastlane/test_output
57 |
58 | # Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Zhiping Yang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # XYPhotoKit
2 |
3 | 基于PhotoKit开发的照片获取方式,支持多选单选(对应的UI略有不同),支持iCloud获取相册等
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ## 使用方式
19 |
20 | 详情见`demo`查看
21 |
22 | ### 相册获取方式
23 |
24 | | 属性 | 说明 |
25 | | --- | --- |
26 | | **Controller** | |
27 | | XYPhotoMultiImagePicker | NavigationController,承载两个VC栈、相册相关参数初始化工作及 delegate & datasource |
28 | | XYPhotoAlbumListController | 系统和用户的相册列表(tableview),根据CLPhotoMultiPickerStartPosition来确定初始展示哪个VC界面 |
29 | | XYPhotoAlbumDetailController | 某个相册的详情(collectionView),单选多选照片的不同交互主要完成于此,可进入大图预览 |
30 | | XYPhotoPreviewViewController | 大图预览(XYHorizontalScrollView)|
31 |
32 | ### 用法
33 |
34 | ```objc
35 |
36 | XYPhotoMultiImagePicker *multiImagePicker = [[XYPhotoMultiImagePicker alloc] init];
37 | multiImagePicker.pickerDelegate = self;
38 | multiImagePicker.pickerDataSource = self;
39 | multiImagePicker.mediaType = PHAssetMediaTypeImage; // 支持图片和视屏
40 | multiImagePicker.maxNumberOfAssets = 9; // 最多选择九张照片
41 | multiImagePicker.assetCollectionViewColumnCount = 4; // 获取
42 | multiImagePicker.startPosition = CLPhotoMultiPickerStartPositionAlbums; // 相册列表开始进入
43 | multiImagePicker.allowNetRequestIfiCloud = YES; // 支持iCloud网络获取
44 | [self presentViewController:multiImagePicker animated:YES completion:nil];
45 |
46 |
47 | #pragma mark - XYPhotoMultiImagePickerDelegate, XYPhotoMultiImagePickerDataSource
48 |
49 | - (void)multiImagePicker:(XYPhotoMultiImagePicker *)multiImagePicker selectedAssets:(NSArray *)assets
50 | {
51 | _selectedAssets = assets;
52 |
53 | NSLog(@"%@", @(assets.count));
54 | }
55 |
56 | - (NSArray *)multiImagePickerLoadPresetSelectedAssets
57 | {
58 | return _selectedAssets;
59 | }
60 |
61 | ```
62 |
63 | ### 相机获取方式
64 |
65 | | 属性 | 说明 |
66 | | --- | --- |
67 | | **Controller** | |
68 | | XYPhotoMultiCameraPicker | NavigationController,承载XYPhotoCameraController、相机相关参数初始化工作及 delegate & datasource |
69 | | XYPhotoCameraController | 基于ImagePickerController,需要有相机和相册两个权限做交互,可进入大图预览 |
70 | | XYPhotoPreviewViewController | 大图预览(XYHorizontalScrollView)|
71 |
72 | ### 用法
73 |
74 | ```objc
75 |
76 | XYPhotoMultiCameraPicker *camera = [[XYPhotoMultiCameraPicker alloc] init];
77 | camera.pickerDelegate = self;
78 | camera.pickerDataSource = self;
79 | camera.maxNumberOfAssets = 9; // 最多选择九张照片
80 | camera.allowNetRequestIfiCloud = YES; // 支持iCloud网络获取
81 | [self presentViewController:camera animated:YES completion:nil];
82 |
83 | #pragma mark - XYPhotoMultiCameraPickerDelegate, XYPhotoMultiCameraPickerDataSource
84 |
85 | - (void)multiCameraPicker:(XYPhotoMultiCameraPicker *)multiImagePicker selectedAssets:(NSArray *)assets
86 | {
87 | _selectedAssets = assets.copy;
88 | NSLog(@"%@",assets);
89 | }
90 |
91 | - (NSArray *)multiCameraPickerLoadPresetSelectedAssets
92 | {
93 | return _selectedAssets;
94 | }
95 |
96 | ```
97 |
98 | ## 综述
99 |
100 | 以上获取都是PHAsset对象,PHAsset 非常灵活,详见[文档](https://developer.apple.com/documentation/photos/phasset),同步异步请求、支持网络、请求progress等等可以查看 `PHImageRequestOptions`
101 |
102 | > 例如:支持网络获取(如果是本地就直接本地获取)asset对象大小为`targetSize`的图片
103 | > 闭包中可能会有多次回调(本地图片)越来越高清,当网络请求时非特殊情况下不可直接获取原图尺寸`CGSizeMake(asset.pixelWidth, asset.pixelHeight)`
104 |
105 | ```objc
106 | PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
107 | options.networkAccessAllowed = YES;
108 |
109 | _imageRequestId = [self.imageManager requestImageForAsset:asset
110 | targetSize:_targetSize
111 | contentMode:PHImageContentModeAspectFit
112 | options:options
113 | resultHandler:^(UIImage *result, NSDictionary *info) {
114 | BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue]
115 | && ![info objectForKey:PHImageErrorKey]
116 | && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
117 | if (downloadFinined) { self.imageRequestId = 0; }
118 | if (result) { self.imageView.image = result; }
119 | }];
120 | ```
121 |
122 |
123 | ## todo & upgrade
124 |
125 | - [x] 视频播放
126 | - [ ] 拆分类的层级(比如接入LocalImagePicker等等)
127 | - [ ] 完善其他获取照片的拓展方法 & 工具(视频压缩、图片压缩,指定相册图片写入等等)
128 | - [ ] 添加iCloud加载的请求等待提示
129 | - [ ] 大图预览添加过渡动画,视频播放、消失添加fade
130 | - [ ] 大图预览改换使用pageController
131 | - [x] 持久化&有效性验证:对PHAsset持久化存储只需储存 `photoAsset.localIdentifier`的唯一标识,验证对象是否还存在于相册中(即有效性)需要对唯一标识做遍历对比
132 |
--------------------------------------------------------------------------------
/XYPhotoKit.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'XYPhotoKit'
3 | s.summary = '照片选择库,提供拍照和相册两种方式,支持单选多选,数据层是PhotoKit'
4 | s.version = '0.0.1'
5 | s.homepage = "https://github.com/ZhipingYang/XYPhotoKit"
6 | s.license = { :type => 'CHELUN', :file => 'LICENSE' }
7 | s.authors = { 'XcodeYang' => 'yangzhiping@chelun.com' }
8 | s.platform = :ios, '8.0'
9 | s.ios.deployment_target = '8.0'
10 | s.source = { :git => 'https://github.com/ZhipingYang/XYPhotoKit.git', :tag => s.version.to_s }
11 |
12 | s.requires_arc = true
13 |
14 | s.source_files = [
15 | "XYPhotoKit/**/*.{h,m}",
16 | ]
17 | s.public_header_files = [
18 | "XYPhotoKit/**/*.h",
19 | ]
20 | s.resources = [
21 | "XYPhotoKit/resource/*.bundle"
22 | ]
23 |
24 | s.frameworks = 'UIKit', 'Photos', 'AVFoundation', 'AssetsLibrary', 'AVKit'
25 |
26 | end
27 |
--------------------------------------------------------------------------------
/XYPhotoKit/DataSources/XYPhotoCollectionDataSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCollectionDataSource.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 |
10 |
11 | @import Foundation;
12 | @import UIKit;
13 | @import Photos;
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @class XYPhotoCollectionDataSource;
18 | @protocol XYPhotoCollectionDataSourceDelegate
19 |
20 | - (void)assetCollectionDataSource:(XYPhotoCollectionDataSource *)dataSource selectedIndex:(NSInteger)selectedIndex inFetchResult:(PHFetchResult *)fetchResult;
21 |
22 | @end
23 |
24 |
25 | @interface XYPhotoCollectionDataSource : NSObject
26 |
27 | @property (nullable, nonatomic, weak) id delegate;
28 | @property (nullable, nonatomic, copy) NSArray *assets;
29 | @property (nonatomic) BOOL shouldCache;
30 |
31 | - (instancetype)init __attribute__((unavailable("此方法已弃用,请使用initWithCollectionView:fetchResult方法")));
32 | + (instancetype)new __attribute__((unavailable("此方法已弃用,请使用initWithCollectionView:fetchResult方法")));
33 | - (instancetype)initWithCollectionView:(UICollectionView *)collectionView fetchResult:(PHFetchResult *)fetchResult;
34 |
35 | @end
36 |
37 | NS_ASSUME_NONNULL_END
38 |
--------------------------------------------------------------------------------
/XYPhotoKit/DataSources/XYPhotoCollectionDataSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCollectionDataSource.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoCollectionDataSource.h"
10 |
11 | #import "XYPhotoDataCategory.h"
12 | #import "XYPhotoCollectionViewCell.h"
13 | #import "XYPhotoCollectionFlowLayout.h"
14 | #import "XYPhotoSelectedAssetManager.h"
15 | #import "XYPhotoKitHelper.h"
16 |
17 | @interface XYPhotoCollectionDataSource ()
18 |
19 | @property (nonatomic, weak) UICollectionView *collectionView;
20 | @property (nonatomic, copy) PHFetchResult *results;
21 | @property (nonatomic, strong) PHCachingImageManager *imageManager;
22 | @property (nonatomic) CGRect previousPreheatRect;
23 |
24 | @end
25 |
26 | @implementation XYPhotoCollectionDataSource
27 |
28 | static NSString *const XYPhotoCollectionDataSourceCellReuseIdentifier = @"XYPhotoCollectionDataSourceCellReuseIdentifier";
29 |
30 | - (instancetype)initWithCollectionView:(UICollectionView *)collectionView fetchResult:(PHFetchResult *)fetchResult
31 | {
32 | if (self = [super init]) {
33 | _collectionView = collectionView;
34 | _collectionView.dataSource = self;
35 | _collectionView.delegate = self;
36 | [_collectionView registerClass:[XYPhotoCollectionViewCell class] forCellWithReuseIdentifier:XYPhotoCollectionDataSourceCellReuseIdentifier];
37 |
38 | _results = fetchResult.copy;
39 |
40 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
41 | if (status == PHAuthorizationStatusAuthorized) {
42 | dispatch_async(dispatch_get_main_queue(), ^{
43 | self.imageManager = [[PHCachingImageManager alloc] init];
44 | [self resetCachedAssets];
45 | [self.collectionView reloadData];
46 | [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
47 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectedAssetsChanged) name:XYPhotoMultiImagePickerNotifications.assetsChanged object:nil];
48 | });
49 | } else {
50 | [UIAlertController xy_showAlertPhotoSettingIfUnauthorized];
51 | }
52 | }];
53 | }
54 | return self;
55 | }
56 |
57 | - (void)dealloc
58 | {
59 | [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
60 | }
61 |
62 | - (NSArray *)assets
63 | {
64 | if (!_results) { return nil; }
65 | NSMutableArray *array = @[].mutableCopy;
66 | [_results enumerateObjectsUsingBlock:^(PHAsset *obj, NSUInteger idx, BOOL * _Nonnull stop) {
67 | [array addObject:obj];
68 | }];
69 | return array;
70 | }
71 |
72 | #pragma mark - Asset Caching
73 |
74 | - (void)resetCachedAssets
75 | {
76 | [self.imageManager stopCachingImagesForAllAssets];
77 | self.previousPreheatRect = CGRectZero;
78 | }
79 |
80 | - (void)updateCachedAssets
81 | {
82 | if (!self.shouldCache) {
83 | return;
84 | }
85 |
86 | CGRect preheatRect = self.collectionView.bounds;
87 | preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect));
88 |
89 | CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect));
90 | if (delta > CGRectGetHeight(self.collectionView.bounds) / 3.0f) {
91 |
92 | NSMutableArray *addedIndexPaths = [NSMutableArray array];
93 | NSMutableArray *removedIndexPaths = [NSMutableArray array];
94 |
95 | [self computeDifferenceBetweenRect:self.previousPreheatRect
96 | andRect:preheatRect
97 | removedHandler:^(CGRect removedRect) {
98 | NSArray *indexPaths = [self.collectionView xy_indexPathsForElementsInRect:removedRect];
99 | [removedIndexPaths addObjectsFromArray:indexPaths];
100 | } addedHandler:^(CGRect addedRect) {
101 | NSArray *indexPaths = [self.collectionView xy_indexPathsForElementsInRect:addedRect];
102 | [addedIndexPaths addObjectsFromArray:indexPaths];
103 | }];
104 |
105 | NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths];
106 | NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths];
107 |
108 | CGSize itemSize = [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout itemSize];
109 | [self.imageManager startCachingImagesForAssets:assetsToStartCaching
110 | targetSize:CGSizeScale(itemSize, [UIScreen mainScreen].scale)
111 | contentMode:PHImageContentModeAspectFill
112 | options:nil];
113 | [self.imageManager stopCachingImagesForAssets:assetsToStopCaching
114 | targetSize:CGSizeScale(itemSize, [UIScreen mainScreen].scale)
115 | contentMode:PHImageContentModeAspectFill
116 | options:nil];
117 |
118 | self.previousPreheatRect = preheatRect;
119 | }
120 | }
121 |
122 | - (void)computeDifferenceBetweenRect:(CGRect)oldRect
123 | andRect:(CGRect)newRect
124 | removedHandler:(void (^)(CGRect removedRect))removedHandler
125 | addedHandler:(void (^)(CGRect addedRect))addedHandler
126 | {
127 | if (CGRectIntersectsRect(newRect, oldRect)) {
128 | // 如果像个区域有交集
129 | CGFloat oldMaxY = CGRectGetMaxY(oldRect);
130 | CGFloat oldMinY = CGRectGetMinY(oldRect);
131 | CGFloat newMaxY = CGRectGetMaxY(newRect);
132 | CGFloat newMinY = CGRectGetMinY(newRect);
133 |
134 | if (newMaxY > oldMaxY) {
135 | CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY));
136 | addedHandler(rectToAdd);
137 | }
138 | if (oldMinY > newMinY) {
139 | CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY));
140 | addedHandler(rectToAdd);
141 | }
142 | if (newMaxY < oldMaxY) {
143 | CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY));
144 | removedHandler(rectToRemove);
145 | }
146 | if (oldMinY < newMinY) {
147 | CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY));
148 | removedHandler(rectToRemove);
149 | }
150 | } else {
151 | // 没有交集
152 | addedHandler(newRect);
153 | removedHandler(oldRect);
154 | }
155 | }
156 |
157 | - (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths
158 | {
159 | if (indexPaths.count == 0) {
160 | return nil;
161 | }
162 |
163 | NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
164 | for (NSIndexPath *indexPath in indexPaths) {
165 | PHAsset *asset = self.results[indexPath.item];
166 | [assets addObject:asset];
167 | }
168 | return assets;
169 | }
170 |
171 | #pragma mark - PHPhotoLibraryChangeObserver
172 |
173 | - (void)photoLibraryDidChange:(PHChange *)changeInstance
174 | {
175 | // 后台线程更新检查
176 | dispatch_async(dispatch_get_main_queue(), ^{
177 |
178 | // assets 被修改了 (增、删、改)
179 | PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.results];
180 | if (collectionChanges) {
181 |
182 | self.results = [collectionChanges fetchResultAfterChanges];
183 |
184 | // 未修改、或位置移动
185 | if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) {
186 | [self.collectionView reloadData];
187 | } else {
188 | // 照片被修改了 insert/delete/update
189 | [self.collectionView performBatchUpdates:^{
190 | NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
191 | if (removedIndexes.count) {
192 | [self.collectionView deleteItemsAtIndexPaths:[removedIndexes xy_indexPathsFromIndexesWithSection:0]];
193 | }
194 | NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
195 | if (insertedIndexes.count) {
196 | [self.collectionView insertItemsAtIndexPaths:[insertedIndexes xy_indexPathsFromIndexesWithSection:0]];
197 | }
198 | NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
199 | if (changedIndexes.count) {
200 | [self.collectionView reloadItemsAtIndexPaths:[changedIndexes xy_indexPathsFromIndexesWithSection:0]];
201 | }
202 | } completion:NULL];
203 | }
204 |
205 | [self resetCachedAssets];
206 | }
207 | });
208 | }
209 |
210 | #pragma mark - UIScrollViewDelegate
211 |
212 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView
213 | {
214 | [self updateCachedAssets];
215 | }
216 |
217 | #pragma mark - UICollectionViewDataSource
218 |
219 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
220 | {
221 | return self.results.count;
222 | }
223 |
224 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
225 | {
226 | XYPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:XYPhotoCollectionDataSourceCellReuseIdentifier forIndexPath:indexPath];
227 | PHAsset *asset = self.results[indexPath.row];
228 | [cell setAsset:asset atIndexPath:indexPath inCollectionView:self.collectionView withCacheManager:_imageManager];
229 | return cell;
230 | }
231 |
232 | #pragma mark - UICollectionViewDelegate
233 |
234 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
235 | {
236 | [collectionView deselectItemAtIndexPath:indexPath animated:YES];
237 |
238 | // 单一选择时,无需查看大图
239 | BOOL isSingleSelected = [XYPhotoSelectedAssetManager sharedManager].maxNumberOfAssets == 1;
240 | if (isSingleSelected && [[XYPhotoSelectedAssetManager sharedManager].delegate respondsToSelector:@selector(doneSelectingAssets)]) {
241 | [[XYPhotoSelectedAssetManager sharedManager] resetSelectedAsset:@[_results[indexPath.row]]];
242 | [[XYPhotoSelectedAssetManager sharedManager].delegate doneSelectingAssets];
243 | } else if ([self.delegate respondsToSelector:@selector(assetCollectionDataSource:selectedIndex:inFetchResult:)]) {
244 | [self.delegate assetCollectionDataSource:self selectedIndex:indexPath.row inFetchResult:self.results];
245 | }
246 | }
247 |
248 | #pragma mark - Notification
249 |
250 | - (void)selectedAssetsChanged
251 | {
252 | if (!self.collectionView.window) {
253 | [self.collectionView reloadData];
254 | }
255 | }
256 |
257 | @end
258 |
--------------------------------------------------------------------------------
/XYPhotoKit/DataSources/XYPhotoTableDataSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoTableDataSource.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 | @import UIKit;
11 | @import Photos;
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @class XYPhotoTableDataSource;
16 |
17 | @protocol XYPhotoTableDataSourceDelegate
18 |
19 | - (void)assetTableDataSource:(XYPhotoTableDataSource *)dataSource selectedAssetCollection:(PHAssetCollection *)assetCollection;
20 |
21 | @end
22 |
23 |
24 | @interface XYPhotoTableDataSource : NSObject
25 |
26 | @property (nullable, nonatomic, weak) id delegate;
27 |
28 | - (instancetype)initWithTableView:(UITableView *)tableView;
29 | - (instancetype)init __attribute__((unavailable("此方法已弃用,请使用initWithCollectionView:fetchResult方法")));
30 | + (instancetype)new __attribute__((unavailable("此方法已弃用,请使用initWithCollectionView:fetchResult方法")));
31 |
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/XYPhotoKit/DataSources/XYPhotoTableDataSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoTableDataSource.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoTableDataSource.h"
10 |
11 | #import "XYPhotoTableCell.h"
12 | #import "XYPhotoSelectedAssetManager.h"
13 | #import "XYPhotoDataCategory.h"
14 |
15 | @interface XYPhotoTableDataSource ()
16 |
17 | @property (nonatomic) UITableView *tableView;
18 | @property (nonatomic) NSArray *collectionFetchResults;
19 | @property (nonatomic) NSArray *> *collectionArrays;
20 |
21 | @end
22 |
23 | @implementation XYPhotoTableDataSource
24 |
25 | NS_ENUM(NSInteger, CLPhotoAlbumDataSourceType) {
26 | CLPhotoAlbumDataSourceTypeAlbums = 0,
27 | CLPhotoAlbumDataSourceTypeTopLevelUserCollections = 1,
28 |
29 | CLPhotoAlbumDataSourceTypeCount = 2
30 | };
31 |
32 | - (void)dealloc
33 | {
34 | [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
35 | }
36 |
37 | - (instancetype)initWithTableView:(UITableView *)tableView
38 | {
39 | if (self = [super init]) {
40 | _tableView = tableView;
41 | _tableView.dataSource = self;
42 | _tableView.delegate = self;
43 | _tableView.rowHeight = CLPhotoCollectionCellRowHeight;
44 | [_tableView registerClass:[XYPhotoTableCell class] forCellReuseIdentifier:XYPhotoTableCellReuseIdentifier];
45 |
46 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
47 | if (status == PHAuthorizationStatusAuthorized) {
48 |
49 | PHFetchResult *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
50 | subtype:PHAssetCollectionSubtypeAlbumRegular
51 | options:nil];
52 | //获取用户的相册集合
53 | PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
54 |
55 | self.collectionFetchResults = @[albums, topLevelUserCollections];
56 |
57 | [self updateCollectionArrays];
58 |
59 | dispatch_async(dispatch_get_main_queue(), ^{
60 | [tableView reloadData];
61 | });
62 |
63 | [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
64 | } else {
65 | [UIAlertController xy_showAlertPhotoSettingIfUnauthorized];
66 | }
67 | }];
68 | }
69 | return self;
70 | }
71 |
72 | - (void)updateCollectionArrays
73 | {
74 | PHFetchResult *albums = self.collectionFetchResults[CLPhotoAlbumDataSourceTypeAlbums];
75 | NSMutableArray *albumsWithAssetsArray = [NSMutableArray arrayWithCapacity:albums.count];
76 | [albums enumerateObjectsUsingBlock:^(PHAssetCollection *obj, NSUInteger idx, BOOL *stop) {
77 | PHFetchResult *assetsInCollection = [PHFetchResult xy_fetchResultWithAssetCollection:obj mediaType:[XYPhotoSelectedAssetManager sharedManager].mediaType];
78 | if (assetsInCollection.count > 0 && assetsInCollection.count < NSNotFound) {
79 | [albumsWithAssetsArray addObject:obj];
80 | }
81 | }];
82 |
83 | PHFetchResult *topLevelUserCollections = self.collectionFetchResults[CLPhotoAlbumDataSourceTypeTopLevelUserCollections];
84 | NSMutableArray *topLevelUserCollectionsWithAssetsArray = [NSMutableArray arrayWithCapacity:topLevelUserCollections.count];
85 | [topLevelUserCollections enumerateObjectsUsingBlock:^(PHCollection *obj, NSUInteger idx, BOOL *stop) {
86 | if ([obj isKindOfClass:[PHAssetCollection class]]) {
87 | PHFetchResult *assetsInCollection = [PHFetchResult xy_fetchResultWithAssetCollection:(PHAssetCollection *)obj mediaType:[XYPhotoSelectedAssetManager sharedManager].mediaType];
88 | if (assetsInCollection.count > 0 && assetsInCollection.count < NSNotFound) {
89 | [topLevelUserCollectionsWithAssetsArray addObject:obj];
90 | }
91 | }
92 | }];
93 |
94 | self.collectionArrays = @[albumsWithAssetsArray, topLevelUserCollectionsWithAssetsArray];
95 | }
96 |
97 | - (PHAssetCollection *)assetCollectionForIndexPath:(NSIndexPath *)indexPath
98 | {
99 | NSArray *fetchResults = self.collectionArrays[indexPath.section];
100 | return fetchResults[indexPath.row];
101 | }
102 |
103 | #pragma mark - PHPhotoLibraryChangeObserver
104 |
105 | - (void)photoLibraryDidChange:(PHChange *)changeInstance
106 | {
107 | dispatch_block_t dispatchBlock = ^{
108 | NSMutableArray *updatedCollectionsFetchResults = nil;
109 |
110 | for (PHFetchResult *collectionsFetchResult in self.collectionFetchResults) {
111 | if (collectionsFetchResult.count<=0) {
112 | continue;
113 | }
114 | PHFetchResultChangeDetails *changeDetails = [changeInstance changeDetailsForFetchResult:collectionsFetchResult];
115 | if (changeDetails) {
116 | if (!updatedCollectionsFetchResults) {
117 | updatedCollectionsFetchResults = [self.collectionFetchResults mutableCopy];
118 | }
119 | [updatedCollectionsFetchResults replaceObjectAtIndex:[self.collectionFetchResults indexOfObject:collectionsFetchResult] withObject:[changeDetails fetchResultAfterChanges]];
120 | }
121 | }
122 |
123 | if (updatedCollectionsFetchResults) {
124 | self.collectionFetchResults = updatedCollectionsFetchResults;
125 | [self updateCollectionArrays];
126 | [self.tableView reloadData];
127 | }
128 | };
129 |
130 | if ([NSThread currentThread] != [NSThread mainThread]) {
131 | dispatch_async(dispatch_get_main_queue(), dispatchBlock);
132 | } else {
133 | dispatchBlock();
134 | }
135 | }
136 |
137 | #pragma mark - UITableViewDataSource
138 |
139 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
140 | {
141 | return CLPhotoAlbumDataSourceTypeCount;
142 | }
143 |
144 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
145 | {
146 | return self.collectionArrays[section].count;
147 | }
148 |
149 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
150 | {
151 | XYPhotoTableCell *cell = [tableView dequeueReusableCellWithIdentifier:XYPhotoTableCellReuseIdentifier forIndexPath:indexPath];
152 | cell.collection = [self assetCollectionForIndexPath:indexPath];
153 | return cell;
154 | }
155 |
156 | #pragma mark - UITableViewDelegate
157 |
158 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
159 | {
160 | PHAssetCollection *assetCollection = [self assetCollectionForIndexPath:indexPath];
161 | if ([self.delegate respondsToSelector:@selector(assetTableDataSource:selectedAssetCollection:)]) {
162 | [self.delegate assetTableDataSource:self selectedAssetCollection:assetCollection];
163 | }
164 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
165 | }
166 |
167 | @end
168 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoAlbumDetailController.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoAlbumDetailController.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import Photos;
10 | @import UIKit;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface XYPhotoAlbumDetailController : UIViewController
15 |
16 | @property (nonatomic, readonly) UICollectionView *collectionView;
17 |
18 | - (instancetype)initWithFetchResult:(PHFetchResult *)fetchResult;
19 |
20 | - (instancetype)init __attribute__((unavailable("此方法已弃用,请使用initWithFetchResult方法")));
21 | + (instancetype)new __attribute__((unavailable("此方法已弃用,请使用initWithFetchResult方法")));
22 |
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoAlbumDetailController.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoAlbumDetailController.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoAlbumDetailController.h"
10 |
11 | #import "XYPhotoCollectionDataSource.h"
12 | #import "XYPhotoCollectionFlowLayout.h"
13 | #import "XYPhotoSelectedAssetPreviewView.h"
14 | #import "XYPhotoSelectedAssetManager.h"
15 | #import "XYPhotoMultiImagePicker.h"
16 | #import "XYPhotoPreviewViewController.h"
17 |
18 | @interface XYPhotoAlbumDetailController ()
19 |
20 | @property (nonatomic, strong) UICollectionView *collectionView;
21 | @property (nonatomic, strong) XYPhotoSelectedAssetPreviewView *bottomView;
22 |
23 | @property (nonatomic, strong) PHFetchResult *fetchResult;
24 | @property (nonatomic, strong) XYPhotoCollectionDataSource *dataSource;
25 |
26 | @end
27 |
28 | @implementation XYPhotoAlbumDetailController
29 |
30 | - (instancetype)initWithFetchResult:(PHFetchResult *)fetchResult
31 | {
32 | if (self = [super init]) {
33 | _fetchResult = fetchResult;
34 | }
35 | return self;
36 | }
37 |
38 | - (void)loadView
39 | {
40 | [super loadView];
41 |
42 | UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(close)];
43 | self.navigationItem.rightBarButtonItems = @[cancel];
44 |
45 | XYPhotoCollectionFlowLayout *layout = [[XYPhotoCollectionFlowLayout alloc] init];
46 | self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
47 | self.collectionView.translatesAutoresizingMaskIntoConstraints = false;
48 | self.collectionView.allowsMultipleSelection = true;
49 | [self.view addSubview:self.collectionView];
50 |
51 | self.bottomView = [[XYPhotoSelectedAssetPreviewView alloc] initWithFrame:self.view.frame];
52 | self.bottomView.translatesAutoresizingMaskIntoConstraints = false;
53 | self.bottomView.delegate = self;
54 | [self.view addSubview:self.bottomView];
55 |
56 | if (@available(iOS 13.0, *)) {
57 | self.collectionView.backgroundColor = [UIColor systemBackgroundColor];
58 | self.view.backgroundColor = [UIColor systemBackgroundColor];
59 | } else {
60 | self.collectionView.backgroundColor = [UIColor whiteColor];
61 | self.view.backgroundColor = [UIColor whiteColor];
62 | }
63 | }
64 |
65 | - (void)viewDidLoad
66 | {
67 | [super viewDidLoad];
68 | [self addConstrains];
69 |
70 | // 等loadView完成后调用
71 | self.dataSource = [[XYPhotoCollectionDataSource alloc] initWithCollectionView:self.collectionView fetchResult:self.fetchResult];
72 | self.dataSource.delegate = self;
73 | }
74 |
75 | - (void)viewDidAppear:(BOOL)animated
76 | {
77 | [super viewDidAppear:animated];
78 | self.dataSource.shouldCache = true;
79 | }
80 |
81 | - (void)addConstrains
82 | {
83 | BOOL isSingleSelected = [XYPhotoSelectedAssetManager sharedManager].maxNumberOfAssets == 1;
84 | self.bottomView.hidden = isSingleSelected;
85 |
86 | [NSLayoutConstraint activateConstraints:@[
87 | [_collectionView.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor],
88 | [_collectionView.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor],
89 | [_collectionView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
90 | [_collectionView.bottomAnchor constraintEqualToAnchor:self.bottomView.topAnchor],
91 | [_bottomView.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor],
92 | [_bottomView.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor],
93 | [_bottomView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
94 | [_bottomView.heightAnchor constraintEqualToConstant:isSingleSelected ? 0:60]
95 | ]];
96 | }
97 |
98 | - (void)close
99 | {
100 | XYPhotoMultiImagePicker *picker = (XYPhotoMultiImagePicker *)self.navigationController;
101 |
102 | if ([picker.pickerDelegate respondsToSelector:@selector(multiImagePickerDidCancel:)]) {
103 | [picker.pickerDelegate multiImagePickerDidCancel:picker];
104 | } else {
105 | [self.navigationController dismissViewControllerAnimated:YES completion:^{
106 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
107 | }];
108 | }
109 | }
110 |
111 | #pragma mark - XYPhotoSelectedAssetPreviewViewDelegate
112 |
113 | - (void)assetsSelectedPreviewView:(XYPhotoSelectedAssetPreviewView *)previewView didClickWithIndex:(NSInteger)index asset:(PHAsset *)asset;
114 | {
115 | XYPhotoPreviewViewController *previewController = [[XYPhotoPreviewViewController alloc] init];
116 | previewController.photos = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
117 | previewController.selectedIndex = index;
118 | [self.navigationController pushViewController:previewController animated:YES];
119 | }
120 |
121 | #pragma mark - XYPhotoCollectionDataSourceDelegate
122 |
123 | - (void)assetCollectionDataSource:(XYPhotoCollectionDataSource *)dataSource selectedIndex:(NSInteger)selectedIndex inFetchResult:(PHFetchResult *)fetchResult
124 | {
125 | XYPhotoPreviewViewController *previewController = [[XYPhotoPreviewViewController alloc] init];
126 | previewController.fetchResult = fetchResult;
127 | previewController.selectedIndex = selectedIndex;
128 | [self.navigationController pushViewController:previewController animated:YES];
129 | }
130 |
131 | @end
132 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoAlbumListController.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoAlbumListController.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 |
10 | @import Photos;
11 | @import UIKit;
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface XYPhotoAlbumListController : UIViewController
16 |
17 | @property (nonatomic, readonly) UITableView *tableView;
18 |
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoAlbumListController.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoAlbumListController.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoAlbumListController.h"
10 | #import "XYPhotoDataCategory.h"
11 | #import "XYPhotoTableDataSource.h"
12 | #import "XYPhotoAlbumDetailController.h"
13 | #import "XYPhotoSelectedAssetManager.h"
14 | #import "XYPhotoKitHelper.h"
15 | #import "XYPhotoMultiImagePicker.h"
16 |
17 | @interface XYPhotoAlbumListController ()
18 |
19 | @property (nonatomic, strong) UITableView *tableView;
20 | @property (nonatomic, strong) XYPhotoTableDataSource *dataSource;
21 |
22 | @end
23 |
24 | @implementation XYPhotoAlbumListController
25 |
26 | - (void)viewDidLoad
27 | {
28 | [super viewDidLoad];
29 |
30 | self.title = XYPhotoImagePickerName.selectAnAlbum;
31 | if (@available(iOS 13.0, *)) {
32 | self.view.backgroundColor = [UIColor systemBackgroundColor];
33 | } else {
34 | self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];
35 | }
36 | UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(close)];
37 | self.navigationItem.rightBarButtonItems = @[cancel];
38 |
39 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
40 | dispatch_async(dispatch_get_main_queue(), ^{
41 | if (status == PHAuthorizationStatusAuthorized) {
42 | [self initTableViewAndData];
43 | } else {
44 | [self showPhotoAccessHelpView];
45 | }
46 | });
47 | }];
48 | }
49 |
50 | - (void)initTableViewAndData
51 | {
52 | if (!self.tableView) {
53 | self.tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
54 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
55 | self.tableView.translatesAutoresizingMaskIntoConstraints = false;
56 | [self.view addSubview:self.tableView];
57 | }
58 |
59 | self.dataSource = [[XYPhotoTableDataSource alloc] initWithTableView:self.tableView];
60 | self.dataSource.delegate = self;
61 |
62 | [NSLayoutConstraint activateConstraints:@[
63 | [_tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
64 | [_tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
65 | [_tableView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
66 | [_tableView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
67 | ]];
68 | }
69 |
70 | - (void)showPhotoAccessHelpView
71 | {
72 | [UIAlertController xy_showAlertPhotoSettingIfUnauthorized];
73 | UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage xy_imageWithName:@"cl_photo_picker_inaccessible"]];
74 | imageView.translatesAutoresizingMaskIntoConstraints = false;
75 | imageView.contentMode = UIViewContentModeCenter;
76 | [self.view addSubview:imageView];
77 |
78 | [NSLayoutConstraint activateConstraints:@[
79 | [imageView.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor],
80 | [imageView.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor],
81 | [imageView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
82 | [imageView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
83 | ]];
84 | }
85 |
86 | - (void)close
87 | {
88 | XYPhotoMultiImagePicker *picker = (XYPhotoMultiImagePicker *)self.navigationController;
89 |
90 | if ([picker.pickerDelegate respondsToSelector:@selector(multiImagePickerDidCancel:)]) {
91 | [picker.pickerDelegate multiImagePickerDidCancel:picker];
92 | } else {
93 | [self.navigationController dismissViewControllerAnimated:YES completion:^{
94 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
95 | }];
96 | }
97 | }
98 |
99 | #pragma mark - XYPhotoTableDataSourceDelegate
100 |
101 | - (void)assetTableDataSource:(XYPhotoTableDataSource *)dataSource selectedAssetCollection:(PHAssetCollection *)assetCollection
102 | {
103 | PHFetchResult *fetchResult = [PHFetchResult xy_fetchResultWithAssetCollection:assetCollection mediaType:XYPhotoSelectedAssetManager.sharedManager.mediaType];
104 | XYPhotoAlbumDetailController *assetsViewController = [[XYPhotoAlbumDetailController alloc] initWithFetchResult:fetchResult];
105 | assetsViewController.title = assetCollection.localizedTitle;
106 | [self.navigationController pushViewController:assetsViewController animated:YES];
107 | }
108 |
109 | @end
110 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoCameraController.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCameraController.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 13/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface XYPhotoCameraController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoCameraController.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCameraController.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 13/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoCameraController.h"
10 | #import "XYPhotoCameraOverlayView.h"
11 | #import "XYPhotoDataCategory.h"
12 | #import "XYPhotoPreviewViewController.h"
13 | #import "XYPhotoMultiCameraPicker.h"
14 | #import "XYPhotoSelectedAssetManager.h"
15 |
16 | @interface XYPhotoCameraController ()
17 |
18 | @property (nonatomic, strong) UIImagePickerController *imagePicker;
19 | @property (nonatomic, strong) XYPhotoCameraOverlayView *overlayView;
20 |
21 | @end
22 |
23 | @implementation XYPhotoCameraController
24 |
25 | - (BOOL)shouldAutorotate
26 | {
27 | return false;
28 | }
29 |
30 | - (BOOL)prefersStatusBarHidden
31 | {
32 | return YES;
33 | }
34 |
35 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations
36 | {
37 | return UIInterfaceOrientationMaskPortrait;
38 | }
39 |
40 | - (void)loadView
41 | {
42 | [super loadView];
43 | self.view.backgroundColor = [UIColor blackColor];
44 |
45 | if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
46 | if ([UIAlertController xy_showAlertCameraSettingIfUnauthorized]) {
47 | self.imagePicker = [[UIImagePickerController alloc] init];
48 | self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
49 | self.imagePicker.delegate = self;
50 | self.imagePicker.showsCameraControls = false;
51 | self.imagePicker.view.translatesAutoresizingMaskIntoConstraints = false;
52 | [self.view addSubview:self.imagePicker.view];
53 | [self addChildViewController:self.imagePicker];
54 | [self.imagePicker didMoveToParentViewController:self];
55 |
56 | [NSLayoutConstraint activateConstraints:@[
57 | [_imagePicker.view.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
58 | [_imagePicker.view.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
59 | [_imagePicker.view.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
60 | [_imagePicker.view.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
61 | ]];
62 | }
63 | } else {
64 | [UIAlertController xy_showTitle:nil message:@"该设备不支持相机功能"];
65 | }
66 |
67 | _overlayView = [[XYPhotoCameraOverlayView alloc] initWithFrame:self.view.bounds];
68 | _overlayView.translatesAutoresizingMaskIntoConstraints = false;
69 | _overlayView.delegate = self;
70 | _overlayView.flashMode = UIImagePickerControllerCameraFlashModeAuto;
71 | _overlayView.cameraDeivce = UIImagePickerControllerCameraDeviceRear;
72 | [self.view addSubview:_overlayView];
73 |
74 | [NSLayoutConstraint activateConstraints:@[
75 | [_overlayView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
76 | [_overlayView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
77 | [_overlayView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
78 | [_overlayView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
79 | ]];
80 | }
81 |
82 | - (void)viewWillAppear:(BOOL)animated
83 | {
84 | [super viewWillAppear:animated];
85 | [self.navigationController setNavigationBarHidden:YES animated:animated];
86 | }
87 |
88 |
89 | #pragma mark - UIImagePickerControllerDelegate
90 |
91 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
92 | {
93 | [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
94 | [PHAssetChangeRequest creationRequestForAssetFromImage:[info valueForKey:UIImagePickerControllerOriginalImage]];
95 | } completionHandler:^(BOOL success, NSError * _Nullable error) {
96 | if (success) {
97 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
98 | PHAsset *asset = [PHAsset xy_getTheCloestAsset];
99 | dispatch_async(dispatch_get_main_queue(), ^{
100 | XYPhotoSelectedAssetManager *sharedManager = XYPhotoSelectedAssetManager.sharedManager;
101 | if (sharedManager.maxNumberOfAssets == 1) {
102 | [sharedManager resetSelectedAsset:@[asset]];
103 | [self finishedCameraOverlayView:nil];
104 | } else {
105 | [sharedManager addSelectedAsset:asset];
106 | }
107 | });
108 | });
109 | } else {
110 | dispatch_async(dispatch_get_main_queue(), ^{
111 | [UIAlertController xy_showTitle:nil message:@"获取照片失败"];
112 | });
113 | }
114 | }];
115 | }
116 |
117 | #pragma mark - XYPhotoCameraOverlayViewDelegate
118 |
119 | - (void)cameraOverlayView:(XYPhotoCameraOverlayView *)overlayView didShutted:(UIButton *)sender
120 | {
121 | // 限制保护
122 | XYPhotoSelectedAssetManager *manager = [XYPhotoSelectedAssetManager sharedManager];
123 | if (manager.maxNumberOfAssets != 0 && manager.maxNumberOfAssets != 1 && manager.maxNumberOfAssets <= manager.selectedAssets.count) {
124 | NSString *tip = (manager.maxNumberLimitText ?: @"").xy_isEmpty ? @"已达到最大照片数限制" : manager.maxNumberLimitText;
125 | [UIAlertController xy_showTitle:@"友情提示" message:tip];
126 | return;
127 | }
128 |
129 | [_imagePicker takePicture];
130 |
131 | UIView *flashView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
132 | if (@available(iOS 13.0, *)) {
133 | [flashView setBackgroundColor:[UIColor systemBackgroundColor]];
134 | } else {
135 | [flashView setBackgroundColor:[UIColor whiteColor]];
136 | }
137 | [self.view insertSubview:flashView belowSubview:_overlayView];
138 | [UIView animateWithDuration:.4f animations:^{
139 | [flashView setAlpha:0.f];
140 | } completion:^(BOOL finished){
141 | [flashView removeFromSuperview];
142 | }];
143 | }
144 |
145 | - (void)cameraOverlayView:(XYPhotoCameraOverlayView *)overlayView didSwitchedCameraDeivce:(UIImagePickerControllerCameraDevice)cameraDeivce
146 | {
147 | [_imagePicker setCameraDevice:cameraDeivce];
148 | }
149 |
150 | - (void)cameraOverlayView:(XYPhotoCameraOverlayView *)overlayView didSwitchedFlashMode:(UIImagePickerControllerCameraFlashMode)flashMode
151 | {
152 | [_imagePicker setCameraFlashMode:flashMode];
153 | }
154 |
155 | - (void)canceledCameraOverlayView:(XYPhotoCameraOverlayView *)overlayView
156 | {
157 | XYPhotoMultiCameraPicker *picker = (XYPhotoMultiCameraPicker *)self.navigationController;
158 |
159 | if ([picker.pickerDelegate respondsToSelector:@selector(multiCameraPickerDidCancel:)]) {
160 | [picker.pickerDelegate multiCameraPickerDidCancel:picker];
161 | } else {
162 | [self dismissViewControllerAnimated:YES completion:^{
163 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
164 | }];
165 | }
166 | }
167 |
168 | - (void)finishedCameraOverlayView:(XYPhotoCameraOverlayView *)overlayView
169 | {
170 | if ([[XYPhotoSelectedAssetManager sharedManager].delegate respondsToSelector:@selector(doneSelectingAssets)]) {
171 | [[XYPhotoSelectedAssetManager sharedManager].delegate doneSelectingAssets];
172 | }
173 | }
174 |
175 | - (void)assetsSelectedPreviewView:(XYPhotoSelectedAssetPreviewView *)previewView didClickWithIndex:(NSInteger)index asset:(PHAsset *)asset
176 | {
177 | XYPhotoPreviewViewController *previewController = [[XYPhotoPreviewViewController alloc] init];
178 | previewController.photos = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
179 | previewController.selectedIndex = index;
180 | [self.navigationController pushViewController:previewController animated:YES];
181 | }
182 |
183 |
184 | @end
185 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoMultiCameraPicker.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoMultiCameraPicker.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 08/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import Photos;
10 | @import UIKit;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @class XYPhotoMultiCameraPicker;
15 | @protocol XYPhotoMultiCameraPickerDelegate
16 |
17 | @required
18 | /**
19 | 代理完成选择Asset
20 |
21 | @param multiImagePicker 导航栏控制器
22 | @param assets 已选中的Asset
23 | */
24 | - (void)multiCameraPicker:(XYPhotoMultiCameraPicker *)multiImagePicker selectedAssets:(NSArray *)assets;
25 |
26 | @optional
27 | /**
28 | 选择Asset的时候,如果delegate有定义此方法,则导航栏右侧点击cancel时回调此函数
29 | **注意:** 需要对 `XYPhotoSelectedAssetManager` 进行 resetManager 操作
30 | 可以选择不实现该函数,则自动完成dimiss和resetManager
31 |
32 | @param multiImagePicker 导航栏控制器
33 | */
34 | - (void)multiCameraPickerDidCancel:(XYPhotoMultiCameraPicker *)multiImagePicker;
35 |
36 | @end
37 |
38 | @protocol XYPhotoMultiCameraPickerDataSource
39 |
40 | /**
41 | 提前载入预设选中的Asset
42 |
43 | @return 已选中的Asset
44 | */
45 | - (nullable NSArray *)multiCameraPickerLoadPresetSelectedAssets;
46 | @end
47 |
48 |
49 | @interface XYPhotoMultiCameraPicker : UINavigationController
50 |
51 | /**
52 | 最大选择数量
53 | Defaults 0
54 | */
55 | @property (nonatomic) NSUInteger maxNumberOfAssets;
56 |
57 | /**
58 | 是否允许网络请求从iCloud下载
59 | Default NO
60 | */
61 | @property (nonatomic) BOOL allowNetRequestIfiCloud;
62 |
63 | @property (nonatomic, weak, nullable) idpickerDataSource;
64 | @property (nonatomic, weak, nullable) idpickerDelegate;
65 |
66 | @end
67 |
68 | NS_ASSUME_NONNULL_END
69 |
70 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoMultiCameraPicker.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoMultiCameraPicker.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 08/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoMultiCameraPicker.h"
10 | #import "XYPhotoSelectedAssetManager.h"
11 | #import "XYPhotoCameraController.h"
12 |
13 | @interface XYPhotoMultiCameraPicker ()
14 |
15 | @end
16 |
17 | @implementation XYPhotoMultiCameraPicker
18 |
19 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
20 | {
21 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
22 | self.allowNetRequestIfiCloud = false;
23 | self.modalPresentationStyle = UIModalPresentationFullScreen;
24 | [self setViewControllers:@[[[XYPhotoCameraController alloc] init]] animated:NO];
25 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
26 | [XYPhotoSelectedAssetManager sharedManager].mediaType = PHAssetMediaTypeImage;
27 | [XYPhotoSelectedAssetManager sharedManager].delegate = self;
28 | }
29 | return self;
30 | }
31 |
32 | - (void)viewDidLoad {
33 | [super viewDidLoad];
34 |
35 | // 不能在初始化的时候调用此方法,需XYPhotoSelectedAssetManager的各个参数设置完毕后执行,即set方法执行完毕
36 | if ([self.pickerDataSource respondsToSelector:@selector(multiCameraPickerLoadPresetSelectedAssets)]) {
37 | [[XYPhotoSelectedAssetManager sharedManager] resetSelectedAsset:[self.pickerDataSource multiCameraPickerLoadPresetSelectedAssets]];
38 | }
39 | }
40 |
41 | - (void)setMaxNumberOfAssets:(NSUInteger)maxNumberOfAssets
42 | {
43 | _maxNumberOfAssets = maxNumberOfAssets;
44 | [XYPhotoSelectedAssetManager sharedManager].maxNumberOfAssets = maxNumberOfAssets;
45 | }
46 |
47 | - (void)setAllowNetRequestIfiCloud:(BOOL)allowNetRequestIfiCloud
48 | {
49 | _allowNetRequestIfiCloud = allowNetRequestIfiCloud;
50 | [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud = allowNetRequestIfiCloud;
51 | }
52 |
53 | #pragma mark - XYPhotoSelectedAssetManagerDelegate
54 |
55 | - (void)doneSelectingAssets
56 | {
57 | if ([self.pickerDelegate respondsToSelector:@selector(multiCameraPicker:selectedAssets:)]) {
58 | [self.pickerDelegate multiCameraPicker:self selectedAssets:[XYPhotoSelectedAssetManager sharedManager].selectedAssets];
59 | }
60 |
61 | [self dismissViewControllerAnimated:YES completion:^{
62 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
63 | }];
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoMultiImagePicker.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoKitDemo.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | @import Photos;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | typedef NS_ENUM(NSInteger, CLPhotoMultiPickerStartPosition){
15 | // 展示相册列表 tableView,可以push到collectionView
16 | CLPhotoMultiPickerStartPositionAlbums,
17 | // 展示全部照片 collectionView,可以pop到 tableview
18 | CLPhotoMultiPickerStartPositionCameraRoll,
19 | };
20 |
21 | @class XYPhotoMultiImagePicker;
22 | @protocol XYPhotoMultiImagePickerDelegate
23 |
24 | @required
25 |
26 | /**
27 | 代理完成选择Asset
28 |
29 | @param multiImagePicker 导航栏控制器
30 | @param assets 已选中的Asset
31 | */
32 | - (void)multiImagePicker:(XYPhotoMultiImagePicker *)multiImagePicker selectedAssets:(NSArray *)assets;
33 |
34 | @optional
35 |
36 | /**
37 | 选择Asset的时候,如果delegate有定义此方法,则导航栏右侧点击cancel时回调此函数
38 | **注意:** 需要对 `XYPhotoSelectedAssetManager` 进行 resetManager 操作
39 | 可以选择不实现该函数,则自动完成dimiss和resetManager
40 |
41 | @param multiImagePicker 导航栏控制器
42 | */
43 | - (void)multiImagePickerDidCancel:(XYPhotoMultiImagePicker *)multiImagePicker;
44 | @end
45 |
46 | @protocol XYPhotoMultiImagePickerDataSource
47 |
48 | /**
49 | 提前载入预设选中的Asset
50 |
51 | @return 已选中的Asset
52 | */
53 | - (NSArray *)multiImagePickerLoadPresetSelectedAssets;
54 |
55 | @optional
56 | /**
57 | 没有相册授权时,展示的提示图片
58 |
59 | @return 展示的图片
60 | */
61 | - (nullable UIImage *)multiImagePickerUnauthorizedPlaceHolderImage;
62 |
63 | @end
64 |
65 | @interface XYPhotoMultiImagePicker : UINavigationController
66 |
67 | /**
68 | 代理回调已选中的asset
69 | */
70 | @property (nonatomic, weak, nullable) id pickerDelegate;
71 |
72 | /**
73 | 传入默认的初始化资源
74 | */
75 | @property (nonatomic, weak, nullable) id pickerDataSource;
76 |
77 | /**
78 | * PHAssetMediaTypeUnknown - 全部展示.
79 | * PHAssetMediaTypeImage - 只展示图片.
80 | * PHAssetMediaTypeVideo - 只展示视频.
81 | * PHAssetMediaTypeAudio - 只展示音频.
82 | */
83 | @property (nonatomic) PHAssetMediaType mediaType;
84 |
85 | /**
86 | 是否允许网络请求从iCloud下载
87 | Default NO
88 | */
89 | @property (nonatomic) BOOL allowNetRequestIfiCloud;
90 |
91 | /**
92 | 两种选择,一种直接选择照片,一种进入相册集列表再选择
93 | */
94 | @property (nonatomic) CLPhotoMultiPickerStartPosition startPosition;
95 |
96 | /**
97 | collectionView一行的collectionCell数量
98 | Default 3.
99 | */
100 | @property (nonatomic) NSInteger assetCollectionViewColumnCount;
101 |
102 | /**
103 | 最大选择数量
104 | Defaults 0
105 | */
106 | @property (nonatomic) NSUInteger maxNumberOfAssets;
107 |
108 | //+ (void)showImagePickerWithConfiger:(void(^)(XYPhotoMultiImagePicker *picker))configer successBlock:(void(^)(NSArray *selectedAsset))successBlock;
109 |
110 | @end
111 |
112 | NS_ASSUME_NONNULL_END
113 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoMultiImagePicker.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoKitDemo.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoMultiImagePicker.h"
10 |
11 | #import "XYPhotoDataCategory.h"
12 | #import "XYPhotoAlbumListController.h"
13 | #import "XYPhotoCollectionViewCell.h"
14 | #import "XYPhotoAlbumDetailController.h"
15 | #import "XYPhotoKitHelper.h"
16 | #import "XYPhotoSelectedAssetManager.h"
17 |
18 | @interface XYPhotoMultiImagePicker ()
19 |
20 | @end
21 |
22 | @implementation XYPhotoMultiImagePicker
23 |
24 | #pragma mark - Initialization
25 |
26 | - (instancetype)init
27 | {
28 | if (self = [super init]) {
29 | self.modalPresentationStyle = UIModalPresentationFullScreen;
30 | self.allowNetRequestIfiCloud = false;
31 | [self setViewControllers:@[[[XYPhotoAlbumListController alloc] init]] animated:NO];
32 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
33 | [XYPhotoSelectedAssetManager sharedManager].delegate = self;
34 | }
35 | return self;
36 | }
37 |
38 | #pragma mark - View Lifecycle
39 |
40 | - (void)viewDidLoad
41 | {
42 | [super viewDidLoad];
43 |
44 | if ([XYPhotoKitHelper isAvailableAccessPhoto]) {
45 | [self setupView];
46 | }
47 |
48 | // 不能在初始化的时候调用此方法,需XYPhotoSelectedAssetManager的各个参数设置完毕后执行,即set方法执行完毕
49 | if ([self.pickerDataSource respondsToSelector:@selector(multiImagePickerLoadPresetSelectedAssets)]) {
50 | [[XYPhotoSelectedAssetManager sharedManager] resetSelectedAsset:[self.pickerDataSource multiImagePickerLoadPresetSelectedAssets]];
51 | }
52 | }
53 |
54 | - (void)dealloc
55 | {
56 | [[NSNotificationCenter defaultCenter] removeObserver:self];
57 | [XYPhotoSelectedAssetManager sharedManager].delegate = nil;
58 | }
59 |
60 | #pragma mark - Setup
61 |
62 | - (void)setupView
63 | {
64 | if (self.startPosition == CLPhotoMultiPickerStartPositionCameraRoll) {
65 | PHFetchResult *fetchResult = [PHFetchResult xy_fetchResultWithAssetsOfType:self.mediaType];
66 | XYPhotoAlbumDetailController *cameraRollViewController = [[XYPhotoAlbumDetailController alloc] initWithFetchResult:fetchResult];
67 | cameraRollViewController.title = XYPhotoImagePickerName.cameraRoll;
68 | NSMutableArray *vcs = self.viewControllers.mutableCopy;
69 | [vcs addObject:cameraRollViewController];
70 | [self setViewControllers:vcs animated:NO];
71 | }
72 | }
73 |
74 | #pragma mark - Values passed to the manager.
75 |
76 | - (void)setMaxNumberOfAssets:(NSUInteger)maxNumberOfAssets
77 | {
78 | _maxNumberOfAssets = maxNumberOfAssets;
79 | [XYPhotoSelectedAssetManager sharedManager].maxNumberOfAssets = maxNumberOfAssets;
80 | }
81 |
82 | - (void)setMediaType:(PHAssetMediaType)mediaType
83 | {
84 | _mediaType = mediaType;
85 | [XYPhotoSelectedAssetManager sharedManager].mediaType = mediaType;
86 | }
87 |
88 | - (void)setAssetCollectionViewColumnCount:(NSInteger)assetCollectionViewColumnCount
89 | {
90 | _assetCollectionViewColumnCount = assetCollectionViewColumnCount;
91 | [XYPhotoSelectedAssetManager sharedManager].assetCollectionViewColumnCount = assetCollectionViewColumnCount;
92 | }
93 |
94 | - (void)setAllowNetRequestIfiCloud:(BOOL)allowNetRequestIfiCloud
95 | {
96 | _allowNetRequestIfiCloud = allowNetRequestIfiCloud;
97 | [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud = allowNetRequestIfiCloud;
98 | }
99 |
100 | #pragma mark - XYPhotoSelectedAssetManagerDelegate
101 |
102 | - (void)doneSelectingAssets
103 | {
104 | if ([self.pickerDelegate respondsToSelector:@selector(multiImagePicker:selectedAssets:)]) {
105 | [self.pickerDelegate multiImagePicker:self selectedAssets:[[XYPhotoSelectedAssetManager sharedManager] selectedAssets]];
106 | }
107 | [self dismissViewControllerAnimated:YES completion:^{
108 | [[XYPhotoSelectedAssetManager sharedManager] resetManager];
109 | }];
110 | }
111 |
112 | @end
113 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoPreviewViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoPreviewViewController.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 31/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 |
10 | @import Photos;
11 | @import UIKit;
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface XYPhotoPreviewViewController : UIViewController
16 |
17 | /**
18 | 查看已选择的asset时,使用该方法
19 | */
20 | @property (nonatomic, strong, nullable) NSArray *photos;
21 |
22 | /**
23 | 大图查看某个相册集时,使用该方法;
24 | 同时,监听原生相册资源变化时更新fetchResult
25 | */
26 | @property (nonatomic, strong, nullable) PHFetchResult *fetchResult;
27 | // 横向scrollview的内容
28 | @property (nonatomic, assign) NSInteger selectedIndex;
29 |
30 | @end
31 |
32 | NS_ASSUME_NONNULL_END
33 |
--------------------------------------------------------------------------------
/XYPhotoKit/ViewControllers/XYPhotoPreviewViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoPreviewViewController.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 31/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoPreviewViewController.h"
10 | #import "XYPhotoPreviewOverlayView.h"
11 | #import "XYPhotoHorizontalScrollItemView.h"
12 |
13 | @interface XYPhotoPreviewViewController ()
14 |
15 | @property (nonatomic, strong) XYPhotoHorizontalScrollView *scrollView;
16 | @property (nonatomic, strong) XYPhotoPreviewOverlayView *overLayView;
17 | @property (nonatomic, assign) BOOL navigationBarHiddenBeforeEnter;
18 |
19 | @end
20 |
21 | @implementation XYPhotoPreviewViewController
22 |
23 | - (void)dealloc
24 | {
25 | [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
26 | }
27 |
28 | - (void)setFetchResult:(PHFetchResult *)fetchResult
29 | {
30 | _fetchResult = fetchResult;
31 | // 查看某个相册大图,则需要观察lib变化
32 | if (fetchResult) {
33 | [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
34 | }
35 | }
36 |
37 | - (void)setSelectedIndex:(NSInteger)selectedIndex
38 | {
39 | _selectedIndex = selectedIndex;
40 | if (_scrollView) {
41 | [_scrollView setCurrentIndex:_selectedIndex animated:NO];
42 | [_overLayView updateSelectedAsset:_fetchResult ? _fetchResult[selectedIndex]:_photos[selectedIndex]];
43 | [_overLayView updateTitleAtIndex:_selectedIndex sum:_fetchResult ? _fetchResult.count:_photos.count];
44 | }
45 | }
46 |
47 | - (BOOL)shouldAutorotate
48 | {
49 | return NO;
50 | }
51 |
52 | - (BOOL)prefersStatusBarHidden {
53 | return YES;
54 | }
55 |
56 | - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
57 | return UIStatusBarAnimationFade;
58 | }
59 |
60 | - (void)loadView
61 | {
62 | [super loadView];
63 | self.view.backgroundColor = [UIColor blackColor];
64 | self.view.clipsToBounds = true;
65 |
66 | _scrollView = [[XYPhotoHorizontalScrollView alloc] initWithFrame:CGRectMake(-5, 0, self.view.frame.size.width+10, self.view.frame.size.height)];
67 | _scrollView.translatesAutoresizingMaskIntoConstraints = false;
68 | _scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
69 | _scrollView.backgroundColor = [UIColor blackColor];
70 | _scrollView.horizontalDelegate = self;
71 | _scrollView.horizontalDataSource = self;
72 | [self.view addSubview:_scrollView];
73 |
74 | _overLayView = [[XYPhotoPreviewOverlayView alloc] initWithFrame:self.view.bounds];
75 | _overLayView.translatesAutoresizingMaskIntoConstraints = false;
76 | _overLayView.delegate = self;
77 | [self.view addSubview:_overLayView];
78 |
79 | [NSLayoutConstraint activateConstraints:@[
80 | [_scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:-5],
81 | [_scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:5],
82 | [_scrollView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
83 | [_scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
84 | [_overLayView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
85 | [_overLayView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
86 | [_overLayView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
87 | [_overLayView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
88 | ]];
89 | }
90 |
91 | - (void)viewWillAppear:(BOOL)animated
92 | {
93 | [super viewWillAppear:animated];
94 | _navigationBarHiddenBeforeEnter = self.navigationController.isNavigationBarHidden;
95 | [self.navigationController setNavigationBarHidden:YES animated:animated];
96 | }
97 |
98 | - (void)viewWillDisappear:(BOOL)animated
99 | {
100 | [super viewWillDisappear:animated];
101 | [self.navigationController setNavigationBarHidden:_navigationBarHiddenBeforeEnter animated:animated];
102 | }
103 |
104 | - (void)viewDidLoad
105 | {
106 | [super viewDidLoad];
107 | [_scrollView reloadData];
108 | [self setSelectedIndex:_selectedIndex];
109 | }
110 |
111 | #pragma mark - BPHorizontalScrollViewDataSource & BPHorizontalScrollViewDelegate
112 | - (XYHorizontalScrollItemView *)horizontalScrollView:(XYPhotoHorizontalScrollView *)scroller itemViewForIndex:(NSInteger)index
113 | {
114 | XYPhotoHorizontalScrollItemView *view = (XYPhotoHorizontalScrollItemView *)[scroller dequeueReusableItemView];
115 | if (!view) {
116 | view = [[XYPhotoHorizontalScrollItemView alloc] initWithFrame:scroller.bounds];
117 | view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
118 | view.photokitDelegate = self;
119 | }
120 | view.frame = scroller.bounds;
121 | view.asset = _fetchResult ? _fetchResult[index] : _photos[index];
122 | return (XYHorizontalScrollItemView *)view;
123 | }
124 |
125 | - (NSInteger)numberOfItems
126 | {
127 | return _fetchResult ? _fetchResult.count : _photos.count;
128 | }
129 |
130 | - (void)horizontalScrollView:(XYPhotoHorizontalScrollView *)scroller didSelectIndex:(NSInteger)index
131 | {
132 | _selectedIndex = index;
133 | PHAsset *asset = _fetchResult ? _fetchResult[index] : _photos[index];
134 | [_overLayView updateSelectedAsset:asset];
135 | [_overLayView updateTitleAtIndex:_selectedIndex sum:_fetchResult ? _fetchResult.count:_photos.count];
136 | }
137 |
138 | #pragma mark - PHPhotoLibraryChangeObserver
139 |
140 | - (void)photoLibraryDidChange:(PHChange *)changeInstance
141 | {
142 | // 后台线程更新检查
143 | dispatch_async(dispatch_get_main_queue(), ^{
144 |
145 | // assets 被修改了 (增、删、改)
146 | PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.fetchResult];
147 | if (collectionChanges) {
148 | self.fetchResult = [collectionChanges fetchResultAfterChanges];
149 | if (self.selectedIndex>=self.fetchResult.count) {
150 | self.selectedIndex = self.fetchResult.count-1;
151 | }
152 | [self.overLayView updateSelectedAsset:self.fetchResult[self.selectedIndex]];
153 | [self.scrollView reloadData];
154 | self.scrollView.currentIndex = self.selectedIndex;
155 | }
156 | });
157 | }
158 |
159 | #pragma mark - XYPhotoPreviewOverlayViewDelegate
160 |
161 | - (void)previewOverlayView:(XYPhotoPreviewOverlayView *)view closeBarButtonItemClick:(UIBarButtonItem *)barbuttonItem
162 | {
163 | [self.navigationController popViewControllerAnimated:YES];
164 | }
165 |
166 | #pragma mark - XYPhotoSelectedAssetPreviewViewDelegate
167 |
168 | - (void)assetsSelectedPreviewView:(XYPhotoSelectedAssetPreviewView *)previewView didClickWithIndex:(NSInteger)index asset:(PHAsset *)asset
169 | {
170 | if (_fetchResult) {
171 | [_fetchResult enumerateObjectsUsingBlock:^(PHAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
172 | if ([obj.localIdentifier isEqualToString:asset.localIdentifier]) {
173 | [self setSelectedIndex:idx];
174 | *stop = true;
175 | }
176 | }];
177 | } else if ([_photos containsObject:asset]) {
178 | [self setSelectedIndex:[_photos indexOfObject:asset]];
179 | }
180 | }
181 |
182 | #pragma mark - XYPhotoHorizontalScrollItemView
183 |
184 | - (void)didTapped:(XYPhotoHorizontalScrollItemView *)scrollItemView;
185 | {
186 | [UIView animateWithDuration:0.3 animations:^{
187 | self.overLayView.alpha = self.overLayView.alpha<=0 ? 1:0;
188 | }];
189 | }
190 |
191 | @end
192 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoBottomPreviewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoBottomPreviewCell.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 01/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | @import Photos;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface XYPhotoBottomPreviewCell : UICollectionViewCell
15 |
16 | @property (nonatomic, readonly) PHAsset *asset;
17 | @property(nonatomic, readonly) UIImageView *imageView;
18 |
19 | - (void)setAsset:(PHAsset *)asset indexPath:(NSIndexPath *)indexPath collectionView:(UICollectionView *)collectionView;
20 |
21 | @end
22 |
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoBottomPreviewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoBottomPreviewCell.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 01/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoBottomPreviewCell.h"
10 | #import "XYPhotoKitHelper.h"
11 | #import "XYPhotoSelectedAssetManager.h"
12 | #import "XYPhotoDataCategory.h"
13 |
14 | @interface XYPhotoBottomPreviewCell()
15 |
16 | @property (nonatomic, strong) PHImageManager *imageManager;
17 |
18 | @property (nonatomic, assign) PHImageRequestID imageRequestId;
19 |
20 | @end
21 |
22 | @implementation XYPhotoBottomPreviewCell
23 |
24 | - (instancetype)initWithFrame:(CGRect)frame
25 | {
26 | if (self = [super initWithFrame:frame]) {
27 | _imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds];
28 | _imageView.contentMode = UIViewContentModeScaleAspectFill;
29 | _imageView.image = [UIImage xy_imageWithName:@"cl_photokit_placeholder"];
30 | _imageView.clipsToBounds = true;
31 | if (@available(iOS 13.0, *)) {
32 | _imageView.backgroundColor = [UIColor systemBackgroundColor];
33 | } else {
34 | _imageView.backgroundColor = [UIColor whiteColor];
35 | }
36 | [self.contentView addSubview:_imageView];
37 |
38 | self.imageManager = [[PHCachingImageManager alloc] init];
39 | self.layer.borderColor = [UIColor lightGrayColor].CGColor;
40 | self.layer.borderWidth = 1.0/[UIScreen mainScreen].scale;
41 | }
42 | return self;
43 | }
44 |
45 | - (void)layoutSubviews
46 | {
47 | [super layoutSubviews];
48 | _imageView.frame = self.contentView.bounds;
49 | }
50 |
51 | - (void)prepareForReuse
52 | {
53 | [super prepareForReuse];
54 | self.highlighted = false;
55 | self.imageView.image = [UIImage xy_imageWithName:@"cl_photokit_placeholder"];
56 | if (_imageRequestId!=0) {
57 | [self.imageManager cancelImageRequest:_imageRequestId];
58 | }
59 | }
60 |
61 | - (void)setHighlighted:(BOOL)highlighted
62 | {
63 | [super setHighlighted:highlighted];
64 | self.layer.borderColor = highlighted ? [UIColor systemBlueColor].CGColor : [UIColor lightGrayColor].CGColor;
65 | }
66 |
67 | - (void)setAsset:(PHAsset *)asset indexPath:(NSIndexPath *)indexPath collectionView:(UICollectionView *)collectionView
68 | {
69 | _asset = asset;
70 |
71 | UICollectionViewLayoutAttributes *cellAttributes = [collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
72 |
73 | // 防止复用时加载延时图片展示多次
74 | if (_imageRequestId!=0) {
75 | [self.imageManager cancelImageRequest:_imageRequestId];
76 | }
77 |
78 | PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
79 | options.networkAccessAllowed = [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud;
80 |
81 | _imageRequestId = [self.imageManager requestImageForAsset:_asset
82 | targetSize:CGSizeScale(cellAttributes.size, [UIScreen mainScreen].scale)
83 | contentMode:PHImageContentModeAspectFill
84 | options:options
85 | resultHandler:^(UIImage *result, NSDictionary *info) {
86 | NSInteger imageRequestId = [[info objectForKey:PHImageResultRequestIDKey] integerValue];
87 | if (imageRequestId>0) {
88 | self.imageRequestId = 0;
89 | }
90 | self.imageView.image = result ?: [UIImage xy_imageWithName:@"cl_photokit_placeholder"];
91 | }];
92 | }
93 |
94 | @end
95 |
96 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoCameraOverlayView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCameraOverlayView.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 11/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoSelectedAssetPreviewView.h"
10 |
11 | @import Photos;
12 |
13 | @class XYPhotoCameraOverlayView;
14 | @protocol XYPhotoCameraOverlayViewDelegate
15 |
16 | /**
17 | 快门按键点击
18 |
19 | @param sender 快门按钮
20 | */
21 | - (void)cameraOverlayView:(XYPhotoCameraOverlayView *)overlayView didShutted:(UIButton *)sender;
22 |
23 | /**
24 | 摄像头翻转切换前后镜头
25 | @param cameraDeivce 前后镜头枚举
26 | */
27 | - (void)cameraOverlayView:(XYPhotoCameraOverlayView *)overlayView didSwitchedCameraDeivce:(UIImagePickerControllerCameraDevice)cameraDeivce;
28 |
29 | /**
30 | 闪光灯控制,自动、开、关
31 | 注意:关闭则照相时是都不会开启软件硬件闪光灯(软件指的6s以后的闪屏补光)
32 | @param flashMode 闪光灯枚举
33 | */
34 | - (void)cameraOverlayView:(XYPhotoCameraOverlayView *)overlayView didSwitchedFlashMode:(UIImagePickerControllerCameraFlashMode)flashMode;
35 |
36 | /**
37 | 取消,关闭照相取图
38 | */
39 | - (void)canceledCameraOverlayView:(XYPhotoCameraOverlayView *)overlayView;
40 |
41 | /**
42 | 完成照相去相片
43 | */
44 | - (void)finishedCameraOverlayView:(XYPhotoCameraOverlayView *)overlayView;
45 |
46 | @end
47 |
48 | @interface XYPhotoCameraOverlayView : UIView
49 |
50 | /**
51 | 实现 UI 元素交互响应的代理方法
52 | */
53 | @property (nonatomic, weak) id delegate;
54 |
55 | /**
56 | 默认选中的asset
57 | */
58 | @property (nonatomic, strong) NSArray *defaultAssetArray;
59 |
60 | /**
61 | default: UIImagePickerControllerCameraFlashModeAuto
62 | */
63 | @property (nonatomic, assign) UIImagePickerControllerCameraFlashMode flashMode;
64 |
65 | /**
66 | default: UIImagePickerControllerCameraDeviceRear
67 | */
68 | @property (nonatomic, assign) UIImagePickerControllerCameraDevice cameraDeivce;
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoCameraOverlayView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCameraOverlayView.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 11/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoCameraOverlayView.h"
10 | #import "XYPhotoSelectedAssetManager.h"
11 | #import "XYPhotoDataCategory.h"
12 | #import "XYPhotoKitHelper.h"
13 |
14 | @interface XYPhotoCameraOverlayView()
15 |
16 | @property (nonatomic, strong) UIView *topBar;
17 | @property (nonatomic, strong) UIButton *flashSwitchButton;
18 | @property (nonatomic, strong) UIButton *deviceSwitchButton;
19 |
20 | @property (nonatomic, strong) UIView *bottomContentView;
21 | @property (nonatomic, strong) XYPhotoSelectedAssetPreviewView *bottomPreview;
22 |
23 | @property (nonatomic, strong) UIButton *shutter;
24 | @property (nonatomic, strong) UILabel *selectedScriptLabel;
25 |
26 | @property (nonatomic, strong) UIButton *closeButton;
27 | @property (nonatomic, strong) UIButton *doneButton;
28 |
29 | @end
30 |
31 | @implementation XYPhotoCameraOverlayView
32 |
33 | - (instancetype)initWithFrame:(CGRect)frame
34 | {
35 | self = [super initWithFrame:frame];
36 | if (self) {
37 | [self initBaseUIElements];
38 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectedAssetsChanged) name:XYPhotoMultiImagePickerNotifications.assetsChanged object:nil];
39 | }
40 | return self;
41 | }
42 |
43 | - (void)initBaseUIElements
44 | {
45 | // top
46 | _topBar = [[UIView alloc] init];
47 | _topBar.translatesAutoresizingMaskIntoConstraints = false;
48 | _topBar.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
49 | [self addSubview:_topBar];
50 |
51 | _flashSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom];
52 | _flashSwitchButton.translatesAutoresizingMaskIntoConstraints = false;
53 | [_flashSwitchButton setImage:[UIImage xy_imageWithName:@"cl_photo_picker_camera_flash"] forState:UIControlStateNormal];
54 | [_flashSwitchButton setTitle:@"自动" forState:UIControlStateNormal];
55 | _flashSwitchButton.titleLabel.font = [UIFont systemFontOfSize:14];
56 | [_flashSwitchButton addTarget:self action:@selector(flashButtonClick:) forControlEvents:UIControlEventTouchUpInside];
57 | [self addSubview:_flashSwitchButton];
58 |
59 | _deviceSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom];
60 | _deviceSwitchButton.translatesAutoresizingMaskIntoConstraints = false;
61 | [_deviceSwitchButton setImage:[UIImage xy_imageWithName:@"cl_photo_picker_camera_devicemode"] forState:UIControlStateNormal];
62 | _deviceSwitchButton.frame = CGRectMake(_topBar.frame.size.width-60, 0, 60, 40);
63 | [_deviceSwitchButton addTarget:self action:@selector(cameraDeviceClick:) forControlEvents:UIControlEventTouchUpInside];
64 | [self addSubview:_deviceSwitchButton];
65 |
66 | // 前置摄像头是否可以闪光
67 | if (![UIImagePickerController isFlashAvailableForCameraDevice:UIImagePickerControllerCameraDeviceRear]) {
68 | _flashSwitchButton.hidden = true;
69 | }
70 | // 判断是否支持前置摄像头
71 | if (![UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]) {
72 | _deviceSwitchButton.hidden = true;
73 | }
74 |
75 | // bottom
76 | _bottomContentView = [[UIView alloc] init];
77 | _bottomContentView.translatesAutoresizingMaskIntoConstraints = false;
78 | _bottomContentView.backgroundColor = [UIColor blackColor];
79 | [self addSubview:_bottomContentView];
80 |
81 | _closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
82 | _closeButton.translatesAutoresizingMaskIntoConstraints = false;
83 | _closeButton.frame = CGRectMake(0, _bottomContentView.frame.size.height-64, 120, 64);
84 | [_closeButton setTitle:@"取消" forState:UIControlStateNormal];
85 | [_closeButton addTarget:self action:@selector(cancelButtonClick:) forControlEvents:UIControlEventTouchUpInside];
86 | [_bottomContentView addSubview:_closeButton];
87 |
88 | _shutter = [UIButton buttonWithType:UIButtonTypeCustom];
89 | _shutter.translatesAutoresizingMaskIntoConstraints = false;
90 | _shutter.frame = CGRectMake((_bottomContentView.frame.size.width-64)/2.0, _bottomContentView.frame.size.height-64, 64, 64);
91 | [_shutter setImage:[UIImage xy_imageWithName:@"cl_photo_picker_shutter"] forState:UIControlStateNormal];
92 | [_shutter setImage:[UIImage xy_imageWithName:@"cl_photo_picker_shutter_h"] forState:UIControlStateHighlighted];
93 | [_shutter addTarget:self action:@selector(shutterClick:) forControlEvents:UIControlEventTouchUpInside];
94 | [_bottomContentView addSubview:_shutter];
95 |
96 | _doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
97 | _doneButton.translatesAutoresizingMaskIntoConstraints = false;
98 | _doneButton.frame = CGRectMake(_bottomContentView.frame.size.width-120, _bottomContentView.frame.size.height-64, 120, 64);
99 | [_doneButton setTitle:@"完成" forState:UIControlStateNormal];
100 | [_doneButton addTarget:self action:@selector(finishedButtonClick:) forControlEvents:UIControlEventTouchUpInside];
101 | [_bottomContentView addSubview:_doneButton];
102 |
103 |
104 | // UIEdgeInsets insets = UIApplication.sharedApplication.keyWindow.safeAreaInsets;
105 | [NSLayoutConstraint activateConstraints:@[
106 | [_topBar.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
107 | [_topBar.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
108 | [_topBar.topAnchor constraintEqualToAnchor:self.topAnchor],
109 | [_topBar.bottomAnchor constraintEqualToAnchor:_flashSwitchButton.bottomAnchor],
110 |
111 | [_flashSwitchButton.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor],
112 | [_flashSwitchButton.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor],
113 | [_flashSwitchButton.heightAnchor constraintEqualToConstant:40],
114 | [_flashSwitchButton.widthAnchor constraintEqualToConstant:80],
115 |
116 | [_deviceSwitchButton.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor],
117 | [_deviceSwitchButton.topAnchor constraintEqualToAnchor:_flashSwitchButton.topAnchor],
118 | [_deviceSwitchButton.heightAnchor constraintEqualToConstant:40],
119 | [_deviceSwitchButton.widthAnchor constraintEqualToConstant:60],
120 |
121 | [_bottomContentView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
122 | [_bottomContentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
123 | [_bottomContentView.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor],
124 | [_bottomContentView.heightAnchor constraintEqualToConstant:140],
125 |
126 | [_closeButton.leadingAnchor constraintEqualToAnchor:_bottomContentView.leadingAnchor],
127 | [_closeButton.bottomAnchor constraintEqualToAnchor:_bottomContentView.bottomAnchor],
128 | [_closeButton.widthAnchor constraintEqualToConstant:120],
129 | [_closeButton.heightAnchor constraintEqualToConstant:64],
130 |
131 | [_shutter.centerXAnchor constraintEqualToAnchor:_bottomContentView.centerXAnchor],
132 | [_shutter.bottomAnchor constraintEqualToAnchor:_bottomContentView.bottomAnchor],
133 | [_shutter.widthAnchor constraintEqualToConstant:64],
134 | [_shutter.heightAnchor constraintEqualToConstant:64],
135 |
136 | [_doneButton.trailingAnchor constraintEqualToAnchor:_bottomContentView.trailingAnchor],
137 | [_doneButton.bottomAnchor constraintEqualToAnchor:_bottomContentView.bottomAnchor],
138 | [_doneButton.widthAnchor constraintEqualToConstant:120],
139 | [_doneButton.heightAnchor constraintEqualToConstant:64],
140 | ]];
141 |
142 | dispatch_block_t block = ^{
143 | self.bottomPreview = [[XYPhotoSelectedAssetPreviewView alloc] init];
144 | self.bottomPreview.translatesAutoresizingMaskIntoConstraints = false;
145 | self.bottomPreview.hideControls = true;
146 | self.bottomPreview.backgroundColor = [UIColor blackColor];
147 | self.bottomPreview.delegate = self.delegate;
148 | [self.bottomContentView addSubview:self.bottomPreview];
149 |
150 | self.selectedScriptLabel = [[UILabel alloc] init];
151 | self.selectedScriptLabel.translatesAutoresizingMaskIntoConstraints = false;
152 | if (@available(iOS 13.0, *)) {
153 | self.selectedScriptLabel.textColor = [UIColor systemBackgroundColor];
154 | } else {
155 | self.selectedScriptLabel.textColor = [UIColor whiteColor];
156 | }
157 | self.selectedScriptLabel.font = [UIFont systemFontOfSize:14];
158 | self.selectedScriptLabel.textAlignment = NSTextAlignmentRight;
159 | [self.bottomContentView addSubview:self.selectedScriptLabel];
160 |
161 | [NSLayoutConstraint activateConstraints:@[
162 | [self.bottomPreview.trailingAnchor constraintEqualToAnchor:self.bottomContentView.trailingAnchor],
163 | [self.bottomPreview.leadingAnchor constraintEqualToAnchor:self.bottomContentView.leadingAnchor],
164 | [self.bottomPreview.topAnchor constraintEqualToAnchor:self.bottomContentView.topAnchor],
165 | [self.bottomPreview.heightAnchor constraintEqualToConstant:60],
166 |
167 | [self.selectedScriptLabel.topAnchor constraintEqualToAnchor:self.bottomPreview.bottomAnchor],
168 | [self.selectedScriptLabel.trailingAnchor constraintEqualToAnchor:self.bottomContentView.trailingAnchor constant:-8]
169 | ]];
170 | [self selectedAssetsChanged];
171 | };
172 |
173 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
174 | dispatch_async(dispatch_get_main_queue(), ^{
175 | if (status == PHAuthorizationStatusAuthorized) {
176 | block();
177 | } else {
178 | [UIAlertController xy_showAlertPhotoSettingIfUnauthorized];
179 | }
180 | });
181 | }];
182 | }
183 |
184 | - (void)didMoveToWindow
185 | {
186 | [super didMoveToWindow];
187 | _bottomPreview.assetArray = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
188 | }
189 |
190 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
191 | {
192 | UIView *view = [super hitTest:point withEvent:event];
193 | return view==self ? nil:view;
194 | }
195 |
196 | #pragma mark - set
197 |
198 | - (void)setFlashMode:(UIImagePickerControllerCameraFlashMode)flashMode
199 | {
200 | _flashMode = flashMode;
201 | switch (_flashMode) {
202 | case UIImagePickerControllerCameraFlashModeAuto: {
203 | [_flashSwitchButton setTitle:@"自动" forState:UIControlStateNormal];
204 | }
205 | break;
206 | case UIImagePickerControllerCameraFlashModeOn: {
207 | [_flashSwitchButton setTitle:@"打开" forState:UIControlStateNormal];
208 | }
209 | break;
210 | case UIImagePickerControllerCameraFlashModeOff: {
211 | [_flashSwitchButton setTitle:@"关闭" forState:UIControlStateNormal];
212 | }
213 | break;
214 | default:
215 | break;
216 | }
217 | }
218 |
219 | - (void)setCameraDeivce:(UIImagePickerControllerCameraDevice)cameraDeivce
220 | {
221 | _cameraDeivce = cameraDeivce;
222 | _flashSwitchButton.hidden = ![UIImagePickerController isFlashAvailableForCameraDevice:_cameraDeivce];
223 | }
224 |
225 | - (void)setDelegate:(id)delegate
226 | {
227 | _delegate = delegate;
228 | _bottomPreview.delegate = delegate;
229 | }
230 |
231 | #pragma mark - actions
232 |
233 | - (void)flashButtonClick:(UIButton *)sender
234 | {
235 | if ([_delegate respondsToSelector:@selector(cameraOverlayView:didSwitchedFlashMode:)] && [UIAlertController xy_showAlertCameraSettingIfUnauthorized]) {
236 |
237 | switch (_flashMode) {
238 | case UIImagePickerControllerCameraFlashModeAuto: {
239 | self.flashMode = UIImagePickerControllerCameraFlashModeOn;
240 | }
241 | break;
242 | case UIImagePickerControllerCameraFlashModeOn: {
243 | self.flashMode = UIImagePickerControllerCameraFlashModeOff;
244 | }
245 | break;
246 | case UIImagePickerControllerCameraFlashModeOff: {
247 | self.flashMode = UIImagePickerControllerCameraFlashModeAuto;
248 | }
249 | break;
250 | default:
251 | break;
252 | }
253 | [_delegate cameraOverlayView:self didSwitchedFlashMode:_flashMode];
254 | }
255 | }
256 |
257 | - (void)cameraDeviceClick:(UIButton *)sender
258 | {
259 | if ([_delegate respondsToSelector:@selector(cameraOverlayView:didSwitchedCameraDeivce:)] && [UIAlertController xy_showAlertCameraSettingIfUnauthorized]) {
260 | self.cameraDeivce = _cameraDeivce == UIImagePickerControllerCameraDeviceRear ? UIImagePickerControllerCameraDeviceFront : UIImagePickerControllerCameraDeviceRear;
261 | self.flashSwitchButton.hidden = ![UIImagePickerController isFlashAvailableForCameraDevice:_cameraDeivce];
262 | [_delegate cameraOverlayView:self didSwitchedCameraDeivce:_cameraDeivce];
263 | }
264 | }
265 |
266 | - (void)cancelButtonClick:(UIButton *)sender
267 | {
268 | if ([_delegate respondsToSelector:@selector(canceledCameraOverlayView:)]) {
269 | [_delegate canceledCameraOverlayView:self];
270 | }
271 | }
272 |
273 | - (void)finishedButtonClick:(UIButton *)sender
274 | {
275 | if ([_delegate respondsToSelector:@selector(finishedCameraOverlayView:)]) {
276 | [_delegate finishedCameraOverlayView:self];
277 | }
278 | }
279 |
280 | - (void)shutterClick:(UIButton *)sender
281 | {
282 | if ([_delegate respondsToSelector:@selector(cameraOverlayView:didShutted:)] && [UIAlertController xy_showAlertCameraSettingIfUnauthorized]) {
283 | [_delegate cameraOverlayView:self didShutted:sender];
284 | }
285 | }
286 |
287 |
288 | #pragma mark - Notification
289 |
290 | - (void)selectedAssetsChanged
291 | {
292 | NSArray *assets = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
293 | NSInteger max = (NSInteger)[XYPhotoSelectedAssetManager sharedManager].maxNumberOfAssets;
294 | if (max==0) {
295 | _selectedScriptLabel.text = @(assets.count).stringValue;
296 | } else {
297 | _selectedScriptLabel.text = [NSString stringWithFormat:@"(%zd / %zd)",assets.count,max];
298 | }
299 | }
300 |
301 |
302 | @end
303 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCollectionViewCell.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | @import Photos;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface XYPhotoCollectionViewCell : UICollectionViewCell
15 |
16 | - (void)setAsset:(PHAsset *)asset atIndexPath:(NSIndexPath *)indexPath inCollectionView:(UICollectionView *)collectionView withCacheManager:(PHCachingImageManager *)cacheManager;
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCollectionViewCell.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 12/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoCollectionViewCell.h"
10 | #import "XYPhotoDataCategory.h"
11 | #import "XYPhotoKitHelper.h"
12 | #import "XYPhotoSelectedAssetManager.h"
13 |
14 | @interface XYPhotoCollectionViewCell ()
15 |
16 | @property (nonatomic) PHImageRequestID imageRequestId;
17 |
18 | @property (nonatomic, strong) PHAsset *asset;
19 |
20 | @property (nonatomic, strong) UIImageView *imageView;
21 |
22 | // 是否选中
23 | @property (nonatomic, strong) UIButton *markIconButon;
24 |
25 | @end
26 |
27 | @implementation XYPhotoCollectionViewCell
28 |
29 | - (instancetype)initWithFrame:(CGRect)frame
30 | {
31 | if (self = [super initWithFrame:frame]) {
32 | _imageView = [[UIImageView alloc] init];
33 | _imageView.translatesAutoresizingMaskIntoConstraints = false;
34 | _imageView.contentMode = UIViewContentModeScaleAspectFill;
35 | _imageView.clipsToBounds = true;
36 | [self.contentView addSubview:_imageView];
37 |
38 | _markIconButon = [UIButton buttonWithType:UIButtonTypeCustom];
39 | _markIconButon.translatesAutoresizingMaskIntoConstraints = false;
40 | [_markIconButon setImage:[UIImage xy_imageWithName:@"cl_photo_picker_unpicked"] forState:UIControlStateNormal];
41 | [_markIconButon setImage:[UIImage xy_imageWithName:@"cl_photo_picker_picked"] forState:UIControlStateSelected];
42 | _markIconButon.imageView.contentMode = UIViewContentModeCenter;
43 | _markIconButon.selected = false;
44 | [_markIconButon addTarget:self action:@selector(markButtonClick) forControlEvents:UIControlEventTouchUpInside];
45 | [self.contentView addSubview:_markIconButon];
46 |
47 | _markIconButon.hidden = [XYPhotoSelectedAssetManager sharedManager].maxNumberOfAssets == 1;
48 |
49 | [NSLayoutConstraint activateConstraints:@[
50 | [_imageView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
51 | [_imageView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
52 | [_imageView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
53 | [_imageView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor],
54 |
55 | [_markIconButon.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
56 | [_markIconButon.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
57 | [_markIconButon.heightAnchor constraintEqualToConstant:35],
58 | [_markIconButon.widthAnchor constraintEqualToConstant:35],
59 | ]];
60 | }
61 | return self;
62 | }
63 |
64 | - (void)prepareForReuse
65 | {
66 | [super prepareForReuse];
67 | _markIconButon.selected = false;
68 | self.imageView.image = nil;
69 | }
70 |
71 | - (void)setAsset:(PHAsset *)asset atIndexPath:(NSIndexPath *)indexPath inCollectionView:(UICollectionView *)collectionView withCacheManager:(PHCachingImageManager *)cacheManager
72 | {
73 | _asset = asset;
74 |
75 | _markIconButon.selected = [[XYPhotoSelectedAssetManager sharedManager].selectedAssets containsObject:asset];
76 |
77 | CGSize itemSize = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout itemSize];
78 |
79 | // 防止复用时加载延时图片展示多次
80 | if (_imageRequestId!=0) {
81 | [cacheManager cancelImageRequest:_imageRequestId];
82 | }
83 | PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
84 | // options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
85 | options.networkAccessAllowed = [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud;
86 | _imageRequestId = [cacheManager requestImageForAsset:asset
87 | targetSize:CGSizeScale(itemSize, [UIScreen mainScreen].scale)
88 | contentMode:PHImageContentModeAspectFill
89 | options:options
90 | resultHandler:^(UIImage *result, NSDictionary *info) {
91 | // NSLog(@"%@ %@ %d", result, info, self.imageRequestId);
92 | // PHImageRequestID requestID = [info[@"PHImageResultRequestIDKey"] intValue];
93 | // if (result && requestID == self.imageRequestId) {
94 | // self.imageView.image = result;
95 | // }
96 | if (result) { self.imageView.image = result; }
97 | }];
98 | }
99 |
100 | - (void)markButtonClick
101 | {
102 | if (_markIconButon.selected) {
103 | [[XYPhotoSelectedAssetManager sharedManager] removeSelectedAsset:_asset];
104 | _markIconButon.selected = false;
105 | } else if ([[XYPhotoSelectedAssetManager sharedManager] addSelectedAsset:_asset]) {
106 | _markIconButon.selected = true;
107 | }
108 | }
109 |
110 | @end
111 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoHorizontalScrollItemView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoHorizontalScrollItemView.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 07/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoHorizontalScrollView.h"
10 |
11 | @class XYPhotoHorizontalScrollItemView;
12 | @protocol XYPhotoHorizontalScrollItemViewDelegate
13 | @optional
14 | - (void)didTapped:(XYPhotoHorizontalScrollItemView *)scrollItemView;
15 | @end
16 |
17 | @import Photos;
18 | @interface XYPhotoHorizontalScrollItemView : UIScrollView
19 |
20 | @property (nonatomic) int index;
21 | @property (nonatomic, strong) PHAsset *asset;
22 | @property (nonatomic, strong) UIImageView *imageView;
23 | @property (nonatomic, weak) id photokitDelegate;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoHorizontalScrollItemView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoHorizontalScrollItemView.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 07/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoHorizontalScrollItemView.h"
10 | #import "XYPhotoKitHelper.h"
11 | #import "XYPhotoSelectedAssetManager.h"
12 | #import "XYPhotoDataCategory.h"
13 |
14 | @import AVKit;
15 | @interface XYPhotoHorizontalScrollItemView()
16 |
17 | @property (nonatomic, strong) PHCachingImageManager *imageManager;
18 | @property (nonatomic, strong) UIButton *playVideoButton;
19 | @property (nonatomic, assign) PHImageRequestID imageRequestId;
20 |
21 | @end
22 |
23 | @implementation XYPhotoHorizontalScrollItemView
24 |
25 | - (instancetype)initWithFrame:(CGRect)frame
26 | {
27 | self = [super initWithFrame:frame];
28 | if (self) {
29 | self.delegate = self;
30 | self.minimumZoomScale = 1.f;
31 | self.maximumZoomScale = 2.f;
32 | self.showsHorizontalScrollIndicator = false;
33 | self.showsVerticalScrollIndicator = false;
34 | self.decelerationRate = UIScrollViewDecelerationRateFast;
35 | self.backgroundColor = [UIColor blackColor];
36 |
37 | _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 0, self.frame.size.width-10, self.frame.size.height)];
38 | _imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
39 | _imageView.contentMode = UIViewContentModeScaleAspectFit;
40 | _imageView.backgroundColor = [UIColor blackColor];
41 | [self addSubview:_imageView];
42 |
43 | _playVideoButton = [UIButton buttonWithType:UIButtonTypeCustom];
44 | [_playVideoButton setImage:[UIImage xy_imageWithName:@"cl_photokit_play"] forState:UIControlStateNormal];
45 | _playVideoButton.frame = CGRectMake((self.frame.size.width-100)/2.f, (self.frame.size.height-100)/2.f, 100, 100);
46 | _playVideoButton.hidden = true;
47 | [_playVideoButton addTarget:self action:@selector(playAction) forControlEvents:UIControlEventTouchUpInside];
48 | [self addSubview:_playVideoButton];
49 |
50 | _imageManager = [[PHCachingImageManager alloc] init];
51 | }
52 | return self;
53 | }
54 |
55 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
56 | {
57 | [super touchesEnded:touches withEvent:event];
58 | UITouch *touch = [touches anyObject];
59 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(viewWasTapped) object:nil];
60 | if (touch.tapCount==1) {
61 | [self performSelector:@selector(viewWasTapped) withObject:nil afterDelay:0.2f];
62 | } else if (touch.tapCount==2) {
63 | [self viewWasDoubleTapped:touch];
64 | }
65 | }
66 |
67 | - (void)setAsset:(PHAsset *)asset
68 | {
69 | _asset = asset;
70 |
71 | // 防止复用时加载延时图片展示多次
72 | if (_imageRequestId!=0) {
73 | [self.imageManager cancelImageRequest:_imageRequestId];
74 | }
75 |
76 | CGSize size = [UIScreen mainScreen].bounds.size;
77 | CGFloat scale = [UIScreen mainScreen].scale;
78 |
79 | if (asset.mediaType == PHAssetMediaTypeVideo) {
80 | self.maximumZoomScale = 1;
81 | } else if (asset.mediaType == PHAssetMediaTypeImage) {
82 | self.maximumZoomScale = MAX(asset.pixelWidth/(scale*size.width), asset.pixelHeight/(scale*size.height));
83 | self.maximumZoomScale = MAX(self.maximumZoomScale, 2);
84 | }
85 | self.playVideoButton.hidden = asset.mediaType != PHAssetMediaTypeVideo;
86 |
87 | // 允许网络请求icould
88 | PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
89 | options.networkAccessAllowed = [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud;
90 |
91 | _imageRequestId = [self.imageManager requestImageForAsset:asset
92 | targetSize:CGSizeMake(asset.pixelWidth, asset.pixelHeight)
93 | contentMode:PHImageContentModeAspectFit
94 | options:options
95 | resultHandler:^(UIImage *result, NSDictionary *info) {
96 | BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
97 | if (downloadFinined) {
98 | self.imageRequestId = 0;
99 | }
100 | if (result) { self.imageView.image = result; }
101 | }];
102 | }
103 |
104 | #pragma mark - actions
105 |
106 | - (void)viewWasTapped
107 | {
108 | if ([_photokitDelegate respondsToSelector:@selector(didTapped:)]) {
109 | [_photokitDelegate didTapped:self];
110 | }
111 | }
112 |
113 | - (void)viewWasDoubleTapped:(UITouch *)touch
114 | {
115 | if (_asset.mediaType == PHAssetMediaTypeImage) {
116 | [self zoomToLocation:[touch locationInView:self]];
117 | }
118 | }
119 |
120 | - (void)playAction
121 | {
122 | if (_asset.mediaType != PHAssetMediaTypeVideo) {
123 | return;
124 | }
125 |
126 | PHAsset *videoAsset = _asset;
127 | PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
128 | options.networkAccessAllowed = [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud;
129 | options.deliveryMode = PHVideoRequestOptionsDeliveryModeFastFormat;
130 |
131 | [[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
132 | dispatch_async(dispatch_get_main_queue(), ^{
133 | AVPlayer *player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:asset]];
134 | AVPlayerViewController *playerViewController = [AVPlayerViewController new];
135 | playerViewController.player = player;
136 | [[UIApplication sharedApplication].xy_topViewController presentViewController:playerViewController animated:NO completion:^{
137 | [player play];
138 | }];
139 | });
140 | }];
141 | }
142 |
143 | #pragma mark - private
144 |
145 | - (void)zoomToLocation:(CGPoint)location
146 | {
147 | float newScale;
148 | CGRect zoomRect;
149 | if ([self isZoomed]) {
150 | zoomRect = self.bounds;
151 | } else {
152 | newScale = [self maximumZoomScale];
153 | zoomRect = [self zoomRectForScale:newScale withCenter:location];
154 | }
155 | [self zoomToRect:zoomRect animated:YES];
156 | }
157 |
158 | - (BOOL)isZoomed
159 | {
160 | return !([self zoomScale] == [self minimumZoomScale]);
161 | }
162 |
163 | - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center
164 | {
165 | CGRect zoomRect;
166 | zoomRect.size.height = _imageView.frame.size.height / scale;
167 | zoomRect.size.width = _imageView.frame.size.width / scale;
168 | zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
169 | zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0)-30;
170 |
171 | return zoomRect;
172 | }
173 |
174 | #pragma mark - UIScrollViewDelegate
175 |
176 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
177 | {
178 | if (_imageView.image) {
179 | return _imageView;
180 | }
181 | return nil;
182 | }
183 |
184 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView
185 | {
186 | CGSize size = _imageView.frame.size;
187 | CGFloat top = floor((MAX(scrollView.contentSize.height, scrollView.frame.size.height)-_imageView.frame.size.height)/2);
188 | CGFloat left = floor((MAX(scrollView.contentSize.width, scrollView.frame.size.width)-_imageView.frame.size.width)/2);
189 | _imageView.frame = CGRectMake(top, left, size.width, size.height);
190 | }
191 |
192 | @end
193 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoHorizontalScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoHorizontalScrollView.h
3 | // YHHB
4 | //
5 | // Created by Hunter Huang on 11/30/11.
6 | // Copyright (c) 2011 vge design. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol XYPhotoHorizontalScrollViewDataSource;
12 | @protocol XYPhotoHorizontalScrollViewDelegate;
13 | @class XYHorizontalScrollItemView;
14 |
15 | @protocol XYHorizontalScrollItemInterface
16 |
17 | @property (nonatomic) int index;
18 |
19 | @end
20 |
21 | @interface XYPhotoHorizontalScrollView : UIScrollView
22 |
23 | @property (nonatomic, weak) id horizontalDataSource;
24 | @property (nonatomic, weak) id horizontalDelegate;
25 | @property (nonatomic, assign) NSInteger currentIndex;
26 | @property (nonatomic, assign) CGFloat viewPadding;
27 |
28 | - (id)dequeueReusableItemView;
29 |
30 | - (void)clearMemory;
31 | - (void)reloadData;
32 |
33 | - (void)resizeToFrame:(CGRect)rect animated:(BOOL)animated duration:(float)duration;
34 |
35 | - (void)setCurrentIndex:(NSInteger)index animated:(BOOL)animated;
36 | - (void)setCurrentIndex:(NSInteger)index animated:(BOOL)animated event:(BOOL)event; //event 表示是否最终调用 horizontalScrollView:didSelectIndex:
37 |
38 | - (id)currentItemView;
39 |
40 | @end
41 |
42 | @protocol XYPhotoHorizontalScrollViewDataSource
43 |
44 | - (NSInteger)numberOfItems;
45 | - (id)horizontalScrollView:(XYPhotoHorizontalScrollView *)scroller itemViewForIndex:(NSInteger)index;
46 |
47 | @end
48 |
49 | @protocol XYPhotoHorizontalScrollViewDelegate
50 |
51 | @optional
52 |
53 | - (void)horizontalScrollView:(XYPhotoHorizontalScrollView *)scroller willSelectIndex:(NSInteger)index;
54 | - (void)horizontalScrollView:(XYPhotoHorizontalScrollView *)scroller didSelectIndex:(NSInteger)index;
55 |
56 | - (void)horizontalScrollViewWillBeginDragging:(XYPhotoHorizontalScrollView *)scroller; //类比 scrollViewWillBeginDragging:
57 | - (void)horizontalScrollViewDidEndDragging:(XYPhotoHorizontalScrollView *)scroller; //类比 scrollViewDidEndDragging:willDecelerate:
58 | - (void)horizontalScrollViewDidScroll:(XYPhotoHorizontalScrollView *)scroller; //类比 scrollViewDidScroll:
59 |
60 | @end
61 |
62 | @interface XYHorizontalScrollItemView : UIView
63 |
64 | @property (nonatomic) int index;
65 |
66 | - (void)viewWillResize;
67 |
68 | @end
69 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoHorizontalScrollView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoHorizontalScrollView.m
3 | // YHHB
4 | //
5 | // Created by Hunter Huang on 11/30/11.
6 | // Copyright (c) 2011 vge design. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoHorizontalScrollView.h"
10 |
11 | static const NSInteger preloadanswerNum = 1;
12 |
13 | @interface XYPhotoHorizontalScrollView()
14 |
15 | @property (nonatomic, strong) NSMutableSet *reusableViews;
16 | @property (nonatomic, assign) NSInteger firstVisibleIndex;
17 | @property (nonatomic, assign) NSInteger lastVisibleIndex;
18 |
19 | @end
20 |
21 | @interface UINavigationController ()
22 |
23 | - (BOOL)horizontalScrollView:(UIScrollView *)scrollView gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
24 |
25 | @end
26 |
27 | @implementation XYPhotoHorizontalScrollView
28 |
29 | @synthesize horizontalDelegate, horizontalDataSource, reusableViews, currentIndex;
30 | @synthesize firstVisibleIndex, lastVisibleIndex, viewPadding;
31 |
32 | - (id)initWithFrame:(CGRect)frame {
33 | self = [super initWithFrame:frame];
34 | if (self) {
35 | // Initialization code
36 | self.backgroundColor = [UIColor clearColor];
37 | self.pagingEnabled = true;
38 | self.scrollsToTop = false;
39 | self.showsVerticalScrollIndicator = false;
40 | self.showsHorizontalScrollIndicator = false;
41 | self.reusableViews = [NSMutableSet set];
42 | self.delegate = self;
43 |
44 | firstVisibleIndex = NSIntegerMax;
45 | lastVisibleIndex = NSIntegerMin;
46 | }
47 | return self;
48 | }
49 |
50 | #pragma mark - public
51 |
52 | //强制固定contentInset为zero
53 | - (void)setContentInset:(UIEdgeInsets)contentInset
54 | {
55 | [super setContentInset:UIEdgeInsetsZero];
56 | }
57 |
58 | //强制固定contentOffset为zero
59 | - (void)setContentOffset:(CGPoint)contentOffset
60 | {
61 | [super setContentOffset:CGPointMake(contentOffset.x, 0)];
62 | }
63 |
64 | - (void)layoutSubviews
65 | {
66 | [super layoutSubviews];
67 |
68 | CGRect visibleBounds = [self bounds];
69 | CGFloat visibleWidth = visibleBounds.size.width;
70 | CGFloat visibleHeight = visibleBounds.size.height;
71 |
72 | NSInteger answerNum = horizontalDataSource?[horizontalDataSource numberOfItems]:0;
73 | self.contentSize = CGSizeMake(answerNum*visibleWidth, visibleHeight);
74 |
75 | CGFloat leftDelta = visibleBounds.origin.x-visibleWidth*preloadanswerNum;
76 | CGFloat extendedVisibleWidth = (preloadanswerNum*2+1)*visibleWidth;
77 | if (leftDelta < 0) {
78 | extendedVisibleWidth += leftDelta;
79 | }
80 | CGRect extendedVisibleBounds = CGRectMake(MAX(0, visibleBounds.origin.x-preloadanswerNum*visibleWidth), 0, extendedVisibleWidth, visibleHeight);
81 | // Recycle all views that are no longer visible
82 | for (UIView *view in [self subviews]) {
83 | if ([view isKindOfClass:[XYHorizontalScrollItemView class]]||[view conformsToProtocol:@protocol(XYHorizontalScrollItemInterface)]) {
84 | CGRect viewFrame = [view frame];
85 | // If the view doesn't intersect, it's not visible, so we can recycle it
86 | if (! CGRectIntersectsRect(viewFrame, extendedVisibleBounds)) {
87 | [view removeFromSuperview];
88 | if (reusableViews.count<5) {
89 | [reusableViews addObject:view];
90 | }
91 | }
92 | }
93 | }
94 |
95 | int startIndex = MAX(0, floorf((extendedVisibleBounds.origin.x)/visibleWidth));
96 | int endIndex = MIN(answerNum, ceilf((extendedVisibleBounds.origin.x+extendedVisibleBounds.size.width)/visibleWidth));// 预加载2张
97 |
98 | for (int index = startIndex; index < endIndex; index++) {
99 | BOOL isItemViewMissing = index < firstVisibleIndex || index >= lastVisibleIndex;
100 | if (isItemViewMissing) {
101 | id itemView = nil;
102 | if (horizontalDataSource) {
103 | itemView = [horizontalDataSource horizontalScrollView:self itemViewForIndex:index];
104 | }
105 | // Set the frame so the view is inserted into the correct position.
106 | itemView.index = index;
107 | [(UIView *)itemView setTag:index+1000];
108 | [(UIView *)itemView setFrame:CGRectMake(index*visibleWidth+viewPadding, 0, visibleWidth-viewPadding*2, visibleHeight)];
109 | [self addSubview:(UIView *)itemView];
110 | }
111 | }
112 |
113 | // Remember which thumb view indexes are visible.
114 | firstVisibleIndex = startIndex;
115 | lastVisibleIndex = endIndex;
116 | }
117 |
118 |
119 | #pragma mark - public 复用
120 |
121 | - (id)dequeueReusableItemView {
122 | XYHorizontalScrollItemView *view = [reusableViews anyObject];
123 | if (view) {
124 | [reusableViews removeObject:view];
125 | }
126 | return view;
127 | }
128 |
129 | //清空复用
130 | - (void)clearMemory
131 | {
132 | [reusableViews removeAllObjects];
133 | reusableViews = [NSMutableSet set];
134 | }
135 |
136 | - (void)reloadData
137 | {
138 | [self enqueueReusableItemViews];
139 | // force to call layoutSubviews
140 | [self layoutSubviews];
141 | }
142 |
143 | - (void)resizeToFrame:(CGRect)rect animated:(BOOL)animated duration:(float)duration
144 | {
145 | if (animated) {
146 | [UIView animateWithDuration:duration animations:^{
147 | self.frame = rect;
148 | }];
149 | } else {
150 | self.frame = rect;
151 | }
152 |
153 | self.contentOffset = CGPointMake(currentIndex*self.frame.size.width, 0);
154 |
155 | [UIView animateWithDuration:animated?duration:0 animations:^{
156 | for (NSInteger i=0, c=[self.subviews count]; ihorizontalDelegate respondsToSelector:@selector(horizontalScrollView:didSelectIndex:)]) {
215 | [self->horizontalDelegate horizontalScrollView:self didSelectIndex:self->currentIndex];
216 | }
217 | }
218 | }];
219 | } else {
220 | [self scrollRectToVisible:rect animated:NO];
221 | if (event) {
222 | if ([horizontalDelegate respondsToSelector:@selector(horizontalScrollView:didSelectIndex:)]) {
223 | [horizontalDelegate horizontalScrollView:self didSelectIndex:currentIndex];
224 | }
225 | }
226 | }
227 |
228 | if (!animated) {
229 | [self layoutSubviews];
230 | }
231 |
232 | }
233 |
234 | - (id)currentItemView
235 | {
236 | return [self findViewWithTag:1000+currentIndex];
237 | }
238 |
239 | #pragma mark - private
240 |
241 | - (void)enqueueReusableItemViews {
242 | for (UIView *view in [self subviews]) {
243 | if ([view isKindOfClass:[XYHorizontalScrollItemView class]]||[view conformsToProtocol:@protocol(XYHorizontalScrollItemInterface)]) {
244 | [view removeFromSuperview];
245 | if (reusableViews.count<5) {
246 | [reusableViews addObject:view];
247 | }
248 | }
249 | }
250 |
251 | firstVisibleIndex = NSIntegerMax;
252 | lastVisibleIndex = NSIntegerMin;
253 | }
254 |
255 |
256 | - (id)findViewWithTag:(NSUInteger)tag
257 | {
258 | for (UIView *sub in self.subviews) {
259 | if (([sub isKindOfClass:[XYHorizontalScrollItemView class]]||[sub conformsToProtocol:@protocol(XYHorizontalScrollItemInterface)]) && sub.tag==tag) {
260 | return (id)sub;
261 | }
262 | }
263 | return nil;
264 | }
265 |
266 | - (void)updateCurrentIndex
267 | {
268 | int index = floorf((self.contentOffset.x+self.frame.size.width/2)/self.frame.size.width);
269 | if (index != currentIndex && index >= 0 && index<(horizontalDataSource?[horizontalDataSource numberOfItems]:0)) {
270 | currentIndex = index;
271 | if ([horizontalDelegate respondsToSelector:@selector(horizontalScrollView:willSelectIndex:)]) {
272 | [horizontalDelegate horizontalScrollView:self willSelectIndex:currentIndex];
273 | }
274 | }
275 | }
276 |
277 | #pragma mark - UIScrollViewDelegate
278 |
279 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView
280 | {
281 | if (scrollView.isDragging) {
282 | [self updateCurrentIndex];
283 | }
284 | if ([self.horizontalDelegate respondsToSelector:@selector(horizontalScrollViewDidScroll:)]) {
285 | [self.horizontalDelegate horizontalScrollViewDidScroll:self];
286 | }
287 | }
288 |
289 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
290 | {
291 | if (!decelerate) {
292 | [self updateCurrentIndex];
293 | if ([self.horizontalDelegate respondsToSelector:@selector(horizontalScrollViewDidEndDragging:)]) {
294 | [self.horizontalDelegate horizontalScrollViewDidEndDragging:self];
295 | }
296 | }
297 | }
298 |
299 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
300 | {
301 | [self updateCurrentIndex];
302 | //停止滚动的代理
303 | if ([horizontalDelegate respondsToSelector:@selector(horizontalScrollView:didSelectIndex:)]) {
304 | [horizontalDelegate horizontalScrollView:self didSelectIndex:currentIndex];
305 | }
306 | }
307 |
308 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
309 | {
310 | [NSObject cancelPreviousPerformRequestsWithTarget:self];
311 | if ([self.horizontalDelegate respondsToSelector:@selector(horizontalScrollViewWillBeginDragging:)]) {
312 | [self.horizontalDelegate horizontalScrollViewWillBeginDragging:self];
313 | }
314 | }
315 |
316 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
317 | {
318 | return NO;
319 | }
320 |
321 | @end
322 |
323 | @implementation XYHorizontalScrollItemView
324 |
325 | - (void)viewDidShow
326 | {
327 |
328 | }
329 |
330 | - (void)viewDidHidden
331 | {
332 |
333 | }
334 |
335 | - (void)viewWillResize
336 | {
337 |
338 | }
339 |
340 | @end
341 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoPreviewOverlayView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoPreviewOverlayView.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 06/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoSelectedAssetPreviewView.h"
10 |
11 | @class XYPhotoPreviewOverlayView;
12 | @protocol XYPhotoPreviewOverlayViewDelegate
13 |
14 | @required
15 | /**
16 | 关闭预览大图
17 | @param barbuttonItem closeItem
18 | */
19 | - (void)previewOverlayView:(XYPhotoPreviewOverlayView *)view closeBarButtonItemClick:(UIBarButtonItem *)barbuttonItem;
20 |
21 | @end
22 |
23 |
24 | @interface XYPhotoPreviewOverlayView : UIView
25 |
26 | @property (nonatomic, weak) id delegate;
27 |
28 | /**
29 | 更新相关UI,如果当前asset的选中状态及滚动到当前asset对应的小图预览
30 |
31 | @param asset 当前asset需要更新
32 | */
33 | - (void)updateSelectedAsset:(PHAsset *)asset;
34 |
35 | /**
36 | 更新头部标题
37 |
38 | @param index 当前展示的索引
39 | @param sum 全部asset个数
40 | */
41 | - (void)updateTitleAtIndex:(NSInteger)index sum:(NSInteger)sum;
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoPreviewOverlayView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoPreviewOverlayView.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 06/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoPreviewOverlayView.h"
10 | #import "XYPhotoSelectedAssetManager.h"
11 | #import "XYPhotoDataCategory.h"
12 |
13 | @interface XYPhotoPreviewOverlayView()
14 | {
15 | UIButton *_rightButton;
16 | UINavigationItem *_navBarItem;
17 | PHAsset *_asset;
18 | }
19 | @property (nonatomic, strong) UINavigationBar *navBar;
20 | @property (nonatomic, strong) XYPhotoSelectedAssetPreviewView *previewView;
21 |
22 | @end
23 |
24 | @implementation XYPhotoPreviewOverlayView
25 |
26 | - (instancetype)initWithFrame:(CGRect)frame
27 | {
28 | self = [super initWithFrame:frame];
29 | if (self) {
30 | self.backgroundColor = [UIColor clearColor];
31 |
32 | UIImageView *topView = [[UIImageView alloc] init];
33 | topView.image = [UIImage xy_imageWithColor:[UIColor colorWithWhite:0 alpha:0.5]];
34 | topView.translatesAutoresizingMaskIntoConstraints = false;
35 | [self addSubview:topView];
36 |
37 | _rightButton = [UIButton buttonWithType:UIButtonTypeCustom];
38 | [_rightButton setImage:[UIImage xy_imageWithName:@"cl_photo_picker_unpicked"] forState:UIControlStateNormal];
39 | [_rightButton setImage:[UIImage xy_imageWithName:@"cl_photo_picker_picked"] forState:UIControlStateSelected];
40 | [_rightButton addTarget:self action:@selector(selectedStateChanged) forControlEvents:UIControlEventTouchUpInside];
41 | _rightButton.selected = false;
42 |
43 | _navBarItem = [[UINavigationItem alloc] initWithTitle:@"照片预览"];
44 | _navBarItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:_rightButton];
45 | _navBarItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop target:self action:@selector(close:)];
46 |
47 | _navBar = [[UINavigationBar alloc] init];
48 | _navBar.translatesAutoresizingMaskIntoConstraints = false;
49 | _navBar.barTintColor = [UIColor whiteColor];
50 | [_navBar setBackgroundImage:[UIImage xy_imageWithColor:[UIColor clearColor]] forBarMetrics:UIBarMetricsDefault];
51 | [_navBar setShadowImage:[UIImage xy_imageWithColor:[UIColor clearColor]]];
52 | [_navBar setItems:@[_navBarItem]];
53 | _navBar.tintColor = [UIColor whiteColor];
54 | [_navBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
55 | [self addSubview:_navBar];
56 |
57 | _previewView = [[XYPhotoSelectedAssetPreviewView alloc] init];
58 | _previewView.translatesAutoresizingMaskIntoConstraints = false;
59 | _previewView.delegate = _delegate;
60 | _previewView.backgroundColor = [[UIColor darkGrayColor] colorWithAlphaComponent:0.8];
61 | [self addSubview:_previewView];
62 |
63 | [NSLayoutConstraint activateConstraints:@[
64 | [_navBar.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor],
65 | [_navBar.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor],
66 | [_navBar.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor],
67 | [_navBar.heightAnchor constraintEqualToConstant:44],
68 | [topView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
69 | [topView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
70 | [topView.topAnchor constraintEqualToAnchor:self.topAnchor],
71 | [topView.bottomAnchor constraintEqualToAnchor:_navBar.bottomAnchor],
72 | [_previewView.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor],
73 | [_previewView.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor],
74 | [_previewView.heightAnchor constraintEqualToConstant:60],
75 | [_previewView.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor],
76 | ]];
77 | }
78 | return self;
79 | }
80 |
81 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
82 | {
83 | UIView *touchView = [super hitTest:point withEvent:event];
84 | return touchView==self ? nil : touchView;
85 | }
86 |
87 | - (void)setDelegate:(id)delegate
88 | {
89 | _delegate = delegate;
90 | _previewView.delegate = delegate;
91 | }
92 |
93 | - (void)updateTitleAtIndex:(NSInteger)index sum:(NSInteger)sum
94 | {
95 | _navBarItem.title = [NSString stringWithFormat:@"%zd / %zd",index+1,sum];
96 | NSMutableAttributedString *firstAtt = [[NSMutableAttributedString alloc] initWithString:@(index+1).stringValue
97 | attributes:@{
98 | NSForegroundColorAttributeName:[UIColor colorWithWhite:0.9 alpha:1],
99 | NSFontAttributeName:[UIFont boldSystemFontOfSize:17]
100 | }];
101 | NSAttributedString *secondAtt = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" / %zd",sum]
102 | attributes:@{
103 | NSForegroundColorAttributeName:[UIColor colorWithWhite:0.9 alpha:1],
104 | NSFontAttributeName:[UIFont systemFontOfSize:14]
105 | }];
106 | [firstAtt appendAttributedString:secondAtt];
107 |
108 | UILabel *label = [[UILabel alloc] init];
109 | label.attributedText = firstAtt;
110 | _navBarItem.titleView = label;
111 | }
112 |
113 | #pragma amrk - actions
114 |
115 | - (void)selectedStateChanged
116 | {
117 | if (_rightButton.selected) {
118 | [[XYPhotoSelectedAssetManager sharedManager] removeSelectedAsset:_asset];
119 | _rightButton.selected = false;
120 | } else if ([[XYPhotoSelectedAssetManager sharedManager] addSelectedAsset:_asset]) {
121 | _rightButton.selected = true;
122 | }
123 | }
124 |
125 | - (void)close:(UIBarButtonItem *)item
126 | {
127 | if ([self.delegate respondsToSelector:@selector(previewOverlayView:closeBarButtonItemClick:)]) {
128 | [self.delegate previewOverlayView:self closeBarButtonItemClick:item];
129 | }
130 | }
131 |
132 | #pragma mark - private
133 |
134 | - (void)updateSelectedAsset:(PHAsset *)asset
135 | {
136 | _asset = asset;
137 | _rightButton.selected = [[XYPhotoSelectedAssetManager sharedManager].selectedAssets containsObject:asset];
138 | [_previewView scrollToAssetIfNeed:asset];
139 | }
140 |
141 | @end
142 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoSelectedAssetPreviewView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoSelectedAssetPreviewView.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | @import Photos;
11 |
12 | @class XYPhotoSelectedAssetPreviewView;
13 |
14 | @protocol XYPhotoSelectedAssetPreviewViewDelegate
15 |
16 | /**
17 | preview 被点击回调
18 |
19 | @param previewView self
20 | @param index 被点击的asset索引位置
21 | @param asset 被点击的asset
22 | */
23 | - (void)assetsSelectedPreviewView:(XYPhotoSelectedAssetPreviewView *)previewView didClickWithIndex:(NSInteger)index asset:(PHAsset *)asset;
24 |
25 | @end
26 |
27 |
28 | @interface XYPhotoSelectedAssetPreviewView : UIView
29 |
30 | /**
31 | 已选择的asset数组,并reload collectionView,监听系统的添加删除已选择的asset并做修改
32 | default: [XYPhotoSelectedAssetManager sharedManager].selectedAssets
33 | */
34 | @property (nonatomic, strong) NSArray *assetArray;
35 | @property (nonatomic, weak) id delegate;
36 |
37 | /**
38 | 隐藏右侧完成按钮及徽标
39 | default:NO
40 | */
41 | @property (nonatomic, assign) BOOL hideControls;
42 |
43 | /**
44 | 滚动到指定的asset,如果已选中的asset数组没有包含asset则不滚动
45 |
46 | @param asset 滚动到指定的照片asset
47 | */
48 | - (void)scrollToAssetIfNeed:(PHAsset *)asset;
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoSelectedAssetPreviewView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoSelectedAssetPreviewView..m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoSelectedAssetPreviewView.h"
10 | #import "XYPhotoCollectionFlowLayout.h"
11 | #import "XYPhotoSelectedAssetManager.h"
12 | #import "XYPhotoBottomPreviewCell.h"
13 | #import "XYPhotoKitHelper.h"
14 | #import "XYPhotoDataCategory.h"
15 |
16 | @interface XYPhotoSelectedAssetPreviewView ()
17 | {
18 | NSInteger _currentIndex;
19 | }
20 | @property (nonatomic, strong) UICollectionView *collectionView;
21 |
22 | @property (nonatomic, strong) UIButton *doneButton;
23 |
24 | @property (nonatomic, strong) UILabel *badgeLabel;
25 |
26 |
27 | @property (nonatomic, strong) UIView *backView;
28 | @property (nonatomic, strong) NSLayoutConstraint *backViewBottomConstraint;
29 | @property (nonatomic, strong) NSLayoutConstraint *doneTrailConstraint;
30 |
31 | @end
32 |
33 | @implementation XYPhotoSelectedAssetPreviewView
34 |
35 | - (void)dealloc
36 | {
37 | [[NSNotificationCenter defaultCenter] removeObserver:self];
38 | }
39 |
40 | - (id)initWithFrame:(CGRect)frame
41 | {
42 | self = [super initWithFrame:frame];
43 | if (self) {
44 | [self initBaseViews];
45 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectedAssetsChanged) name:XYPhotoMultiImagePickerNotifications.assetsChanged object:nil];
46 | // 用于初始化
47 | self.assetArray = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
48 | // 用于调整collection的cell布局(意义性不大,本可以忽略但是这边要保留,等同于`setAssetArray:`**不要去除这片脏代码**)
49 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
50 | self.assetArray = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
51 | self.doneButton.enabled = self.assetArray.count>0;
52 | self.badgeLabel.text = @(self.assetArray.count).stringValue;
53 | [self.collectionView reloadData];
54 | });
55 | }
56 | return self;
57 | }
58 |
59 | - (void)initBaseViews
60 | {
61 | self.backgroundColor = [UIColor clearColor];
62 | self.clipsToBounds = false;
63 | self.layer.masksToBounds = false;
64 |
65 | _backView = [UIView new];
66 | _backView.translatesAutoresizingMaskIntoConstraints = false;
67 | _backView.backgroundColor = [UIColor darkGrayColor];
68 | [self addSubview:_backView];
69 |
70 | // 预览已选择的assets
71 | XYPhotoCollectionFlowLayout *layout = [[XYPhotoCollectionFlowLayout alloc] init];
72 | layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
73 | self.collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:layout];
74 | self.collectionView.translatesAutoresizingMaskIntoConstraints = false;
75 | [self.collectionView registerClass:[XYPhotoBottomPreviewCell class] forCellWithReuseIdentifier:NSStringFromClass([XYPhotoBottomPreviewCell class])];
76 | self.collectionView.backgroundColor = [UIColor clearColor];
77 | self.collectionView.showsHorizontalScrollIndicator = false;
78 | self.collectionView.alwaysBounceHorizontal = true;
79 | self.collectionView.dataSource = self;
80 | self.collectionView.delegate = self;
81 | [self addSubview:self.collectionView];
82 |
83 | // 完成按钮
84 | self.doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
85 | self.doneButton.translatesAutoresizingMaskIntoConstraints = false;
86 | [self.doneButton setBackgroundImage:[UIImage xy_imageWithColor:[UIColor systemBlueColor]] forState:UIControlStateNormal];
87 | [self.doneButton setBackgroundImage:[UIImage xy_imageWithColor:[UIColor lightGrayColor]] forState:UIControlStateDisabled];
88 | self.doneButton.enabled = false;
89 | self.doneButton.layer.cornerRadius = 4;
90 | self.doneButton.clipsToBounds = true;
91 | self.doneButton.titleLabel.font = [UIFont systemFontOfSize:14];
92 | [self.doneButton setTitle:@"完成" forState:UIControlStateNormal];
93 | [self.doneButton addTarget:[XYPhotoSelectedAssetManager sharedManager].delegate action:@selector(doneSelectingAssets) forControlEvents:UIControlEventTouchUpInside];
94 | [self addSubview:self.doneButton];
95 |
96 | // 徽标
97 | self.badgeLabel = [[UILabel alloc] init];
98 | self.badgeLabel.translatesAutoresizingMaskIntoConstraints = false;
99 | self.badgeLabel.text = @"0";
100 | self.badgeLabel.backgroundColor = [UIColor whiteColor];
101 | self.badgeLabel.textAlignment = NSTextAlignmentCenter;
102 | self.badgeLabel.font = [UIFont systemFontOfSize:8];
103 | self.badgeLabel.textColor = [UIColor systemBlueColor];
104 | self.badgeLabel.layer.cornerRadius = 9;
105 | self.badgeLabel.clipsToBounds = true;
106 | [self addSubview:self.badgeLabel];
107 |
108 | UIEdgeInsets safeInsets = UIApplication.sharedApplication.keyWindow.safeAreaInsets;
109 | _backViewBottomConstraint = [_backView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:safeInsets.bottom];
110 | _doneTrailConstraint = [_doneButton.trailingAnchor constraintEqualToAnchor:self.trailingAnchor];
111 |
112 | [NSLayoutConstraint activateConstraints:@[
113 | [_backView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:-safeInsets.left],
114 | [_backView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:safeInsets.right],
115 | [_backView.topAnchor constraintEqualToAnchor:self.topAnchor],
116 | _backViewBottomConstraint,
117 |
118 | [_collectionView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:8],
119 | [_collectionView.topAnchor constraintEqualToAnchor:self.topAnchor constant:8],
120 | [_collectionView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-8],
121 | [_collectionView.trailingAnchor constraintEqualToAnchor:_doneButton.leadingAnchor constant:-15],
122 |
123 | [_doneButton.widthAnchor constraintEqualToConstant:55],
124 | [_doneButton.heightAnchor constraintEqualToConstant:30],
125 | [_doneButton.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
126 | _doneTrailConstraint,
127 |
128 | [_badgeLabel.widthAnchor constraintEqualToConstant:18],
129 | [_badgeLabel.heightAnchor constraintEqualToConstant:18],
130 | [_badgeLabel.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-6],
131 | [_badgeLabel.centerYAnchor constraintEqualToAnchor:self.centerYAnchor constant:-12],
132 | ]];
133 |
134 | self.hideControls = false;
135 | }
136 |
137 | - (void)setHideControls:(BOOL)hideControls
138 | {
139 | _hideControls = hideControls;
140 | _doneButton.hidden = hideControls;
141 | _badgeLabel.hidden = hideControls;
142 | _doneTrailConstraint.constant = hideControls ? (55+15-8) : -10;
143 |
144 | UIEdgeInsets safeInsets = UIApplication.sharedApplication.keyWindow.safeAreaInsets;
145 | _backViewBottomConstraint.constant = hideControls ? 0 : safeInsets.bottom;
146 | }
147 |
148 | - (void)setAssetArray:(NSArray *)assetArray
149 | {
150 | _assetArray = assetArray;
151 | _doneButton.enabled = assetArray.count>0;
152 | _badgeLabel.text = @(assetArray.count).stringValue;
153 | [_collectionView reloadData];
154 |
155 | // 添加完成滚动到最后一个
156 | if (assetArray.count>0) {
157 | [self setCurrentIndex:assetArray.count - 1];
158 | }
159 | }
160 |
161 | - (void)setCurrentIndex:(NSInteger)currentIndex
162 | {
163 | // 无符号与有符号比较出现bug ((NSInteger)-1) > ((NSUInteger)2)
164 | _currentIndex = currentIndex >= (NSInteger)_assetArray.count ? (_assetArray.count-1) : currentIndex;
165 | [_collectionView reloadData];
166 |
167 | if (_currentIndex >= 0) {
168 | [_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:_currentIndex inSection:0]
169 | atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
170 | animated:YES];
171 | }
172 | }
173 |
174 | - (void)scrollToAssetIfNeed:(PHAsset *)asset
175 | {
176 | if ([[XYPhotoSelectedAssetManager sharedManager].selectedAssets containsObject:asset]) {
177 | NSInteger index = [[XYPhotoSelectedAssetManager sharedManager].selectedAssets indexOfObject:asset];
178 | [self setCurrentIndex:index];
179 | } else {
180 | [self setCurrentIndex:-1];
181 | }
182 | }
183 |
184 | #pragma mark - UICollectionViewDataSource
185 |
186 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
187 | {
188 | XYPhotoSelectedAssetManager *manager = [XYPhotoSelectedAssetManager sharedManager];
189 | if (manager.maxNumberOfAssets==0 || self.assetArray.count= self.assetArray.count) {
218 | return;
219 | }
220 |
221 | if ([self.delegate respondsToSelector:@selector(assetsSelectedPreviewView:didClickWithIndex:asset:)]) {
222 | [self.delegate assetsSelectedPreviewView:self didClickWithIndex:indexPath.row asset:self.assetArray[indexPath.row]];
223 | }
224 | _currentIndex = indexPath.row;
225 | [_collectionView reloadData];
226 | }
227 |
228 | #pragma mark - Notification
229 |
230 | - (void)selectedAssetsChanged
231 | {
232 | self.assetArray = [XYPhotoSelectedAssetManager sharedManager].selectedAssets;
233 | [self.collectionView reloadData];
234 | }
235 |
236 | @end
237 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoTableCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoTableCell.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import Photos;
10 | @import UIKit;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | FOUNDATION_EXPORT NSString *const XYPhotoTableCellReuseIdentifier;
15 |
16 | static CGFloat const CLPhotoCollectionCellRowHeight = 86.0f;
17 |
18 | @interface XYPhotoTableCell : UITableViewCell
19 |
20 | @property (nonatomic, strong) PHAssetCollection *collection;
21 |
22 | @end
23 |
24 | NS_ASSUME_NONNULL_END
25 |
--------------------------------------------------------------------------------
/XYPhotoKit/Views/XYPhotoTableCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoTableCell.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoTableCell.h"
10 | #import "XYPhotoKitHelper.h"
11 | #import "XYPhotoSelectedAssetManager.h"
12 | #import "XYPhotoDataCategory.h"
13 |
14 | NSString *const XYPhotoTableCellReuseIdentifier = @"XYPhotoTableCellReuseIdentifier";
15 |
16 | @interface XYPhotoTableCell ()
17 |
18 | @property (nonatomic, strong) UIImageView *mainImageView;
19 | @property (nonatomic, strong) UIImageView *centerImageView;
20 | @property (nonatomic, strong) UIImageView *backImageView;
21 |
22 | @property (nonatomic, strong) UILabel *titleLabel;
23 | @property (nonatomic, strong) UILabel *subtitleLabel;
24 |
25 | @end
26 |
27 | @implementation XYPhotoTableCell
28 |
29 | - (instancetype)init
30 | {
31 | if (self = [super init]) {
32 | [self commonSetup];
33 | }
34 | return self;
35 | }
36 |
37 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
38 | {
39 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
40 | [self commonSetup];
41 | }
42 | return self;
43 | }
44 |
45 | - (id)initWithCoder:(NSCoder *)aDecoder
46 | {
47 | if (self = [super initWithCoder:aDecoder]) {
48 | [self commonSetup];
49 | }
50 | return self;
51 | }
52 |
53 | - (instancetype)initWithFrame:(CGRect)frame {
54 | if (self = [super initWithFrame:frame]) {
55 | [self commonSetup];
56 | }
57 | return self;
58 | }
59 |
60 | - (void)commonSetup
61 | {
62 | self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
63 |
64 | UIImageView *(^creatImageBlock)(CGRect frame) = ^(CGRect frame){
65 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
66 | imageView.contentMode = UIViewContentModeScaleAspectFill;
67 | imageView.clipsToBounds = true;
68 | imageView.layer.borderColor = [UIColor whiteColor].CGColor;
69 | imageView.layer.borderWidth = 1.0/[UIScreen mainScreen].scale;
70 | [self.contentView addSubview:imageView];
71 | return imageView;
72 | };
73 |
74 | CGFloat width = 68, left = 16, top = 11;
75 | self.backImageView = creatImageBlock(CGRectMake(left+2*2, top-2*2, width-4*2, width-4*2));
76 | self.centerImageView = creatImageBlock(CGRectMake(left+2, top-2, width-4, width-4));
77 | self.mainImageView = creatImageBlock(CGRectMake(left, top, width, width));
78 |
79 | self.titleLabel = [[UILabel alloc] init];
80 | [self.contentView addSubview:self.titleLabel];
81 |
82 | self.subtitleLabel = [[UILabel alloc] init];
83 | self.subtitleLabel.font = [UIFont systemFontOfSize:12];
84 | [self.contentView addSubview:self.subtitleLabel];
85 | }
86 |
87 | - (void)layoutSubviews
88 | {
89 | [super layoutSubviews];
90 |
91 | self.titleLabel.frame = CGRectMake(100, 20, self.contentView.frame.size.width-100, 20);
92 | self.subtitleLabel.frame = CGRectMake(100, CGRectGetMaxY(self.titleLabel.frame)+4, self.contentView.frame.size.width-100, 15);
93 | }
94 |
95 | - (void)setCollection:(PHAssetCollection *)collection
96 | {
97 | _collection = collection;
98 |
99 | PHFetchOptions *fetchOptions = [PHFetchOptions new];
100 | fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
101 |
102 | PHAssetMediaType type = [XYPhotoSelectedAssetManager sharedManager].mediaType;
103 | if (type != PHAssetMediaTypeUnknown) {
104 | fetchOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType == %@", @(type)];
105 | }
106 |
107 | PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions];
108 |
109 | if (fetchResult.count<=0) {
110 | UIImage *placeholderImage = [UIImage xy_imageWithName:@"cl_photokit_placeholder"];
111 | [@[_mainImageView, _centerImageView, _backImageView] enumerateObjectsUsingBlock:^(UIImageView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
112 | obj.hidden = false;
113 | obj.image = placeholderImage;
114 | }];
115 | } else {
116 | [self setImageView:_mainImageView withAsset:fetchResult.firstObject];
117 |
118 | _centerImageView.hidden = fetchResult.count<2;
119 | if (!_centerImageView.hidden) {
120 | [self setImageView:_centerImageView withAsset:fetchResult[1]];
121 | }
122 |
123 | _backImageView.hidden = fetchResult.count<3;
124 | if (!_backImageView.hidden) {
125 | [self setImageView:_backImageView withAsset:fetchResult[2]];
126 | }
127 | }
128 |
129 | self.titleLabel.text = collection.localizedTitle;
130 | self.subtitleLabel.text = [NSString stringWithFormat:@"%lu", (long)fetchResult.count];
131 | }
132 |
133 | - (void)setImageView:(UIImageView *)imageView withAsset:(PHAsset *)asset
134 | {
135 | PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
136 | options.networkAccessAllowed = [XYPhotoSelectedAssetManager sharedManager].allowNetRequestIfiCloud;
137 |
138 | [[PHImageManager defaultManager] requestImageForAsset:asset
139 | targetSize:CGSizeScale(imageView.frame.size, [UIScreen mainScreen].scale)
140 | contentMode:PHImageContentModeAspectFill
141 | options:options
142 | resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
143 | imageView.image = result;
144 | }];
145 | }
146 |
147 | + (CGFloat)cellHeightWithItem:(id)item tableView:(UITableView *)tableView
148 | {
149 | return CLPhotoCollectionCellRowHeight;
150 | }
151 |
152 | @end
153 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoCollectionFlowLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCollectionFlowLayout.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 |
10 | @import UIKit;
11 |
12 | FOUNDATION_EXPORT NSUInteger const XYPhotoCollectionFlowLayoutDefaultColumns;
13 |
14 | @interface XYPhotoCollectionFlowLayout : UICollectionViewFlowLayout
15 |
16 | @end
17 |
18 |
19 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoCollectionFlowLayout.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoCollectionFlowLayout.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoCollectionFlowLayout.h"
10 |
11 | #import "XYPhotoSelectedAssetManager.h"
12 |
13 | NSUInteger const XYPhotoCollectionFlowLayoutDefaultColumns = 3;
14 |
15 | @interface XYPhotoCollectionFlowLayout ()
16 |
17 | @property (nonatomic) NSUInteger numberOfColumns;
18 |
19 | @end
20 |
21 | @implementation XYPhotoCollectionFlowLayout
22 |
23 | static CGFloat const XYPhotoCollectionFlowLayoutDefaultSpacing = 2.0f;
24 |
25 | - (instancetype)init
26 | {
27 | if (self = [super init]) {
28 | self.scrollDirection = UICollectionViewScrollDirectionVertical;
29 | [self setupDefaults];
30 | }
31 | return self;
32 | }
33 |
34 | - (void)awakeFromNib
35 | {
36 | [super awakeFromNib];
37 | [self setupDefaults];
38 | }
39 |
40 | - (void)setupDefaults
41 | {
42 | _numberOfColumns = [XYPhotoSelectedAssetManager sharedManager].assetCollectionViewColumnCount;
43 |
44 | self.minimumInteritemSpacing = XYPhotoCollectionFlowLayoutDefaultSpacing;
45 | self.minimumLineSpacing = XYPhotoCollectionFlowLayoutDefaultSpacing;
46 | }
47 |
48 | - (void)setScrollDirection:(UICollectionViewScrollDirection)scrollDirection
49 | {
50 | if (scrollDirection == UICollectionViewScrollDirectionVertical) {
51 | self.minimumInteritemSpacing = XYPhotoCollectionFlowLayoutDefaultSpacing;
52 | self.minimumLineSpacing = XYPhotoCollectionFlowLayoutDefaultSpacing;
53 | } else {
54 | self.minimumInteritemSpacing = 0;
55 | self.minimumLineSpacing = XYPhotoCollectionFlowLayoutDefaultSpacing*3;
56 | }
57 | [super setScrollDirection:scrollDirection];
58 | }
59 |
60 | - (CGSize)itemSize
61 | {
62 | if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
63 | CGFloat totalWidth = CGRectGetWidth(self.collectionView.frame);
64 | totalWidth -= (self.minimumInteritemSpacing * (self.numberOfColumns - 1));
65 | CGFloat cellDimension = totalWidth / self.numberOfColumns;
66 | return CGSizeMake(cellDimension, cellDimension);
67 | } else {
68 | CGFloat totalHeight = CGRectGetHeight(self.collectionView.frame);
69 | totalHeight -= self.minimumInteritemSpacing * 2;
70 | return CGSizeMake(totalHeight, totalHeight);
71 | }
72 | }
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoDataCategory.h:
--------------------------------------------------------------------------------
1 | //
2 | // CLPhotoDataCategory.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | @import Photos;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface NSString (CLPhotoKit)
15 |
16 | @property (nonatomic, readonly) BOOL xy_isEmpty;
17 |
18 | @end
19 |
20 | @interface NSIndexSet (CLPhotoKit)
21 |
22 | - (NSArray *)xy_indexPathsFromIndexesWithSection:(NSUInteger)section;
23 |
24 | @end
25 |
26 | @interface PHFetchResult (CLPhotoKit)
27 |
28 | + (instancetype)xy_fetchResultWithAssetCollection:(PHAssetCollection *)assetCollection mediaType:(PHAssetMediaType)type;
29 |
30 | + (instancetype)xy_fetchResultWithAssetsOfType:(PHAssetMediaType)type;
31 |
32 | @end
33 |
34 |
35 | @interface UICollectionView (CLPhotoKit)
36 |
37 | - (nullable NSArray *)xy_indexPathsForElementsInRect:(CGRect)rect;
38 |
39 | @end
40 |
41 | @interface UIImage (CLPhotoKit)
42 |
43 | + (nullable UIImage *)xy_imageWithName:(NSString *)imageName;
44 |
45 | + (UIImage *)xy_imageWithColor:(UIColor *)color;
46 |
47 |
48 | @end
49 |
50 | @interface UIAlertController (CLPhotoKit)
51 |
52 | + (BOOL)xy_showAlertPhotoSettingIfUnauthorized;
53 |
54 | + (BOOL)xy_showAlertCameraSettingIfUnauthorized;
55 |
56 | + (void)xy_showTitle:(nullable NSString *)title message:(nullable NSString *)msg;
57 |
58 | @end
59 |
60 | @interface UIApplication (CLPhotoKit)
61 |
62 | @property (nonatomic, readonly, nullable) UIViewController *xy_topViewController;
63 |
64 | @end
65 |
66 |
67 | @interface PHAsset (CLPhotoKit)
68 |
69 | + (nullable PHAsset *)xy_getTheCloestAsset;
70 |
71 | //
72 | ///**
73 | // 同步请求指定大小图片
74 | //
75 | // @param size 目标尺寸
76 | // @return 图片
77 | // */
78 | //- (UIImage *)xy_getSynchImageSize:(CGSize)size;
79 | //
80 | ///**
81 | // 同步请求指定大小缩略图
82 | //
83 | // @param size 目标尺寸
84 | // @return 图片
85 | // */
86 | //- (UIImage *)xy_getSynchThumbnailWithSize:(CGSize)size;
87 | //
88 | ///**
89 | // 同步请求原图
90 | //
91 | // @return 图片
92 | // */
93 | //- (UIImage *)xy_getSynchOriginImage;
94 | //
95 |
96 | @end
97 |
98 | NS_ASSUME_NONNULL_END
99 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoDataCategory.m:
--------------------------------------------------------------------------------
1 | //
2 | // CLPhotoDataCategory.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoDataCategory.h"
10 |
11 | @import AVFoundation;
12 | @import Photos;
13 |
14 | @implementation NSString (CLPhotoKit)
15 |
16 | - (BOOL)xy_isEmpty
17 | {
18 | NSString *tempStr = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
19 | if ([tempStr length] == 0 || [self isEqualToString:@"NULL"] || [self isEqualToString:@"null"] || [self isEqualToString:@"(null)"]) {
20 | return YES;
21 | }
22 | return NO;
23 | }
24 |
25 | @end
26 |
27 | @implementation NSIndexSet (CLPhotoKit)
28 |
29 | - (NSArray *)xy_indexPathsFromIndexesWithSection:(NSUInteger)section
30 | {
31 | NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:self.count];
32 | [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
33 | [indexPaths addObject:[NSIndexPath indexPathForItem:idx inSection:section]];
34 | }];
35 | return indexPaths;
36 | }
37 |
38 | @end
39 |
40 |
41 | @implementation PHFetchResult (CLPhotoKit)
42 |
43 | + (instancetype)xy_fetchResultWithAssetCollection:(PHAssetCollection *)assetCollection mediaType:(PHAssetMediaType)type
44 | {
45 | PHFetchOptions *fetchOptions = [PHFetchOptions new];
46 |
47 | if (type != PHAssetMediaTypeUnknown) {
48 | fetchOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType == %@", @(type)];
49 | }
50 |
51 | fetchOptions.sortDescriptors = @[[self xy_creationDateSortDescriptor]];
52 | return [PHAsset fetchAssetsInAssetCollection:assetCollection options:fetchOptions];
53 | }
54 |
55 | + (instancetype)xy_fetchResultWithAssetsOfType:(PHAssetMediaType)type
56 | {
57 | PHFetchOptions *fetchOptions = [PHFetchOptions new];
58 | fetchOptions.sortDescriptors = @[[self xy_creationDateSortDescriptor]];
59 |
60 | if (type != PHAssetMediaTypeUnknown) {
61 | return [PHAsset fetchAssetsWithMediaType:type options:fetchOptions];
62 | }
63 | return [PHAsset fetchAssetsWithOptions:fetchOptions];
64 | }
65 |
66 | + (NSSortDescriptor *)xy_creationDateSortDescriptor
67 | {
68 | return [NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO];
69 | }
70 |
71 | @end
72 |
73 |
74 | @implementation UICollectionView (CLPhotoKit)
75 |
76 | - (NSArray *)xy_indexPathsForElementsInRect:(CGRect)rect
77 | {
78 | NSArray *allLayoutAttributes = [self.collectionViewLayout layoutAttributesForElementsInRect:rect];
79 | if (!allLayoutAttributes.count) {
80 | return nil;
81 | }
82 |
83 | NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:allLayoutAttributes.count];
84 | for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes) {
85 | NSIndexPath *indexPath = layoutAttributes.indexPath;
86 | [indexPaths addObject:indexPath];
87 | }
88 | return indexPaths;
89 | }
90 |
91 | @end
92 |
93 | @implementation UIAlertController (CLPhotoKit)
94 |
95 | + (BOOL)xy_showAlertPhotoSettingIfUnauthorized
96 | {
97 | PHAuthorizationStatus photoStatus = PHPhotoLibrary.authorizationStatus;
98 | if (photoStatus == PHAuthorizationStatusRestricted || photoStatus == PHAuthorizationStatusDenied) {
99 | NSString *mes = photoStatus==PHAuthorizationStatusDenied ? @"该功能需要相册服务,你可以在设置中打开对App的相册服务":@"该设备的相册功能被限制了";
100 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:mes preferredStyle:UIAlertControllerStyleAlert];
101 | [alertController addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:nil]];
102 | if (photoStatus == PHAuthorizationStatusDenied) {
103 | [alertController addAction:[UIAlertAction actionWithTitle:@"去开启" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
104 | [UIApplication.sharedApplication openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
105 | }]];
106 | }
107 | [[UIApplication sharedApplication].xy_topViewController presentViewController:alertController animated:YES completion:nil];
108 | return NO;
109 | }
110 | return YES;
111 | }
112 |
113 | + (BOOL)xy_showAlertCameraSettingIfUnauthorized
114 | {
115 | AVAuthorizationStatus videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
116 | if (videoStatus==AVAuthorizationStatusDenied || videoStatus==AVAuthorizationStatusRestricted) {
117 | NSString *mes = videoStatus==AVAuthorizationStatusDenied ? @"该功能需要相机服务,你可以在设置中打开对App的相机服务":@"该应用的相机功能被限制了";
118 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:mes preferredStyle:UIAlertControllerStyleAlert];
119 | [alertController addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:nil]];
120 | if (videoStatus == AVAuthorizationStatusDenied) {
121 | [alertController addAction:[UIAlertAction actionWithTitle:@"去开启" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
122 | [UIApplication.sharedApplication openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
123 | }]];
124 | }
125 | [[UIApplication sharedApplication].xy_topViewController presentViewController:alertController animated:YES completion:nil];
126 | return NO;
127 | }
128 | return YES;
129 | }
130 |
131 | + (void)xy_showTitle:(NSString *)title message:(NSString *)msg
132 | {
133 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
134 | [alert addAction:[UIAlertAction actionWithTitle:@"Sure" style:UIAlertActionStyleCancel handler:nil]];
135 | [[UIApplication sharedApplication].xy_topViewController presentViewController:alert animated:YES completion:nil];
136 | }
137 |
138 | @end
139 |
140 | @implementation UIImage (CLPhotoKit)
141 |
142 | + (UIImage *)xy_imageWithName:(NSString *)imageName
143 | {
144 | UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"XYPhotoKit.bundle/image/%@",imageName]];
145 | return image;
146 | }
147 |
148 | + (UIImage *)xy_imageWithColor:(UIColor *)color
149 | {
150 | CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
151 | UIGraphicsBeginImageContext(rect.size);
152 | CGContextRef context = UIGraphicsGetCurrentContext();
153 |
154 | CGContextSetFillColorWithColor(context, [color CGColor]);
155 | CGContextFillRect(context, rect);
156 |
157 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
158 | UIGraphicsEndImageContext();
159 |
160 | return image;
161 | }
162 |
163 | @end
164 |
165 | @implementation UIApplication (CLPhotoKit)
166 |
167 | - (UIViewController *)xy_topViewController
168 | {
169 | UIViewController *rootController = [[[UIApplication sharedApplication].delegate window] rootViewController];
170 |
171 | while (rootController.presentedViewController!=nil || [rootController isKindOfClass:[UINavigationController class]]) {
172 | if (rootController.presentedViewController!=nil) {
173 | rootController = rootController.presentedViewController;
174 | }
175 | if ([rootController isKindOfClass:[UINavigationController class]]) {
176 | rootController = [[(UINavigationController *)rootController viewControllers] lastObject];
177 | }
178 | }
179 | return rootController;
180 | }
181 |
182 | @end
183 |
184 | @implementation PHAsset (CLPhotoKit)
185 |
186 | + (nullable PHAsset *)xy_getTheCloestAsset
187 | {
188 | PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
189 | fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
190 | PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
191 | return [fetchResult lastObject];
192 | }
193 |
194 | //- (UIImage *)xy_getImageSize:(CGSize)size
195 | //{
196 | // return nil;
197 | //}
198 | //
199 | //- (UIImage *)xy_getThumbnailWithSize:(CGSize)size
200 | //{
201 | // return nil;
202 | //}
203 | //
204 | //- (UIImage *)xy_getOriginImage
205 | //{
206 | // return nil;
207 | //}
208 | //
209 | //- (UIImage *)xy_getImageWithSize:(CGSize)size iCloud:(BOOL)iCould
210 | //{
211 | // return nil;
212 | //}
213 |
214 | @end
215 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_camera_devicemode@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_camera_devicemode@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_camera_flash@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_camera_flash@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_inaccessible@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_inaccessible@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_picked@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_picked@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_shutter@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_shutter@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_shutter_h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_shutter_h@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_unpicked@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photo_picker_unpicked@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photokit_placeholder@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photokit_placeholder@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photokit_play@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photokit_play@2x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKit.bundle/image/cl_photokit_play@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKit/XYPhotoKit.bundle/image/cl_photokit_play@3x.png
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKitHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoKitHelper.h
3 | // XYPhotoKitDemo-iOS
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 |
11 |
12 | CG_INLINE CGSize
13 | CGSizeScale(CGSize size, CGFloat scale) {
14 | return CGSizeMake(size.width * scale, size.height * scale);
15 | }
16 |
17 | @interface XYPhotoKitHelper : NSObject
18 |
19 | //通知名字
20 | FOUNDATION_EXPORT const struct XYPhotoMultiImagePickerNotifications {
21 | // asset 添加或删除.
22 | __unsafe_unretained NSString *assetsChanged;
23 | // 成功删除
24 | __unsafe_unretained NSString *assetsDeleted;
25 | // 成功添加
26 | __unsafe_unretained NSString *assetsAdded;
27 |
28 | } XYPhotoMultiImagePickerNotifications;
29 |
30 |
31 | FOUNDATION_EXPORT const struct XYPhotoImagePickerName {
32 | __unsafe_unretained NSString *selectAnAlbum;
33 | __unsafe_unretained NSString *cameraRoll;
34 | } XYPhotoImagePickerName;
35 |
36 |
37 | + (BOOL)isAvailableAccessPhoto;
38 |
39 | + (BOOL)isAuthorizedAccessPhoto;
40 |
41 | + (BOOL)isAvailableAccessCamera;
42 |
43 | + (BOOL)isAuthorizedAccessCamera;
44 |
45 |
46 | @end
47 |
48 |
49 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoKitHelper.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoKitHelper.m
3 | // XYPhotoKitDemo-iOS
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 | #import "XYPhotoKitHelper.h"
9 |
10 | @import AVFoundation;
11 | @import Photos;
12 |
13 | @implementation XYPhotoKitHelper
14 |
15 | const struct XYPhotoMultiImagePickerNotifications XYPhotoMultiImagePickerNotifications = {
16 | .assetsChanged = @"XYPhotoMultiImagePickerNotificationAssetsChanged",
17 | .assetsDeleted = @"XYPhotoMultiImagePickerNotificationAssetsDeleted",
18 | .assetsAdded = @"XYPhotoMultiImagePickerNotificationAssetsAdded",
19 | };
20 |
21 | const struct XYPhotoImagePickerName XYPhotoImagePickerName = {
22 | .selectAnAlbum = @"相册列表",
23 | .cameraRoll = @"Camera Roll",
24 | };
25 |
26 | + (BOOL)isAvailableAccessPhoto
27 | {
28 | PHAuthorizationStatus status = PHPhotoLibrary.authorizationStatus;
29 | return status == PHAuthorizationStatusAuthorized || status == PHAuthorizationStatusNotDetermined;
30 | }
31 |
32 | + (BOOL)isAuthorizedAccessPhoto
33 | {
34 | PHAuthorizationStatus status = PHPhotoLibrary.authorizationStatus;
35 | return status == PHAuthorizationStatusAuthorized;
36 | }
37 |
38 | + (BOOL)isAvailableAccessCamera
39 | {
40 | AVAuthorizationStatus videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
41 | BOOL hasCamera = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
42 | return hasCamera && (videoStatus == AVAuthorizationStatusAuthorized || videoStatus == AVAuthorizationStatusNotDetermined);
43 | }
44 |
45 | + (BOOL)isAuthorizedAccessCamera
46 | {
47 | AVAuthorizationStatus videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
48 | BOOL hasCamera = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
49 | return hasCamera && videoStatus == AVAuthorizationStatusAuthorized;
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoSelectedAssetManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoSelectedAssetManager.h
3 | // XYPhotoKitDemo-iOS
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 | @import Photos;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @protocol XYPhotoSelectedAssetManagerDelegate
15 | - (void)doneSelectingAssets;
16 | @end
17 |
18 | @interface XYPhotoSelectedAssetManager : NSObject
19 |
20 | + (instancetype)alloc __attribute__((unavailable("此方法已弃用,请使用 sharedManager")));
21 | - (instancetype)init __attribute__((unavailable("此方法已弃用,请使用 sharedManager")));
22 | + (instancetype)new __attribute__((unavailable("此方法已弃用,请使用 sharedManager")));
23 |
24 | + (instancetype)sharedManager;
25 |
26 | /**
27 | 为了下次使用重置单例.
28 | */
29 | - (void)resetManager;
30 |
31 | /**
32 | 代理回调,结束asset选择
33 | */
34 | @property (nonatomic, weak, nullable) id delegate;
35 |
36 | /**
37 | Unknown = 0,Image,Video,Audio
38 | */
39 | @property (nonatomic) PHAssetMediaType mediaType;
40 |
41 | /**
42 | 是否允许网络请求从iCloud下载
43 | */
44 | @property (nonatomic) BOOL allowNetRequestIfiCloud;
45 |
46 | /**
47 | 横向列数
48 | */
49 | @property (nonatomic) NSInteger assetCollectionViewColumnCount;
50 |
51 | /**
52 | Defaults=0,可以无限制选择asset个数
53 | */
54 | @property (nonatomic) NSUInteger maxNumberOfAssets;
55 |
56 | /**
57 | 达到最大限制后的文案说明
58 | */
59 | @property (nonatomic, copy, nullable) NSString *maxNumberLimitText;
60 |
61 | /**
62 | 添加资源,要求asset类型要匹配
63 | @param asset 添加的asset
64 | @return yes成功,no失败(根据最大个数和asset类型判断)
65 | */
66 | - (BOOL)addSelectedAsset:(PHAsset *)asset;
67 |
68 | /**
69 | 删除资源,无限制
70 | @param asset 被删除asset
71 | */
72 | - (void)removeSelectedAsset:(PHAsset *)asset;
73 |
74 | /**
75 | 重置已选中的asset
76 | */
77 | - (void)resetSelectedAsset:(nullable NSArray *)assets;
78 |
79 | /**
80 | 已选择的asset列表
81 | @return 浅拷贝返回
82 | */
83 | - (NSArray *)selectedAssets;
84 |
85 | @end
86 |
87 | NS_ASSUME_NONNULL_END
88 |
--------------------------------------------------------------------------------
/XYPhotoKit/XYPhotoSelectedAssetManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // XYPhotoSelectedAssetManager.m
3 | // XYPhotoKitDemo-iOS
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYPhotoSelectedAssetManager.h"
10 | #import "XYPhotoDataCategory.h"
11 | #import "XYPhotoCollectionViewCell.h"
12 | #import "XYPhotoCollectionFlowLayout.h"
13 | #import "XYPhotoKitHelper.h"
14 |
15 | @interface XYPhotoSelectedAssetManager ()
16 |
17 | @property (nonatomic) NSMutableArray *selectedAssetsMutableArray;
18 |
19 | @end
20 |
21 | @implementation XYPhotoSelectedAssetManager
22 |
23 | + (instancetype)sharedManager
24 | {
25 | static XYPhotoSelectedAssetManager *SharedManager = nil;
26 |
27 | static dispatch_once_t onceToken;
28 | dispatch_once(&onceToken, ^{
29 | SharedManager = [[super alloc] initUniqueInstance];
30 | [SharedManager resetManager];
31 | });
32 |
33 | return SharedManager;
34 | }
35 |
36 | -(instancetype)initUniqueInstance;
37 | {
38 | return [super init];
39 | }
40 |
41 | - (void)resetManager
42 | {
43 | self.selectedAssetsMutableArray = [NSMutableArray array];
44 | self.mediaType = PHAssetMediaTypeUnknown;
45 | self.assetCollectionViewColumnCount = XYPhotoCollectionFlowLayoutDefaultColumns;
46 | self.maxNumberOfAssets = 0;
47 | }
48 |
49 | - (BOOL)addSelectedAsset:(PHAsset *)asset
50 | {
51 | if ([self assetIsInMediaType:asset] && [self canAddMoreAssets]) {
52 | if (![self.selectedAssetsMutableArray containsObject:asset]) {
53 | [self.selectedAssetsMutableArray addObject:asset];
54 | }
55 |
56 | [[NSNotificationCenter defaultCenter] postNotificationName:XYPhotoMultiImagePickerNotifications.assetsChanged object:asset];
57 | return YES;
58 | }
59 | return NO;
60 | }
61 |
62 | - (BOOL)canAddMoreAssets
63 | {
64 | if (self.maxNumberOfAssets == 0) {
65 | return YES;
66 | } else if (self.selectedAssetsMutableArray.count < self.maxNumberOfAssets) {
67 | return YES;
68 | } else {
69 | NSString *tip = (self.maxNumberLimitText ?: @"").xy_isEmpty ? @"已达到最大照片数限制" : self.maxNumberLimitText;
70 | [UIAlertController xy_showTitle:@"友情提示" message:tip];
71 | return NO;
72 | }
73 | }
74 |
75 | - (void)removeSelectedAsset:(PHAsset *)asset
76 | {
77 | [self.selectedAssetsMutableArray removeObject:asset];
78 |
79 | [[NSNotificationCenter defaultCenter] postNotificationName:XYPhotoMultiImagePickerNotifications.assetsChanged object:asset];
80 | }
81 |
82 | - (void)resetSelectedAsset:(NSArray *)assets
83 | {
84 | [self.selectedAssetsMutableArray removeAllObjects];
85 |
86 | NSMutableArray *deleteAssets = @[].mutableCopy;
87 | [assets enumerateObjectsUsingBlock:^(PHAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
88 | if (![self assetIsInMediaType:obj]) {
89 | [deleteAssets addObject:obj];
90 | }
91 | }];
92 | NSMutableArray *newAssets = assets.mutableCopy;
93 | [newAssets removeObjectsInArray:deleteAssets];
94 |
95 | if (self.maxNumberOfAssets <= 0 || (self.maxNumberOfAssets>0 && self.maxNumberOfAssets>newAssets.count)) {
96 | // 个数无限制 或者 未达到最大个数限制
97 | [self.selectedAssetsMutableArray addObjectsFromArray:newAssets];
98 | } else {
99 | // 超出限制个数,裁剪
100 | [self.selectedAssetsMutableArray addObjectsFromArray:[newAssets subarrayWithRange:NSMakeRange(0, self.maxNumberOfAssets)]];
101 | }
102 |
103 | [[NSNotificationCenter defaultCenter] postNotificationName:XYPhotoMultiImagePickerNotifications.assetsChanged object:nil];
104 | }
105 |
106 | - (NSArray *)selectedAssets
107 | {
108 | return [self.selectedAssetsMutableArray copy];
109 | }
110 |
111 | #pragma mark - Helpers
112 |
113 | - (BOOL)assetIsInMediaType:(PHAsset *)asset;
114 | {
115 | if (![asset isKindOfClass:[PHAsset class]]) {
116 | return NO;
117 | }
118 |
119 | if (self.mediaType == PHAssetMediaTypeUnknown) {
120 | return YES;
121 | } else if (asset.mediaType == self.mediaType) {
122 | return YES;
123 | }
124 | return NO;
125 | }
126 |
127 | @end
128 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 651B29681F6EC1730085BCE3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 651B29671F6EC1730085BCE3 /* AppDelegate.m */; };
11 | 651B296E1F6EC1730085BCE3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 651B296C1F6EC1730085BCE3 /* Main.storyboard */; };
12 | 651B29701F6EC1730085BCE3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 651B296F1F6EC1730085BCE3 /* Assets.xcassets */; };
13 | 651B29731F6EC1730085BCE3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 651B29711F6EC1730085BCE3 /* LaunchScreen.storyboard */; };
14 | 651B29761F6EC1730085BCE3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 651B29751F6EC1730085BCE3 /* main.m */; };
15 | 658075301F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075051F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.m */; };
16 | 658075311F6EDB62002408A5 /* XYPhotoDataCategory.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075071F6EDB62002408A5 /* XYPhotoDataCategory.m */; };
17 | 658075321F6EDB62002408A5 /* XYPhotoKitHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075091F6EDB62002408A5 /* XYPhotoKitHelper.m */; };
18 | 658075331F6EDB62002408A5 /* XYPhotoSelectedAssetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6580750B1F6EDB62002408A5 /* XYPhotoSelectedAssetManager.m */; };
19 | 658075341F6EDB62002408A5 /* XYPhotoKit.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 6580750C1F6EDB62002408A5 /* XYPhotoKit.bundle */; };
20 | 658075351F6EDB62002408A5 /* XYPhotoCollectionDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 6580750F1F6EDB62002408A5 /* XYPhotoCollectionDataSource.m */; };
21 | 658075361F6EDB62002408A5 /* XYPhotoTableDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075111F6EDB62002408A5 /* XYPhotoTableDataSource.m */; };
22 | 658075371F6EDB62002408A5 /* XYPhotoAlbumDetailController.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075141F6EDB62002408A5 /* XYPhotoAlbumDetailController.m */; };
23 | 658075381F6EDB62002408A5 /* XYPhotoAlbumListController.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075161F6EDB62002408A5 /* XYPhotoAlbumListController.m */; };
24 | 658075391F6EDB62002408A5 /* XYPhotoCameraController.m in Sources */ = {isa = PBXBuildFile; fileRef = 658075181F6EDB62002408A5 /* XYPhotoCameraController.m */; };
25 | 6580753A1F6EDB62002408A5 /* XYPhotoMultiCameraPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6580751A1F6EDB62002408A5 /* XYPhotoMultiCameraPicker.m */; };
26 | 6580753B1F6EDB62002408A5 /* XYPhotoMultiImagePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6580751C1F6EDB62002408A5 /* XYPhotoMultiImagePicker.m */; };
27 | 6580753C1F6EDB62002408A5 /* XYPhotoPreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6580751E1F6EDB62002408A5 /* XYPhotoPreviewViewController.m */; };
28 | DBFD4E3923FFDAC000C1A472 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E3523FFDABF00C1A472 /* RootViewController.m */; };
29 | DBFD4E3A23FFDAC000C1A472 /* XYDemoCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E3723FFDABF00C1A472 /* XYDemoCollectionViewCell.m */; };
30 | DBFD4E4B23FFE51000C1A472 /* XYPhotoBottomPreviewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E3C23FFE50F00C1A472 /* XYPhotoBottomPreviewCell.m */; };
31 | DBFD4E4C23FFE51000C1A472 /* XYPhotoPreviewOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4123FFE51000C1A472 /* XYPhotoPreviewOverlayView.m */; };
32 | DBFD4E4D23FFE51000C1A472 /* XYPhotoHorizontalScrollItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4323FFE51000C1A472 /* XYPhotoHorizontalScrollItemView.m */; };
33 | DBFD4E4E23FFE51000C1A472 /* XYPhotoCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4423FFE51000C1A472 /* XYPhotoCollectionViewCell.m */; };
34 | DBFD4E4F23FFE51000C1A472 /* XYPhotoSelectedAssetPreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4523FFE51000C1A472 /* XYPhotoSelectedAssetPreviewView.m */; };
35 | DBFD4E5023FFE51000C1A472 /* XYPhotoTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4623FFE51000C1A472 /* XYPhotoTableCell.m */; };
36 | DBFD4E5123FFE51000C1A472 /* XYPhotoCameraOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4823FFE51000C1A472 /* XYPhotoCameraOverlayView.m */; };
37 | DBFD4E5223FFE51000C1A472 /* XYPhotoHorizontalScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = DBFD4E4A23FFE51000C1A472 /* XYPhotoHorizontalScrollView.m */; };
38 | /* End PBXBuildFile section */
39 |
40 | /* Begin PBXFileReference section */
41 | 651B29631F6EC1730085BCE3 /* XYPhotoKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XYPhotoKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 651B29661F6EC1730085BCE3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
43 | 651B29671F6EC1730085BCE3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
44 | 651B296D1F6EC1730085BCE3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
45 | 651B296F1F6EC1730085BCE3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
46 | 651B29721F6EC1730085BCE3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
47 | 651B29741F6EC1730085BCE3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 651B29751F6EC1730085BCE3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
49 | 658075041F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoCollectionFlowLayout.h; sourceTree = ""; };
50 | 658075051F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoCollectionFlowLayout.m; sourceTree = ""; };
51 | 658075061F6EDB62002408A5 /* XYPhotoDataCategory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoDataCategory.h; sourceTree = ""; };
52 | 658075071F6EDB62002408A5 /* XYPhotoDataCategory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoDataCategory.m; sourceTree = ""; };
53 | 658075081F6EDB62002408A5 /* XYPhotoKitHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoKitHelper.h; sourceTree = ""; };
54 | 658075091F6EDB62002408A5 /* XYPhotoKitHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoKitHelper.m; sourceTree = ""; };
55 | 6580750A1F6EDB62002408A5 /* XYPhotoSelectedAssetManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoSelectedAssetManager.h; sourceTree = ""; };
56 | 6580750B1F6EDB62002408A5 /* XYPhotoSelectedAssetManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoSelectedAssetManager.m; sourceTree = ""; };
57 | 6580750C1F6EDB62002408A5 /* XYPhotoKit.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = XYPhotoKit.bundle; sourceTree = ""; };
58 | 6580750E1F6EDB62002408A5 /* XYPhotoCollectionDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoCollectionDataSource.h; sourceTree = ""; };
59 | 6580750F1F6EDB62002408A5 /* XYPhotoCollectionDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoCollectionDataSource.m; sourceTree = ""; };
60 | 658075101F6EDB62002408A5 /* XYPhotoTableDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoTableDataSource.h; sourceTree = ""; };
61 | 658075111F6EDB62002408A5 /* XYPhotoTableDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoTableDataSource.m; sourceTree = ""; };
62 | 658075131F6EDB62002408A5 /* XYPhotoAlbumDetailController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoAlbumDetailController.h; sourceTree = ""; };
63 | 658075141F6EDB62002408A5 /* XYPhotoAlbumDetailController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoAlbumDetailController.m; sourceTree = ""; };
64 | 658075151F6EDB62002408A5 /* XYPhotoAlbumListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoAlbumListController.h; sourceTree = ""; };
65 | 658075161F6EDB62002408A5 /* XYPhotoAlbumListController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoAlbumListController.m; sourceTree = ""; };
66 | 658075171F6EDB62002408A5 /* XYPhotoCameraController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoCameraController.h; sourceTree = ""; };
67 | 658075181F6EDB62002408A5 /* XYPhotoCameraController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoCameraController.m; sourceTree = ""; };
68 | 658075191F6EDB62002408A5 /* XYPhotoMultiCameraPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoMultiCameraPicker.h; sourceTree = ""; };
69 | 6580751A1F6EDB62002408A5 /* XYPhotoMultiCameraPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoMultiCameraPicker.m; sourceTree = ""; };
70 | 6580751B1F6EDB62002408A5 /* XYPhotoMultiImagePicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoMultiImagePicker.h; sourceTree = ""; };
71 | 6580751C1F6EDB62002408A5 /* XYPhotoMultiImagePicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoMultiImagePicker.m; sourceTree = ""; };
72 | 6580751D1F6EDB62002408A5 /* XYPhotoPreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoPreviewViewController.h; sourceTree = ""; };
73 | 6580751E1F6EDB62002408A5 /* XYPhotoPreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoPreviewViewController.m; sourceTree = ""; };
74 | DBFD4E3523FFDABF00C1A472 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; };
75 | DBFD4E3623FFDABF00C1A472 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; };
76 | DBFD4E3723FFDABF00C1A472 /* XYDemoCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYDemoCollectionViewCell.m; sourceTree = ""; };
77 | DBFD4E3823FFDAC000C1A472 /* XYDemoCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYDemoCollectionViewCell.h; sourceTree = ""; };
78 | DBFD4E3B23FFE50F00C1A472 /* XYPhotoTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoTableCell.h; sourceTree = ""; };
79 | DBFD4E3C23FFE50F00C1A472 /* XYPhotoBottomPreviewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoBottomPreviewCell.m; sourceTree = ""; };
80 | DBFD4E3D23FFE50F00C1A472 /* XYPhotoSelectedAssetPreviewView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoSelectedAssetPreviewView.h; sourceTree = ""; };
81 | DBFD4E3E23FFE50F00C1A472 /* XYPhotoCameraOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoCameraOverlayView.h; sourceTree = ""; };
82 | DBFD4E3F23FFE50F00C1A472 /* XYPhotoBottomPreviewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoBottomPreviewCell.h; sourceTree = ""; };
83 | DBFD4E4023FFE50F00C1A472 /* XYPhotoHorizontalScrollItemView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoHorizontalScrollItemView.h; sourceTree = ""; };
84 | DBFD4E4123FFE51000C1A472 /* XYPhotoPreviewOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoPreviewOverlayView.m; sourceTree = ""; };
85 | DBFD4E4223FFE51000C1A472 /* XYPhotoCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoCollectionViewCell.h; sourceTree = ""; };
86 | DBFD4E4323FFE51000C1A472 /* XYPhotoHorizontalScrollItemView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoHorizontalScrollItemView.m; sourceTree = ""; };
87 | DBFD4E4423FFE51000C1A472 /* XYPhotoCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoCollectionViewCell.m; sourceTree = ""; };
88 | DBFD4E4523FFE51000C1A472 /* XYPhotoSelectedAssetPreviewView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoSelectedAssetPreviewView.m; sourceTree = ""; };
89 | DBFD4E4623FFE51000C1A472 /* XYPhotoTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoTableCell.m; sourceTree = ""; };
90 | DBFD4E4723FFE51000C1A472 /* XYPhotoHorizontalScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoHorizontalScrollView.h; sourceTree = ""; };
91 | DBFD4E4823FFE51000C1A472 /* XYPhotoCameraOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoCameraOverlayView.m; sourceTree = ""; };
92 | DBFD4E4923FFE51000C1A472 /* XYPhotoPreviewOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYPhotoPreviewOverlayView.h; sourceTree = ""; };
93 | DBFD4E4A23FFE51000C1A472 /* XYPhotoHorizontalScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYPhotoHorizontalScrollView.m; sourceTree = ""; };
94 | /* End PBXFileReference section */
95 |
96 | /* Begin PBXFrameworksBuildPhase section */
97 | 651B29601F6EC1730085BCE3 /* Frameworks */ = {
98 | isa = PBXFrameworksBuildPhase;
99 | buildActionMask = 2147483647;
100 | files = (
101 | );
102 | runOnlyForDeploymentPostprocessing = 0;
103 | };
104 | /* End PBXFrameworksBuildPhase section */
105 |
106 | /* Begin PBXGroup section */
107 | 651B295A1F6EC1730085BCE3 = {
108 | isa = PBXGroup;
109 | children = (
110 | 658075031F6EDB62002408A5 /* XYPhotoKit */,
111 | 651B29651F6EC1730085BCE3 /* XYPhotoKitDemo */,
112 | 651B29641F6EC1730085BCE3 /* Products */,
113 | );
114 | sourceTree = "";
115 | };
116 | 651B29641F6EC1730085BCE3 /* Products */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 651B29631F6EC1730085BCE3 /* XYPhotoKitDemo.app */,
120 | );
121 | name = Products;
122 | sourceTree = "";
123 | };
124 | 651B29651F6EC1730085BCE3 /* XYPhotoKitDemo */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 651B29661F6EC1730085BCE3 /* AppDelegate.h */,
128 | 651B29671F6EC1730085BCE3 /* AppDelegate.m */,
129 | DBFD4E3823FFDAC000C1A472 /* XYDemoCollectionViewCell.h */,
130 | DBFD4E3723FFDABF00C1A472 /* XYDemoCollectionViewCell.m */,
131 | DBFD4E3623FFDABF00C1A472 /* RootViewController.h */,
132 | DBFD4E3523FFDABF00C1A472 /* RootViewController.m */,
133 | 651B296C1F6EC1730085BCE3 /* Main.storyboard */,
134 | 651B296F1F6EC1730085BCE3 /* Assets.xcassets */,
135 | 651B29711F6EC1730085BCE3 /* LaunchScreen.storyboard */,
136 | 651B29741F6EC1730085BCE3 /* Info.plist */,
137 | 651B29751F6EC1730085BCE3 /* main.m */,
138 | );
139 | path = XYPhotoKitDemo;
140 | sourceTree = "";
141 | };
142 | 658075031F6EDB62002408A5 /* XYPhotoKit */ = {
143 | isa = PBXGroup;
144 | children = (
145 | 658075041F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.h */,
146 | 658075051F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.m */,
147 | 658075061F6EDB62002408A5 /* XYPhotoDataCategory.h */,
148 | 658075071F6EDB62002408A5 /* XYPhotoDataCategory.m */,
149 | 658075081F6EDB62002408A5 /* XYPhotoKitHelper.h */,
150 | 658075091F6EDB62002408A5 /* XYPhotoKitHelper.m */,
151 | 6580750A1F6EDB62002408A5 /* XYPhotoSelectedAssetManager.h */,
152 | 6580750B1F6EDB62002408A5 /* XYPhotoSelectedAssetManager.m */,
153 | 6580750C1F6EDB62002408A5 /* XYPhotoKit.bundle */,
154 | 6580750D1F6EDB62002408A5 /* DataSources */,
155 | 658075121F6EDB62002408A5 /* ViewControllers */,
156 | 6580751F1F6EDB62002408A5 /* Views */,
157 | );
158 | name = XYPhotoKit;
159 | path = ../XYPhotoKit;
160 | sourceTree = "";
161 | };
162 | 6580750D1F6EDB62002408A5 /* DataSources */ = {
163 | isa = PBXGroup;
164 | children = (
165 | 6580750E1F6EDB62002408A5 /* XYPhotoCollectionDataSource.h */,
166 | 6580750F1F6EDB62002408A5 /* XYPhotoCollectionDataSource.m */,
167 | 658075101F6EDB62002408A5 /* XYPhotoTableDataSource.h */,
168 | 658075111F6EDB62002408A5 /* XYPhotoTableDataSource.m */,
169 | );
170 | path = DataSources;
171 | sourceTree = "";
172 | };
173 | 658075121F6EDB62002408A5 /* ViewControllers */ = {
174 | isa = PBXGroup;
175 | children = (
176 | 658075131F6EDB62002408A5 /* XYPhotoAlbumDetailController.h */,
177 | 658075141F6EDB62002408A5 /* XYPhotoAlbumDetailController.m */,
178 | 658075151F6EDB62002408A5 /* XYPhotoAlbumListController.h */,
179 | 658075161F6EDB62002408A5 /* XYPhotoAlbumListController.m */,
180 | 658075171F6EDB62002408A5 /* XYPhotoCameraController.h */,
181 | 658075181F6EDB62002408A5 /* XYPhotoCameraController.m */,
182 | 658075191F6EDB62002408A5 /* XYPhotoMultiCameraPicker.h */,
183 | 6580751A1F6EDB62002408A5 /* XYPhotoMultiCameraPicker.m */,
184 | 6580751B1F6EDB62002408A5 /* XYPhotoMultiImagePicker.h */,
185 | 6580751C1F6EDB62002408A5 /* XYPhotoMultiImagePicker.m */,
186 | 6580751D1F6EDB62002408A5 /* XYPhotoPreviewViewController.h */,
187 | 6580751E1F6EDB62002408A5 /* XYPhotoPreviewViewController.m */,
188 | );
189 | path = ViewControllers;
190 | sourceTree = "";
191 | };
192 | 6580751F1F6EDB62002408A5 /* Views */ = {
193 | isa = PBXGroup;
194 | children = (
195 | DBFD4E3F23FFE50F00C1A472 /* XYPhotoBottomPreviewCell.h */,
196 | DBFD4E3C23FFE50F00C1A472 /* XYPhotoBottomPreviewCell.m */,
197 | DBFD4E3E23FFE50F00C1A472 /* XYPhotoCameraOverlayView.h */,
198 | DBFD4E4823FFE51000C1A472 /* XYPhotoCameraOverlayView.m */,
199 | DBFD4E4223FFE51000C1A472 /* XYPhotoCollectionViewCell.h */,
200 | DBFD4E4423FFE51000C1A472 /* XYPhotoCollectionViewCell.m */,
201 | DBFD4E4023FFE50F00C1A472 /* XYPhotoHorizontalScrollItemView.h */,
202 | DBFD4E4323FFE51000C1A472 /* XYPhotoHorizontalScrollItemView.m */,
203 | DBFD4E4723FFE51000C1A472 /* XYPhotoHorizontalScrollView.h */,
204 | DBFD4E4A23FFE51000C1A472 /* XYPhotoHorizontalScrollView.m */,
205 | DBFD4E4923FFE51000C1A472 /* XYPhotoPreviewOverlayView.h */,
206 | DBFD4E4123FFE51000C1A472 /* XYPhotoPreviewOverlayView.m */,
207 | DBFD4E3D23FFE50F00C1A472 /* XYPhotoSelectedAssetPreviewView.h */,
208 | DBFD4E4523FFE51000C1A472 /* XYPhotoSelectedAssetPreviewView.m */,
209 | DBFD4E3B23FFE50F00C1A472 /* XYPhotoTableCell.h */,
210 | DBFD4E4623FFE51000C1A472 /* XYPhotoTableCell.m */,
211 | );
212 | path = Views;
213 | sourceTree = "";
214 | };
215 | /* End PBXGroup section */
216 |
217 | /* Begin PBXNativeTarget section */
218 | 651B29621F6EC1730085BCE3 /* XYPhotoKitDemo */ = {
219 | isa = PBXNativeTarget;
220 | buildConfigurationList = 651B29791F6EC1730085BCE3 /* Build configuration list for PBXNativeTarget "XYPhotoKitDemo" */;
221 | buildPhases = (
222 | 651B295F1F6EC1730085BCE3 /* Sources */,
223 | 651B29601F6EC1730085BCE3 /* Frameworks */,
224 | 651B29611F6EC1730085BCE3 /* Resources */,
225 | );
226 | buildRules = (
227 | );
228 | dependencies = (
229 | );
230 | name = XYPhotoKitDemo;
231 | productName = XYPhotoKitDemo;
232 | productReference = 651B29631F6EC1730085BCE3 /* XYPhotoKitDemo.app */;
233 | productType = "com.apple.product-type.application";
234 | };
235 | /* End PBXNativeTarget section */
236 |
237 | /* Begin PBXProject section */
238 | 651B295B1F6EC1730085BCE3 /* Project object */ = {
239 | isa = PBXProject;
240 | attributes = {
241 | LastUpgradeCheck = 1130;
242 | ORGANIZATIONNAME = Daniel;
243 | TargetAttributes = {
244 | 651B29621F6EC1730085BCE3 = {
245 | CreatedOnToolsVersion = 9.0;
246 | ProvisioningStyle = Automatic;
247 | };
248 | };
249 | };
250 | buildConfigurationList = 651B295E1F6EC1730085BCE3 /* Build configuration list for PBXProject "XYPhotoKitDemo" */;
251 | compatibilityVersion = "Xcode 8.0";
252 | developmentRegion = en;
253 | hasScannedForEncodings = 0;
254 | knownRegions = (
255 | en,
256 | Base,
257 | );
258 | mainGroup = 651B295A1F6EC1730085BCE3;
259 | productRefGroup = 651B29641F6EC1730085BCE3 /* Products */;
260 | projectDirPath = "";
261 | projectRoot = "";
262 | targets = (
263 | 651B29621F6EC1730085BCE3 /* XYPhotoKitDemo */,
264 | );
265 | };
266 | /* End PBXProject section */
267 |
268 | /* Begin PBXResourcesBuildPhase section */
269 | 651B29611F6EC1730085BCE3 /* Resources */ = {
270 | isa = PBXResourcesBuildPhase;
271 | buildActionMask = 2147483647;
272 | files = (
273 | 651B29731F6EC1730085BCE3 /* LaunchScreen.storyboard in Resources */,
274 | 651B29701F6EC1730085BCE3 /* Assets.xcassets in Resources */,
275 | 658075341F6EDB62002408A5 /* XYPhotoKit.bundle in Resources */,
276 | 651B296E1F6EC1730085BCE3 /* Main.storyboard in Resources */,
277 | );
278 | runOnlyForDeploymentPostprocessing = 0;
279 | };
280 | /* End PBXResourcesBuildPhase section */
281 |
282 | /* Begin PBXSourcesBuildPhase section */
283 | 651B295F1F6EC1730085BCE3 /* Sources */ = {
284 | isa = PBXSourcesBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | DBFD4E5123FFE51000C1A472 /* XYPhotoCameraOverlayView.m in Sources */,
288 | 6580753B1F6EDB62002408A5 /* XYPhotoMultiImagePicker.m in Sources */,
289 | DBFD4E3A23FFDAC000C1A472 /* XYDemoCollectionViewCell.m in Sources */,
290 | 658075381F6EDB62002408A5 /* XYPhotoAlbumListController.m in Sources */,
291 | 6580753C1F6EDB62002408A5 /* XYPhotoPreviewViewController.m in Sources */,
292 | DBFD4E5223FFE51000C1A472 /* XYPhotoHorizontalScrollView.m in Sources */,
293 | 651B29761F6EC1730085BCE3 /* main.m in Sources */,
294 | 658075361F6EDB62002408A5 /* XYPhotoTableDataSource.m in Sources */,
295 | DBFD4E4B23FFE51000C1A472 /* XYPhotoBottomPreviewCell.m in Sources */,
296 | 658075321F6EDB62002408A5 /* XYPhotoKitHelper.m in Sources */,
297 | DBFD4E4E23FFE51000C1A472 /* XYPhotoCollectionViewCell.m in Sources */,
298 | 658075311F6EDB62002408A5 /* XYPhotoDataCategory.m in Sources */,
299 | 658075351F6EDB62002408A5 /* XYPhotoCollectionDataSource.m in Sources */,
300 | DBFD4E3923FFDAC000C1A472 /* RootViewController.m in Sources */,
301 | 658075331F6EDB62002408A5 /* XYPhotoSelectedAssetManager.m in Sources */,
302 | 658075301F6EDB62002408A5 /* XYPhotoCollectionFlowLayout.m in Sources */,
303 | DBFD4E4F23FFE51000C1A472 /* XYPhotoSelectedAssetPreviewView.m in Sources */,
304 | 658075371F6EDB62002408A5 /* XYPhotoAlbumDetailController.m in Sources */,
305 | DBFD4E4C23FFE51000C1A472 /* XYPhotoPreviewOverlayView.m in Sources */,
306 | 651B29681F6EC1730085BCE3 /* AppDelegate.m in Sources */,
307 | DBFD4E4D23FFE51000C1A472 /* XYPhotoHorizontalScrollItemView.m in Sources */,
308 | 658075391F6EDB62002408A5 /* XYPhotoCameraController.m in Sources */,
309 | DBFD4E5023FFE51000C1A472 /* XYPhotoTableCell.m in Sources */,
310 | 6580753A1F6EDB62002408A5 /* XYPhotoMultiCameraPicker.m in Sources */,
311 | );
312 | runOnlyForDeploymentPostprocessing = 0;
313 | };
314 | /* End PBXSourcesBuildPhase section */
315 |
316 | /* Begin PBXVariantGroup section */
317 | 651B296C1F6EC1730085BCE3 /* Main.storyboard */ = {
318 | isa = PBXVariantGroup;
319 | children = (
320 | 651B296D1F6EC1730085BCE3 /* Base */,
321 | );
322 | name = Main.storyboard;
323 | sourceTree = "";
324 | };
325 | 651B29711F6EC1730085BCE3 /* LaunchScreen.storyboard */ = {
326 | isa = PBXVariantGroup;
327 | children = (
328 | 651B29721F6EC1730085BCE3 /* Base */,
329 | );
330 | name = LaunchScreen.storyboard;
331 | sourceTree = "";
332 | };
333 | /* End PBXVariantGroup section */
334 |
335 | /* Begin XCBuildConfiguration section */
336 | 651B29771F6EC1730085BCE3 /* Debug */ = {
337 | isa = XCBuildConfiguration;
338 | buildSettings = {
339 | ALWAYS_SEARCH_USER_PATHS = NO;
340 | CLANG_ANALYZER_NONNULL = YES;
341 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
343 | CLANG_CXX_LIBRARY = "libc++";
344 | CLANG_ENABLE_MODULES = YES;
345 | CLANG_ENABLE_OBJC_ARC = YES;
346 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
347 | CLANG_WARN_BOOL_CONVERSION = YES;
348 | CLANG_WARN_COMMA = YES;
349 | CLANG_WARN_CONSTANT_CONVERSION = YES;
350 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
351 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
352 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
353 | CLANG_WARN_EMPTY_BODY = YES;
354 | CLANG_WARN_ENUM_CONVERSION = YES;
355 | CLANG_WARN_INFINITE_RECURSION = YES;
356 | CLANG_WARN_INT_CONVERSION = YES;
357 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
358 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
359 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
361 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
362 | CLANG_WARN_STRICT_PROTOTYPES = YES;
363 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
364 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
365 | CLANG_WARN_UNREACHABLE_CODE = YES;
366 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
367 | CODE_SIGN_IDENTITY = "iPhone Developer";
368 | COPY_PHASE_STRIP = NO;
369 | DEBUG_INFORMATION_FORMAT = dwarf;
370 | ENABLE_STRICT_OBJC_MSGSEND = YES;
371 | ENABLE_TESTABILITY = YES;
372 | GCC_C_LANGUAGE_STANDARD = gnu11;
373 | GCC_DYNAMIC_NO_PIC = NO;
374 | GCC_NO_COMMON_BLOCKS = YES;
375 | GCC_OPTIMIZATION_LEVEL = 0;
376 | GCC_PREPROCESSOR_DEFINITIONS = (
377 | "DEBUG=1",
378 | "$(inherited)",
379 | );
380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
382 | GCC_WARN_UNDECLARED_SELECTOR = YES;
383 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
384 | GCC_WARN_UNUSED_FUNCTION = YES;
385 | GCC_WARN_UNUSED_VARIABLE = YES;
386 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
387 | MTL_ENABLE_DEBUG_INFO = YES;
388 | ONLY_ACTIVE_ARCH = YES;
389 | SDKROOT = iphoneos;
390 | };
391 | name = Debug;
392 | };
393 | 651B29781F6EC1730085BCE3 /* Release */ = {
394 | isa = XCBuildConfiguration;
395 | buildSettings = {
396 | ALWAYS_SEARCH_USER_PATHS = NO;
397 | CLANG_ANALYZER_NONNULL = YES;
398 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
399 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
400 | CLANG_CXX_LIBRARY = "libc++";
401 | CLANG_ENABLE_MODULES = YES;
402 | CLANG_ENABLE_OBJC_ARC = YES;
403 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
404 | CLANG_WARN_BOOL_CONVERSION = YES;
405 | CLANG_WARN_COMMA = YES;
406 | CLANG_WARN_CONSTANT_CONVERSION = YES;
407 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
408 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
409 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
410 | CLANG_WARN_EMPTY_BODY = YES;
411 | CLANG_WARN_ENUM_CONVERSION = YES;
412 | CLANG_WARN_INFINITE_RECURSION = YES;
413 | CLANG_WARN_INT_CONVERSION = YES;
414 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
415 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
416 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
418 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
419 | CLANG_WARN_STRICT_PROTOTYPES = YES;
420 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
421 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
422 | CLANG_WARN_UNREACHABLE_CODE = YES;
423 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
424 | CODE_SIGN_IDENTITY = "iPhone Developer";
425 | COPY_PHASE_STRIP = NO;
426 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
427 | ENABLE_NS_ASSERTIONS = NO;
428 | ENABLE_STRICT_OBJC_MSGSEND = YES;
429 | GCC_C_LANGUAGE_STANDARD = gnu11;
430 | GCC_NO_COMMON_BLOCKS = YES;
431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
433 | GCC_WARN_UNDECLARED_SELECTOR = YES;
434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
435 | GCC_WARN_UNUSED_FUNCTION = YES;
436 | GCC_WARN_UNUSED_VARIABLE = YES;
437 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
438 | MTL_ENABLE_DEBUG_INFO = NO;
439 | SDKROOT = iphoneos;
440 | VALIDATE_PRODUCT = YES;
441 | };
442 | name = Release;
443 | };
444 | 651B297A1F6EC1730085BCE3 /* Debug */ = {
445 | isa = XCBuildConfiguration;
446 | buildSettings = {
447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
448 | CODE_SIGN_IDENTITY = "Apple Development";
449 | CODE_SIGN_STYLE = Automatic;
450 | DEVELOPMENT_TEAM = M932RC5J66;
451 | INFOPLIST_FILE = XYPhotoKitDemo/Info.plist;
452 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
454 | PRODUCT_BUNDLE_IDENTIFIER = com.xcodeyang.XYPhotoKitDemo;
455 | PRODUCT_NAME = "$(TARGET_NAME)";
456 | PROVISIONING_PROFILE = "";
457 | PROVISIONING_PROFILE_SPECIFIER = "";
458 | TARGETED_DEVICE_FAMILY = "1,2";
459 | };
460 | name = Debug;
461 | };
462 | 651B297B1F6EC1730085BCE3 /* Release */ = {
463 | isa = XCBuildConfiguration;
464 | buildSettings = {
465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
466 | CODE_SIGN_IDENTITY = "Apple Development";
467 | CODE_SIGN_STYLE = Automatic;
468 | DEVELOPMENT_TEAM = M932RC5J66;
469 | INFOPLIST_FILE = XYPhotoKitDemo/Info.plist;
470 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
471 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
472 | PRODUCT_BUNDLE_IDENTIFIER = com.xcodeyang.XYPhotoKitDemo;
473 | PRODUCT_NAME = "$(TARGET_NAME)";
474 | PROVISIONING_PROFILE = "";
475 | PROVISIONING_PROFILE_SPECIFIER = "";
476 | TARGETED_DEVICE_FAMILY = "1,2";
477 | };
478 | name = Release;
479 | };
480 | /* End XCBuildConfiguration section */
481 |
482 | /* Begin XCConfigurationList section */
483 | 651B295E1F6EC1730085BCE3 /* Build configuration list for PBXProject "XYPhotoKitDemo" */ = {
484 | isa = XCConfigurationList;
485 | buildConfigurations = (
486 | 651B29771F6EC1730085BCE3 /* Debug */,
487 | 651B29781F6EC1730085BCE3 /* Release */,
488 | );
489 | defaultConfigurationIsVisible = 0;
490 | defaultConfigurationName = Release;
491 | };
492 | 651B29791F6EC1730085BCE3 /* Build configuration list for PBXNativeTarget "XYPhotoKitDemo" */ = {
493 | isa = XCConfigurationList;
494 | buildConfigurations = (
495 | 651B297A1F6EC1730085BCE3 /* Debug */,
496 | 651B297B1F6EC1730085BCE3 /* Release */,
497 | );
498 | defaultConfigurationIsVisible = 0;
499 | defaultConfigurationName = Release;
500 | };
501 | /* End XCConfigurationList section */
502 | };
503 | rootObject = 651B295B1F6EC1730085BCE3 /* Project object */;
504 | }
505 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // XYPhotoKitDemo
4 | //
5 | // Created by Daniel on 17/09/2017.
6 | // Copyright © 2017 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by Daniel on 17/09/2017.
6 | // Copyright © 2017 Daniel. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/Assets.xcassets/add.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "add@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/Assets.xcassets/add.imageset/add@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhipingYang/XYPhotoKit/c190f037d42e813eb64ec01fe1704894501c724a/XYPhotoKitDemo/XYPhotoKitDemo/Assets.xcassets/add.imageset/add@2x.png
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPhotoLibraryUsageDescription
6 | XY调试需要
7 | NSCameraUsageDescription
8 | XY调试需要
9 | CFBundleDevelopmentRegion
10 | $(DEVELOPMENT_LANGUAGE)
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/RootViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // CLPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RootViewController : UITableViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/RootViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // CLPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 30/08/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "RootViewController.h"
10 | #import "XYPhotoMultiCameraPicker.h"
11 | #import "XYPhotoMultiImagePicker.h"
12 | #import "XYDemoCollectionViewCell.h"
13 |
14 | @interface RootViewController ()
17 |
18 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
19 |
20 | @property (weak, nonatomic) IBOutlet UISegmentedControl *mediaType;
21 | @property (weak, nonatomic) IBOutlet UITextField *maxNumber;
22 | @property (weak, nonatomic) IBOutlet UITextField *columnNumber;
23 | @property (weak, nonatomic) IBOutlet UISegmentedControl *startFrom;
24 | @property (weak, nonatomic) IBOutlet UISwitch *iCloudRequest;
25 |
26 | @property (nonatomic, strong) NSArray *selectedAssets;
27 |
28 | @end
29 |
30 | @implementation RootViewController
31 |
32 | - (void)showCameraPicker
33 | {
34 | XYPhotoMultiCameraPicker *camera = [[XYPhotoMultiCameraPicker alloc] init];
35 | camera.pickerDelegate = self;
36 | camera.pickerDataSource = self;
37 | camera.maxNumberOfAssets = [self.maxNumber.text integerValue];
38 | camera.allowNetRequestIfiCloud = self.iCloudRequest.on;
39 | [self presentViewController:camera animated:YES completion:nil];
40 | }
41 |
42 | - (void)showImagePicker
43 | {
44 | XYPhotoMultiImagePicker *multiImagePicker = [[XYPhotoMultiImagePicker alloc] init];
45 | multiImagePicker.pickerDelegate = self;
46 | multiImagePicker.pickerDataSource = self;
47 |
48 | switch (self.mediaType.selectedSegmentIndex) {
49 | case 0: multiImagePicker.mediaType = PHAssetMediaTypeImage; break;
50 | case 1: multiImagePicker.mediaType = PHAssetMediaTypeVideo; break;
51 | default: multiImagePicker.mediaType = PHAssetMediaTypeUnknown; break;
52 | }
53 |
54 | multiImagePicker.maxNumberOfAssets = [self.maxNumber.text integerValue];
55 | multiImagePicker.assetCollectionViewColumnCount = self.columnNumber.text.integerValue;
56 | multiImagePicker.startPosition = self.startFrom.selectedSegmentIndex==0 ? CLPhotoMultiPickerStartPositionAlbums : CLPhotoMultiPickerStartPositionCameraRoll;
57 | multiImagePicker.allowNetRequestIfiCloud = self.iCloudRequest.on;
58 | [self presentViewController:multiImagePicker animated:YES completion:nil];
59 | }
60 |
61 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
62 | {
63 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
64 | if (indexPath.row==0 && indexPath.section==0) {
65 | [self showImagePicker];
66 | } else if(indexPath.row==1 && indexPath.section==0) {
67 | [self showCameraPicker];
68 | }
69 | }
70 |
71 | #pragma mark - XYPhotoMultiCameraPickerDelegate, XYPhotoMultiCameraPickerDataSource
72 |
73 | - (void)multiCameraPicker:(XYPhotoMultiCameraPicker *)multiImagePicker selectedAssets:(NSArray *)assets
74 | {
75 | _selectedAssets = assets.copy;
76 | [self.collectionView reloadData];
77 | NSLog(@"%@",assets);
78 | }
79 |
80 | - (NSArray *)multiCameraPickerLoadPresetSelectedAssets
81 | {
82 | return _selectedAssets;
83 | }
84 |
85 | #pragma mark - XYPhotoMultiImagePickerDelegate, XYPhotoMultiImagePickerDataSource
86 |
87 | - (void)multiImagePicker:(XYPhotoMultiImagePicker *)multiImagePicker selectedAssets:(NSArray *)assets
88 | {
89 | _selectedAssets = assets;
90 | [self.collectionView reloadData];
91 | NSLog(@"%@", @(assets.count));
92 | for (PHAsset *asset in assets) {
93 | NSLog(@"%@", asset);
94 | }
95 | }
96 |
97 | - (NSArray *)multiImagePickerLoadPresetSelectedAssets
98 | {
99 | return _selectedAssets;
100 | }
101 |
102 | #pragma mark - UICollectionViewDataSource
103 |
104 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
105 | {
106 | return _selectedAssets.count + 1;
107 | }
108 |
109 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
110 | {
111 | XYDemoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([XYDemoCollectionViewCell class]) forIndexPath:indexPath];
112 | if (indexPath.row == _selectedAssets.count) {
113 | cell.imageVew.image = [UIImage imageNamed:@"add"];
114 | } else {
115 | PHAsset *asset = _selectedAssets[indexPath.row];
116 | [[PHImageManager defaultManager] requestImageForAsset:asset
117 | targetSize:CGSizeMake(160, 160)
118 | contentMode:PHImageContentModeAspectFill
119 | options:nil
120 | resultHandler:^(UIImage *result, NSDictionary *info) {
121 | cell.imageVew.image = result;
122 | }];
123 | }
124 | return cell;
125 | }
126 |
127 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
128 | {
129 | [collectionView deselectItemAtIndexPath:indexPath animated:YES];
130 | if (indexPath.row == _selectedAssets.count) {
131 | UIAlertController *alrt = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
132 | [alrt addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
133 | [alrt addAction:[UIAlertAction actionWithTitle:@"相机" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
134 | [self showCameraPicker];
135 | }]];
136 | [alrt addAction:[UIAlertAction actionWithTitle:@"相册" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
137 | [self showImagePicker];
138 | }]];
139 | [self presentViewController:alrt animated:YES completion:nil];
140 | }
141 | }
142 |
143 | @end
144 |
145 |
146 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/XYDemoCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // CLDemoCollectionViewCell.h
3 | // CLPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 13/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface XYDemoCollectionViewCell : UICollectionViewCell
12 |
13 | @property (weak, nonatomic) IBOutlet UIImageView *imageVew;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/XYDemoCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // CLDemoCollectionViewCell.m
3 | // CLPhotoKitDemo
4 | //
5 | // Created by XcodeYang on 13/09/2017.
6 | // Copyright © 2017 XcodeYang. All rights reserved.
7 | //
8 |
9 | #import "XYDemoCollectionViewCell.h"
10 |
11 | @implementation XYDemoCollectionViewCell
12 |
13 | - (void)awakeFromNib
14 | {
15 | [super awakeFromNib];
16 | self.contentView.layer.borderWidth = 0.5;
17 | self.contentView.layer.borderColor = [UIColor lightGrayColor].CGColor;
18 | }
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/XYPhotoKitDemo/XYPhotoKitDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // XYPhotoKitDemo
4 | //
5 | // Created by Daniel on 17/09/2017.
6 | // Copyright © 2017 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------