Commit 41fcf581 cgx

意见反馈本地相册图片读取及拍照

1 个父辈 7facae2d
正在显示 61 个修改的文件 包含 6755 行增加4 行删除
......@@ -73,6 +73,22 @@
D0878F48280BD18F005F1B7F /* LoginView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F47280BD18F005F1B7F /* LoginView.m */; };
D0878F4C280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F4B280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m */; };
D0878F4F280C087E005F1B7F /* LoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F4E280C087E005F1B7F /* LoginController.m */; };
D08F79D4281A1838000D99DD /* TZImageCropManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79B9281A1838000D99DD /* TZImageCropManager.m */; };
D08F79D5281A1838000D99DD /* TZAssetCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79BB281A1838000D99DD /* TZAssetCell.m */; };
D08F79D6281A1838000D99DD /* TZVideoPlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79BC281A1838000D99DD /* TZVideoPlayerController.m */; };
D08F79D7281A1838000D99DD /* TZAssetModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79BD281A1838000D99DD /* TZAssetModel.m */; };
D08F79D8281A1838000D99DD /* NSBundle+TZImagePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79BE281A1838000D99DD /* NSBundle+TZImagePicker.m */; };
D08F79D9281A1838000D99DD /* UIView+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79C1281A1838000D99DD /* UIView+Layout.m */; };
D08F79DA281A1838000D99DD /* TZPhotoPreviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79C2281A1838000D99DD /* TZPhotoPreviewController.m */; };
D08F79DB281A1838000D99DD /* TZImagePickerController.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D08F79C3281A1838000D99DD /* TZImagePickerController.bundle */; };
D08F79DC281A1838000D99DD /* TZProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79C4281A1838000D99DD /* TZProgressView.m */; };
D08F79DD281A1838000D99DD /* TZImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79C7281A1838000D99DD /* TZImageManager.m */; };
D08F79DE281A1838000D99DD /* TZPhotoPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79C9281A1838000D99DD /* TZPhotoPickerController.m */; };
D08F79DF281A1838000D99DD /* TZPhotoPreviewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79CA281A1838000D99DD /* TZPhotoPreviewCell.m */; };
D08F79E0281A1838000D99DD /* TZImagePickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79D0281A1838000D99DD /* TZImagePickerController.m */; };
D08F79E1281A1838000D99DD /* TZLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79D1281A1838000D99DD /* TZLocationManager.m */; };
D08F79E2281A1838000D99DD /* TZGifPhotoPreviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79D2281A1838000D99DD /* TZGifPhotoPreviewController.m */; };
D08F79E6281A198E000D99DD /* FeedImageCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D08F79E5281A198E000D99DD /* FeedImageCollectionCell.m */; };
D091BBB72808F9DA00487A50 /* BreathTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = D091BBB62808F9DA00487A50 /* BreathTextView.m */; };
D091BBBB2809079600487A50 /* NSString+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = D091BBBA2809079600487A50 /* NSString+Extras.m */; };
D0930F122801124E006B497A /* BaseNaviController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F112801124E006B497A /* BaseNaviController.m */; };
......@@ -255,6 +271,37 @@
D0878F4B280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILabel+YBAttributeTextTapAction.m"; sourceTree = "<group>"; };
D0878F4D280C087E005F1B7F /* LoginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginController.h; sourceTree = "<group>"; };
D0878F4E280C087E005F1B7F /* LoginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginController.m; sourceTree = "<group>"; };
D08F79B7281A1838000D99DD /* TZPhotoPreviewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZPhotoPreviewCell.h; sourceTree = "<group>"; };
D08F79B8281A1838000D99DD /* TZPhotoPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZPhotoPickerController.h; sourceTree = "<group>"; };
D08F79B9281A1838000D99DD /* TZImageCropManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZImageCropManager.m; sourceTree = "<group>"; };
D08F79BA281A1838000D99DD /* TZImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZImageManager.h; sourceTree = "<group>"; };
D08F79BB281A1838000D99DD /* TZAssetCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZAssetCell.m; sourceTree = "<group>"; };
D08F79BC281A1838000D99DD /* TZVideoPlayerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZVideoPlayerController.m; sourceTree = "<group>"; };
D08F79BD281A1838000D99DD /* TZAssetModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZAssetModel.m; sourceTree = "<group>"; };
D08F79BE281A1838000D99DD /* NSBundle+TZImagePicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+TZImagePicker.m"; sourceTree = "<group>"; };
D08F79BF281A1838000D99DD /* TZLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZLocationManager.h; sourceTree = "<group>"; };
D08F79C0281A1838000D99DD /* TZImagePickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZImagePickerController.h; sourceTree = "<group>"; };
D08F79C1281A1838000D99DD /* UIView+Layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Layout.m"; sourceTree = "<group>"; };
D08F79C2281A1838000D99DD /* TZPhotoPreviewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZPhotoPreviewController.m; sourceTree = "<group>"; };
D08F79C3281A1838000D99DD /* TZImagePickerController.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TZImagePickerController.bundle; sourceTree = "<group>"; };
D08F79C4281A1838000D99DD /* TZProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZProgressView.m; sourceTree = "<group>"; };
D08F79C5281A1838000D99DD /* TZGifPhotoPreviewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZGifPhotoPreviewController.h; sourceTree = "<group>"; };
D08F79C6281A1838000D99DD /* TZAssetCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZAssetCell.h; sourceTree = "<group>"; };
D08F79C7281A1838000D99DD /* TZImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZImageManager.m; sourceTree = "<group>"; };
D08F79C8281A1838000D99DD /* TZImageCropManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZImageCropManager.h; sourceTree = "<group>"; };
D08F79C9281A1838000D99DD /* TZPhotoPickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZPhotoPickerController.m; sourceTree = "<group>"; };
D08F79CA281A1838000D99DD /* TZPhotoPreviewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZPhotoPreviewCell.m; sourceTree = "<group>"; };
D08F79CB281A1838000D99DD /* NSBundle+TZImagePicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+TZImagePicker.h"; sourceTree = "<group>"; };
D08F79CC281A1838000D99DD /* TZAssetModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZAssetModel.h; sourceTree = "<group>"; };
D08F79CD281A1838000D99DD /* TZVideoPlayerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZVideoPlayerController.h; sourceTree = "<group>"; };
D08F79CE281A1838000D99DD /* TZPhotoPreviewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZPhotoPreviewController.h; sourceTree = "<group>"; };
D08F79CF281A1838000D99DD /* UIView+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Layout.h"; sourceTree = "<group>"; };
D08F79D0281A1838000D99DD /* TZImagePickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZImagePickerController.m; sourceTree = "<group>"; };
D08F79D1281A1838000D99DD /* TZLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZLocationManager.m; sourceTree = "<group>"; };
D08F79D2281A1838000D99DD /* TZGifPhotoPreviewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZGifPhotoPreviewController.m; sourceTree = "<group>"; };
D08F79E3281A1931000D99DD /* TZProgressView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TZProgressView.h; sourceTree = "<group>"; };
D08F79E4281A198E000D99DD /* FeedImageCollectionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeedImageCollectionCell.h; sourceTree = "<group>"; };
D08F79E5281A198E000D99DD /* FeedImageCollectionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedImageCollectionCell.m; sourceTree = "<group>"; };
D091BBB52808F9DA00487A50 /* BreathTextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BreathTextView.h; sourceTree = "<group>"; };
D091BBB62808F9DA00487A50 /* BreathTextView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BreathTextView.m; sourceTree = "<group>"; };
D091BBB82809024100487A50 /* DreamSleepBeta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DreamSleepBeta.entitlements; sourceTree = "<group>"; };
......@@ -714,6 +761,42 @@
path = YBAttributeTextTapAction;
sourceTree = "<group>";
};
D08F79B6281A1838000D99DD /* TZImagePickerController */ = {
isa = PBXGroup;
children = (
D08F79B7281A1838000D99DD /* TZPhotoPreviewCell.h */,
D08F79CA281A1838000D99DD /* TZPhotoPreviewCell.m */,
D08F79B8281A1838000D99DD /* TZPhotoPickerController.h */,
D08F79C9281A1838000D99DD /* TZPhotoPickerController.m */,
D08F79C0281A1838000D99DD /* TZImagePickerController.h */,
D08F79D0281A1838000D99DD /* TZImagePickerController.m */,
D08F79C8281A1838000D99DD /* TZImageCropManager.h */,
D08F79B9281A1838000D99DD /* TZImageCropManager.m */,
D08F79BA281A1838000D99DD /* TZImageManager.h */,
D08F79C7281A1838000D99DD /* TZImageManager.m */,
D08F79C6281A1838000D99DD /* TZAssetCell.h */,
D08F79BB281A1838000D99DD /* TZAssetCell.m */,
D08F79CD281A1838000D99DD /* TZVideoPlayerController.h */,
D08F79BC281A1838000D99DD /* TZVideoPlayerController.m */,
D08F79CC281A1838000D99DD /* TZAssetModel.h */,
D08F79BD281A1838000D99DD /* TZAssetModel.m */,
D08F79BF281A1838000D99DD /* TZLocationManager.h */,
D08F79D1281A1838000D99DD /* TZLocationManager.m */,
D08F79CF281A1838000D99DD /* UIView+Layout.h */,
D08F79C1281A1838000D99DD /* UIView+Layout.m */,
D08F79CE281A1838000D99DD /* TZPhotoPreviewController.h */,
D08F79C2281A1838000D99DD /* TZPhotoPreviewController.m */,
D08F79E3281A1931000D99DD /* TZProgressView.h */,
D08F79C4281A1838000D99DD /* TZProgressView.m */,
D08F79C5281A1838000D99DD /* TZGifPhotoPreviewController.h */,
D08F79D2281A1838000D99DD /* TZGifPhotoPreviewController.m */,
D08F79CB281A1838000D99DD /* NSBundle+TZImagePicker.h */,
D08F79BE281A1838000D99DD /* NSBundle+TZImagePicker.m */,
D08F79C3281A1838000D99DD /* TZImagePickerController.bundle */,
);
path = TZImagePickerController;
sourceTree = "<group>";
};
D0930F142801874B006B497A /* DSNavigationTools */ = {
isa = PBXGroup;
children = (
......@@ -835,6 +918,8 @@
D0D3826428124E34005BB219 /* Feedback */ = {
isa = PBXGroup;
children = (
D08F79E4281A198E000D99DD /* FeedImageCollectionCell.h */,
D08F79E5281A198E000D99DD /* FeedImageCollectionCell.m */,
D0AEFE7D2817DD1500230DC6 /* MyFeedCell.h */,
D0AEFE7E2817DD1500230DC6 /* MyFeedCell.m */,
D0AEFE7F2817DD1500230DC6 /* MyFeedListController.h */,
......@@ -887,6 +972,7 @@
D07DAC912810557D0067A1BF /* RSKImageCropper */,
D0E65FF32807A654006562F2 /* HYBUnicodeReadable */,
D0878F49280BEFF9005F1B7F /* YBAttributeTextTapAction */,
D08F79B6281A1838000D99DD /* TZImagePickerController */,
);
path = Vendors;
sourceTree = "<group>";
......@@ -1055,6 +1141,7 @@
D046FE1328100342000295AC /* README.txt in Resources */,
D0C09EDA28007D9100709D4C /* relax_lottie.json in Resources */,
D0CFD3D027FB3B920002982B /* launcher@3x.png in Resources */,
D08F79DB281A1838000D99DD /* TZImagePickerController.bundle in Resources */,
D0C09ED928007D9100709D4C /* 478normal_lottie.json in Resources */,
D0B5ECAF27F2D9DE003EDFE3 /* Main.storyboard in Resources */,
D0CFD3D127FB3B920002982B /* launcher.png in Resources */,
......@@ -1123,6 +1210,7 @@
D07DACA82810557D0067A1BF /* RSKImageCropViewController.m in Sources */,
D0E65FFB2807A654006562F2 /* NSDictionary+HYBUnicodeReadable.m in Sources */,
D0E65FFA2807A654006562F2 /* NSArray+HYBUnicodeReadable.m in Sources */,
D08F79DE281A1838000D99DD /* TZPhotoPickerController.m in Sources */,
D04B3DC027F6F82D0022F8DF /* ProfileController.m in Sources */,
D01C9D7028168BFC00163567 /* NSDate+Extension.m in Sources */,
D0C50B3027FD1BEB00DC68F0 /* PrivacyView.m in Sources */,
......@@ -1140,9 +1228,11 @@
D07DAC902810546A0067A1BF /* DSImagePickerController.m in Sources */,
D0DF90AE2814390000FC0F64 /* BannerModel.m in Sources */,
D0E65FFF2807AC5E006562F2 /* DSProgressHUD.m in Sources */,
D08F79E2281A1838000D99DD /* TZGifPhotoPreviewController.m in Sources */,
D0E660022807B5AE006562F2 /* SafeSleepRequestModel.m in Sources */,
D0E6600828080F97006562F2 /* SystemSetController.m in Sources */,
D091BBB72808F9DA00487A50 /* BreathTextView.m in Sources */,
D08F79E1281A1838000D99DD /* TZLocationManager.m in Sources */,
D04B3DBB27F6F7940022F8DF /* AISleepCoachController.m in Sources */,
D07A65832815911D00C15AF6 /* DsMaskView.m in Sources */,
D0BE32E62814D42B006539BE /* HomeRequestModel.m in Sources */,
......@@ -1150,13 +1240,19 @@
D027EE2627FB3DC0004BBA61 /* NetLoadingStateView.m in Sources */,
D0C09EE028007E5F00709D4C /* BreatheController.m in Sources */,
D0C50B4627FD66FB00DC68F0 /* DSConstUtil.m in Sources */,
D08F79DC281A1838000D99DD /* TZProgressView.m in Sources */,
D01814D227FFCBAF00583D4E /* CWFlowLayout.m in Sources */,
D07A4B2D280EA73B00BA0EC0 /* UserInfoView.m in Sources */,
D0878F48280BD18F005F1B7F /* LoginView.m in Sources */,
D08F79D4281A1838000D99DD /* TZImageCropManager.m in Sources */,
D0E660052807D02C006562F2 /* UserRequestModel.m in Sources */,
D0F82286280C44D10039F586 /* SetTableView.m in Sources */,
D0930F1B2801874B006B497A /* UINavigationController+Pop.m in Sources */,
D08F79D9281A1838000D99DD /* UIView+Layout.m in Sources */,
D08F79D8281A1838000D99DD /* NSBundle+TZImagePicker.m in Sources */,
D08F79E6281A198E000D99DD /* FeedImageCollectionCell.m in Sources */,
D07DACA52810557D0067A1BF /* RSKInternalUtility.m in Sources */,
D08F79E0281A1838000D99DD /* TZImagePickerController.m in Sources */,
D07DACAB2810557D0067A1BF /* RSKImageScrollView.m in Sources */,
D0AEFE812817DD1500230DC6 /* MyFeedCell.m in Sources */,
D0506B1528051ED400229278 /* SafeSleepModel.m in Sources */,
......@@ -1178,6 +1274,7 @@
D09BF337280E605900E5F06C /* NSObject+Extras.m in Sources */,
D07DACA72810557D0067A1BF /* UIApplication+RSKImageCropper.m in Sources */,
D027EE2927FB51D0004BBA61 /* DSGifHeader.m in Sources */,
D08F79DA281A1838000D99DD /* TZPhotoPreviewController.m in Sources */,
D01814EE28002DC700583D4E /* HeaderDataModel.m in Sources */,
D0506B1B2805500E00229278 /* SafeHelperCollectionViewCell.m in Sources */,
D01814DC27FFD92200583D4E /* DSDataSource.m in Sources */,
......@@ -1192,16 +1289,21 @@
D0930F122801124E006B497A /* BaseNaviController.m in Sources */,
D0878F4F280C087E005F1B7F /* LoginController.m in Sources */,
D07A657D28158A1900C15AF6 /* DsWebController.m in Sources */,
D08F79D6281A1838000D99DD /* TZVideoPlayerController.m in Sources */,
D0930F1A2801874B006B497A /* UIViewController+Swizzling.m in Sources */,
D01C9D6728167F9D00163567 /* InfoModifyAlertView.m in Sources */,
D0BB9AA728164DFB00DB209E /* NickNameController.m in Sources */,
D08F79DF281A1838000D99DD /* TZPhotoPreviewCell.m in Sources */,
D0F808FE280421C20097899F /* ThreeMinuteController.m in Sources */,
D07A4B27280E9BAA00BA0EC0 /* AccountController.m in Sources */,
D062A09E281838B800B4F48A /* FeedbackRequestModel.m in Sources */,
D08F79D5281A1838000D99DD /* TZAssetCell.m in Sources */,
D01814E8280020F900583D4E /* CWPageControl.m in Sources */,
D027EE3027FB52DA004BBA61 /* UIImage+Extras.m in Sources */,
D08F79DD281A1838000D99DD /* TZImageManager.m in Sources */,
D0506B1828054ECD00229278 /* SafeHelperCollectionView.m in Sources */,
D04B3DC327F6F9390022F8DF /* HomeViewController.m in Sources */,
D08F79D7281A1838000D99DD /* TZAssetModel.m in Sources */,
D01C9D6F28168BFC00163567 /* WSDatePickerView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......
......@@ -85,5 +85,9 @@
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>NSCameraUsageDescription</key>
<string>拍照</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>相册</string>
</dict>
</plist>
#import <UIKit/UIKit.h>
/// 意见反馈图片自定义cell
@interface FeedImageCollectionCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIImageView *addIcon;
@property (nonatomic, strong) UIButton *deleteBtn;
@property (nonatomic, strong) id asset;
@end
#import "FeedImageCollectionCell.h"
#import <Photos/Photos.h>
@implementation FeedImageCollectionCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.imageView];
[self addSubview:self.deleteBtn];
[self.imageView addSubview:self.addIcon];
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.bottom.equalTo(self);
make.top.equalTo(self).offset(15);
make.right.equalTo(self).offset(-15);
make.width.height.equalTo(@77);
}];
[self.deleteBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.top.equalTo(self);
make.width.height.equalTo(@30);
}];
[self.addIcon mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.equalTo(@30);
make.center.equalTo(self.imageView);
}];
}
return self;
}
- (void)setAsset:(id)asset {
_asset = asset;
if ([asset isKindOfClass:[PHAsset class]]) {
// PHAsset *phAsset = asset;
}
}
#pragma mark - lazy
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [UIImageView new];
_imageView.dk_backgroundColorPicker = DKColorPickerWithColors(ColorFromHex(0xf0f0f0), CornerViewDarkColor, DSWhite);
_imageView.contentMode = UIViewContentModeScaleToFill;
[_imageView cornerRadius:12];
}
return _imageView;
}
- (UIImageView *)addIcon {
if (!_addIcon) {
_addIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"add_img"]];
}
return _addIcon;
}
- (UIButton *)deleteBtn {
if (!_deleteBtn) {
_deleteBtn = [UIButton new];
[_deleteBtn setImage:[UIImage imageNamed:@"image_delete"] forState:UIControlStateNormal];
}
return _deleteBtn;
}
@end
......@@ -8,8 +8,15 @@
#import "FeedbackController.h"
#import "MyFeedListController.h"
#import "FeedbackRequestModel.h"
#import "TZImagePickerController.h"
#import "FeedImageCollectionCell.h"
@interface FeedbackController () <MyFeedListControllerDelegate, UITextViewDelegate>
// 最大图片上传数量
static int MaxUpdateImgCount = 3;
// 相册显示列数
static int AlbumColumnCount = 4;
@interface FeedbackController () <MyFeedListControllerDelegate, UITextViewDelegate, TZImagePickerControllerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UINavigationControllerDelegate>
@property (strong, nonatomic) UILabel *redLab;
@property (assign, nonatomic) int unreadCount;
@property (strong, nonatomic) UITextView *feedTV;
......@@ -17,7 +24,11 @@
@property (strong, nonatomic) UIButton *commitBtn;
@end
@implementation FeedbackController
@implementation FeedbackController {
NSMutableArray *_selectedPhotos;
NSMutableArray *_selectedAssets;
BOOL _isSelectOriginalPhoto;
}
- (void)viewDidLoad {
[super viewDidLoad];
......@@ -25,6 +36,9 @@
self.navigationItem.title = @"意见反馈";
self.view.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
_selectedPhotos = [NSMutableArray array];
_selectedAssets = [NSMutableArray array];
[self setNaviRightItem];
[self.view addSubview:self.feedTV];
......@@ -93,6 +107,152 @@
}];
}
- (void)deleteBtnClik:(UIButton *)sender {
[_selectedPhotos removeObjectAtIndex:sender.tag];
[_selectedAssets removeObjectAtIndex:sender.tag];
[self.imgCollectionView performBatchUpdates:^{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:sender.tag inSection:0];
[self.imgCollectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
[self.imgCollectionView reloadData];
}];
}
#pragma mark - UICollectionViewDataSource && UICollectionViewDelegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _selectedPhotos.count + 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
FeedImageCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"FeedImageCollectionCell" forIndexPath:indexPath];
if (indexPath.row == 0) {
cell.imageView.image = nil;
cell.addIcon.hidden = NO;
cell.deleteBtn.hidden = YES;
} else {
cell.imageView.image = _selectedPhotos[indexPath.row-1];
cell.asset = _selectedAssets[indexPath.row-1];
cell.addIcon.hidden = YES;
cell.deleteBtn.hidden = NO;
cell.deleteBtn.tag = indexPath.row - 1;
[cell.deleteBtn addTarget:self action:@selector(deleteBtnClik:) forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) { // 打开相册
[self pushTZImagePickerController];
} else { // 预览照片或者视频
id asset = _selectedAssets[(indexPath.row - 1)];
BOOL isVideo = NO;
if ([asset isKindOfClass:[PHAsset class]]) {
PHAsset *phAsset = asset;
isVideo = phAsset.mediaType == PHAssetMediaTypeVideo;
}
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithSelectedAssets:_selectedAssets selectedPhotos:_selectedPhotos index:(indexPath.row - 1)];
imagePickerVc.maxImagesCount = MaxUpdateImgCount;
imagePickerVc.allowPickingOriginalPhoto = YES;
imagePickerVc.allowPickingMultipleVideo =NO;
imagePickerVc.showSelectedIndex = YES;
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
WS(weakSelf);
[imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
self->_selectedPhotos = [NSMutableArray arrayWithArray:photos];
self->_selectedAssets = [NSMutableArray arrayWithArray:assets];
self->_isSelectOriginalPhoto = isSelectOriginalPhoto;
[weakSelf.imgCollectionView reloadData];
}];
[self presentViewController:imagePickerVc animated:YES completion:nil];
}
}
#pragma mark - TZImagePickerController
- (void)pushTZImagePickerController {
if (MaxUpdateImgCount <= 0) { return; }
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:MaxUpdateImgCount columnNumber:AlbumColumnCount delegate:self pushPhotoPickerVc:NO];
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
// 设置目前已经选中的图片数组
if (MaxUpdateImgCount > 1) { imagePickerVc.selectedAssets = _selectedAssets; }
// 在内部显示拍照按钮
imagePickerVc.allowTakePicture = YES;
imagePickerVc.iconThemeColor = ColorFromRGBA(31.0, 185.0, 34.0, 1.0);
imagePickerVc.showPhotoCannotSelectLayer = YES;
imagePickerVc.cannotSelectLayerColor = [DSWhite colorWithAlphaComponent:0.8];
// 设置是否可以选择视频/图片/原图
imagePickerVc.allowPickingVideo = NO;
imagePickerVc.allowPickingImage = YES;
imagePickerVc.allowPickingOriginalPhoto = YES;
imagePickerVc.allowPickingGif = NO;
imagePickerVc.allowPickingOriginalPhoto = NO;
// 是否可以多选视频
imagePickerVc.allowPickingMultipleVideo = NO;
// 照片排列按修改时间升序
imagePickerVc.sortAscendingByModificationDate = YES;
imagePickerVc.showSelectBtn = NO;
imagePickerVc.allowCrop = NO;
imagePickerVc.needCircleCrop = NO;
// 设置竖屏下的裁剪尺寸
NSInteger left = 30;
NSInteger widthHeight = self.view.width - 2 * left;
NSInteger top = (self.view.height - widthHeight) / 2;
imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
// 设置是否显示图片序号
imagePickerVc.showSelectedIndex = YES;
// 你可以通过block或者代理,来得到用户选择的照片.
[imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
}];
[self presentViewController:imagePickerVc animated:YES completion:nil];
}
#pragma mark - TZImagePickerControllerDelegate
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray<NSDictionary *> *)infos {
_selectedPhotos = [NSMutableArray arrayWithArray:photos];
_selectedAssets = [NSMutableArray arrayWithArray:assets];
_isSelectOriginalPhoto = isSelectOriginalPhoto;
[self.imgCollectionView reloadData];
// 1.打印图片名字
[self printAssetsName:assets];
// 2.图片位置信息
for (PHAsset *phAsset in assets) {
DSLog(@"location:%@", phAsset.location);
}
}
// 如果用户选择了一个gif图片,下面的handle会被执行
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingGifImage:(UIImage *)animatedImage sourceAssets:(id)asset {
_selectedPhotos = [NSMutableArray arrayWithArray:@[animatedImage]];
_selectedAssets = [NSMutableArray arrayWithArray:@[asset]];
[self.imgCollectionView reloadData];
}
// 决定相册显示与否
- (BOOL)isAlbumCanSelect:(NSString *)albumName result:(id)result {
return YES;
}
// 决定asset显示与否
- (BOOL)isAssetCanSelect:(id)asset {
return YES;
}
#pragma mark - Private
- (void)printAssetsName:(NSArray *)assets {
/// 打印图片名字
NSString *fileName;
for (id asset in assets) {
if ([asset isKindOfClass:[PHAsset class]]) {
PHAsset *phAsset = (PHAsset *)asset;
fileName = [phAsset valueForKey:@"filename"];
}
}
}
#pragma mark - lazy
- (UITextView *)feedTV {
if (!_feedTV) {
......@@ -108,8 +268,20 @@
- (UICollectionView *)imgCollectionView {
if (!_imgCollectionView) {
UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(92, 92);
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 0;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_imgCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_imgCollectionView.showsHorizontalScrollIndicator = NO;
_imgCollectionView.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
_imgCollectionView.dataSource = self;
_imgCollectionView.delegate = self;
_imgCollectionView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
[_imgCollectionView registerClass:[FeedImageCollectionCell class] forCellWithReuseIdentifier:@"FeedImageCollectionCell"];
}
return _imgCollectionView;
}
......@@ -163,7 +335,7 @@
make.top.equalTo(self.feedTV.mas_bottom).offset(26);
make.left.equalTo(@15);
make.right.equalTo(@-15);
make.height.equalTo(@77);
make.height.equalTo(@92);
}];
[self.commitBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.imgCollectionView.mas_bottom).offset(60);
......
{
"images" : [
{
"filename" : "add_img.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "add_img@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "add_img@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "image_delete.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "image_delete@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "image_delete@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// NSBundle+TZImagePicker.h
// TZImagePickerController
//
// Created by 谭真 on 16/08/18.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NSBundle (TZImagePicker)
+ (NSBundle *)tz_imagePickerBundle;
+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value;
+ (NSString *)tz_localizedStringForKey:(NSString *)key;
@end
//
// NSBundle+TZImagePicker.m
// TZImagePickerController
//
// Created by 谭真 on 16/08/18.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "NSBundle+TZImagePicker.h"
#import "TZImagePickerController.h"
@implementation NSBundle (TZImagePicker)
+ (NSBundle *)tz_imagePickerBundle {
NSBundle *bundle = [NSBundle bundleForClass:[TZImagePickerController class]];
NSURL *url = [bundle URLForResource:@"TZImagePickerController" withExtension:@"bundle"];
bundle = [NSBundle bundleWithURL:url];
return bundle;
}
+ (NSString *)tz_localizedStringForKey:(NSString *)key {
return [self tz_localizedStringForKey:key value:@""];
}
+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value {
NSBundle *bundle = [TZImagePickerConfig sharedInstance].languageBundle;
NSString *value1 = [bundle localizedStringForKey:key value:value table:nil];
return value1;
}
@end
//
// TZAssetCell.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <Photos/Photos.h>
typedef enum : NSUInteger {
TZAssetCellTypePhoto = 0,
TZAssetCellTypeLivePhoto,
TZAssetCellTypePhotoGif,
TZAssetCellTypeVideo,
TZAssetCellTypeAudio,
} TZAssetCellType;
@class TZAssetModel;
@interface TZAssetCell : UICollectionViewCell
@property (weak, nonatomic) UIButton *selectPhotoButton;
@property (weak, nonatomic) UIButton *cannotSelectLayerButton;
@property (nonatomic, strong) TZAssetModel *model;
@property (assign, nonatomic) NSInteger index;
@property (nonatomic, copy) void (^didSelectPhotoBlock)(BOOL);
@property (nonatomic, assign) TZAssetCellType type;
@property (nonatomic, assign) BOOL allowPickingGif;
@property (nonatomic, assign) BOOL allowPickingMultipleVideo;
@property (nonatomic, copy) NSString *representedAssetIdentifier;
@property (nonatomic, assign) int32_t imageRequestID;
@property (nonatomic, strong) UIImage *photoSelImage;
@property (nonatomic, strong) UIImage *photoDefImage;
@property (nonatomic, assign) BOOL showSelectBtn;
@property (assign, nonatomic) BOOL allowPreview;
@property (assign, nonatomic) BOOL useCachedImage;
@end
@class TZAlbumModel;
@interface TZAlbumCell : UITableViewCell
@property (nonatomic, strong) TZAlbumModel *model;
@property (weak, nonatomic) UIButton *selectedCountButton;
@end
@interface TZAssetCameraCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *imageView;
@end
//
// TZAssetCell.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "TZAssetCell.h"
#import "TZAssetModel.h"
#import "UIView+Layout.h"
#import "TZImageManager.h"
#import "TZImagePickerController.h"
#import "TZProgressView.h"
@interface TZAssetCell ()
@property (weak, nonatomic) UIImageView *imageView; // The photo / 照片
@property (weak, nonatomic) UIImageView *selectImageView;
@property (weak, nonatomic) UILabel *indexLabel;
@property (weak, nonatomic) UIView *bottomView;
@property (weak, nonatomic) UILabel *timeLength;
@property (nonatomic, weak) UIImageView *videoImgView;
@property (nonatomic, strong) TZProgressView *progressView;
@property (nonatomic, assign) int32_t bigImageRequestID;
@end
@implementation TZAssetCell
- (void)setModel:(TZAssetModel *)model {
_model = model;
if (iOS8Later) {
self.representedAssetIdentifier = [[TZImageManager manager] getAssetIdentifier:model.asset];
}
if (self.useCachedImage && model.cachedImage) {
self.imageView.image = model.cachedImage;
} else {
int32_t imageRequestID = [[TZImageManager manager] getPhotoWithAsset:model.asset photoWidth:self.tz_width completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (self->_progressView) {
self.progressView.hidden = YES;
self.imageView.alpha = 1.0;
}
// Set the cell's thumbnail image if it's still showing the same asset.
if (!iOS8Later) {
self.imageView.image = photo; return;
}
if ([self.representedAssetIdentifier isEqualToString:[[TZImageManager manager] getAssetIdentifier:model.asset]]) {
self.imageView.image = photo;
} else {
// NSLog(@"this cell is showing other asset");
[[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID];
}
if (!isDegraded) {
self.imageRequestID = 0;
}
self.model.cachedImage = photo;
} progressHandler:nil networkAccessAllowed:NO];
if (imageRequestID && self.imageRequestID && imageRequestID != self.imageRequestID) {
[[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID];
// NSLog(@"cancelImageRequest %d",self.imageRequestID);
}
self.imageRequestID = imageRequestID;
}
self.selectPhotoButton.selected = model.isSelected;
self.selectImageView.image = self.selectPhotoButton.isSelected ? self.photoSelImage : self.photoDefImage;
self.indexLabel.hidden = !self.selectPhotoButton.isSelected;
self.type = (NSInteger)model.type;
// 让宽度/高度小于 最小可选照片尺寸 的图片不能选中
if (![[TZImageManager manager] isPhotoSelectableWithAsset:model.asset]) {
if (_selectImageView.hidden == NO) {
self.selectPhotoButton.hidden = YES;
_selectImageView.hidden = YES;
}
}
// 如果用户选中了该图片,提前获取一下大图
if (model.isSelected) {
[self fetchBigImage];
}
if (model.needOscillatoryAnimation) {
[UIView showOscillatoryAnimationWithLayer:self.selectImageView.layer type:TZOscillatoryAnimationToBigger];
model.needOscillatoryAnimation = NO;
}
[self setNeedsLayout];
}
- (void)setIndex:(NSInteger)index {
_index = index;
self.indexLabel.text = [NSString stringWithFormat:@"%zd", index];
[self.contentView bringSubviewToFront:self.indexLabel];
}
- (void)setShowSelectBtn:(BOOL)showSelectBtn {
_showSelectBtn = showSelectBtn;
if (!self.selectPhotoButton.hidden) {
self.selectPhotoButton.hidden = !showSelectBtn;
}
if (!self.selectImageView.hidden) {
self.selectImageView.hidden = !showSelectBtn;
}
}
- (void)setType:(TZAssetCellType)type {
_type = type;
if (type == TZAssetCellTypePhoto || type == TZAssetCellTypeLivePhoto || (type == TZAssetCellTypePhotoGif && !self.allowPickingGif) || self.allowPickingMultipleVideo) {
_selectImageView.hidden = NO;
_selectPhotoButton.hidden = NO;
_bottomView.hidden = YES;
} else { // Video of Gif
_selectImageView.hidden = YES;
_selectPhotoButton.hidden = YES;
}
if (type == TZAssetCellTypeVideo) {
self.bottomView.hidden = NO;
self.timeLength.text = _model.timeLength;
self.videoImgView.hidden = NO;
_timeLength.tz_left = self.videoImgView.tz_right;
_timeLength.textAlignment = NSTextAlignmentRight;
} else if (type == TZAssetCellTypePhotoGif && self.allowPickingGif) {
self.bottomView.hidden = NO;
self.timeLength.text = @"GIF";
self.videoImgView.hidden = YES;
_timeLength.tz_left = 5;
_timeLength.textAlignment = NSTextAlignmentLeft;
}
}
- (void)selectPhotoButtonClick:(UIButton *)sender {
if (self.didSelectPhotoBlock) {
self.didSelectPhotoBlock(sender.isSelected);
}
self.selectImageView.image = sender.isSelected ? self.photoSelImage : self.photoDefImage;
if (sender.isSelected) {
if (![TZImagePickerConfig sharedInstance].showSelectedIndex) {
[UIView showOscillatoryAnimationWithLayer:_selectImageView.layer type:TZOscillatoryAnimationToBigger];
}
// 用户选中了该图片,提前获取一下大图
[self fetchBigImage];
} else { // 取消选中,取消大图的获取
if (_bigImageRequestID && _progressView) {
[[PHImageManager defaultManager] cancelImageRequest:_bigImageRequestID];
[self hideProgressView];
}
}
}
- (void)hideProgressView {
self.progressView.hidden = YES;
self.imageView.alpha = 1.0;
}
- (void)fetchBigImage {
_bigImageRequestID = [[TZImageManager manager] getPhotoWithAsset:_model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (self->_progressView) {
[self hideProgressView];
}
} progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
if (self->_model.isSelected) {
progress = progress > 0.02 ? progress : 0.02;;
self.progressView.progress = progress;
self.progressView.hidden = NO;
self.imageView.alpha = 0.4;
if (progress >= 1) {
[self hideProgressView];
}
} else {
*stop = YES;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
} networkAccessAllowed:YES];
}
#pragma mark - Lazy load
- (UIButton *)selectPhotoButton {
if (_selectPhotoButton == nil) {
UIButton *selectPhotoButton = [[UIButton alloc] init];
[selectPhotoButton addTarget:self action:@selector(selectPhotoButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:selectPhotoButton];
_selectPhotoButton = selectPhotoButton;
}
return _selectPhotoButton;
}
- (UIImageView *)imageView {
if (_imageView == nil) {
UIImageView *imageView = [[UIImageView alloc] init];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
[self.contentView addSubview:imageView];
_imageView = imageView;
}
return _imageView;
}
- (UIImageView *)selectImageView {
if (_selectImageView == nil) {
UIImageView *selectImageView = [[UIImageView alloc] init];
selectImageView.contentMode = UIViewContentModeCenter;
selectImageView.clipsToBounds = YES;
[self.contentView addSubview:selectImageView];
_selectImageView = selectImageView;
}
return _selectImageView;
}
- (UIView *)bottomView {
if (_bottomView == nil) {
UIView *bottomView = [[UIView alloc] init];
static NSInteger rgb = 0;
bottomView.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.8];
[self.contentView addSubview:bottomView];
_bottomView = bottomView;
}
return _bottomView;
}
- (UIButton *)cannotSelectLayerButton {
if (_cannotSelectLayerButton == nil) {
UIButton *cannotSelectLayerButton = [[UIButton alloc] init];
[self.contentView addSubview:cannotSelectLayerButton];
_cannotSelectLayerButton = cannotSelectLayerButton;
}
return _cannotSelectLayerButton;
}
- (UIImageView *)videoImgView {
if (_videoImgView == nil) {
UIImageView *videoImgView = [[UIImageView alloc] init];
[videoImgView setImage:[UIImage imageNamedFromMyBundle:@"VideoSendIcon"]];
[self.bottomView addSubview:videoImgView];
_videoImgView = videoImgView;
}
return _videoImgView;
}
- (UILabel *)timeLength {
if (_timeLength == nil) {
UILabel *timeLength = [[UILabel alloc] init];
timeLength.font = [UIFont boldSystemFontOfSize:11];
timeLength.textColor = [UIColor whiteColor];
timeLength.textAlignment = NSTextAlignmentRight;
[self.bottomView addSubview:timeLength];
_timeLength = timeLength;
}
return _timeLength;
}
- (UILabel *)indexLabel {
if (_indexLabel == nil) {
UILabel *indexLabel = [[UILabel alloc] init];
indexLabel.font = [UIFont systemFontOfSize:14];
indexLabel.textColor = [UIColor whiteColor];
indexLabel.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:indexLabel];
_indexLabel = indexLabel;
}
return _indexLabel;
}
- (TZProgressView *)progressView {
if (_progressView == nil) {
_progressView = [[TZProgressView alloc] init];
_progressView.hidden = YES;
[self addSubview:_progressView];
}
return _progressView;
}
- (void)layoutSubviews {
[super layoutSubviews];
_cannotSelectLayerButton.frame = self.bounds;
if (self.allowPreview) {
_selectPhotoButton.frame = CGRectMake(self.tz_width - 44, 0, 44, 44);
} else {
_selectPhotoButton.frame = self.bounds;
}
_selectImageView.frame = CGRectMake(self.tz_width - 27, 3, 24, 24);
if (_selectImageView.image.size.width <= 27) {
_selectImageView.contentMode = UIViewContentModeCenter;
} else {
_selectImageView.contentMode = UIViewContentModeScaleAspectFit;
}
_indexLabel.frame = _selectImageView.frame;
_imageView.frame = CGRectMake(0, 0, self.tz_width, self.tz_height);
static CGFloat progressWH = 20;
CGFloat progressXY = (self.tz_width - progressWH) / 2;
_progressView.frame = CGRectMake(progressXY, progressXY, progressWH, progressWH);
_bottomView.frame = CGRectMake(0, self.tz_height - 17, self.tz_width, 17);
_videoImgView.frame = CGRectMake(8, 0, 17, 17);
_timeLength.frame = CGRectMake(self.videoImgView.tz_right, 0, self.tz_width - self.videoImgView.tz_right - 5, 17);
self.type = (NSInteger)self.model.type;
self.showSelectBtn = self.showSelectBtn;
[self.contentView bringSubviewToFront:_bottomView];
[self.contentView bringSubviewToFront:_cannotSelectLayerButton];
[self.contentView bringSubviewToFront:_selectPhotoButton];
[self.contentView bringSubviewToFront:_selectImageView];
[self.contentView bringSubviewToFront:_indexLabel];
}
@end
@interface TZAlbumCell ()
@property (weak, nonatomic) UIImageView *posterImageView;
@property (weak, nonatomic) UILabel *titleLabel;
@end
@implementation TZAlbumCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return self;
}
- (void)setModel:(TZAlbumModel *)model {
_model = model;
NSMutableAttributedString *nameString = [[NSMutableAttributedString alloc] initWithString:model.name attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor blackColor]}];
NSAttributedString *countString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" (%zd)",model.count] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor lightGrayColor]}];
[nameString appendAttributedString:countString];
self.titleLabel.attributedText = nameString;
[[TZImageManager manager] getPostImageWithAlbumModel:model completion:^(UIImage *postImage) {
self.posterImageView.image = postImage;
}];
if (model.selectedCount) {
self.selectedCountButton.hidden = NO;
[self.selectedCountButton setTitle:[NSString stringWithFormat:@"%zd",model.selectedCount] forState:UIControlStateNormal];
} else {
self.selectedCountButton.hidden = YES;
}
}
/// For fitting iOS6
- (void)layoutSubviews {
if (iOS7Later) [super layoutSubviews];
_selectedCountButton.frame = CGRectMake(self.tz_width - 24 - 30, 23, 24, 24);
NSInteger titleHeight = ceil(self.titleLabel.font.lineHeight);
self.titleLabel.frame = CGRectMake(80, (self.tz_height - titleHeight) / 2, self.tz_width - 80 - 50, titleHeight);
self.posterImageView.frame = CGRectMake(0, 0, 70, 70);
}
- (void)layoutSublayersOfLayer:(CALayer *)layer {
if (iOS7Later) [super layoutSublayersOfLayer:layer];
}
#pragma mark - Lazy load
- (UIImageView *)posterImageView {
if (_posterImageView == nil) {
UIImageView *posterImageView = [[UIImageView alloc] init];
posterImageView.contentMode = UIViewContentModeScaleAspectFill;
posterImageView.clipsToBounds = YES;
[self.contentView addSubview:posterImageView];
_posterImageView = posterImageView;
}
return _posterImageView;
}
- (UILabel *)titleLabel {
if (_titleLabel == nil) {
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont boldSystemFontOfSize:17];
titleLabel.textColor = [UIColor blackColor];
titleLabel.textAlignment = NSTextAlignmentLeft;
[self.contentView addSubview:titleLabel];
_titleLabel = titleLabel;
}
return _titleLabel;
}
- (UIButton *)selectedCountButton {
if (_selectedCountButton == nil) {
UIButton *selectedCountButton = [[UIButton alloc] init];
selectedCountButton.layer.cornerRadius = 12;
selectedCountButton.clipsToBounds = YES;
selectedCountButton.backgroundColor = [UIColor redColor];
[selectedCountButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
selectedCountButton.titleLabel.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:selectedCountButton];
_selectedCountButton = selectedCountButton;
}
return _selectedCountButton;
}
@end
@implementation TZAssetCameraCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
_imageView = [[UIImageView alloc] init];
_imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.contentView addSubview:_imageView];
self.clipsToBounds = YES;
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
_imageView.frame = self.bounds;
}
@end
//
// TZAssetModel.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TZAssetModelMediaTypePhoto = 0,
TZAssetModelMediaTypeLivePhoto,
TZAssetModelMediaTypePhotoGif,
TZAssetModelMediaTypeVideo,
TZAssetModelMediaTypeAudio
} TZAssetModelMediaType;
@class PHAsset;
@interface TZAssetModel : NSObject
@property (nonatomic, strong) id asset; ///< PHAsset or ALAsset
@property (nonatomic, assign) BOOL isSelected; ///< The select status of a photo, default is No
@property (nonatomic, assign) TZAssetModelMediaType type;
@property (assign, nonatomic) BOOL needOscillatoryAnimation;
@property (nonatomic, copy) NSString *timeLength;
@property (strong, nonatomic) UIImage *cachedImage;
/// Init a photo dataModel With a asset
/// 用一个PHAsset/ALAsset实例,初始化一个照片模型
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type;
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength;
@end
@class PHFetchResult;
@interface TZAlbumModel : NSObject
@property (nonatomic, strong) NSString *name; ///< The album name
@property (nonatomic, assign) NSInteger count; ///< Count of photos the album contain
@property (nonatomic, strong) id result; ///< PHFetchResult<PHAsset> or ALAssetsGroup<ALAsset>
@property (nonatomic, strong) NSArray *models;
@property (nonatomic, strong) NSArray *selectedModels;
@property (nonatomic, assign) NSUInteger selectedCount;
@property (nonatomic, assign) BOOL isCameraRoll;
- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets;
@end
//
// TZAssetModel.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "TZAssetModel.h"
#import "TZImageManager.h"
@implementation TZAssetModel
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type{
TZAssetModel *model = [[TZAssetModel alloc] init];
model.asset = asset;
model.isSelected = NO;
model.type = type;
return model;
}
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength {
TZAssetModel *model = [self modelWithAsset:asset type:type];
model.timeLength = timeLength;
return model;
}
@end
@implementation TZAlbumModel
- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets {
_result = result;
if (needFetchAssets) {
[[TZImageManager manager] getAssetsFromFetchResult:result completion:^(NSArray<TZAssetModel *> *models) {
self->_models = models;
if (self->_selectedModels) {
[self checkSelectedModels];
}
}];
}
}
- (void)setSelectedModels:(NSArray *)selectedModels {
_selectedModels = selectedModels;
if (_models) {
[self checkSelectedModels];
}
}
- (void)checkSelectedModels {
self.selectedCount = 0;
NSMutableArray *selectedAssets = [NSMutableArray array];
for (TZAssetModel *model in _selectedModels) {
[selectedAssets addObject:model.asset];
}
for (TZAssetModel *model in _models) {
if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) {
self.selectedCount ++;
}
}
}
- (NSString *)name {
if (_name) {
return _name;
}
return @"";
}
@end
//
// TZGifPhotoPreviewController.h
// TZImagePickerController
//
// Created by ttouch on 2016/12/13.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAssetModel;
@interface TZGifPhotoPreviewController : UIViewController
@property (nonatomic, strong) TZAssetModel *model;
@end
//
// TZGifPhotoPreviewController.m
// TZImagePickerController
//
// Created by ttouch on 2016/12/13.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZGifPhotoPreviewController.h"
#import "TZImagePickerController.h"
#import "TZAssetModel.h"
#import "UIView+Layout.h"
#import "TZPhotoPreviewCell.h"
#import "TZImageManager.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@interface TZGifPhotoPreviewController () {
UIView *_toolBar;
UIButton *_doneButton;
UIProgressView *_progress;
TZPhotoPreviewView *_previewView;
UIStatusBarStyle _originStatusBarStyle;
}
@end
@implementation TZGifPhotoPreviewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
self.navigationItem.title = [NSString stringWithFormat:@"GIF %@",tzImagePickerVc.previewBtnTitleStr];
}
[self configPreviewView];
[self configBottomToolBar];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
[UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
}
- (void)configPreviewView {
_previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero];
_previewView.model = self.model;
__weak typeof(self) weakSelf = self;
[_previewView setSingleTapGestureBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf signleTapAction];
}];
[self.view addSubview:_previewView];
}
- (void)configBottomToolBar {
_toolBar = [[UIView alloc] initWithFrame:CGRectZero];
CGFloat rgb = 34 / 255.0;
_toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7];
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
[_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
[_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal];
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal];
} else {
[_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal];
[_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal];
}
[_toolBar addSubview:_doneButton];
UILabel *byteLabel = [[UILabel alloc] init];
byteLabel.textColor = [UIColor whiteColor];
byteLabel.font = [UIFont systemFontOfSize:13];
byteLabel.frame = CGRectMake(10, 0, 100, 44);
[[TZImageManager manager] getPhotosBytesWithArray:@[_model] completion:^(NSString *totalBytes) {
byteLabel.text = totalBytes;
}];
[_toolBar addSubview:byteLabel];
[self.view addSubview:_toolBar];
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_previewView.frame = self.view.bounds;
_previewView.scrollView.frame = self.view.bounds;
CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44;
_toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight);
_doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44);
}
#pragma mark - Click Event
- (void)signleTapAction {
_toolBar.hidden = !_toolBar.isHidden;
[self.navigationController setNavigationBarHidden:_toolBar.isHidden];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (iOS7Later) {
if (_toolBar.isHidden) {
[UIApplication sharedApplication].statusBarHidden = YES;
} else if (tzImagePickerVc.needShowStatusBar) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
}
}
- (void)doneButtonClick {
if (self.navigationController) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
if (imagePickerVc.autoDismiss) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
} else {
[self callDelegateMethod];
}
} else {
[self dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
}
}
- (void)callDelegateMethod {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
UIImage *animatedImage = _previewView.imageView.image;
if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingGifImage:sourceAssets:)]) {
[imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingGifImage:animatedImage sourceAssets:_model.asset];
}
if (imagePickerVc.didFinishPickingGifImageHandle) {
imagePickerVc.didFinishPickingGifImageHandle(animatedImage,_model.asset);
}
}
#pragma clang diagnostic pop
@end
//
// TZImageCropManager.h
// TZImagePickerController
//
// Created by 谭真 on 2016/12/5.
// Copyright © 2016年 谭真. All rights reserved.
// 图片裁剪管理类
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface TZImageCropManager : NSObject
/// 裁剪框背景的处理
+ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop;
/*
1.7.2 为了解决多位同学对于图片裁剪的需求,我这两天有空便在研究图片裁剪
幸好有tuyou的PhotoTweaks库做参考,裁剪的功能实现起来简单许多
该方法和其内部引用的方法基本来自于tuyou的PhotoTweaks库,我做了稍许删减和修改
感谢tuyou同学在github开源了优秀的裁剪库PhotoTweaks,表示感谢
PhotoTweaks库的github链接:https://github.com/itouch2/PhotoTweaks
*/
/// 获得裁剪后的图片
+ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView;
/// 获取圆形图片
+ (UIImage *)circularClipImage:(UIImage *)image;
@end
/// 该分类的代码来自SDWebImage:https://github.com/rs/SDWebImage
/// 为了防止冲突,我将分类名字和方法名字做了修改
@interface UIImage (TZGif)
+ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data;
@end
//
// TZImageCropManager.m
// TZImagePickerController
//
// Created by 谭真 on 2016/12/5.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZImageCropManager.h"
#import "UIView+Layout.h"
#import <ImageIO/ImageIO.h>
#import "TZImageManager.h"
#import "TZImagePickerController.h"
@implementation TZImageCropManager
/// 裁剪框背景的处理
+ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop {
UIBezierPath *path= [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds];
CAShapeLayer *layer = [CAShapeLayer layer];
if (needCircleCrop) { // 圆形裁剪框
[path appendPath:[UIBezierPath bezierPathWithArcCenter:containerView.center radius:cropRect.size.width / 2 startAngle:0 endAngle: 2 * M_PI clockwise:NO]];
} else { // 矩形裁剪框
[path appendPath:[UIBezierPath bezierPathWithRect:cropRect]];
}
layer.path = path.CGPath;
layer.fillRule = kCAFillRuleEvenOdd;
layer.fillColor = [[UIColor blackColor] CGColor];
layer.opacity = 0.5;
[view.layer addSublayer:layer];
}
/// 获得裁剪后的图片
+ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView {
CGAffineTransform transform = CGAffineTransformIdentity;
// 平移的处理
CGRect imageViewRect = [imageView convertRect:imageView.bounds toView:containerView];
CGPoint point = CGPointMake(imageViewRect.origin.x + imageViewRect.size.width / 2, imageViewRect.origin.y + imageViewRect.size.height / 2);
CGFloat xMargin = containerView.tz_width - CGRectGetMaxX(rect) - rect.origin.x;
CGPoint zeroPoint = CGPointMake((CGRectGetWidth(containerView.frame) - xMargin) / 2, containerView.center.y);
CGPoint translation = CGPointMake(point.x - zeroPoint.x, point.y - zeroPoint.y);
transform = CGAffineTransformTranslate(transform, translation.x, translation.y);
// 缩放的处理
transform = CGAffineTransformScale(transform, zoomScale, zoomScale);
CGImageRef imageRef = [self newTransformedImage:transform
sourceImage:imageView.image.CGImage
sourceSize:imageView.image.size
outputWidth:rect.size.width * [UIScreen mainScreen].scale
cropSize:rect.size
imageViewSize:imageView.frame.size];
UIImage *cropedImage = [UIImage imageWithCGImage:imageRef];
cropedImage = [[TZImageManager manager] fixOrientation:cropedImage];
CGImageRelease(imageRef);
return cropedImage;
}
+ (CGImageRef)newTransformedImage:(CGAffineTransform)transform sourceImage:(CGImageRef)sourceImage sourceSize:(CGSize)sourceSize outputWidth:(CGFloat)outputWidth cropSize:(CGSize)cropSize imageViewSize:(CGSize)imageViewSize {
CGImageRef source = [self newScaledImage:sourceImage toSize:sourceSize];
CGFloat aspect = cropSize.height/cropSize.width;
CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect);
CGContextRef context = CGBitmapContextCreate(NULL, outputSize.width, outputSize.height, CGImageGetBitsPerComponent(source), 0, CGImageGetColorSpace(source), CGImageGetBitmapInfo(source));
CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height));
CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width / cropSize.width, outputSize.height / cropSize.height);
uiCoords = CGAffineTransformTranslate(uiCoords, cropSize.width/2.0, cropSize.height / 2.0);
uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0);
CGContextConcatCTM(context, uiCoords);
CGContextConcatCTM(context, transform);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2, -imageViewSize.height/2.0, imageViewSize.width, imageViewSize.height), source);
CGImageRef resultRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGImageRelease(source);
return resultRef;
}
+ (CGImageRef)newScaledImage:(CGImageRef)source toSize:(CGSize)size {
CGSize srcSize = size;
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(rgbColorSpace);
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
CGContextTranslateCTM(context, size.width/2, size.height/2);
CGContextDrawImage(context, CGRectMake(-srcSize.width/2, -srcSize.height/2, srcSize.width, srcSize.height), source);
CGImageRef resultRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return resultRef;
}
/// 获取圆形图片
+ (UIImage *)circularClipImage:(UIImage *)image {
UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextAddEllipseInRect(ctx, rect);
CGContextClip(ctx);
[image drawInRect:rect];
UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return circleImage;
}
@end
@implementation UIImage (TZGif)
+ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage;
if (count <= 1) {
animatedImage = [[UIImage alloc] initWithData:data];
}
else {
// images数组过大时内存会飙升,在这里限制下最大count
NSInteger maxCount = [TZImagePickerConfig sharedInstance].gifPreviewMaxImagesCount ?: 200;
NSInteger interval = MAX((count + maxCount / 2) / maxCount, 1);
NSMutableArray *images = [NSMutableArray array];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i+=interval) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
if (!image) {
continue;
}
duration += [self sd_frameDurationAtIndex:i source:source] * MIN(interval, 3);
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
CGImageRelease(image);
}
if (!duration) {
duration = (1.0f / 10.0f) * count;
}
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
CFRelease(source);
return animatedImage;
}
+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
float frameDuration = 0.1f;
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
if (delayTimeUnclampedProp) {
frameDuration = [delayTimeUnclampedProp floatValue];
}
else {
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
if (delayTimeProp) {
frameDuration = [delayTimeProp floatValue];
}
}
// Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
// We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
// a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
// for more information.
if (frameDuration < 0.011f) {
frameDuration = 0.100f;
}
CFRelease(cfFrameProperties);
return frameDuration;
}
@end
//
// TZImageManager.h
// TZImagePickerController
//
// Created by 谭真 on 16/1/4.
// Copyright © 2016年 谭真. All rights reserved.
// 图片资源获取管理类
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
#import "TZAssetModel.h"
@class TZAlbumModel,TZAssetModel;
@protocol TZImagePickerControllerDelegate;
@interface TZImageManager : NSObject
@property (nonatomic, strong) PHCachingImageManager *cachingImageManager;
+ (instancetype)manager NS_SWIFT_NAME(default());
+ (void)deallocManager;
@property (weak, nonatomic) id<TZImagePickerControllerDelegate> pickerDelegate;
@property (nonatomic, assign) BOOL shouldFixOrientation;
/// Default is 600px / 默认600像素宽
@property (nonatomic, assign) CGFloat photoPreviewMaxWidth;
/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽
@property (nonatomic, assign) CGFloat photoWidth;
/// Default is 4, Use in photos collectionView in TZPhotoPickerController
/// 默认4列, TZPhotoPickerController中的照片collectionView
@property (nonatomic, assign) NSInteger columnNumber;
/// Sort photos ascending by modificationDate,Default is YES
/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个
@property (nonatomic, assign) BOOL sortAscendingByModificationDate;
/// Minimum selectable photo width, Default is 0
/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中
@property (nonatomic, assign) NSInteger minPhotoWidthSelectable;
@property (nonatomic, assign) NSInteger minPhotoHeightSelectable;
@property (nonatomic, assign) BOOL hideWhenCanNotSelect;
/// Return YES if Authorized 返回YES如果得到了授权
- (BOOL)authorizationStatusAuthorized;
+ (NSInteger)authorizationStatus;
- (void)requestAuthorizationWithCompletion:(void (^)(void))completion;
/// Get Album 获得相册/相册数组
- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion;
- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *models))completion;
/// Get Assets 获得Asset数组
- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *models))completion;
- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *models))completion;
- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *model))completion;
/// Get photo 获得照片
- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *postImage))completion;
- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion;
- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion;
- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
/// Get full Image 获取原图
/// 如下两个方法completion一般会调多次,一般会先返回缩略图,再返回原图(详见方法内部使用的系统API的说明),如果info[PHImageResultIsDegradedKey] 为 YES,则表明当前返回的是缩略图,否则是原图。
- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion;
- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion;
// 该方法中,completion只会走一次
- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion;
/// Save photo 保存照片
- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion;
- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion;
/// Save video 保存视频
- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion;
- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion;
/// Get video 获得视频
- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem * playerItem, NSDictionary * info))completion;
- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion;
/// Export video 导出视频 presetName: 预设名字,默认值是AVAssetExportPreset640x480
- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure;
- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure;
/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success:
- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion __attribute__((deprecated("Use -getVideoOutputPathWithAsset:failure:success:")));
/// Get photo bytes 获得一组照片的大小
- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion;
/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset
- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset;
- (NSString *)getAssetIdentifier:(id)asset;
- (BOOL)isCameraRollAlbum:(id)metadata;
/// 检查照片大小是否满足最小要求
- (BOOL)isPhotoSelectableWithAsset:(id)asset;
- (CGSize)photoSizeWithAsset:(id)asset;
/// 修正图片转向
- (UIImage *)fixOrientation:(UIImage *)aImage;
/// 获取asset的资源类型
- (TZAssetModelMediaType)getAssetType:(id)asset;
/// 缩放图片至新尺寸
- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size;
@end
//@interface TZSortDescriptor : NSSortDescriptor
//
//@end
//
// TZImageManager.m
// TZImagePickerController
//
// Created by 谭真 on 16/1/4.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZImageManager.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import "TZAssetModel.h"
#import "TZImagePickerController.h"
@interface TZImageManager ()
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@property (nonatomic, strong) ALAssetsLibrary *assetLibrary;
@end
@implementation TZImageManager
CGSize AssetGridThumbnailSize;
CGFloat TZScreenWidth;
CGFloat TZScreenScale;
static TZImageManager *manager;
static dispatch_once_t onceToken;
+ (instancetype)manager {
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
if (iOS8Later) {
// manager.cachingImageManager = [[PHCachingImageManager alloc] init];
// manager.cachingImageManager.allowsCachingHighQualityImages = YES;
}
[manager configTZScreenWidth];
});
return manager;
}
+ (void)deallocManager {
onceToken = 0;
manager = nil;
}
- (void)setPhotoWidth:(CGFloat)photoWidth {
_photoWidth = photoWidth;
TZScreenWidth = photoWidth / 2;
}
- (void)setColumnNumber:(NSInteger)columnNumber {
[self configTZScreenWidth];
_columnNumber = columnNumber;
CGFloat margin = 4;
CGFloat itemWH = (TZScreenWidth - 2 * margin - 4) / columnNumber - margin;
AssetGridThumbnailSize = CGSizeMake(itemWH * TZScreenScale, itemWH * TZScreenScale);
}
- (void)configTZScreenWidth {
TZScreenWidth = [UIScreen mainScreen].bounds.size.width;
// 测试发现,如果scale在plus真机上取到3.0,内存会增大特别多。故这里写死成2.0
TZScreenScale = 2.0;
if (TZScreenWidth > 700) {
TZScreenScale = 1.5;
}
}
- (ALAssetsLibrary *)assetLibrary {
if (_assetLibrary == nil) _assetLibrary = [[ALAssetsLibrary alloc] init];
return _assetLibrary;
}
/// Return YES if Authorized 返回YES如果得到了授权
- (BOOL)authorizationStatusAuthorized {
NSInteger status = [self.class authorizationStatus];
if (status == 0) {
/**
* 当某些情况下AuthorizationStatus == AuthorizationStatusNotDetermined时,无法弹出系统首次使用的授权alertView,系统应用设置里亦没有相册的设置,此时将无法使用,故作以下操作,弹出系统首次使用的授权alertView
*/
[self requestAuthorizationWithCompletion:nil];
}
return status == 3;
}
+ (NSInteger)authorizationStatus {
if (iOS8Later) {
return [PHPhotoLibrary authorizationStatus];
} else {
return [ALAssetsLibrary authorizationStatus];
}
return NO;
}
- (void)requestAuthorizationWithCompletion:(void (^)(void))completion {
void (^callCompletionBlock)(void) = ^(){
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion();
}
});
};
if (iOS8Later) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
callCompletionBlock();
}];
});
} else {
[self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
callCompletionBlock();
} failureBlock:^(NSError *error) {
callCompletionBlock();
}];
}
}
#pragma mark - Get Album
/// Get Album 获得相册/相册数组
- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion {
__block TZAlbumModel *model;
if (iOS8Later) {
PHFetchOptions *option = [[PHFetchOptions alloc] init];
if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];
if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
PHAssetMediaTypeVideo];
// option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]];
if (!self.sortAscendingByModificationDate) {
option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]];
}
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
for (PHAssetCollection *collection in smartAlbums) {
// 有可能是PHCollectionList类的的对象,过滤掉
if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
// 过滤空相册
if (collection.estimatedAssetCount <= 0) continue;
if ([self isCameraRollAlbum:collection]) {
PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option];
model = [self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets];
if (completion) completion(model);
break;
}
}
} else {
[self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([group numberOfAssets] < 1) return;
if ([self isCameraRollAlbum:group]) {
NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
model = [self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets];
if (completion) completion(model);
*stop = YES;
}
} failureBlock:nil];
}
}
- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *))completion{
NSMutableArray *albumArr = [NSMutableArray array];
if (iOS8Later) {
PHFetchOptions *option = [[PHFetchOptions alloc] init];
if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];
if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
PHAssetMediaTypeVideo];
// option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]];
if (!self.sortAscendingByModificationDate) {
option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]];
}
// 我的照片流 1.6.10重新加入..
PHFetchResult *myPhotoStreamAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil];
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil];
PHFetchResult *sharedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumCloudShared options:nil];
NSArray *allAlbums = @[myPhotoStreamAlbum,smartAlbums,topLevelUserCollections,syncedAlbums,sharedAlbums];
for (PHFetchResult *fetchResult in allAlbums) {
for (PHAssetCollection *collection in fetchResult) {
// 有可能是PHCollectionList类的的对象,过滤掉
if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
// 过滤空相册
if (collection.estimatedAssetCount <= 0) continue;
PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option];
if (fetchResult.count < 1) continue;
if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) {
if (![self.pickerDelegate isAlbumCanSelect:collection.localizedTitle result:fetchResult]) {
continue;
}
}
if (collection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumAllHidden) continue;
if (collection.assetCollectionSubtype == 1000000201) continue; //『最近删除』相册
if ([self isCameraRollAlbum:collection]) {
[albumArr insertObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0];
} else {
[albumArr addObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:NO needFetchAssets:needFetchAssets]];
}
}
}
if (completion && albumArr.count > 0) completion(albumArr);
} else {
[self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group == nil) {
if (completion && albumArr.count > 0) completion(albumArr);
}
if ([group numberOfAssets] < 1) return;
NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) {
if (![self.pickerDelegate isAlbumCanSelect:name result:group]) {
return;
}
}
if ([self isCameraRollAlbum:group]) {
[albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0];
} else if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupPhotoStream) {
if (albumArr.count) {
[albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets] atIndex:1];
} else {
[albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]];
}
} else {
[albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]];
}
} failureBlock:nil];
}
}
#pragma mark - Get Assets
/// Get Assets 获得照片数组
- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *))completion {
TZImagePickerConfig *config = [TZImagePickerConfig sharedInstance];
return [self getAssetsFromFetchResult:result allowPickingVideo:config.allowPickingVideo allowPickingImage:config.allowPickingImage completion:completion];
}
- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *))completion {
NSMutableArray *photoArr = [NSMutableArray array];
if ([result isKindOfClass:[PHFetchResult class]]) {
PHFetchResult *fetchResult = (PHFetchResult *)result;
[fetchResult enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
TZAssetModel *model = [self assetModelWithAsset:obj allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
if (model) {
[photoArr addObject:model];
}
}];
if (completion) completion(photoArr);
} else if ([result isKindOfClass:[ALAssetsGroup class]]) {
ALAssetsGroup *group = (ALAssetsGroup *)result;
if (allowPickingImage && allowPickingVideo) {
[group setAssetsFilter:[ALAssetsFilter allAssets]];
} else if (allowPickingVideo) {
[group setAssetsFilter:[ALAssetsFilter allVideos]];
} else if (allowPickingImage) {
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
}
ALAssetsGroupEnumerationResultsBlock resultBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result == nil) {
if (completion) completion(photoArr);
}
TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
if (model) {
[photoArr addObject:model];
}
};
if (self.sortAscendingByModificationDate) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (resultBlock) { resultBlock(result,index,stop); }
}];
} else {
[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (resultBlock) { resultBlock(result,index,stop); }
}];
}
}
}
/// Get asset at index 获得下标为index的单个照片
/// if index beyond bounds, return nil in callback 如果索引越界, 在回调中返回 nil
- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *))completion {
if ([result isKindOfClass:[PHFetchResult class]]) {
PHFetchResult *fetchResult = (PHFetchResult *)result;
PHAsset *asset;
@try {
asset = fetchResult[index];
}
@catch (NSException* e) {
if (completion) completion(nil);
return;
}
TZAssetModel *model = [self assetModelWithAsset:asset allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
if (completion) completion(model);
} else if ([result isKindOfClass:[ALAssetsGroup class]]) {
ALAssetsGroup *group = (ALAssetsGroup *)result;
if (allowPickingImage && allowPickingVideo) {
[group setAssetsFilter:[ALAssetsFilter allAssets]];
} else if (allowPickingVideo) {
[group setAssetsFilter:[ALAssetsFilter allVideos]];
} else if (allowPickingImage) {
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
}
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index];
@try {
[group enumerateAssetsAtIndexes:indexSet options:NSEnumerationConcurrent usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (!result) return;
TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage];
if (completion) completion(model);
}];
}
@catch (NSException* e) {
if (completion) completion(nil);
}
}
}
- (TZAssetModel *)assetModelWithAsset:(id)asset allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage {
BOOL canSelect = YES;
if ([self.pickerDelegate respondsToSelector:@selector(isAssetCanSelect:)]) {
canSelect = [self.pickerDelegate isAssetCanSelect:asset];
}
if (!canSelect) return nil;
TZAssetModel *model;
TZAssetModelMediaType type = [self getAssetType:asset];
if ([asset isKindOfClass:[PHAsset class]]) {
if (!allowPickingVideo && type == TZAssetModelMediaTypeVideo) return nil;
if (!allowPickingImage && type == TZAssetModelMediaTypePhoto) return nil;
if (!allowPickingImage && type == TZAssetModelMediaTypePhotoGif) return nil;
PHAsset *phAsset = (PHAsset *)asset;
if (self.hideWhenCanNotSelect) {
// 过滤掉尺寸不满足要求的图片
if (![self isPhotoSelectableWithAsset:phAsset]) {
return nil;
}
}
NSString *timeLength = type == TZAssetModelMediaTypeVideo ? [NSString stringWithFormat:@"%0.0f",phAsset.duration] : @"";
timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue];
model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength];
} else {
if (!allowPickingVideo){
model = [TZAssetModel modelWithAsset:asset type:type];
return model;
}
/// Allow picking video
if (type == TZAssetModelMediaTypeVideo) {
NSTimeInterval duration = [[asset valueForProperty:ALAssetPropertyDuration] doubleValue];
NSString *timeLength = [NSString stringWithFormat:@"%0.0f",duration];
timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue];
model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength];
} else {
if (self.hideWhenCanNotSelect) {
// 过滤掉尺寸不满足要求的图片
if (![self isPhotoSelectableWithAsset:asset]) {
return nil;
}
}
model = [TZAssetModel modelWithAsset:asset type:type];
}
}
return model;
}
- (TZAssetModelMediaType)getAssetType:(id)asset {
TZAssetModelMediaType type = TZAssetModelMediaTypePhoto;
if ([asset isKindOfClass:[PHAsset class]]) {
PHAsset *phAsset = (PHAsset *)asset;
if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo;
else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio;
else if (phAsset.mediaType == PHAssetMediaTypeImage) {
if (iOS9_1Later) {
// if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) type = TZAssetModelMediaTypeLivePhoto;
}
// Gif
if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) {
type = TZAssetModelMediaTypePhotoGif;
}
}
} else {
if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) {
type = TZAssetModelMediaTypeVideo;
}
}
return type;
}
- (NSString *)getNewTimeFromDurationSecond:(NSInteger)duration {
NSString *newTime;
if (duration < 10) {
newTime = [NSString stringWithFormat:@"0:0%zd",duration];
} else if (duration < 60) {
newTime = [NSString stringWithFormat:@"0:%zd",duration];
} else {
NSInteger min = duration / 60;
NSInteger sec = duration - (min * 60);
if (sec < 10) {
newTime = [NSString stringWithFormat:@"%zd:0%zd",min,sec];
} else {
newTime = [NSString stringWithFormat:@"%zd:%zd",min,sec];
}
}
return newTime;
}
/// Get photo bytes 获得一组照片的大小
- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion {
if (!photos || !photos.count) {
if (completion) completion(@"0B");
return;
}
__block NSInteger dataLength = 0;
__block NSInteger assetCount = 0;
for (NSInteger i = 0; i < photos.count; i++) {
TZAssetModel *model = photos[i];
if ([model.asset isKindOfClass:[PHAsset class]]) {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.resizeMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager] requestImageDataForAsset:model.asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
if (model.type != TZAssetModelMediaTypeVideo) dataLength += imageData.length;
assetCount ++;
if (assetCount >= photos.count) {
NSString *bytes = [self getBytesFromDataLength:dataLength];
if (completion) completion(bytes);
}
}];
} else if ([model.asset isKindOfClass:[ALAsset class]]) {
ALAssetRepresentation *representation = [model.asset defaultRepresentation];
if (model.type != TZAssetModelMediaTypeVideo) dataLength += (NSInteger)representation.size;
if (i >= photos.count - 1) {
NSString *bytes = [self getBytesFromDataLength:dataLength];
if (completion) completion(bytes);
}
}
}
}
- (NSString *)getBytesFromDataLength:(NSInteger)dataLength {
NSString *bytes;
if (dataLength >= 0.1 * (1024 * 1024)) {
bytes = [NSString stringWithFormat:@"%0.1fM",dataLength/1024/1024.0];
} else if (dataLength >= 1024) {
bytes = [NSString stringWithFormat:@"%0.0fK",dataLength/1024.0];
} else {
bytes = [NSString stringWithFormat:@"%zdB",dataLength];
}
return bytes;
}
#pragma mark - Get Photo
/// Get photo 获得照片本身
- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *, NSDictionary *, BOOL isDegraded))completion {
CGFloat fullScreenWidth = TZScreenWidth;
if (fullScreenWidth > _photoPreviewMaxWidth) {
fullScreenWidth = _photoPreviewMaxWidth;
}
return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:nil networkAccessAllowed:YES];
}
- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion {
return [self getPhotoWithAsset:asset photoWidth:photoWidth completion:completion progressHandler:nil networkAccessAllowed:YES];
}
- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed {
CGFloat fullScreenWidth = TZScreenWidth;
if (fullScreenWidth > _photoPreviewMaxWidth) {
fullScreenWidth = _photoPreviewMaxWidth;
}
return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:progressHandler networkAccessAllowed:networkAccessAllowed];
}
- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed {
if ([asset isKindOfClass:[PHAsset class]]) {
CGSize imageSize;
if (photoWidth < TZScreenWidth && photoWidth < _photoPreviewMaxWidth) {
imageSize = AssetGridThumbnailSize;
} else {
PHAsset *phAsset = (PHAsset *)asset;
CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight;
CGFloat pixelWidth = photoWidth * TZScreenScale * 1.5;
// 超宽图片
if (aspectRatio > 1.8) {
pixelWidth = pixelWidth * aspectRatio;
}
// 超高图片
if (aspectRatio < 0.2) {
pixelWidth = pixelWidth * 0.5;
}
CGFloat pixelHeight = pixelWidth / aspectRatio;
imageSize = CGSizeMake(pixelWidth, pixelHeight);
}
__block UIImage *image;
// 修复获取图片时出现的瞬间内存过高问题
// 下面两行代码,来自hsjcom,他的github是:https://github.com/hsjcom 表示感谢
PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
option.resizeMode = PHImageRequestOptionsResizeModeFast;
int32_t imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
image = result;
}
BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
if (downloadFinined && result) {
result = [self fixOrientation:result];
if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]);
}
// Download image from iCloud / 从iCloud下载图片
if ([info objectForKey:PHImageResultIsInCloudKey] && !result && networkAccessAllowed) {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (progressHandler) {
progressHandler(progress, error, stop, info);
}
});
};
options.networkAccessAllowed = YES;
options.resizeMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1];
resultImage = [self scaleImage:resultImage toSize:imageSize];
if (!resultImage) {
resultImage = image;
}
resultImage = [self fixOrientation:resultImage];
if (completion) completion(resultImage,info,NO);
}];
}
}];
return imageRequestID;
} else if ([asset isKindOfClass:[ALAsset class]]) {
ALAsset *alAsset = (ALAsset *)asset;
dispatch_async(dispatch_get_global_queue(0,0), ^{
CGImageRef thumbnailImageRef = alAsset.thumbnail;
UIImage *thumbnailImage = [UIImage imageWithCGImage:thumbnailImageRef scale:2.0 orientation:UIImageOrientationUp];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(thumbnailImage,nil,YES);
if (photoWidth == TZScreenWidth || photoWidth == self->_photoPreviewMaxWidth) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
ALAssetRepresentation *assetRep = [alAsset defaultRepresentation];
CGImageRef fullScrennImageRef = [assetRep fullScreenImage];
UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(fullScrennImage,nil,NO);
});
});
}
});
});
}
return 0;
}
/// Get postImage / 获取封面图
- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *))completion {
if (iOS8Later) {
id asset = [model.result lastObject];
if (!self.sortAscendingByModificationDate) {
asset = [model.result firstObject];
}
[[TZImageManager manager] getPhotoWithAsset:asset photoWidth:80 completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (completion) completion(photo);
}];
} else {
ALAssetsGroup *group = model.result;
UIImage *postImage = [UIImage imageWithCGImage:group.posterImage];
if (completion) completion(postImage);
}
}
/// Get Original Photo / 获取原图
- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion {
[self getOriginalPhotoWithAsset:asset newCompletion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (completion) {
completion(photo,info);
}
}];
}
- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion {
if ([asset isKindOfClass:[PHAsset class]]) {
PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init];
option.networkAccessAllowed = YES;
option.resizeMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage *result, NSDictionary *info) {
BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
if (downloadFinined && result) {
result = [self fixOrientation:result];
BOOL isDegraded = [[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (completion) completion(result,info,isDegraded);
}
}];
} else if ([asset isKindOfClass:[ALAsset class]]) {
ALAsset *alAsset = (ALAsset *)asset;
ALAssetRepresentation *assetRep = [alAsset defaultRepresentation];
dispatch_async(dispatch_get_global_queue(0,0), ^{
CGImageRef originalImageRef = [assetRep fullResolutionImage];
UIImage *originalImage = [UIImage imageWithCGImage:originalImageRef scale:1.0 orientation:UIImageOrientationUp];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(originalImage,nil,NO);
});
});
}
}
- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion {
if ([asset isKindOfClass:[PHAsset class]]) {
PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
option.networkAccessAllowed = YES;
if ([[asset valueForKey:@"filename"] hasSuffix:@"GIF"]) {
// if version isn't PHImageRequestOptionsVersionOriginal, the gif may cann't play
option.version = PHImageRequestOptionsVersionOriginal;
}
option.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:option resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
if (downloadFinined && imageData) {
if (completion) completion(imageData,info,NO);
}
}];
} else if ([asset isKindOfClass:[ALAsset class]]) {
ALAsset *alAsset = (ALAsset *)asset;
ALAssetRepresentation *assetRep = [alAsset defaultRepresentation];
Byte *imageBuffer = (Byte *)malloc(assetRep.size);
NSUInteger bufferSize = [assetRep getBytes:imageBuffer fromOffset:0.0 length:assetRep.size error:nil];
NSData *imageData = [NSData dataWithBytesNoCopy:imageBuffer length:bufferSize freeWhenDone:YES];
if (completion) completion(imageData,nil,NO);
}
}
#pragma mark - Save photo
- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion {
[self savePhotoWithImage:image location:nil completion:completion];
}
- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion {
if (iOS8Later) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
if (iOS9Later) {
NSData *data = UIImageJPEGRepresentation(image, 0.9);
PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
options.shouldMoveFile = YES;
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset];
[request addResourceWithType:PHAssetResourceTypePhoto data:data options:options];
if (location) {
request.location = location;
}
request.creationDate = [NSDate date];
} else {
PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
if (location) {
request.location = location;
}
request.creationDate = [NSDate date];
}
} completionHandler:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success && completion) {
completion(nil);
} else if (error) {
NSLog(@"保存照片出错:%@",error.localizedDescription);
if (completion) {
completion(error);
}
}
});
}];
} else {
[self.assetLibrary writeImageToSavedPhotosAlbum:image.CGImage orientation:[self orientationFromImage:image] completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"保存图片失败:%@",error.localizedDescription);
if (completion) {
completion(error);
}
} else {
// 多给系统0.5秒的时间,让系统去更新相册数据
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (completion) {
completion(nil);
}
});
}
}];
}
}
#pragma mark - Save video
- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion {
[self saveVideoWithUrl:url location:nil completion:completion];
}
- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion {
if (iOS8Later) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
if (iOS9Later) {
PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
options.shouldMoveFile = YES;
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset];
[request addResourceWithType:PHAssetResourceTypeVideo fileURL:url options:options];
if (location) {
request.location = location;
}
request.creationDate = [NSDate date];
} else {
PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
if (location) {
request.location = location;
}
request.creationDate = [NSDate date];
}
} completionHandler:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success && completion) {
completion(nil);
} else if (error) {
NSLog(@"保存视频出错:%@",error.localizedDescription);
if (completion) {
completion(error);
}
}
});
}];
} else {
[self.assetLibrary writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"保存视频出错:%@",error.localizedDescription);
if (completion) {
completion(error);
}
} else {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (completion) {
completion(nil);
}
});
}
}];
}
}
#pragma mark - Get Video
/// Get Video / 获取视频
- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem *, NSDictionary *))completion {
[self getVideoWithAsset:asset progressHandler:nil completion:completion];
}
- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion {
if ([asset isKindOfClass:[PHAsset class]]) {
PHVideoRequestOptions *option = [[PHVideoRequestOptions alloc] init];
option.networkAccessAllowed = YES;
option.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (progressHandler) {
progressHandler(progress, error, stop, info);
}
});
};
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:option resultHandler:^(AVPlayerItem *playerItem, NSDictionary *info) {
if (completion) completion(playerItem,info);
}];
} else if ([asset isKindOfClass:[ALAsset class]]) {
ALAsset *alAsset = (ALAsset *)asset;
ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation];
NSString *uti = [defaultRepresentation UTI];
NSURL *videoURL = [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:uti];
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL];
if (completion && playerItem) completion(playerItem,nil);
}
}
#pragma mark - Export video
/// Export Video / 导出视频
- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure {
[self getVideoOutputPathWithAsset:asset presetName:AVAssetExportPreset640x480 success:success failure:failure];
}
- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure {
if ([asset isKindOfClass:[PHAsset class]]) {
PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
options.networkAccessAllowed = YES;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){
// NSLog(@"Info:\n%@",info);
AVURLAsset *videoAsset = (AVURLAsset*)avasset;
// NSLog(@"AVAsset URL: %@",myAsset.URL);
[self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure];
}];
} else if ([asset isKindOfClass:[ALAsset class]]) {
NSURL *videoURL =[asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
[self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure];
}
}
/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success:
- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion {
[self getVideoOutputPathWithAsset:asset success:completion failure:nil];
}
- (void)startExportVideoWithVideoAsset:(AVURLAsset *)videoAsset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure {
// Find compatible presets by video asset.
NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset];
// Begin to compress video
// Now we just compress to low resolution if it supports
// If you need to upload to the server, but server does't support to upload by streaming,
// You can compress the resolution to lower. Or you can support more higher resolution.
if ([presets containsObject:presetName]) {
AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:presetName];
NSDateFormatter *formater = [[NSDateFormatter alloc] init];
[formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss-SSS"];
NSString *outputPath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/output-%@.mp4", [formater stringFromDate:[NSDate date]]];
// NSLog(@"video outputPath = %@",outputPath);
session.outputURL = [NSURL fileURLWithPath:outputPath];
// Optimize for network use.
session.shouldOptimizeForNetworkUse = true;
NSArray *supportedTypeArray = session.supportedFileTypes;
if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
session.outputFileType = AVFileTypeMPEG4;
} else if (supportedTypeArray.count == 0) {
if (failure) {
failure(@"该视频类型暂不支持导出", nil);
}
NSLog(@"No supported file types 视频类型暂不支持导出");
return;
} else {
session.outputFileType = [supportedTypeArray objectAtIndex:0];
}
if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"]]) {
[[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"] withIntermediateDirectories:YES attributes:nil error:nil];
}
AVMutableVideoComposition *videoComposition = [self fixedCompositionWithAsset:videoAsset];
if (videoComposition.renderSize.width) {
// 修正视频转向
session.videoComposition = videoComposition;
}
// Begin to export video to the output path asynchronously.
[session exportAsynchronouslyWithCompletionHandler:^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (session.status) {
case AVAssetExportSessionStatusUnknown: {
NSLog(@"AVAssetExportSessionStatusUnknown");
} break;
case AVAssetExportSessionStatusWaiting: {
NSLog(@"AVAssetExportSessionStatusWaiting");
} break;
case AVAssetExportSessionStatusExporting: {
NSLog(@"AVAssetExportSessionStatusExporting");
} break;
case AVAssetExportSessionStatusCompleted: {
NSLog(@"AVAssetExportSessionStatusCompleted");
if (success) {
success(outputPath);
}
} break;
case AVAssetExportSessionStatusFailed: {
NSLog(@"AVAssetExportSessionStatusFailed");
if (failure) {
failure(@"视频导出失败", session.error);
}
} break;
case AVAssetExportSessionStatusCancelled: {
NSLog(@"AVAssetExportSessionStatusCancelled");
if (failure) {
failure(@"导出任务已被取消", nil);
}
} break;
default: break;
}
});
}];
} else {
if (failure) {
NSString *errorMessage = [NSString stringWithFormat:@"当前设备不支持该预设:%@", presetName];
failure(errorMessage, nil);
}
}
}
/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset
- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset {
if (iOS8Later) {
return [assets containsObject:asset];
} else {
NSMutableArray *selectedAssetUrls = [NSMutableArray array];
for (ALAsset *asset_item in assets) {
[selectedAssetUrls addObject:[asset_item valueForProperty:ALAssetPropertyURLs]];
}
return [selectedAssetUrls containsObject:[asset valueForProperty:ALAssetPropertyURLs]];
}
}
- (BOOL)isCameraRollAlbum:(id)metadata {
if ([metadata isKindOfClass:[PHAssetCollection class]]) {
NSString *versionStr = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@""];
if (versionStr.length <= 1) {
versionStr = [versionStr stringByAppendingString:@"00"];
} else if (versionStr.length <= 2) {
versionStr = [versionStr stringByAppendingString:@"0"];
}
CGFloat version = versionStr.floatValue;
// 目前已知8.0.0 ~ 8.0.2系统,拍照后的图片会保存在最近添加中
if (version >= 800 && version <= 802) {
return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumRecentlyAdded;
} else {
return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumUserLibrary;
}
}
if ([metadata isKindOfClass:[ALAssetsGroup class]]) {
ALAssetsGroup *group = metadata;
return ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupSavedPhotos);
}
return NO;
}
- (NSString *)getAssetIdentifier:(id)asset {
if (iOS8Later) {
PHAsset *phAsset = (PHAsset *)asset;
return phAsset.localIdentifier;
} else {
ALAsset *alAsset = (ALAsset *)asset;
NSURL *assetUrl = [alAsset valueForProperty:ALAssetPropertyAssetURL];
return assetUrl.absoluteString;
}
}
/// 检查照片大小是否满足最小要求
- (BOOL)isPhotoSelectableWithAsset:(id)asset {
CGSize photoSize = [self photoSizeWithAsset:asset];
if (self.minPhotoWidthSelectable > photoSize.width || self.minPhotoHeightSelectable > photoSize.height) {
return NO;
}
return YES;
}
- (CGSize)photoSizeWithAsset:(id)asset {
if (iOS8Later) {
PHAsset *phAsset = (PHAsset *)asset;
return CGSizeMake(phAsset.pixelWidth, phAsset.pixelHeight);
} else {
ALAsset *alAsset = (ALAsset *)asset;
return alAsset.defaultRepresentation.dimensions;
}
}
#pragma mark - Private Method
- (TZAlbumModel *)modelWithResult:(id)result name:(NSString *)name isCameraRoll:(BOOL)isCameraRoll needFetchAssets:(BOOL)needFetchAssets {
TZAlbumModel *model = [[TZAlbumModel alloc] init];
[model setResult:result needFetchAssets:needFetchAssets];
model.name = name;
model.isCameraRoll = isCameraRoll;
if ([result isKindOfClass:[PHFetchResult class]]) {
PHFetchResult *fetchResult = (PHFetchResult *)result;
model.count = fetchResult.count;
} else if ([result isKindOfClass:[ALAssetsGroup class]]) {
ALAssetsGroup *group = (ALAssetsGroup *)result;
model.count = [group numberOfAssets];
}
return model;
}
/// 缩放图片至新尺寸
- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size {
if (image.size.width > size.width) {
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
/* 好像不怎么管用:https://mp.weixin.qq.com/s/CiqMlEIp1Ir2EJSDGgMooQ
CGFloat maxPixelSize = MAX(size.width, size.height);
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)UIImageJPEGRepresentation(image, 0.9), nil);
NSDictionary *options = @{(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways:(__bridge id)kCFBooleanTrue,
(__bridge id)kCGImageSourceThumbnailMaxPixelSize:[NSNumber numberWithFloat:maxPixelSize]
};
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:2 orientation:image.imageOrientation];
CGImageRelease(imageRef);
CFRelease(sourceRef);
return newImage;
*/
} else {
return image;
}
}
- (ALAssetOrientation)orientationFromImage:(UIImage *)image {
NSInteger orientation = image.imageOrientation;
return orientation;
}
/// 获取优化后的视频转向信息
- (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset {
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
// 视频转向
int degrees = [self degressFromVideoFileWithAsset:videoAsset];
if (degrees != 0) {
CGAffineTransform translateToCenter;
CGAffineTransform mixedTransform;
videoComposition.frameDuration = CMTimeMake(1, 30);
NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]);
AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
if (degrees == 90) {
// 顺时针旋转90°
translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2);
videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
[roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
} else if(degrees == 180){
// 顺时针旋转180°
translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI);
videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
[roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
} else if(degrees == 270){
// 顺时针旋转270°
translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width);
mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0);
videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
[roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
}
roateInstruction.layerInstructions = @[roateLayerInstruction];
// 加入视频方向信息
videoComposition.instructions = @[roateInstruction];
}
return videoComposition;
}
/// 获取视频角度
- (int)degressFromVideoFileWithAsset:(AVAsset *)asset {
int degress = 0;
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if([tracks count] > 0) {
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
CGAffineTransform t = videoTrack.preferredTransform;
if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
// Portrait
degress = 90;
} else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
// PortraitUpsideDown
degress = 270;
} else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
// LandscapeRight
degress = 0;
} else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
// LandscapeLeft
degress = 180;
}
}
return degress;
}
/// 修正图片转向
- (UIImage *)fixOrientation:(UIImage *)aImage {
if (!self.shouldFixOrientation) return aImage;
// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
#pragma clang diagnostic pop
@end
//@implementation TZSortDescriptor
//
//- (id)reversedSortDescriptor {
// return [NSNumber numberWithBool:![TZImageManager manager].sortAscendingByModificationDate];
//}
//
//@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StringsTable</key>
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Group</string>
</dict>
<dict>
<key>Type</key>
<string>PSTextFieldSpecifier</string>
<key>Title</key>
<string>Name</string>
<key>Key</key>
<string>name_preference</string>
<key>DefaultValue</key>
<string></string>
<key>IsSecure</key>
<false/>
<key>KeyboardType</key>
<string>Alphabet</string>
<key>AutocapitalizationType</key>
<string>None</string>
<key>AutocorrectionType</key>
<string>No</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Enabled</string>
<key>Key</key>
<string>enabled_preference</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSSliderSpecifier</string>
<key>Key</key>
<string>slider_preference</string>
<key>DefaultValue</key>
<real>0.5</real>
<key>MinimumValue</key>
<integer>0</integer>
<key>MaximumValue</key>
<integer>1</integer>
<key>MinimumValueImage</key>
<string></string>
<key>MaximumValueImage</key>
<string></string>
</dict>
</array>
</dict>
</plist>
"OK" = "Xác nhận";
"Back" = "Quay lại";
"Done" = "Hoàn thành";
"Sorry" = "Xin lỗi";
"Cancel" = "Hủy";
"Setting" = "Cài đặt";
"Photos" = "Hình";
"Videos" = "Clip";
"Preview" = "Xem trước";
"Full image" = "Hình gốc";
"Processing..." = "Đang xử lý...";
"Can not use camera" = "Máy chụp hình không khả dụng";
"Synchronizing photos from iCloud" = "Đang đồng bộ hình ảnh từ ICloud";
"Can not choose both video and photo" = "Trong lúc chọn hình ảnh không cùng lúc chọn video";
"Can not choose both photo and GIF" = "Trong lúc chọn hình ảnh không cùng lúc chọn hình GIF";
"Select the video when in multi state, we will handle the video as a photo" = "Chọn hình ảnh cùng video, video sẽ bị mặc nhận thành hình ảnh và gửi đi.";
"Can not jump to the privacy settings page, please go to the settings page by self, thank you" = "Không thể chuyển tự động qua trang cài đặt riêng tư, bạn hãy thoát ra cà điều chỉnh lại, cám ơn bạn.";
"Select a maximum of %zd photos" = "Bạn chỉ được chọn nhiều nhất %zd tấm hình";
"Select a minimum of %zd photos" = "Chọn ít nhất %zd tấm hình";
"Allow %@ to access your album in \"Settings -> Privacy -> Photos\"" = "Vui lòng tại mục iPhone \" Cài đặt – quyền riêng tư - Ảnh\" mở quyền cho phép %@ truy cập ảnh.";
"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\"" = "Vui lòng tại mục iPhone \" Cài đặt – quyền riêng tư - Ảnh\" mở quyền cho phép %@ truy cập máy ảnh";
//
// TZImagePickerController.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
// version 2.1.1 - 2018.05.07
// 更多信息,请前往项目的github地址:https://github.com/banchichen/TZImagePickerController
/*
经过测试,比起xib的方式,把TZAssetCell改用纯代码的方式来写,滑动帧数明显提高了(约提高10帧左右)
最初发现这个问题并修复的是@小鱼周凌宇同学,她的博客地址: http://zhoulingyu.com/
表示感谢~
原来xib确实会导致性能问题啊...大家也要注意了...
*/
#import <UIKit/UIKit.h>
#import "TZAssetModel.h"
#import "NSBundle+TZImagePicker.h"
#import "TZImageManager.h"
#import "TZVideoPlayerController.h"
#import "TZGifPhotoPreviewController.h"
#import "TZLocationManager.h"
#import "TZPhotoPreviewController.h"
#define iOS7Later ([UIDevice currentDevice].systemVersion.floatValue >= 7.0f)
#define iOS8Later ([UIDevice currentDevice].systemVersion.floatValue >= 8.0f)
#define iOS9Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.0f)
#define iOS9_1Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.1f)
#define iOS11Later ([UIDevice currentDevice].systemVersion.floatValue >= 11.0f)
@protocol TZImagePickerControllerDelegate;
@interface TZImagePickerController : UINavigationController
#pragma mark -
/// Use this init method / 用这个初始化方法
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate;
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate;
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc;
/// This init method just for previewing photos / 用这个初始化方法以预览图片
- (instancetype)initWithSelectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index;
/// This init method for crop photo / 用这个初始化方法以裁剪图片
- (instancetype)initCropTypeWithAsset:(id)asset photo:(UIImage *)photo completion:(void (^)(UIImage *cropImage,id asset))completion;
#pragma mark -
/// Default is 9 / 默认最大可选9张图片
@property (nonatomic, assign) NSInteger maxImagesCount;
/// The minimum count photos user must pick, Default is 0
/// 最小照片必选张数,默认是0
@property (nonatomic, assign) NSInteger minImagesCount;
/// Always enale the done button, not require minimum 1 photo be picked
/// 让完成按钮一直可以点击,无须最少选择一张图片
@property (nonatomic, assign) BOOL alwaysEnableDoneBtn;
/// Sort photos ascending by modificationDate,Default is YES
/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个
@property (nonatomic, assign) BOOL sortAscendingByModificationDate;
/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽
@property (nonatomic, assign) CGFloat photoWidth;
/// Default is 600px / 默认600像素宽
@property (nonatomic, assign) CGFloat photoPreviewMaxWidth;
/// Default is 15, While fetching photo, HUD will dismiss automatic if timeout;
/// 超时时间,默认为15秒,当取图片时间超过15秒还没有取成功时,会自动dismiss HUD;
@property (nonatomic, assign) NSInteger timeout;
/// Default is YES, if set NO, the original photo button will hide. user can't picking original photo.
/// 默认为YES,如果设置为NO,原图按钮将隐藏,用户不能选择发送原图
@property (nonatomic, assign) BOOL allowPickingOriginalPhoto;
/// Default is YES, if set NO, user can't picking video.
/// 默认为YES,如果设置为NO,用户将不能选择视频
@property (nonatomic, assign) BOOL allowPickingVideo;
/// Default is NO / 默认为NO,为YES时可以多选视频/gif图片,和照片共享最大可选张数maxImagesCount的限制
@property (nonatomic, assign) BOOL allowPickingMultipleVideo;
/// Default is NO, if set YES, user can picking gif image.
/// 默认为NO,如果设置为YES,用户可以选择gif图片
@property (nonatomic, assign) BOOL allowPickingGif;
/// Default is YES, if set NO, user can't picking image.
/// 默认为YES,如果设置为NO,用户将不能选择发送图片
@property(nonatomic, assign) BOOL allowPickingImage;
/// Default is YES, if set NO, user can't take picture.
/// 默认为YES,如果设置为NO, 用户将不能拍摄照片
@property(nonatomic, assign) BOOL allowTakePicture;
/// Default is YES, if set NO, user can't take video.
/// 默认为YES,如果设置为NO, 用户将不能拍摄视频
@property(nonatomic, assign) BOOL allowTakeVideo;
/// Default value is 10 minutes / 视频最大拍摄时间,默认是10分钟,单位是秒
@property (assign, nonatomic) NSTimeInterval videoMaximumDuration;
/// 首选语言,如果设置了就用该语言,不设则取当前系统语言。
/// 由于目前只支持中文、繁体中文、英文、越南语。故该属性只支持zh-Hans、zh-Hant、en、vi四种值,其余值无效。
@property (copy, nonatomic) NSString *preferredLanguage;
/// 语言bundle,preferredLanguage变化时languageBundle会变化
/// 可通过手动设置bundle,让选择器支持新的的语言(需要在设置preferredLanguage后设置languageBundle)。欢迎提交PR把语言文件提交上来~
@property (strong, nonatomic) NSBundle *languageBundle;
/// Default is YES, if set NO, user can't preview photo.
/// 默认为YES,如果设置为NO,预览按钮将隐藏,用户将不能去预览照片
@property (nonatomic, assign) BOOL allowPreview;
/// Default is YES, if set NO, the picker don't dismiss itself.
/// 默认为YES,如果设置为NO, 选择器将不会自己dismiss
@property(nonatomic, assign) BOOL autoDismiss;
/// Default is NO, if set YES, in the delegate method the photos and infos will be nil, only assets hava value.
/// 默认为NO,如果设置为YES,代理方法里photos和infos会是nil,只返回assets
@property (assign, nonatomic) BOOL onlyReturnAsset;
/// Default is NO, if set YES, will show the image's selected index.
/// 默认为NO,如果设置为YES,会显示照片的选中序号
@property (assign, nonatomic) BOOL showSelectedIndex;
/// Default is NO, if set YES, when selected photos's count up to maxImagesCount, other photo will show float layer what's color is cannotSelectLayerColor.
/// 默认是NO,如果设置为YES,当照片选择张数达到maxImagesCount时,其它照片会显示颜色为cannotSelectLayerColor的浮层
@property (assign, nonatomic) BOOL showPhotoCannotSelectLayer;
/// Default is white color with 0.8 alpha;
@property (strong, nonatomic) UIColor *cannotSelectLayerColor;
/// The photos user have selected
/// 用户选中过的图片数组
@property (nonatomic, strong) NSMutableArray *selectedAssets;
@property (nonatomic, strong) NSMutableArray<TZAssetModel *> *selectedModels;
@property (nonatomic, strong) NSMutableArray *selectedAssetIds;
- (void)addSelectedModel:(TZAssetModel *)model;
- (void)removeSelectedModel:(TZAssetModel *)model;
/// Minimum selectable photo width, Default is 0
/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中
@property (nonatomic, assign) NSInteger minPhotoWidthSelectable;
@property (nonatomic, assign) NSInteger minPhotoHeightSelectable;
/// Hide the photo what can not be selected, Default is NO
/// 隐藏不可以选中的图片,默认是NO,不推荐将其设置为YES
@property (nonatomic, assign) BOOL hideWhenCanNotSelect;
/// Deprecated, Use statusBarStyle (顶部statusBar 是否为系统默认的黑色,默认为NO)
@property (nonatomic, assign) BOOL isStatusBarDefault __attribute__((deprecated("Use -statusBarStyle.")));
/// statusBar的样式,默认为UIStatusBarStyleLightContent
@property (assign, nonatomic) UIStatusBarStyle statusBarStyle;
#pragma mark -
/// Single selection mode, valid when maxImagesCount = 1
/// 单选模式,maxImagesCount为1时才生效
@property (nonatomic, assign) BOOL showSelectBtn; ///< 在单选模式下,照片列表页中,显示选择按钮,默认为NO
@property (nonatomic, assign) BOOL allowCrop; ///< 允许裁剪,默认为YES,showSelectBtn为NO才生效
@property (nonatomic, assign) CGRect cropRect; ///< 裁剪框的尺寸
@property (nonatomic, assign) CGRect cropRectPortrait; ///< 裁剪框的尺寸(竖屏)
@property (nonatomic, assign) CGRect cropRectLandscape; ///< 裁剪框的尺寸(横屏)
@property (nonatomic, assign) BOOL needCircleCrop; ///< 需要圆形裁剪框
@property (nonatomic, assign) NSInteger circleCropRadius; ///< 圆形裁剪框半径大小
@property (nonatomic, copy) void (^cropViewSettingBlock)(UIView *cropView); ///< 自定义裁剪框的其他属性
@property (nonatomic, copy) void (^navLeftBarButtonSettingBlock)(UIButton *leftButton); ///< 自定义返回按钮样式及其属性
#pragma mark -
- (id)showAlertWithTitle:(NSString *)title;
- (void)hideAlertView:(id)alertView;
- (void)showProgressHUD;
- (void)hideProgressHUD;
@property (nonatomic, assign) BOOL isSelectOriginalPhoto;
@property (assign, nonatomic) BOOL needShowStatusBar;
#pragma mark -
@property (nonatomic, copy) NSString *takePictureImageName __attribute__((deprecated("Use -takePictureImage.")));
@property (nonatomic, copy) NSString *photoSelImageName __attribute__((deprecated("Use -photoSelImage.")));
@property (nonatomic, copy) NSString *photoDefImageName __attribute__((deprecated("Use -photoDefImage.")));
@property (nonatomic, copy) NSString *photoOriginSelImageName __attribute__((deprecated("Use -photoOriginSelImage.")));
@property (nonatomic, copy) NSString *photoOriginDefImageName __attribute__((deprecated("Use -photoOriginDefImage.")));
@property (nonatomic, copy) NSString *photoPreviewOriginDefImageName __attribute__((deprecated("Use -photoPreviewOriginDefImage.")));
@property (nonatomic, copy) NSString *photoNumberIconImageName __attribute__((deprecated("Use -photoNumberIconImage.")));
@property (nonatomic, strong) UIImage *takePictureImage;
@property (nonatomic, strong) UIImage *photoSelImage;
@property (nonatomic, strong) UIImage *photoDefImage;
@property (nonatomic, strong) UIImage *photoOriginSelImage;
@property (nonatomic, strong) UIImage *photoOriginDefImage;
@property (nonatomic, strong) UIImage *photoPreviewOriginDefImage;
@property (nonatomic, strong) UIImage *photoNumberIconImage;
#pragma mark -
/// Appearance / 外观颜色 + 按钮文字
@property (nonatomic, strong) UIColor *oKButtonTitleColorNormal;
@property (nonatomic, strong) UIColor *oKButtonTitleColorDisabled;
@property (nonatomic, strong) UIColor *naviBgColor;
@property (nonatomic, strong) UIColor *naviTitleColor;
@property (nonatomic, strong) UIFont *naviTitleFont;
@property (nonatomic, strong) UIColor *barItemTextColor;
@property (nonatomic, strong) UIFont *barItemTextFont;
@property (nonatomic, copy) NSString *doneBtnTitleStr;
@property (nonatomic, copy) NSString *cancelBtnTitleStr;
@property (nonatomic, copy) NSString *previewBtnTitleStr;
@property (nonatomic, copy) NSString *fullImageBtnTitleStr;
@property (nonatomic, copy) NSString *settingBtnTitleStr;
@property (nonatomic, copy) NSString *processHintStr;
/// Icon theme color, default is green color like wechat, the value is r:31 g:185 b:34. Currently only support image selection icon when showSelectedIndex is YES
/// icon主题色,默认是微信的绿色,值是r:31 g:185 b:34。目前仅支持showSelectedIndex为YES时的图片选中icon
@property (strong, nonatomic) UIColor *iconThemeColor;
#pragma mark -
- (void)cancelButtonClick;
// The picker should dismiss itself; when it dismissed these handle will be called.
// You can also set autoDismiss to NO, then the picker don't dismiss itself.
// If isOriginalPhoto is YES, user picked the original photo.
// You can get original photo with asset, by the method [[TZImageManager manager] getOriginalPhotoWithAsset:completion:].
// The UIImage Object in photos default width is 828px, you can set it by photoWidth property.
// 这个照片选择器会自己dismiss,当选择器dismiss的时候,会执行下面的handle
// 你也可以设置autoDismiss属性为NO,选择器就不会自己dismis了
// 如果isSelectOriginalPhoto为YES,表明用户选择了原图
// 你可以通过一个asset获得原图,通过这个方法:[[TZImageManager manager] getOriginalPhotoWithAsset:completion:]
// photos数组里的UIImage对象,默认是828像素宽,你可以通过设置photoWidth属性的值来改变它
@property (nonatomic, copy) void (^didFinishPickingPhotosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto);
@property (nonatomic, copy) void (^didFinishPickingPhotosWithInfosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto,NSArray<NSDictionary *> *infos);
@property (nonatomic, copy) void (^imagePickerControllerDidCancelHandle)(void);
// If user picking a video, this handle will be called.
// If system version > iOS8,asset is kind of PHAsset class, else is ALAsset class.
// 如果用户选择了一个视频,下面的handle会被执行
// 如果系统版本大于iOS8,asset是PHAsset类的对象,否则是ALAsset类的对象
@property (nonatomic, copy) void (^didFinishPickingVideoHandle)(UIImage *coverImage,id asset);
// If user picking a gif image, this callback will be called.
// 如果用户选择了一个gif图片,下面的handle会被执行
@property (nonatomic, copy) void (^didFinishPickingGifImageHandle)(UIImage *animatedImage,id sourceAssets);
@property (nonatomic, weak) id<TZImagePickerControllerDelegate> pickerDelegate;
@end
@protocol TZImagePickerControllerDelegate <NSObject>
@optional
// The picker should dismiss itself; when it dismissed these handle will be called.
// You can also set autoDismiss to NO, then the picker don't dismiss itself.
// If isOriginalPhoto is YES, user picked the original photo.
// You can get original photo with asset, by the method [[TZImageManager manager] getOriginalPhotoWithAsset:completion:].
// The UIImage Object in photos default width is 828px, you can set it by photoWidth property.
// 这个照片选择器会自己dismiss,当选择器dismiss的时候,会执行下面的handle
// 你也可以设置autoDismiss属性为NO,选择器就不会自己dismis了
// 如果isSelectOriginalPhoto为YES,表明用户选择了原图
// 你可以通过一个asset获得原图,通过这个方法:[[TZImageManager manager] getOriginalPhotoWithAsset:completion:]
// photos数组里的UIImage对象,默认是828像素宽,你可以通过设置photoWidth属性的值来改变它
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto;
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray<NSDictionary *> *)infos;
//- (void)imagePickerControllerDidCancel:(TZImagePickerController *)picker __attribute__((deprecated("Use -tz_imagePickerControllerDidCancel:.")));
- (void)tz_imagePickerControllerDidCancel:(TZImagePickerController *)picker;
// If user picking a video, this callback will be called.
// If system version > iOS8,asset is kind of PHAsset class, else is ALAsset class.
// 如果用户选择了一个视频,下面的handle会被执行
// 如果系统版本大于iOS8,asset是PHAsset类的对象,否则是ALAsset类的对象
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset;
// If user picking a gif image, this callback will be called.
// 如果用户选择了一个gif图片,下面的handle会被执行
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingGifImage:(UIImage *)animatedImage sourceAssets:(id)asset;
// Decide album show or not't
// 决定相册显示与否 albumName:相册名字 result:相册原始数据
- (BOOL)isAlbumCanSelect:(NSString *)albumName result:(id)result;
// Decide asset show or not't
// 决定照片显示与否
- (BOOL)isAssetCanSelect:(id)asset;
@end
@interface TZAlbumPickerController : UIViewController
@property (nonatomic, assign) NSInteger columnNumber;
@property (assign, nonatomic) BOOL isFirstAppear;
- (void)configTableView;
@end
@interface UIImage (MyBundle)
+ (UIImage *)imageNamedFromMyBundle:(NSString *)name;
@end
@interface NSString (TzExtension)
- (BOOL)tz_containsString:(NSString *)string;
- (CGSize)tz_calculateSizeWithAttributes:(NSDictionary *)attributes maxSize:(CGSize)maxSize;
@end
@interface TZCommonTools : NSObject
+ (BOOL)tz_isIPhoneX;
+ (CGFloat)tz_statusBarHeight;
// 获得Info.plist数据字典
+ (NSDictionary *)tz_getInfoDictionary;
@end
@interface TZImagePickerConfig : NSObject
+ (instancetype)sharedInstance;
@property (copy, nonatomic) NSString *preferredLanguage;
@property(nonatomic, assign) BOOL allowPickingImage;
@property (nonatomic, assign) BOOL allowPickingVideo;
@property (strong, nonatomic) NSBundle *languageBundle;
/// 默认是200,如果一个GIF过大,里面图片个数可能超过1000,会导致内存飙升而崩溃
@property (assign, nonatomic) NSInteger gifPreviewMaxImagesCount;
@property (assign, nonatomic) BOOL showSelectedIndex;
@end
//
// TZImagePickerController.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
// version 2.1.1 - 2018.05.07
// 更多信息,请前往项目的github地址:https://github.com/banchichen/TZImagePickerController
#import "TZImagePickerController.h"
#import "TZPhotoPickerController.h"
#import "TZPhotoPreviewController.h"
#import "TZAssetModel.h"
#import "TZAssetCell.h"
#import "UIView+Layout.h"
#import "TZImageManager.h"
#import <sys/utsname.h>
@interface TZImagePickerController () {
NSTimer *_timer;
UILabel *_tipLabel;
UIButton *_settingBtn;
BOOL _pushPhotoPickerVc;
BOOL _didPushPhotoPickerVc;
UIButton *_progressHUD;
UIView *_HUDContainer;
UIActivityIndicatorView *_HUDIndicatorView;
UILabel *_HUDLabel;
UIStatusBarStyle _originStatusBarStyle;
}
/// Default is 4, Use in photos collectionView in TZPhotoPickerController
/// 默认4列, TZPhotoPickerController中的照片collectionView
@property (nonatomic, assign) NSInteger columnNumber;
@end
@implementation TZImagePickerController
- (instancetype)init {
self = [super init];
if (self) {
self = [self initWithMaxImagesCount:9 delegate:nil];
}
return self;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)viewDidLoad {
[super viewDidLoad];
// self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden;
self.view.backgroundColor = [UIColor whiteColor];
// self.navigationBar.barStyle = UIBarStyleBlack;
// self.navigationBar.translucent = YES;
[TZImageManager manager].shouldFixOrientation = NO;
// Default appearance, you can reset these after this method
// 默认的外观,你可以在这个方法后重置
self.oKButtonTitleColorNormal = [UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0];
self.oKButtonTitleColorDisabled = [UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:0.5];
// if (iOS7Later) {
// self.navigationBar.barTintColor = [UIColor colorWithRed:(34/255.0) green:(34/255.0) blue:(34/255.0) alpha:1.0];
// self.navigationBar.tintColor = [UIColor whiteColor];
// self.automaticallyAdjustsScrollViewInsets = NO;
// if (self.needShowStatusBar) [UIApplication sharedApplication].statusBarHidden = NO;
// }
}
- (void)setNaviBgColor:(UIColor *)naviBgColor {
_naviBgColor = naviBgColor;
if (iOS7Later) {
self.navigationBar.barTintColor = naviBgColor;
}
}
- (void)setNaviTitleColor:(UIColor *)naviTitleColor {
_naviTitleColor = naviTitleColor;
[self configNaviTitleAppearance];
}
- (void)setNaviTitleFont:(UIFont *)naviTitleFont {
_naviTitleFont = naviTitleFont;
[self configNaviTitleAppearance];
}
- (void)configNaviTitleAppearance {
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
if (self.naviTitleColor) {
textAttrs[NSForegroundColorAttributeName] = self.naviTitleColor;
}
if (self.naviTitleFont) {
textAttrs[NSFontAttributeName] = self.naviTitleFont;
}
self.navigationBar.titleTextAttributes = textAttrs;
}
- (void)setBarItemTextFont:(UIFont *)barItemTextFont {
_barItemTextFont = barItemTextFont;
[self configBarButtonItemAppearance];
}
- (void)setBarItemTextColor:(UIColor *)barItemTextColor {
_barItemTextColor = barItemTextColor;
[self configBarButtonItemAppearance];
}
- (void)setIsStatusBarDefault:(BOOL)isStatusBarDefault {
_isStatusBarDefault = isStatusBarDefault;
if (isStatusBarDefault) {
self.statusBarStyle = iOS7Later ? UIStatusBarStyleDefault : UIStatusBarStyleBlackOpaque;
} else {
self.statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque;
}
}
- (void)configBarButtonItemAppearance {
UIBarButtonItem *barItem;
if (iOS9Later) {
barItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]];
} else {
barItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil];
}
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
textAttrs[NSForegroundColorAttributeName] = self.barItemTextColor;
textAttrs[NSFontAttributeName] = self.barItemTextFont;
[barItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
}
#pragma mark - UINavigationControllerDelegate && 解决UIImagePickerController导航透明问题
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
if ([navigationController isKindOfClass:[UIImagePickerController class]]) {
self.navigationBarHidden = YES;
if (@available(iOS 13.0, *)) {
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
// 导航栏背景色
appearance.backgroundColor = DSBlack;
// 隐藏分割线
appearance.shadowColor = DSClearColor;
// 生效
self.navigationBar.scrollEdgeAppearance = appearance;
// 滚动视图滚动后导航栏背景色不会被影响
self.navigationBar.standardAppearance = appearance;
}
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
[UIApplication sharedApplication].statusBarStyle = self.statusBarStyle;
if ([self.takePictureImageName isEqualToString:@"takePicture80"]) {
if (self.allowTakePicture && !self.allowTakeVideo) {
self.takePictureImageName = @"takePicture";
}
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
[self hideProgressHUD];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate {
return [self initWithMaxImagesCount:maxImagesCount columnNumber:4 delegate:delegate pushPhotoPickerVc:YES];
}
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate {
return [self initWithMaxImagesCount:maxImagesCount columnNumber:columnNumber delegate:delegate pushPhotoPickerVc:YES];
}
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc {
_pushPhotoPickerVc = pushPhotoPickerVc;
TZAlbumPickerController *albumPickerVc = [[TZAlbumPickerController alloc] init];
albumPickerVc.isFirstAppear = YES;
albumPickerVc.columnNumber = columnNumber;
self = [super initWithRootViewController:albumPickerVc];
if (self) {
self.maxImagesCount = maxImagesCount > 0 ? maxImagesCount : 9; // Default is 9 / 默认最大可选9张图片
self.pickerDelegate = delegate;
self.selectedModels = [NSMutableArray array];
// Allow user picking original photo and video, you also can set No after this method
// 默认准许用户选择原图和视频, 你也可以在这个方法后置为NO
self.allowPickingOriginalPhoto = YES;
self.allowPickingVideo = YES;
self.allowPickingImage = YES;
self.allowTakePicture = YES;
self.allowTakeVideo = YES;
self.videoMaximumDuration = 10 * 60;
self.sortAscendingByModificationDate = YES;
self.autoDismiss = YES;
self.columnNumber = columnNumber;
[self configDefaultSetting];
if (![[TZImageManager manager] authorizationStatusAuthorized]) {
_tipLabel = [[UILabel alloc] init];
_tipLabel.frame = CGRectMake(8, 120, self.view.tz_width - 16, 60);
_tipLabel.textAlignment = NSTextAlignmentCenter;
_tipLabel.numberOfLines = 0;
_tipLabel.font = [UIFont systemFontOfSize:16];
_tipLabel.textColor = [UIColor blackColor];
NSDictionary *infoDict = [TZCommonTools tz_getInfoDictionary];
NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"];
if (!appName) appName = [infoDict valueForKey:@"CFBundleName"];
NSString *tipText = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Allow %@ to access your album in \"Settings -> Privacy -> Photos\""],appName];
_tipLabel.text = tipText;
[self.view addSubview:_tipLabel];
if (iOS8Later) {
_settingBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_settingBtn setTitle:self.settingBtnTitleStr forState:UIControlStateNormal];
_settingBtn.frame = CGRectMake(0, 180, self.view.tz_width, 44);
_settingBtn.titleLabel.font = [UIFont systemFontOfSize:18];
[_settingBtn addTarget:self action:@selector(settingBtnClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_settingBtn];
}
if ([TZImageManager authorizationStatus] == 0) {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(observeAuthrizationStatusChange) userInfo:nil repeats:NO];
}
} else {
[self pushPhotoPickerVc];
}
}
return self;
}
/// This init method just for previewing photos / 用这个初始化方法以预览图片
- (instancetype)initWithSelectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index{
TZPhotoPreviewController *previewVc = [[TZPhotoPreviewController alloc] init];
self = [super initWithRootViewController:previewVc];
if (self) {
self.selectedAssets = [NSMutableArray arrayWithArray:selectedAssets];
self.allowPickingOriginalPhoto = self.allowPickingOriginalPhoto;
[self configDefaultSetting];
previewVc.photos = [NSMutableArray arrayWithArray:selectedPhotos];
previewVc.currentIndex = index;
__weak typeof(self) weakSelf = self;
[previewVc setDoneButtonClickBlockWithPreviewType:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf dismissViewControllerAnimated:YES completion:^{
if (!strongSelf) return;
if (strongSelf.didFinishPickingPhotosHandle) {
strongSelf.didFinishPickingPhotosHandle(photos,assets,isSelectOriginalPhoto);
}
}];
}];
}
return self;
}
/// This init method for crop photo / 用这个初始化方法以裁剪图片
- (instancetype)initCropTypeWithAsset:(id)asset photo:(UIImage *)photo completion:(void (^)(UIImage *cropImage,id asset))completion {
TZPhotoPreviewController *previewVc = [[TZPhotoPreviewController alloc] init];
self = [super initWithRootViewController:previewVc];
if (self) {
self.maxImagesCount = 1;
self.allowCrop = YES;
self.selectedAssets = [NSMutableArray arrayWithArray:@[asset]];
[self configDefaultSetting];
previewVc.photos = [NSMutableArray arrayWithArray:@[photo]];
previewVc.isCropImage = YES;
previewVc.currentIndex = 0;
__weak typeof(self) weakSelf = self;
[previewVc setDoneButtonClickBlockCropMode:^(UIImage *cropImage, id asset) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf dismissViewControllerAnimated:YES completion:^{
if (completion) {
completion(cropImage,asset);
}
}];
}];
}
return self;
}
- (void)configDefaultSetting {
self.timeout = 15;
self.photoWidth = 828.0;
self.photoPreviewMaxWidth = 600;
// self.naviTitleColor = [UIColor whiteColor];
// self.naviTitleFont = [UIFont systemFontOfSize:17];
// self.barItemTextFont = [UIFont systemFontOfSize:15];
// self.barItemTextColor = [UIColor whiteColor];
self.allowPreview = YES;
// self.statusBarStyle = UIStatusBarStyleLightContent;
self.cannotSelectLayerColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];
self.iconThemeColor = [UIColor colorWithRed:31 / 255.0 green:185 / 255.0 blue:34 / 255.0 alpha:1.0];
[self configDefaultBtnTitle];
CGFloat cropViewWH = MIN(self.view.tz_width, self.view.tz_height) / 3 * 2;
self.cropRect = CGRectMake((self.view.tz_width - cropViewWH) / 2, (self.view.tz_height - cropViewWH) / 2, cropViewWH, cropViewWH);
}
- (void)configDefaultImageName {
self.takePictureImageName = @"takePicture80";
self.photoSelImageName = @"photo_sel_photoPickerVc";
self.photoDefImageName = @"photo_def_photoPickerVc";
self.photoNumberIconImage = [self createImageWithColor:nil size:CGSizeMake(48, 48) radius:24]; // @"photo_number_icon";
self.photoPreviewOriginDefImageName = @"preview_original_def";
self.photoOriginDefImageName = @"photo_original_def";
self.photoOriginSelImageName = @"photo_original_sel";
}
- (void)setTakePictureImageName:(NSString *)takePictureImageName {
_takePictureImageName = takePictureImageName;
_takePictureImage = [UIImage imageNamedFromMyBundle:takePictureImageName];
}
- (void)setPhotoSelImageName:(NSString *)photoSelImageName {
_photoSelImageName = photoSelImageName;
_photoSelImage = [UIImage imageNamedFromMyBundle:photoSelImageName];
}
- (void)setPhotoDefImageName:(NSString *)photoDefImageName {
_photoDefImageName = photoDefImageName;
_photoDefImage = [UIImage imageNamedFromMyBundle:photoDefImageName];
}
- (void)setPhotoNumberIconImageName:(NSString *)photoNumberIconImageName {
_photoNumberIconImageName = photoNumberIconImageName;
_photoNumberIconImage = [UIImage imageNamedFromMyBundle:photoNumberIconImageName];
}
- (void)setPhotoPreviewOriginDefImageName:(NSString *)photoPreviewOriginDefImageName {
_photoPreviewOriginDefImageName = photoPreviewOriginDefImageName;
_photoPreviewOriginDefImage = [UIImage imageNamedFromMyBundle:photoPreviewOriginDefImageName];
}
- (void)setPhotoOriginDefImageName:(NSString *)photoOriginDefImageName {
_photoOriginDefImageName = photoOriginDefImageName;
_photoOriginDefImage = [UIImage imageNamedFromMyBundle:photoOriginDefImageName];
}
- (void)setPhotoOriginSelImageName:(NSString *)photoOriginSelImageName {
_photoOriginSelImageName = photoOriginSelImageName;
_photoOriginSelImage = [UIImage imageNamedFromMyBundle:photoOriginSelImageName];
}
- (void)setIconThemeColor:(UIColor *)iconThemeColor {
_iconThemeColor = iconThemeColor;
[self configDefaultImageName];
}
- (void)configDefaultBtnTitle {
self.doneBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Done"];
self.cancelBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Cancel"];
self.previewBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Preview"];
self.fullImageBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Full image"];
self.settingBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Setting"];
self.processHintStr = [NSBundle tz_localizedStringForKey:@"Processing..."];
}
- (void)setShowSelectedIndex:(BOOL)showSelectedIndex {
_showSelectedIndex = showSelectedIndex;
if (showSelectedIndex) {
self.photoSelImage = [self createImageWithColor:nil size:CGSizeMake(48, 48) radius:24];
}
[TZImagePickerConfig sharedInstance].showSelectedIndex = showSelectedIndex;
}
- (void)observeAuthrizationStatusChange {
[_timer invalidate];
_timer = nil;
if ([TZImageManager authorizationStatus] == 0) {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(observeAuthrizationStatusChange) userInfo:nil repeats:NO];
}
if ([[TZImageManager manager] authorizationStatusAuthorized]) {
[_tipLabel removeFromSuperview];
[_settingBtn removeFromSuperview];
[self pushPhotoPickerVc];
TZAlbumPickerController *albumPickerVc = (TZAlbumPickerController *)self.visibleViewController;
if ([albumPickerVc isKindOfClass:[TZAlbumPickerController class]]) {
[albumPickerVc configTableView];
}
}
}
- (void)pushPhotoPickerVc {
_didPushPhotoPickerVc = NO;
// 1.6.8 判断是否需要push到照片选择页,如果_pushPhotoPickerVc为NO,则不push
if (!_didPushPhotoPickerVc && _pushPhotoPickerVc) {
TZPhotoPickerController *photoPickerVc = [[TZPhotoPickerController alloc] init];
photoPickerVc.isFirstAppear = YES;
photoPickerVc.columnNumber = self.columnNumber;
[[TZImageManager manager] getCameraRollAlbum:self.allowPickingVideo allowPickingImage:self.allowPickingImage needFetchAssets:NO completion:^(TZAlbumModel *model) {
photoPickerVc.model = model;
[self pushViewController:photoPickerVc animated:YES];
self->_didPushPhotoPickerVc = YES;
}];
}
}
- (id)showAlertWithTitle:(NSString *)title {
if (iOS8Later) {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:[NSBundle tz_localizedStringForKey:@"OK"] style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
return alertController;
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:nil delegate:nil cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"OK"] otherButtonTitles:nil, nil];
[alertView show];
return alertView;
}
}
- (void)hideAlertView:(id)alertView {
if ([alertView isKindOfClass:[UIAlertController class]]) {
UIAlertController *alertC = alertView;
[alertC dismissViewControllerAnimated:YES completion:nil];
} else if ([alertView isKindOfClass:[UIAlertView class]]) {
UIAlertView *alertV = alertView;
[alertV dismissWithClickedButtonIndex:0 animated:YES];
}
alertView = nil;
}
- (void)showProgressHUD {
if (!_progressHUD) {
_progressHUD = [UIButton buttonWithType:UIButtonTypeCustom];
[_progressHUD setBackgroundColor:[UIColor clearColor]];
_HUDContainer = [[UIView alloc] init];
_HUDContainer.layer.cornerRadius = 8;
_HUDContainer.clipsToBounds = YES;
_HUDContainer.backgroundColor = [UIColor darkGrayColor];
_HUDContainer.alpha = 0.7;
_HUDIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
_HUDLabel = [[UILabel alloc] init];
_HUDLabel.textAlignment = NSTextAlignmentCenter;
_HUDLabel.text = self.processHintStr;
_HUDLabel.font = [UIFont systemFontOfSize:15];
_HUDLabel.textColor = [UIColor whiteColor];
[_HUDContainer addSubview:_HUDLabel];
[_HUDContainer addSubview:_HUDIndicatorView];
[_progressHUD addSubview:_HUDContainer];
}
[_HUDIndicatorView startAnimating];
[[UIApplication sharedApplication].keyWindow addSubview:_progressHUD];
// if over time, dismiss HUD automatic
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf hideProgressHUD];
});
}
- (void)hideProgressHUD {
if (_progressHUD) {
[_HUDIndicatorView stopAnimating];
[_progressHUD removeFromSuperview];
}
}
- (void)setMaxImagesCount:(NSInteger)maxImagesCount {
_maxImagesCount = maxImagesCount;
if (maxImagesCount > 1) {
_showSelectBtn = YES;
_allowCrop = NO;
}
}
- (void)setShowSelectBtn:(BOOL)showSelectBtn {
_showSelectBtn = showSelectBtn;
// 多选模式下,不允许让showSelectBtn为NO
if (!showSelectBtn && _maxImagesCount > 1) {
_showSelectBtn = YES;
}
}
- (void)setAllowCrop:(BOOL)allowCrop {
_allowCrop = _maxImagesCount > 1 ? NO : allowCrop;
if (allowCrop) { // 允许裁剪的时候,不能选原图和GIF
self.allowPickingOriginalPhoto = NO;
self.allowPickingGif = NO;
}
}
- (void)setCircleCropRadius:(NSInteger)circleCropRadius {
_circleCropRadius = circleCropRadius;
self.cropRect = CGRectMake(self.view.tz_width / 2 - circleCropRadius, self.view.tz_height / 2 - _circleCropRadius, _circleCropRadius * 2, _circleCropRadius * 2);
}
- (void)setCropRect:(CGRect)cropRect {
_cropRect = cropRect;
_cropRectPortrait = cropRect;
CGFloat widthHeight = cropRect.size.width;
_cropRectLandscape = CGRectMake((self.view.tz_height - widthHeight) / 2, cropRect.origin.x, widthHeight, widthHeight);
}
- (void)setTimeout:(NSInteger)timeout {
_timeout = timeout;
if (timeout < 5) {
_timeout = 5;
} else if (_timeout > 60) {
_timeout = 60;
}
}
- (void)setPickerDelegate:(id<TZImagePickerControllerDelegate>)pickerDelegate {
_pickerDelegate = pickerDelegate;
[TZImageManager manager].pickerDelegate = pickerDelegate;
}
- (void)setColumnNumber:(NSInteger)columnNumber {
_columnNumber = columnNumber;
if (columnNumber <= 2) {
_columnNumber = 2;
} else if (columnNumber >= 6) {
_columnNumber = 6;
}
TZAlbumPickerController *albumPickerVc = [self.childViewControllers firstObject];
albumPickerVc.columnNumber = _columnNumber;
[TZImageManager manager].columnNumber = _columnNumber;
}
- (void)setMinPhotoWidthSelectable:(NSInteger)minPhotoWidthSelectable {
_minPhotoWidthSelectable = minPhotoWidthSelectable;
[TZImageManager manager].minPhotoWidthSelectable = minPhotoWidthSelectable;
}
- (void)setMinPhotoHeightSelectable:(NSInteger)minPhotoHeightSelectable {
_minPhotoHeightSelectable = minPhotoHeightSelectable;
[TZImageManager manager].minPhotoHeightSelectable = minPhotoHeightSelectable;
}
- (void)setHideWhenCanNotSelect:(BOOL)hideWhenCanNotSelect {
_hideWhenCanNotSelect = hideWhenCanNotSelect;
[TZImageManager manager].hideWhenCanNotSelect = hideWhenCanNotSelect;
}
- (void)setPhotoPreviewMaxWidth:(CGFloat)photoPreviewMaxWidth {
_photoPreviewMaxWidth = photoPreviewMaxWidth;
if (photoPreviewMaxWidth > 800) {
_photoPreviewMaxWidth = 800;
} else if (photoPreviewMaxWidth < 500) {
_photoPreviewMaxWidth = 500;
}
[TZImageManager manager].photoPreviewMaxWidth = _photoPreviewMaxWidth;
}
- (void)setPhotoWidth:(CGFloat)photoWidth {
_photoWidth = photoWidth;
[TZImageManager manager].photoWidth = photoWidth;
}
- (void)setSelectedAssets:(NSMutableArray *)selectedAssets {
_selectedAssets = selectedAssets;
_selectedModels = [NSMutableArray array];
_selectedAssetIds = [NSMutableArray array];
for (id asset in selectedAssets) {
TZAssetModel *model = [TZAssetModel modelWithAsset:asset type:[[TZImageManager manager] getAssetType:asset]];
model.isSelected = YES;
[self addSelectedModel:model];
}
}
- (void)setAllowPickingImage:(BOOL)allowPickingImage {
_allowPickingImage = allowPickingImage;
[TZImagePickerConfig sharedInstance].allowPickingImage = allowPickingImage;
if (!allowPickingImage) {
_allowTakePicture = NO;
}
}
- (void)setAllowPickingVideo:(BOOL)allowPickingVideo {
_allowPickingVideo = allowPickingVideo;
[TZImagePickerConfig sharedInstance].allowPickingVideo = allowPickingVideo;
if (!allowPickingVideo) {
_allowTakeVideo = NO;
}
}
- (void)setPreferredLanguage:(NSString *)preferredLanguage {
_preferredLanguage = preferredLanguage;
[TZImagePickerConfig sharedInstance].preferredLanguage = preferredLanguage;
[self configDefaultBtnTitle];
}
- (void)setLanguageBundle:(NSBundle *)languageBundle {
_languageBundle = languageBundle;
[TZImagePickerConfig sharedInstance].languageBundle = languageBundle;
[self configDefaultBtnTitle];
}
- (void)setSortAscendingByModificationDate:(BOOL)sortAscendingByModificationDate {
_sortAscendingByModificationDate = sortAscendingByModificationDate;
[TZImageManager manager].sortAscendingByModificationDate = sortAscendingByModificationDate;
}
- (void)settingBtnClick {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (iOS7Later) {
viewController.automaticallyAdjustsScrollViewInsets = NO;
}
[super pushViewController:viewController animated:animated];
}
- (void)dealloc {
}
- (void)addSelectedModel:(TZAssetModel *)model {
[_selectedModels addObject:model];
NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset];
[_selectedAssetIds addObject:assetId];
}
- (void)removeSelectedModel:(TZAssetModel *)model {
[_selectedModels removeObject:model];
NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset];
[_selectedAssetIds removeObject:assetId];
}
- (UIImage *)createImageWithColor:(UIColor *)color size:(CGSize)size radius:(CGFloat)radius {
if (!color) {
color = self.iconThemeColor;
}
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
CGContextAddPath(context, path.CGPath);
CGContextFillPath(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
#pragma mark - UIContentContainer
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[self willInterfaceOrientionChange];
if (size.width > size.height) {
_cropRect = _cropRectLandscape;
} else {
_cropRect = _cropRectPortrait;
}
}
//- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// [self willInterfaceOrientionChange];
// if (toInterfaceOrientation >= 3) {
// _cropRect = _cropRectLandscape;
// } else {
// _cropRect = _cropRectPortrait;
// }
//}
- (void)willInterfaceOrientionChange {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (![UIApplication sharedApplication].statusBarHidden) {
if (iOS7Later && self.needShowStatusBar) [UIApplication sharedApplication].statusBarHidden = NO;
}
});
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_HUDContainer.frame = CGRectMake((self.view.tz_width - 120) / 2, (self.view.tz_height - 90) / 2, 120, 90);
_HUDIndicatorView.frame = CGRectMake(45, 15, 30, 30);
_HUDLabel.frame = CGRectMake(0,40, 120, 50);
}
#pragma mark - Public
- (void)cancelButtonClick {
if (self.autoDismiss) {
[self dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
} else {
[self callDelegateMethod];
}
}
- (void)callDelegateMethod {
if ([self.pickerDelegate respondsToSelector:@selector(tz_imagePickerControllerDidCancel:)]) {
[self.pickerDelegate tz_imagePickerControllerDidCancel:self];
}
if (self.imagePickerControllerDidCancelHandle) {
self.imagePickerControllerDidCancelHandle();
}
}
@end
@interface TZAlbumPickerController ()<UITableViewDataSource,UITableViewDelegate> {
UITableView *_tableView;
}
@property (nonatomic, strong) NSMutableArray *albumArr;
@end
@implementation TZAlbumPickerController
- (void)viewDidLoad {
[super viewDidLoad];
self.isFirstAppear = YES;
self.view.backgroundColor = [UIColor whiteColor];
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:imagePickerVc.cancelBtnTitleStr style:UIBarButtonItemStylePlain target:imagePickerVc action:@selector(cancelButtonClick)];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[imagePickerVc hideProgressHUD];
if (imagePickerVc.allowPickingImage) {
self.navigationItem.title = [NSBundle tz_localizedStringForKey:@"Photos"];
} else if (imagePickerVc.allowPickingVideo) {
self.navigationItem.title = [NSBundle tz_localizedStringForKey:@"Videos"];
}
if (self.isFirstAppear && !imagePickerVc.navLeftBarButtonSettingBlock) {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Back"] style:UIBarButtonItemStylePlain target:nil action:nil];
}
[self configTableView];
}
- (void)configTableView {
if (![[TZImageManager manager] authorizationStatusAuthorized]) {
return;
}
if (self.isFirstAppear) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[imagePickerVc showProgressHUD];
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[[TZImageManager manager] getAllAlbums:imagePickerVc.allowPickingVideo allowPickingImage:imagePickerVc.allowPickingImage needFetchAssets:!self.isFirstAppear completion:^(NSArray<TZAlbumModel *> *models) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_albumArr = [NSMutableArray arrayWithArray:models];
for (TZAlbumModel *albumModel in self->_albumArr) {
albumModel.selectedModels = imagePickerVc.selectedModels;
}
[imagePickerVc hideProgressHUD];
if (self.isFirstAppear) {
self.isFirstAppear = NO;
[self configTableView];
}
if (!self->_tableView) {
self->_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self->_tableView.rowHeight = 70;
self->_tableView.tableFooterView = [[UIView alloc] init];
self->_tableView.dataSource = self;
self->_tableView.delegate = self;
[self->_tableView registerClass:[TZAlbumCell class] forCellReuseIdentifier:@"TZAlbumCell"];
[self.view addSubview:self->_tableView];
} else {
[self->_tableView reloadData];
}
});
}];
});
}
- (void)dealloc {
// NSLog(@"%@ dealloc",NSStringFromClass(self.class));
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat top = 0;
CGFloat tableViewHeight = 0;
CGFloat naviBarHeight = self.navigationController.navigationBar.tz_height;
BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden;
if (self.navigationController.navigationBar.isTranslucent) {
top = naviBarHeight;
if (iOS7Later && !isStatusBarHidden) top += [TZCommonTools tz_statusBarHeight];
tableViewHeight = self.view.tz_height - top;
} else {
tableViewHeight = self.view.tz_height;
}
_tableView.frame = CGRectMake(0, top, self.view.tz_width, tableViewHeight);
}
#pragma mark - UITableViewDataSource && Delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _albumArr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TZAlbumCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TZAlbumCell"];
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
cell.selectedCountButton.backgroundColor = imagePickerVc.iconThemeColor;
cell.model = _albumArr[indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
TZPhotoPickerController *photoPickerVc = [[TZPhotoPickerController alloc] init];
photoPickerVc.columnNumber = self.columnNumber;
TZAlbumModel *model = _albumArr[indexPath.row];
photoPickerVc.model = model;
[self.navigationController pushViewController:photoPickerVc animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
#pragma clang diagnostic pop
@end
@implementation UIImage (MyBundle)
+ (UIImage *)imageNamedFromMyBundle:(NSString *)name {
NSBundle *imageBundle = [NSBundle tz_imagePickerBundle];
name = [name stringByAppendingString:@"@2x"];
NSString *imagePath = [imageBundle pathForResource:name ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
if (!image) {
// 兼容业务方自己设置图片的方式
name = [name stringByReplacingOccurrencesOfString:@"@2x" withString:@""];
image = [UIImage imageNamed:name];
}
return image;
}
@end
@implementation NSString (TzExtension)
- (BOOL)tz_containsString:(NSString *)string {
if (iOS8Later) {
return [self containsString:string];
} else {
NSRange range = [self rangeOfString:string];
return range.location != NSNotFound;
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (CGSize)tz_calculateSizeWithAttributes:(NSDictionary *)attributes maxSize:(CGSize)maxSize {
CGSize size;
if (iOS7Later) {
size = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesFontLeading attributes:attributes context:nil].size;
} else {
size = [self sizeWithFont:attributes[NSFontAttributeName] constrainedToSize:maxSize];
}
return size;
}
#pragma clang diagnostic pop
@end
@implementation TZCommonTools
+ (BOOL)tz_isIPhoneX {
struct utsname systemInfo;
uname(&systemInfo);
NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding];
if ([platform isEqualToString:@"i386"] || [platform isEqualToString:@"x86_64"]) {
// 模拟器下采用屏幕的高度来判断
return (CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(375, 812)) ||
CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(812, 375)));
}
// iPhone10,6是美版iPhoneX 感谢hegelsu指出:https://github.com/banchichen/TZImagePickerController/issues/635
BOOL isIPhoneX = [platform isEqualToString:@"iPhone10,3"] || [platform isEqualToString:@"iPhone10,6"];
return isIPhoneX;
}
+ (CGFloat)tz_statusBarHeight {
return [self tz_isIPhoneX] ? 44 : 20;
}
// 获得Info.plist数据字典
+ (NSDictionary *)tz_getInfoDictionary {
NSDictionary *infoDict = [NSBundle mainBundle].localizedInfoDictionary;
if (!infoDict || !infoDict.count) {
infoDict = [NSBundle mainBundle].infoDictionary;
}
if (!infoDict || !infoDict.count) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
infoDict = [NSDictionary dictionaryWithContentsOfFile:path];
}
return infoDict ? infoDict : @{};
}
@end
@implementation TZImagePickerConfig
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static TZImagePickerConfig *config = nil;
dispatch_once(&onceToken, ^{
if (config == nil) {
config = [[TZImagePickerConfig alloc] init];
config.preferredLanguage = nil;
config.gifPreviewMaxImagesCount = 200;
}
});
return config;
}
- (void)setPreferredLanguage:(NSString *)preferredLanguage {
_preferredLanguage = preferredLanguage;
if (!preferredLanguage || !preferredLanguage.length) {
preferredLanguage = [NSLocale preferredLanguages].firstObject;
}
if ([preferredLanguage rangeOfString:@"zh-Hans"].location != NSNotFound) {
preferredLanguage = @"zh-Hans";
} else if ([preferredLanguage rangeOfString:@"zh-Hant"].location != NSNotFound) {
preferredLanguage = @"zh-Hant";
} else if ([preferredLanguage rangeOfString:@"vi"].location != NSNotFound) {
preferredLanguage = @"vi";
} else {
preferredLanguage = @"en";
}
_languageBundle = [NSBundle bundleWithPath:[[NSBundle tz_imagePickerBundle] pathForResource:preferredLanguage ofType:@"lproj"]];
}
@end
//
// TZLocationManager.h
// TZImagePickerController
//
// Created by 谭真 on 2017/06/03.
// Copyright © 2017年 谭真. All rights reserved.
// 定位管理类
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
@interface TZLocationManager : NSObject
+ (instancetype)manager;
/// 开始定位
- (void)startLocation;
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock;
- (void)startLocationWithGeocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock;
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock geocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock;
@end
//
// TZLocationManager.m
// TZImagePickerController
//
// Created by 谭真 on 2017/06/03.
// Copyright © 2017年 谭真. All rights reserved.
// 定位管理类
#import "TZLocationManager.h"
#import "TZImagePickerController.h"
@interface TZLocationManager ()<CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager;
/// 定位成功的回调block
@property (nonatomic, copy) void (^successBlock)(NSArray<CLLocation *> *);
/// 编码成功的回调block
@property (nonatomic, copy) void (^geocodeBlock)(NSArray *geocodeArray);
/// 定位失败的回调block
@property (nonatomic, copy) void (^failureBlock)(NSError *error);
@end
@implementation TZLocationManager
+ (instancetype)manager {
static TZLocationManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
manager.locationManager = [[CLLocationManager alloc] init];
manager.locationManager.delegate = manager;
if (iOS8Later) {
[manager.locationManager requestWhenInUseAuthorization];
}
});
return manager;
}
- (void)startLocation {
[self startLocationWithSuccessBlock:nil failureBlock:nil geocoderBlock:nil];
}
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock {
[self startLocationWithSuccessBlock:successBlock failureBlock:failureBlock geocoderBlock:nil];
}
- (void)startLocationWithGeocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock {
[self startLocationWithSuccessBlock:nil failureBlock:nil geocoderBlock:geocoderBlock];
}
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock geocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock {
[self.locationManager startUpdatingLocation];
_successBlock = successBlock;
_geocodeBlock = geocoderBlock;
_failureBlock = failureBlock;
}
#pragma mark - CLLocationManagerDelegate
/// 地理位置发生改变时触发
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
[manager stopUpdatingLocation];
if (_successBlock) {
_successBlock(locations);
}
if (_geocodeBlock && locations.count) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:[locations firstObject] completionHandler:^(NSArray *array, NSError *error) {
self->_geocodeBlock(array);
}];
}
}
/// 定位失败回调方法
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(@"定位失败, 错误: %@",error);
switch([error code]) {
case kCLErrorDenied: { // 用户禁止了定位权限
} break;
default: break;
}
if (_failureBlock) {
_failureBlock(error);
}
}
@end
//
// TZPhotoPickerController.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAlbumModel;
@interface TZPhotoPickerController : UIViewController
@property (nonatomic, assign) BOOL isFirstAppear;
@property (nonatomic, assign) NSInteger columnNumber;
@property (nonatomic, strong) TZAlbumModel *model;
@end
@interface TZCollectionView : UICollectionView
@end
//
// TZPhotoPickerController.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "TZPhotoPickerController.h"
#import "TZImagePickerController.h"
#import "TZPhotoPreviewController.h"
#import "TZAssetCell.h"
#import "TZAssetModel.h"
#import "UIView+Layout.h"
#import "TZImageManager.h"
#import "TZVideoPlayerController.h"
#import "TZGifPhotoPreviewController.h"
#import "TZLocationManager.h"
#import <MobileCoreServices/MobileCoreServices.h>
@interface TZPhotoPickerController ()<UICollectionViewDataSource,UICollectionViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIAlertViewDelegate> {
NSMutableArray *_models;
UIView *_bottomToolBar;
UIButton *_previewButton;
UIButton *_doneButton;
UIImageView *_numberImageView;
UILabel *_numberLabel;
UIButton *_originalPhotoButton;
UILabel *_originalPhotoLabel;
UIView *_divideLine;
BOOL _shouldScrollToBottom;
BOOL _showTakePhotoBtn;
CGFloat _offsetItemCount;
}
@property CGRect previousPreheatRect;
@property (nonatomic, assign) BOOL isSelectOriginalPhoto;
@property (nonatomic, strong) TZCollectionView *collectionView;
@property (strong, nonatomic) UICollectionViewFlowLayout *layout;
@property (nonatomic, strong) UIImagePickerController *imagePickerVc;
@property (strong, nonatomic) CLLocation *location;
@property (assign, nonatomic) BOOL useCachedImage;
@end
static CGSize AssetGridThumbnailSize;
static CGFloat itemMargin = 5;
@implementation TZPhotoPickerController
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (UIImagePickerController *)imagePickerVc {
if (_imagePickerVc == nil) {
_imagePickerVc = [[UIImagePickerController alloc] init];
_imagePickerVc.delegate = self;
// set appearance / 改变相册选择页的导航栏外观
if (iOS7Later) {
_imagePickerVc.navigationBar.barTintColor = self.navigationController.navigationBar.barTintColor;
}
_imagePickerVc.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;
UIBarButtonItem *tzBarItem, *BarItem;
if (iOS9Later) {
tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]];
BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]];
} else {
tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil];
BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil];
}
NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal];
[BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal];
}
return _imagePickerVc;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.isFirstAppear = YES;
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
_isSelectOriginalPhoto = tzImagePickerVc.isSelectOriginalPhoto;
_shouldScrollToBottom = YES;
self.view.backgroundColor = [UIColor whiteColor];
self.navigationItem.title = _model.name;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:tzImagePickerVc.cancelBtnTitleStr style:UIBarButtonItemStylePlain target:tzImagePickerVc action:@selector(cancelButtonClick)];
if (tzImagePickerVc.navLeftBarButtonSettingBlock) {
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
leftButton.frame = CGRectMake(0, 0, 44, 44);
[leftButton addTarget:self action:@selector(navLeftBarButtonClick) forControlEvents:UIControlEventTouchUpInside];
tzImagePickerVc.navLeftBarButtonSettingBlock(leftButton);
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton];
}
_showTakePhotoBtn = _model.isCameraRoll && ((tzImagePickerVc.allowTakePicture && tzImagePickerVc.allowPickingImage) || (tzImagePickerVc.allowTakeVideo && tzImagePickerVc.allowPickingVideo));
// [self resetCachedAssets];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
}
- (void)fetchAssetModels {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (_isFirstAppear && !_model.models.count) {
[tzImagePickerVc showProgressHUD];
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if (!tzImagePickerVc.sortAscendingByModificationDate && self->_isFirstAppear && iOS8Later && self->_model.isCameraRoll) {
[[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage needFetchAssets:YES completion:^(TZAlbumModel *model) {
self->_model = model;
self->_models = [NSMutableArray arrayWithArray:self->_model.models];
[self initSubviews];
}];
} else {
if (self->_showTakePhotoBtn || !iOS8Later || self->_isFirstAppear) {
[[TZImageManager manager] getAssetsFromFetchResult:self->_model.result completion:^(NSArray<TZAssetModel *> *models) {
self->_models = [NSMutableArray arrayWithArray:models];
[self initSubviews];
}];
} else {
self->_models = [NSMutableArray arrayWithArray:self->_model.models];
[self initSubviews];
}
}
});
}
- (void)initSubviews {
dispatch_async(dispatch_get_main_queue(), ^{
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
[tzImagePickerVc hideProgressHUD];
[self checkSelectedModels];
[self configCollectionView];
self->_collectionView.hidden = YES;
[self configBottomToolBar];
[self scrollCollectionViewToBottom];
});
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
tzImagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
}
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (void)configCollectionView {
_layout = [[UICollectionViewFlowLayout alloc] init];
_collectionView = [[TZCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout];
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.alwaysBounceHorizontal = NO;
_collectionView.contentInset = UIEdgeInsetsMake(itemMargin, itemMargin, itemMargin, itemMargin);
if (_showTakePhotoBtn) {
_collectionView.contentSize = CGSizeMake(self.view.tz_width, ((_model.count + self.columnNumber) / self.columnNumber) * self.view.tz_width);
} else {
_collectionView.contentSize = CGSizeMake(self.view.tz_width, ((_model.count + self.columnNumber - 1) / self.columnNumber) * self.view.tz_width);
}
[self.view addSubview:_collectionView];
[_collectionView registerClass:[TZAssetCell class] forCellWithReuseIdentifier:@"TZAssetCell"];
[_collectionView registerClass:[TZAssetCameraCell class] forCellWithReuseIdentifier:@"TZAssetCameraCell"];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Determine the size of the thumbnails to request from the PHCachingImageManager
CGFloat scale = 2.0;
if ([UIScreen mainScreen].bounds.size.width > 600) {
scale = 1.0;
}
CGSize cellSize = ((UICollectionViewFlowLayout *)_collectionView.collectionViewLayout).itemSize;
AssetGridThumbnailSize = CGSizeMake(cellSize.width * scale, cellSize.height * scale);
if (!_models) {
[self fetchAssetModels];
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (iOS8Later) {
// [self updateCachedAssets];
}
}
- (void)configBottomToolBar {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (!tzImagePickerVc.showSelectBtn) return;
_bottomToolBar = [[UIView alloc] initWithFrame:CGRectZero];
CGFloat rgb = 253 / 255.0;
_bottomToolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0];
_previewButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_previewButton addTarget:self action:@selector(previewButtonClick) forControlEvents:UIControlEventTouchUpInside];
_previewButton.titleLabel.font = [UIFont systemFontOfSize:16];
[_previewButton setTitle:tzImagePickerVc.previewBtnTitleStr forState:UIControlStateNormal];
[_previewButton setTitle:tzImagePickerVc.previewBtnTitleStr forState:UIControlStateDisabled];
[_previewButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_previewButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
_previewButton.enabled = tzImagePickerVc.selectedModels.count;
if (tzImagePickerVc.allowPickingOriginalPhoto) {
_originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
_originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0);
[_originalPhotoButton addTarget:self action:@selector(originalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside];
_originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16];
[_originalPhotoButton setTitle:tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateNormal];
[_originalPhotoButton setTitle:tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateSelected];
[_originalPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
[_originalPhotoButton setTitleColor:[UIColor blackColor] forState:UIControlStateSelected];
[_originalPhotoButton setImage:tzImagePickerVc.photoOriginDefImage forState:UIControlStateNormal];
[_originalPhotoButton setImage:tzImagePickerVc.photoOriginSelImage forState:UIControlStateSelected];
_originalPhotoButton.imageView.clipsToBounds = YES;
_originalPhotoButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
_originalPhotoButton.selected = _isSelectOriginalPhoto;
_originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0;
_originalPhotoLabel = [[UILabel alloc] init];
_originalPhotoLabel.textAlignment = NSTextAlignmentLeft;
_originalPhotoLabel.font = [UIFont systemFontOfSize:16];
_originalPhotoLabel.textColor = [UIColor blackColor];
if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes];
}
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
[_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside];
[_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal];
[_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateDisabled];
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal];
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorDisabled forState:UIControlStateDisabled];
_doneButton.enabled = tzImagePickerVc.selectedModels.count || tzImagePickerVc.alwaysEnableDoneBtn;
_numberImageView = [[UIImageView alloc] initWithImage:tzImagePickerVc.photoNumberIconImage];
_numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0;
_numberImageView.clipsToBounds = YES;
_numberImageView.contentMode = UIViewContentModeScaleAspectFit;
_numberImageView.backgroundColor = [UIColor clearColor];
_numberLabel = [[UILabel alloc] init];
_numberLabel.font = [UIFont systemFontOfSize:15];
_numberLabel.textColor = [UIColor whiteColor];
_numberLabel.textAlignment = NSTextAlignmentCenter;
_numberLabel.text = [NSString stringWithFormat:@"%zd",tzImagePickerVc.selectedModels.count];
_numberLabel.hidden = tzImagePickerVc.selectedModels.count <= 0;
_numberLabel.backgroundColor = [UIColor clearColor];
_divideLine = [[UIView alloc] init];
CGFloat rgb2 = 222 / 255.0;
_divideLine.backgroundColor = [UIColor colorWithRed:rgb2 green:rgb2 blue:rgb2 alpha:1.0];
[_bottomToolBar addSubview:_divideLine];
[_bottomToolBar addSubview:_previewButton];
[_bottomToolBar addSubview:_doneButton];
[_bottomToolBar addSubview:_numberImageView];
[_bottomToolBar addSubview:_numberLabel];
[_bottomToolBar addSubview:_originalPhotoButton];
[self.view addSubview:_bottomToolBar];
[_originalPhotoButton addSubview:_originalPhotoLabel];
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
CGFloat top = 0;
CGFloat collectionViewHeight = 0;
CGFloat naviBarHeight = self.navigationController.navigationBar.tz_height;
BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden;
CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 50 + (83 - 49) : 50;
if (self.navigationController.navigationBar.isTranslucent) {
top = naviBarHeight;
if (iOS7Later && !isStatusBarHidden) top += [TZCommonTools tz_statusBarHeight];
collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - toolBarHeight - top : self.view.tz_height - top;;
} else {
collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - toolBarHeight : self.view.tz_height;
}
_collectionView.frame = CGRectMake(0, top, self.view.tz_width, collectionViewHeight);
CGFloat itemWH = (self.view.tz_width - (self.columnNumber + 1) * itemMargin) / self.columnNumber;
_layout.itemSize = CGSizeMake(itemWH, itemWH);
_layout.minimumInteritemSpacing = itemMargin;
_layout.minimumLineSpacing = itemMargin;
[_collectionView setCollectionViewLayout:_layout];
if (_offsetItemCount > 0) {
CGFloat offsetY = _offsetItemCount * (_layout.itemSize.height + _layout.minimumLineSpacing);
[_collectionView setContentOffset:CGPointMake(0, offsetY)];
}
CGFloat toolBarTop = 0;
if (!self.navigationController.navigationBar.isHidden) {
toolBarTop = self.view.tz_height - toolBarHeight;
} else {
CGFloat navigationHeight = naviBarHeight;
if (iOS7Later) navigationHeight += [TZCommonTools tz_statusBarHeight];
toolBarTop = self.view.tz_height - toolBarHeight - navigationHeight;
}
_bottomToolBar.frame = CGRectMake(0, toolBarTop, self.view.tz_width, toolBarHeight);
CGFloat previewWidth = [tzImagePickerVc.previewBtnTitleStr tz_calculateSizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} maxSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].width + 2;
if (!tzImagePickerVc.allowPreview) {
previewWidth = 0.0;
}
_previewButton.frame = CGRectMake(10, 3, previewWidth, 44);
_previewButton.tz_width = !tzImagePickerVc.showSelectBtn ? 0 : previewWidth;
if (tzImagePickerVc.allowPickingOriginalPhoto) {
CGFloat fullImageWidth = [tzImagePickerVc.fullImageBtnTitleStr tz_calculateSizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} maxSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].width;
_originalPhotoButton.frame = CGRectMake(CGRectGetMaxX(_previewButton.frame), 0, fullImageWidth + 56, 50);
_originalPhotoLabel.frame = CGRectMake(fullImageWidth + 46, 0, 80, 50);
}
[_doneButton sizeToFit];
_doneButton.frame = CGRectMake(self.view.tz_width - _doneButton.tz_width - 12, 0, _doneButton.tz_width, 50);
_numberImageView.frame = CGRectMake(_doneButton.tz_left - 24 - 5, 13, 24, 24);
_numberLabel.frame = _numberImageView.frame;
_divideLine.frame = CGRectMake(0, 0, self.view.tz_width, 1);
[TZImageManager manager].columnNumber = [TZImageManager manager].columnNumber;
[self.collectionView reloadData];
}
#pragma mark - Notification
- (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti {
_offsetItemCount = _collectionView.contentOffset.y / (_layout.itemSize.height + _layout.minimumLineSpacing);
}
#pragma mark - Click Event
- (void)navLeftBarButtonClick{
[self.navigationController popViewControllerAnimated:YES];
}
- (void)previewButtonClick {
TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init];
[self pushPhotoPrevireViewController:photoPreviewVc];
}
- (void)originalPhotoButtonClick {
_originalPhotoButton.selected = !_originalPhotoButton.isSelected;
_isSelectOriginalPhoto = _originalPhotoButton.isSelected;
_originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;
if (_isSelectOriginalPhoto) {
[self getSelectedPhotoBytes];
}
}
- (void)doneButtonClick {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
// 1.6.8 判断是否满足最小必选张数的限制
if (tzImagePickerVc.minImagesCount && tzImagePickerVc.selectedModels.count < tzImagePickerVc.minImagesCount) {
NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a minimum of %zd photos"], tzImagePickerVc.minImagesCount];
[tzImagePickerVc showAlertWithTitle:title];
return;
}
[tzImagePickerVc showProgressHUD];
NSMutableArray *assets = [NSMutableArray array];
NSMutableArray *photos;
NSMutableArray *infoArr;
if (tzImagePickerVc.onlyReturnAsset) { // not fetch image
for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) {
TZAssetModel *model = tzImagePickerVc.selectedModels[i];
[assets addObject:model.asset];
}
} else { // fetch image
photos = [NSMutableArray array];
infoArr = [NSMutableArray array];
for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { [photos addObject:@1];[assets addObject:@1];[infoArr addObject:@1]; }
__block BOOL havenotShowAlert = YES;
[TZImageManager manager].shouldFixOrientation = YES;
__block id alertView;
for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) {
TZAssetModel *model = tzImagePickerVc.selectedModels[i];
[[TZImageManager manager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (isDegraded) return;
if (photo) {
photo = [[TZImageManager manager] scaleImage:photo toSize:CGSizeMake(tzImagePickerVc.photoWidth, (int)(tzImagePickerVc.photoWidth * photo.size.height / photo.size.width))];
[photos replaceObjectAtIndex:i withObject:photo];
}
if (info) [infoArr replaceObjectAtIndex:i withObject:info];
[assets replaceObjectAtIndex:i withObject:model.asset];
for (id item in photos) { if ([item isKindOfClass:[NSNumber class]]) return; }
if (havenotShowAlert) {
[tzImagePickerVc hideAlertView:alertView];
[self didGetAllPhotos:photos assets:assets infoArr:infoArr];
}
} progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
// 如果图片正在从iCloud同步中,提醒用户
if (progress < 1 && havenotShowAlert && !alertView) {
[tzImagePickerVc hideProgressHUD];
alertView = [tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Synchronizing photos from iCloud"]];
havenotShowAlert = NO;
return;
}
if (progress >= 1) {
havenotShowAlert = YES;
}
} networkAccessAllowed:YES];
}
}
if (tzImagePickerVc.selectedModels.count <= 0 || tzImagePickerVc.onlyReturnAsset) {
[self didGetAllPhotos:photos assets:assets infoArr:infoArr];
}
}
- (void)didGetAllPhotos:(NSArray *)photos assets:(NSArray *)assets infoArr:(NSArray *)infoArr {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
[tzImagePickerVc hideProgressHUD];
if (tzImagePickerVc.autoDismiss) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethodWithPhotos:photos assets:assets infoArr:infoArr];
}];
} else {
[self callDelegateMethodWithPhotos:photos assets:assets infoArr:infoArr];
}
}
- (void)callDelegateMethodWithPhotos:(NSArray *)photos assets:(NSArray *)assets infoArr:(NSArray *)infoArr {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (!tzImagePickerVc.allowPickingImage && tzImagePickerVc.allowPickingVideo && tzImagePickerVc.maxImagesCount == 1) {
if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingVideo:sourceAssets:)]) {
[tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingVideo:[photos firstObject] sourceAssets:[assets firstObject]];
}
if (tzImagePickerVc.didFinishPickingVideoHandle) {
tzImagePickerVc.didFinishPickingVideoHandle([photos firstObject], [assets firstObject]);
}
return;
}
if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingPhotos:sourceAssets:isSelectOriginalPhoto:)]) {
[tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingPhotos:photos sourceAssets:assets isSelectOriginalPhoto:_isSelectOriginalPhoto];
}
if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingPhotos:sourceAssets:isSelectOriginalPhoto:infos:)]) {
[tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingPhotos:photos sourceAssets:assets isSelectOriginalPhoto:_isSelectOriginalPhoto infos:infoArr];
}
if (tzImagePickerVc.didFinishPickingPhotosHandle) {
tzImagePickerVc.didFinishPickingPhotosHandle(photos,assets,_isSelectOriginalPhoto);
}
if (tzImagePickerVc.didFinishPickingPhotosWithInfosHandle) {
tzImagePickerVc.didFinishPickingPhotosWithInfosHandle(photos,assets,_isSelectOriginalPhoto,infoArr);
}
}
#pragma mark - UICollectionViewDataSource && Delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (_showTakePhotoBtn) {
return _models.count + 1;
}
return _models.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// the cell lead to take a picture / 去拍照的cell
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.row >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.row == 0)) && _showTakePhotoBtn) {
TZAssetCameraCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCameraCell" forIndexPath:indexPath];
cell.imageView.image = tzImagePickerVc.takePictureImage;
if ([tzImagePickerVc.takePictureImageName isEqualToString:@"takePicture80"]) {
cell.imageView.contentMode = UIViewContentModeCenter;
CGFloat rgb = 223 / 255.0;
cell.imageView.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0];
} else {
cell.imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500];
}
return cell;
}
// the cell dipaly photo or video / 展示照片或视频的cell
TZAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCell" forIndexPath:indexPath];
cell.allowPickingMultipleVideo = tzImagePickerVc.allowPickingMultipleVideo;
cell.photoDefImage = tzImagePickerVc.photoDefImage;
cell.photoSelImage = tzImagePickerVc.photoSelImage;
cell.useCachedImage = self.useCachedImage;
TZAssetModel *model;
if (tzImagePickerVc.sortAscendingByModificationDate || !_showTakePhotoBtn) {
model = _models[indexPath.row];
} else {
model = _models[indexPath.row - 1];
}
cell.allowPickingGif = tzImagePickerVc.allowPickingGif;
cell.model = model;
if (model.isSelected && tzImagePickerVc.showSelectedIndex) {
NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset];
cell.index = [tzImagePickerVc.selectedAssetIds indexOfObject:assetId] + 1;
}
cell.showSelectBtn = tzImagePickerVc.showSelectBtn;
cell.allowPreview = tzImagePickerVc.allowPreview;
if (tzImagePickerVc.selectedModels.count >= tzImagePickerVc.maxImagesCount && tzImagePickerVc.showPhotoCannotSelectLayer && !model.isSelected) {
cell.cannotSelectLayerButton.backgroundColor = tzImagePickerVc.cannotSelectLayerColor;
cell.cannotSelectLayerButton.hidden = NO;
} else {
cell.cannotSelectLayerButton.hidden = YES;
}
__weak typeof(cell) weakCell = cell;
__weak typeof(self) weakSelf = self;
__weak typeof(_numberImageView.layer) weakLayer = _numberImageView.layer;
cell.didSelectPhotoBlock = ^(BOOL isSelected) {
__strong typeof(weakCell) strongCell = weakCell;
__strong typeof(weakSelf) strongSelf = weakSelf;
__strong typeof(weakLayer) strongLayer = weakLayer;
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)strongSelf.navigationController;
// 1. cancel select / 取消选择
if (isSelected) {
strongCell.selectPhotoButton.selected = NO;
model.isSelected = NO;
NSArray *selectedModels = [NSArray arrayWithArray:tzImagePickerVc.selectedModels];
for (TZAssetModel *model_item in selectedModels) {
if ([[[TZImageManager manager] getAssetIdentifier:model.asset] isEqualToString:[[TZImageManager manager] getAssetIdentifier:model_item.asset]]) {
[tzImagePickerVc removeSelectedModel:model_item];
break;
}
}
[strongSelf refreshBottomToolBarStatus];
if (tzImagePickerVc.showSelectedIndex) {
[strongSelf setUseCachedImageAndReloadData];
}
[UIView showOscillatoryAnimationWithLayer:strongLayer type:TZOscillatoryAnimationToSmaller];
} else {
// 2. select:check if over the maxImagesCount / 选择照片,检查是否超过了最大个数的限制
if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) {
strongCell.selectPhotoButton.selected = YES;
model.isSelected = YES;
if (tzImagePickerVc.showSelectedIndex) {
model.needOscillatoryAnimation = YES;
[strongSelf setUseCachedImageAndReloadData];
}
[tzImagePickerVc addSelectedModel:model];
[strongSelf refreshBottomToolBarStatus];
[UIView showOscillatoryAnimationWithLayer:strongLayer type:TZOscillatoryAnimationToSmaller];
} else {
NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a maximum of %zd photos"], tzImagePickerVc.maxImagesCount];
[tzImagePickerVc showAlertWithTitle:title];
}
}
if (tzImagePickerVc.showPhotoCannotSelectLayer && !tzImagePickerVc.showSelectedIndex) {
[strongSelf.collectionView reloadData];
}
};
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// take a photo / 去拍照
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.row >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.row == 0)) && _showTakePhotoBtn) {
[self takePhoto]; return;
}
// preview phote or video / 预览照片或视频
NSInteger index = indexPath.row;
if (!tzImagePickerVc.sortAscendingByModificationDate && _showTakePhotoBtn) {
index = indexPath.row - 1;
}
TZAssetModel *model = _models[index];
if (model.type == TZAssetModelMediaTypeVideo && !tzImagePickerVc.allowPickingMultipleVideo) {
if (tzImagePickerVc.selectedModels.count > 0) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both video and photo"]];
} else {
TZVideoPlayerController *videoPlayerVc = [[TZVideoPlayerController alloc] init];
videoPlayerVc.model = model;
[self.navigationController pushViewController:videoPlayerVc animated:YES];
}
} else if (model.type == TZAssetModelMediaTypePhotoGif && tzImagePickerVc.allowPickingGif && !tzImagePickerVc.allowPickingMultipleVideo) {
if (tzImagePickerVc.selectedModels.count > 0) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both photo and GIF"]];
} else {
TZGifPhotoPreviewController *gifPreviewVc = [[TZGifPhotoPreviewController alloc] init];
gifPreviewVc.model = model;
[self.navigationController pushViewController:gifPreviewVc animated:YES];
}
} else {
TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init];
photoPreviewVc.currentIndex = index;
photoPreviewVc.models = _models;
[self pushPhotoPrevireViewController:photoPreviewVc];
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (iOS8Later) {
// [self updateCachedAssets];
}
}
#pragma mark - Private Method
- (void)setUseCachedImageAndReloadData {
self.useCachedImage = YES;
[self.collectionView reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.useCachedImage = NO;
});
}
/// 拍照按钮点击事件
- (void)takePhoto {
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if ((authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied) && iOS7Later) {
NSDictionary *infoDict = [TZCommonTools tz_getInfoDictionary];
// 无权限 做一个友好的提示
NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"];
if (!appName) appName = [infoDict valueForKey:@"CFBundleName"];
NSString *message = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\""],appName];
if (iOS8Later) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Can not use camera"] message:message delegate:self cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"Cancel"] otherButtonTitles:[NSBundle tz_localizedStringForKey:@"Setting"], nil];
[alert show];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Can not use camera"] message:message delegate:self cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"OK"] otherButtonTitles:nil];
[alert show];
}
} else if (authStatus == AVAuthorizationStatusNotDetermined) {
// fix issue 466, 防止用户首次拍照拒绝授权时相机页黑屏
if (iOS7Later) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[self pushImagePickerController];
});
}
}];
} else {
[self pushImagePickerController];
}
} else {
[self pushImagePickerController];
}
}
// 调用相机
- (void)pushImagePickerController {
// 提前定位
__weak typeof(self) weakSelf = self;
[[TZLocationManager manager] startLocationWithSuccessBlock:^(NSArray<CLLocation *> *locations) {
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.location = [locations firstObject];
} failureBlock:^(NSError *error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.location = nil;
}];
UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
if ([UIImagePickerController isSourceTypeAvailable: sourceType]) {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
self.imagePickerVc.sourceType = sourceType;
NSMutableArray *mediaTypes = [NSMutableArray array];
if (tzImagePickerVc.allowTakePicture) {
[mediaTypes addObject:(NSString *)kUTTypeImage];
}
if (tzImagePickerVc.allowTakeVideo) {
[mediaTypes addObject:(NSString *)kUTTypeMovie];
self.imagePickerVc.videoMaximumDuration = tzImagePickerVc.videoMaximumDuration;
}
self.imagePickerVc.mediaTypes= mediaTypes;
if (iOS8Later) {
_imagePickerVc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
}
[self presentViewController:_imagePickerVc animated:YES completion:nil];
} else {
NSLog(@"模拟器中无法打开照相机,请在真机中使用");
}
}
- (void)refreshBottomToolBarStatus {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
_previewButton.enabled = tzImagePickerVc.selectedModels.count > 0;
_doneButton.enabled = tzImagePickerVc.selectedModels.count > 0 || tzImagePickerVc.alwaysEnableDoneBtn;
_numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0;
_numberLabel.hidden = tzImagePickerVc.selectedModels.count <= 0;
_numberLabel.text = [NSString stringWithFormat:@"%zd",tzImagePickerVc.selectedModels.count];
_originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0;
_originalPhotoButton.selected = (_isSelectOriginalPhoto && _originalPhotoButton.enabled);
_originalPhotoLabel.hidden = (!_originalPhotoButton.isSelected);
if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes];
}
- (void)pushPhotoPrevireViewController:(TZPhotoPreviewController *)photoPreviewVc {
__weak typeof(self) weakSelf = self;
photoPreviewVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
[photoPreviewVc setBackButtonClickBlock:^(BOOL isSelectOriginalPhoto) {
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.isSelectOriginalPhoto = isSelectOriginalPhoto;
[strongSelf.collectionView reloadData];
[strongSelf refreshBottomToolBarStatus];
}];
[photoPreviewVc setDoneButtonClickBlock:^(BOOL isSelectOriginalPhoto) {
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.isSelectOriginalPhoto = isSelectOriginalPhoto;
[strongSelf doneButtonClick];
}];
[photoPreviewVc setDoneButtonClickBlockCropMode:^(UIImage *cropedImage, id asset) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf didGetAllPhotos:@[cropedImage] assets:@[asset] infoArr:nil];
}];
[self.navigationController pushViewController:photoPreviewVc animated:YES];
}
- (void)getSelectedPhotoBytes {
// 越南语 && 5屏幕时会显示不下,暂时这样处理
if ([[TZImagePickerConfig sharedInstance].preferredLanguage isEqualToString:@"vi"] && self.view.tz_width <= 320) {
return;
}
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[[TZImageManager manager] getPhotosBytesWithArray:imagePickerVc.selectedModels completion:^(NSString *totalBytes) {
self->_originalPhotoLabel.text = [NSString stringWithFormat:@"(%@)",totalBytes];
}];
}
- (void)scrollCollectionViewToBottom {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (_shouldScrollToBottom && _models.count > 0) {
NSInteger item = 0;
if (tzImagePickerVc.sortAscendingByModificationDate) {
item = _models.count - 1;
if (_showTakePhotoBtn) {
item += 1;
}
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self->_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:NO];
self->_shouldScrollToBottom = NO;
self->_collectionView.hidden = NO;
});
} else {
_collectionView.hidden = NO;
}
}
- (void)checkSelectedModels {
NSMutableArray *selectedAssets = [NSMutableArray array];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
for (TZAssetModel *model in tzImagePickerVc.selectedModels) {
[selectedAssets addObject:model.asset];
}
for (TZAssetModel *model in _models) {
model.isSelected = NO;
if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) {
model.isSelected = YES;
}
}
}
#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) { // 去设置界面,开启相机访问权限
if (iOS8Later) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
}
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[picker dismissViewControllerAnimated:YES completion:nil];
NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
if ([type isEqualToString:@"public.image"]) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[imagePickerVc showProgressHUD];
UIImage *photo = [info objectForKey:UIImagePickerControllerOriginalImage];
if (photo) {
[[TZImageManager manager] savePhotoWithImage:photo location:self.location completion:^(NSError *error){
if (!error) {
[self reloadPhotoArrayWithMediaType:type];
}
}];
self.location = nil;
}
} else if ([type isEqualToString:@"public.movie"]) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
[imagePickerVc showProgressHUD];
NSURL *videoUrl = [info objectForKey:UIImagePickerControllerMediaURL];
if (videoUrl) {
[[TZImageManager manager] saveVideoWithUrl:videoUrl location:self.location completion:^(NSError *error) {
if (!error) {
[self reloadPhotoArrayWithMediaType:type];
}
}];
self.location = nil;
}
}
}
- (void)reloadPhotoArrayWithMediaType:(NSString *)mediaType {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
[[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage needFetchAssets:NO completion:^(TZAlbumModel *model) {
self->_model = model;
[[TZImageManager manager] getAssetsFromFetchResult:self->_model.result completion:^(NSArray<TZAssetModel *> *models) {
[tzImagePickerVc hideProgressHUD];
TZAssetModel *assetModel;
if (tzImagePickerVc.sortAscendingByModificationDate) {
assetModel = [models lastObject];
[self->_models addObject:assetModel];
} else {
assetModel = [models firstObject];
[self->_models insertObject:assetModel atIndex:0];
}
if (tzImagePickerVc.maxImagesCount <= 1) {
if (tzImagePickerVc.allowCrop) {
TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init];
if (tzImagePickerVc.sortAscendingByModificationDate) {
photoPreviewVc.currentIndex = self->_models.count - 1;
} else {
photoPreviewVc.currentIndex = 0;
}
photoPreviewVc.models = self->_models;
[self pushPhotoPrevireViewController:photoPreviewVc];
} else {
[tzImagePickerVc addSelectedModel:assetModel];
[self doneButtonClick];
}
return;
}
if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) {
if ([mediaType isEqualToString:@"public.movie"] && !tzImagePickerVc.allowPickingMultipleVideo) {
// 不能多选视频的情况下,不选中拍摄的视频
} else {
assetModel.isSelected = YES;
[tzImagePickerVc addSelectedModel:assetModel];
[self refreshBottomToolBarStatus];
}
}
self->_collectionView.hidden = YES;
[self->_collectionView reloadData];
self->_shouldScrollToBottom = YES;
[self scrollCollectionViewToBottom];
}];
}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)dealloc {
// NSLog(@"%@ dealloc",NSStringFromClass(self.class));
}
#pragma mark - Asset Caching
- (void)resetCachedAssets {
[[TZImageManager manager].cachingImageManager stopCachingImagesForAllAssets];
self.previousPreheatRect = CGRectZero;
}
- (void)updateCachedAssets {
BOOL isViewVisible = [self isViewLoaded] && [[self view] window] != nil;
if (!isViewVisible) { return; }
// The preheat window is twice the height of the visible rect.
CGRect preheatRect = _collectionView.bounds;
preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect));
/*
Check if the collection view is showing an area that is significantly
different to the last preheated area.
*/
CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect));
if (delta > CGRectGetHeight(_collectionView.bounds) / 3.0f) {
// Compute the assets to start caching and to stop caching.
NSMutableArray *addedIndexPaths = [NSMutableArray array];
NSMutableArray *removedIndexPaths = [NSMutableArray array];
[self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect removedHandler:^(CGRect removedRect) {
NSArray *indexPaths = [self aapl_indexPathsForElementsInRect:removedRect];
[removedIndexPaths addObjectsFromArray:indexPaths];
} addedHandler:^(CGRect addedRect) {
NSArray *indexPaths = [self aapl_indexPathsForElementsInRect:addedRect];
[addedIndexPaths addObjectsFromArray:indexPaths];
}];
NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths];
NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths];
// Update the assets the PHCachingImageManager is caching.
[[TZImageManager manager].cachingImageManager startCachingImagesForAssets:assetsToStartCaching
targetSize:AssetGridThumbnailSize
contentMode:PHImageContentModeAspectFill
options:nil];
[[TZImageManager manager].cachingImageManager stopCachingImagesForAssets:assetsToStopCaching
targetSize:AssetGridThumbnailSize
contentMode:PHImageContentModeAspectFill
options:nil];
// Store the preheat rect to compare against in the future.
self.previousPreheatRect = preheatRect;
}
}
- (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect removedHandler:(void (^)(CGRect removedRect))removedHandler addedHandler:(void (^)(CGRect addedRect))addedHandler {
if (CGRectIntersectsRect(newRect, oldRect)) {
CGFloat oldMaxY = CGRectGetMaxY(oldRect);
CGFloat oldMinY = CGRectGetMinY(oldRect);
CGFloat newMaxY = CGRectGetMaxY(newRect);
CGFloat newMinY = CGRectGetMinY(newRect);
if (newMaxY > oldMaxY) {
CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY));
addedHandler(rectToAdd);
}
if (oldMinY > newMinY) {
CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY));
addedHandler(rectToAdd);
}
if (newMaxY < oldMaxY) {
CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY));
removedHandler(rectToRemove);
}
if (oldMinY < newMinY) {
CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY));
removedHandler(rectToRemove);
}
} else {
addedHandler(newRect);
removedHandler(oldRect);
}
}
- (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths {
if (indexPaths.count == 0) { return nil; }
NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
for (NSIndexPath *indexPath in indexPaths) {
if (indexPath.item < _models.count) {
TZAssetModel *model = _models[indexPath.item];
[assets addObject:model.asset];
}
}
return assets;
}
- (NSArray *)aapl_indexPathsForElementsInRect:(CGRect)rect {
NSArray *allLayoutAttributes = [_collectionView.collectionViewLayout layoutAttributesForElementsInRect:rect];
if (allLayoutAttributes.count == 0) { return nil; }
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:allLayoutAttributes.count];
for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes) {
NSIndexPath *indexPath = layoutAttributes.indexPath;
[indexPaths addObject:indexPath];
}
return indexPaths;
}
#pragma clang diagnostic pop
@end
@implementation TZCollectionView
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:[UIControl class]]) {
return YES;
}
return [super touchesShouldCancelInContentView:view];
}
@end
//
// TZPhotoPreviewCell.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAssetModel;
@interface TZAssetPreviewCell : UICollectionViewCell
@property (nonatomic, strong) TZAssetModel *model;
@property (nonatomic, copy) void (^singleTapGestureBlock)(void);
- (void)configSubviews;
- (void)photoPreviewCollectionViewDidScroll;
@end
@class TZAssetModel,TZProgressView,TZPhotoPreviewView;
@interface TZPhotoPreviewCell : TZAssetPreviewCell
@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress);
@property (nonatomic, strong) TZPhotoPreviewView *previewView;
@property (nonatomic, assign) BOOL allowCrop;
@property (nonatomic, assign) CGRect cropRect;
- (void)recoverSubviews;
@end
@interface TZPhotoPreviewView : UIView
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *imageContainerView;
@property (nonatomic, strong) TZProgressView *progressView;
@property (nonatomic, assign) BOOL allowCrop;
@property (nonatomic, assign) CGRect cropRect;
@property (nonatomic, strong) TZAssetModel *model;
@property (nonatomic, strong) id asset;
@property (nonatomic, copy) void (^singleTapGestureBlock)(void);
@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress);
@property (nonatomic, assign) int32_t imageRequestID;
- (void)recoverSubviews;
@end
@class AVPlayer, AVPlayerLayer;
@interface TZVideoPreviewCell : TZAssetPreviewCell
@property (strong, nonatomic) AVPlayer *player;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
@property (strong, nonatomic) UIButton *playButton;
@property (strong, nonatomic) UIImage *cover;
- (void)pausePlayerAndShowNaviBar;
@end
@interface TZGifPreviewCell : TZAssetPreviewCell
@property (strong, nonatomic) TZPhotoPreviewView *previewView;
@end
//
// TZPhotoPreviewCell.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "TZPhotoPreviewCell.h"
#import "TZAssetModel.h"
#import "UIView+Layout.h"
#import "TZImageManager.h"
#import "TZProgressView.h"
#import "TZImageCropManager.h"
#import <MediaPlayer/MediaPlayer.h>
#import "TZImagePickerController.h"
@implementation TZAssetPreviewCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor blackColor];
[self configSubviews];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(photoPreviewCollectionViewDidScroll) name:@"photoPreviewCollectionViewDidScroll" object:nil];
}
return self;
}
- (void)configSubviews {
}
#pragma mark - Notification
- (void)photoPreviewCollectionViewDidScroll {
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
@implementation TZPhotoPreviewCell
- (void)configSubviews {
self.previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero];
__weak typeof(self) weakSelf = self;
[self.previewView setSingleTapGestureBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.singleTapGestureBlock) {
strongSelf.singleTapGestureBlock();
}
}];
[self.previewView setImageProgressUpdateBlock:^(double progress) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.imageProgressUpdateBlock) {
strongSelf.imageProgressUpdateBlock(progress);
}
}];
[self addSubview:self.previewView];
}
- (void)setModel:(TZAssetModel *)model {
[super setModel:model];
_previewView.asset = model.asset;
}
- (void)recoverSubviews {
[_previewView recoverSubviews];
}
- (void)setAllowCrop:(BOOL)allowCrop {
_allowCrop = allowCrop;
_previewView.allowCrop = allowCrop;
}
- (void)setCropRect:(CGRect)cropRect {
_cropRect = cropRect;
_previewView.cropRect = cropRect;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.previewView.frame = self.bounds;
}
@end
@interface TZPhotoPreviewView ()<UIScrollViewDelegate>
@end
@implementation TZPhotoPreviewView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_scrollView = [[UIScrollView alloc] init];
_scrollView.bouncesZoom = YES;
_scrollView.maximumZoomScale = 2.5;
_scrollView.minimumZoomScale = 1.0;
_scrollView.multipleTouchEnabled = YES;
_scrollView.delegate = self;
_scrollView.scrollsToTop = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = YES;
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView.delaysContentTouches = NO;
_scrollView.canCancelContentTouches = YES;
_scrollView.alwaysBounceVertical = NO;
if (iOS11Later) {
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
[self addSubview:_scrollView];
_imageContainerView = [[UIView alloc] init];
_imageContainerView.clipsToBounds = YES;
_imageContainerView.contentMode = UIViewContentModeScaleAspectFill;
[_scrollView addSubview:_imageContainerView];
_imageView = [[UIImageView alloc] init];
_imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.clipsToBounds = YES;
[_imageContainerView addSubview:_imageView];
UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
[self addGestureRecognizer:tap1];
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
tap2.numberOfTapsRequired = 2;
[tap1 requireGestureRecognizerToFail:tap2];
[self addGestureRecognizer:tap2];
[self configProgressView];
}
return self;
}
- (void)configProgressView {
_progressView = [[TZProgressView alloc] init];
_progressView.hidden = YES;
[self addSubview:_progressView];
}
- (void)setModel:(TZAssetModel *)model {
_model = model;
[_scrollView setZoomScale:1.0 animated:NO];
if (model.type == TZAssetModelMediaTypePhotoGif) {
// 先显示缩略图
[[TZImageManager manager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
self.imageView.image = photo;
[self resizeSubviews];
// 再显示gif动图
[[TZImageManager manager] getOriginalPhotoDataWithAsset:model.asset completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) {
if (!isDegraded) {
self.imageView.image = [UIImage sd_tz_animatedGIFWithData:data];
[self resizeSubviews];
}
}];
} progressHandler:nil networkAccessAllowed:NO];
} else {
self.asset = model.asset;
}
}
- (void)setAsset:(id)asset {
if (_asset && self.imageRequestID) {
[[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID];
}
_asset = asset;
self.imageRequestID = [[TZImageManager manager] getPhotoWithAsset:asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (![asset isEqual:self->_asset]) return;
self.imageView.image = photo;
[self resizeSubviews];
self->_progressView.hidden = YES;
if (self.imageProgressUpdateBlock) {
self.imageProgressUpdateBlock(1);
}
if (!isDegraded) {
self.imageRequestID = 0;
}
} progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
if (![asset isEqual:self->_asset]) return;
self->_progressView.hidden = NO;
[self bringSubviewToFront:self->_progressView];
progress = progress > 0.02 ? progress : 0.02;
self->_progressView.progress = progress;
if (self.imageProgressUpdateBlock && progress < 1) {
self.imageProgressUpdateBlock(progress);
}
if (progress >= 1) {
self->_progressView.hidden = YES;
self.imageRequestID = 0;
}
} networkAccessAllowed:YES];
}
- (void)recoverSubviews {
[_scrollView setZoomScale:1.0 animated:NO];
[self resizeSubviews];
}
- (void)resizeSubviews {
_imageContainerView.tz_origin = CGPointZero;
_imageContainerView.tz_width = self.scrollView.tz_width;
UIImage *image = _imageView.image;
if (image.size.height / image.size.width > self.tz_height / self.scrollView.tz_width) {
_imageContainerView.tz_height = floor(image.size.height / (image.size.width / self.scrollView.tz_width));
} else {
CGFloat height = image.size.height / image.size.width * self.scrollView.tz_width;
if (height < 1 || isnan(height)) height = self.tz_height;
height = floor(height);
_imageContainerView.tz_height = height;
_imageContainerView.tz_centerY = self.tz_height / 2;
}
if (_imageContainerView.tz_height > self.tz_height && _imageContainerView.tz_height - self.tz_height <= 1) {
_imageContainerView.tz_height = self.tz_height;
}
CGFloat contentSizeH = MAX(_imageContainerView.tz_height, self.tz_height);
_scrollView.contentSize = CGSizeMake(self.scrollView.tz_width, contentSizeH);
[_scrollView scrollRectToVisible:self.bounds animated:NO];
_scrollView.alwaysBounceVertical = _imageContainerView.tz_height <= self.tz_height ? NO : YES;
_imageView.frame = _imageContainerView.bounds;
[self refreshScrollViewContentSize];
}
- (void)setAllowCrop:(BOOL)allowCrop {
_allowCrop = allowCrop;
_scrollView.maximumZoomScale = allowCrop ? 4.0 : 2.5;
if ([self.asset isKindOfClass:[PHAsset class]]) {
PHAsset *phAsset = (PHAsset *)self.asset;
CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight;
// 优化超宽图片的显示
if (aspectRatio > 1.5) {
self.scrollView.maximumZoomScale *= aspectRatio / 1.5;
}
}
}
- (void)refreshScrollViewContentSize {
if (_allowCrop) {
// 1.7.2 如果允许裁剪,需要让图片的任意部分都能在裁剪框内,于是对_scrollView做了如下处理:
// 1.让contentSize增大(裁剪框右下角的图片部分)
CGFloat contentWidthAdd = self.scrollView.tz_width - CGRectGetMaxX(_cropRect);
CGFloat contentHeightAdd = (MIN(_imageContainerView.tz_height, self.tz_height) - self.cropRect.size.height) / 2;
CGFloat newSizeW = self.scrollView.contentSize.width + contentWidthAdd;
CGFloat newSizeH = MAX(self.scrollView.contentSize.height, self.tz_height) + contentHeightAdd;
_scrollView.contentSize = CGSizeMake(newSizeW, newSizeH);
_scrollView.alwaysBounceVertical = YES;
// 2.让scrollView新增滑动区域(裁剪框左上角的图片部分)
if (contentHeightAdd > 0 || contentWidthAdd > 0) {
_scrollView.contentInset = UIEdgeInsetsMake(contentHeightAdd, _cropRect.origin.x, 0, 0);
} else {
_scrollView.contentInset = UIEdgeInsetsZero;
}
}
}
- (void)layoutSubviews {
[super layoutSubviews];
_scrollView.frame = CGRectMake(10, 0, self.tz_width - 20, self.tz_height);
static CGFloat progressWH = 40;
CGFloat progressX = (self.tz_width - progressWH) / 2;
CGFloat progressY = (self.tz_height - progressWH) / 2;
_progressView.frame = CGRectMake(progressX, progressY, progressWH, progressWH);
[self recoverSubviews];
}
#pragma mark - UITapGestureRecognizer Event
- (void)doubleTap:(UITapGestureRecognizer *)tap {
if (_scrollView.zoomScale > 1.0) {
_scrollView.contentInset = UIEdgeInsetsZero;
[_scrollView setZoomScale:1.0 animated:YES];
} else {
CGPoint touchPoint = [tap locationInView:self.imageView];
CGFloat newZoomScale = _scrollView.maximumZoomScale;
CGFloat xsize = self.frame.size.width / newZoomScale;
CGFloat ysize = self.frame.size.height / newZoomScale;
[_scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
}
}
- (void)singleTap:(UITapGestureRecognizer *)tap {
if (self.singleTapGestureBlock) {
self.singleTapGestureBlock();
}
}
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _imageContainerView;
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
scrollView.contentInset = UIEdgeInsetsZero;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self refreshImageContainerViewCenter];
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
[self refreshScrollViewContentSize];
}
#pragma mark - Private
- (void)refreshImageContainerViewCenter {
CGFloat offsetX = (_scrollView.tz_width > _scrollView.contentSize.width) ? ((_scrollView.tz_width - _scrollView.contentSize.width) * 0.5) : 0.0;
CGFloat offsetY = (_scrollView.tz_height > _scrollView.contentSize.height) ? ((_scrollView.tz_height - _scrollView.contentSize.height) * 0.5) : 0.0;
self.imageContainerView.center = CGPointMake(_scrollView.contentSize.width * 0.5 + offsetX, _scrollView.contentSize.height * 0.5 + offsetY);
}
@end
@implementation TZVideoPreviewCell
- (void)configSubviews {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)configPlayButton {
if (_playButton) {
[_playButton removeFromSuperview];
}
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted];
[_playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_playButton];
}
- (void)setModel:(TZAssetModel *)model {
[super setModel:model];
[self configMoviePlayer];
}
- (void)configMoviePlayer {
if (_player) {
[_playerLayer removeFromSuperlayer];
_playerLayer = nil;
[_player pause];
_player = nil;
}
[[TZImageManager manager] getPhotoWithAsset:self.model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
self->_cover = photo;
}];
[[TZImageManager manager] getVideoWithAsset:self.model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_player = [AVPlayer playerWithPlayerItem:playerItem];
self->_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self->_player];
self->_playerLayer.backgroundColor = [UIColor blackColor].CGColor;
self->_playerLayer.frame = self.bounds;
[self.layer addSublayer:self->_playerLayer];
[self configPlayButton];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self->_player.currentItem];
});
}];
}
- (void)layoutSubviews {
[super layoutSubviews];
_playerLayer.frame = self.bounds;
_playButton.frame = CGRectMake(0, 64, self.tz_width, self.tz_height - 64 - 44);
}
- (void)photoPreviewCollectionViewDidScroll {
[self pausePlayerAndShowNaviBar];
}
#pragma mark - Click Event
- (void)playButtonClick {
CMTime currentTime = _player.currentItem.currentTime;
CMTime durationTime = _player.currentItem.duration;
if (_player.rate == 0.0f) {
if (currentTime.value == durationTime.value) [_player.currentItem seekToTime:CMTimeMake(0, 1)];
[_player play];
[_playButton setImage:nil forState:UIControlStateNormal];
if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES;
if (self.singleTapGestureBlock) {
self.singleTapGestureBlock();
}
} else {
[self pausePlayerAndShowNaviBar];
}
}
- (void)pausePlayerAndShowNaviBar {
if (_player.rate != 0.0) {
[_player pause];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
if (self.singleTapGestureBlock) {
self.singleTapGestureBlock();
}
}
}
@end
@implementation TZGifPreviewCell
- (void)configSubviews {
[self configPreviewView];
}
- (void)configPreviewView {
_previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero];
__weak typeof(self) weakSelf = self;
[_previewView setSingleTapGestureBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf signleTapAction];
}];
[self addSubview:_previewView];
}
- (void)setModel:(TZAssetModel *)model {
[super setModel:model];
_previewView.model = self.model;
}
- (void)layoutSubviews {
[super layoutSubviews];
_previewView.frame = self.bounds;
}
#pragma mark - Click Event
- (void)signleTapAction {
if (self.singleTapGestureBlock) {
self.singleTapGestureBlock();
}
}
@end
//
// TZPhotoPreviewController.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TZPhotoPreviewController : UIViewController
@property (nonatomic, strong) NSMutableArray *models; ///< All photo models / 所有图片模型数组
@property (nonatomic, strong) NSMutableArray *photos; ///< All photos / 所有图片数组
@property (nonatomic, assign) NSInteger currentIndex; ///< Index of the photo user click / 用户点击的图片的索引
@property (nonatomic, assign) BOOL isSelectOriginalPhoto; ///< If YES,return original photo / 是否返回原图
@property (nonatomic, assign) BOOL isCropImage;
/// Return the new selected photos / 返回最新的选中图片数组
@property (nonatomic, copy) void (^backButtonClickBlock)(BOOL isSelectOriginalPhoto);
@property (nonatomic, copy) void (^doneButtonClickBlock)(BOOL isSelectOriginalPhoto);
@property (nonatomic, copy) void (^doneButtonClickBlockCropMode)(UIImage *cropedImage,id asset);
@property (nonatomic, copy) void (^doneButtonClickBlockWithPreviewType)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto);
@end
//
// TZPhotoPreviewController.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "TZPhotoPreviewController.h"
#import "TZPhotoPreviewCell.h"
#import "TZAssetModel.h"
#import "UIView+Layout.h"
#import "TZImagePickerController.h"
#import "TZImageManager.h"
#import "TZImageCropManager.h"
@interface TZPhotoPreviewController ()<UICollectionViewDataSource,UICollectionViewDelegate,UIScrollViewDelegate> {
UICollectionView *_collectionView;
UICollectionViewFlowLayout *_layout;
NSArray *_photosTemp;
NSArray *_assetsTemp;
UIView *_naviBar;
UIButton *_backButton;
UIButton *_selectButton;
UILabel *_indexLabel;
UIView *_toolBar;
UIButton *_doneButton;
UIImageView *_numberImageView;
UILabel *_numberLabel;
UIButton *_originalPhotoButton;
UILabel *_originalPhotoLabel;
CGFloat _offsetItemCount;
}
@property (nonatomic, assign) BOOL isHideNaviBar;
@property (nonatomic, strong) UIView *cropBgView;
@property (nonatomic, strong) UIView *cropView;
@property (nonatomic, assign) double progress;
@property (strong, nonatomic) id alertView;
@end
@implementation TZPhotoPreviewController
- (void)viewDidLoad {
[super viewDidLoad];
[TZImageManager manager].shouldFixOrientation = YES;
__weak typeof(self) weakSelf = self;
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)weakSelf.navigationController;
if (!self.models.count) {
self.models = [NSMutableArray arrayWithArray:_tzImagePickerVc.selectedModels];
_assetsTemp = [NSMutableArray arrayWithArray:_tzImagePickerVc.selectedAssets];
}
[self configCollectionView];
[self configCustomNaviBar];
[self configBottomToolBar];
self.view.clipsToBounds = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
}
- (void)setPhotos:(NSMutableArray *)photos {
_photos = photos;
_photosTemp = [NSArray arrayWithArray:photos];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:YES];
if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES;
if (_currentIndex) [_collectionView setContentOffset:CGPointMake((self.view.tz_width + 20) * _currentIndex, 0) animated:NO];
[self refreshNaviBarAndBottomBarState];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc.needShowStatusBar && iOS7Later) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
[TZImageManager manager].shouldFixOrientation = NO;
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (void)configCustomNaviBar {
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
_naviBar = [[UIView alloc] initWithFrame:CGRectZero];
_naviBar.backgroundColor = [UIColor colorWithRed:(34/255.0) green:(34/255.0) blue:(34/255.0) alpha:0.7];
_backButton = [[UIButton alloc] initWithFrame:CGRectZero];
[_backButton setImage:[UIImage imageNamedFromMyBundle:@"navi_back"] forState:UIControlStateNormal];
[_backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[_backButton addTarget:self action:@selector(backButtonClick) forControlEvents:UIControlEventTouchUpInside];
_selectButton = [[UIButton alloc] initWithFrame:CGRectZero];
[_selectButton setImage:tzImagePickerVc.photoDefImage forState:UIControlStateNormal];
[_selectButton setImage:tzImagePickerVc.photoSelImage forState:UIControlStateSelected];
_selectButton.imageView.clipsToBounds = YES;
_selectButton.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 0);
_selectButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
[_selectButton addTarget:self action:@selector(select:) forControlEvents:UIControlEventTouchUpInside];
_selectButton.hidden = !tzImagePickerVc.showSelectBtn;
_indexLabel = [[UILabel alloc] init];
_indexLabel.font = [UIFont systemFontOfSize:14];
_indexLabel.textColor = [UIColor whiteColor];
_indexLabel.textAlignment = NSTextAlignmentCenter;
[_naviBar addSubview:_selectButton];
[_naviBar addSubview:_indexLabel];
[_naviBar addSubview:_backButton];
[self.view addSubview:_naviBar];
}
- (void)configBottomToolBar {
_toolBar = [[UIView alloc] initWithFrame:CGRectZero];
static CGFloat rgb = 34 / 255.0;
_toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7];
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (_tzImagePickerVc.allowPickingOriginalPhoto) {
_originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
_originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0);
_originalPhotoButton.backgroundColor = [UIColor clearColor];
[_originalPhotoButton addTarget:self action:@selector(originalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside];
_originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:13];
[_originalPhotoButton setTitle:_tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateNormal];
[_originalPhotoButton setTitle:_tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateSelected];
[_originalPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
[_originalPhotoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
[_originalPhotoButton setImage:_tzImagePickerVc.photoPreviewOriginDefImage forState:UIControlStateNormal];
[_originalPhotoButton setImage:_tzImagePickerVc.photoOriginSelImage forState:UIControlStateSelected];
_originalPhotoLabel = [[UILabel alloc] init];
_originalPhotoLabel.textAlignment = NSTextAlignmentLeft;
_originalPhotoLabel.font = [UIFont systemFontOfSize:13];
_originalPhotoLabel.textColor = [UIColor whiteColor];
_originalPhotoLabel.backgroundColor = [UIColor clearColor];
if (_isSelectOriginalPhoto) [self showPhotoBytes];
}
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
[_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside];
[_doneButton setTitle:_tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal];
[_doneButton setTitleColor:_tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal];
_numberImageView = [[UIImageView alloc] initWithImage:_tzImagePickerVc.photoNumberIconImage];
_numberImageView.backgroundColor = [UIColor clearColor];
_numberImageView.clipsToBounds = YES;
_numberImageView.contentMode = UIViewContentModeScaleAspectFit;
_numberImageView.hidden = _tzImagePickerVc.selectedModels.count <= 0;
_numberLabel = [[UILabel alloc] init];
_numberLabel.font = [UIFont systemFontOfSize:15];
_numberLabel.textColor = [UIColor whiteColor];
_numberLabel.textAlignment = NSTextAlignmentCenter;
_numberLabel.text = [NSString stringWithFormat:@"%zd",_tzImagePickerVc.selectedModels.count];
_numberLabel.hidden = _tzImagePickerVc.selectedModels.count <= 0;
_numberLabel.backgroundColor = [UIColor clearColor];
[_originalPhotoButton addSubview:_originalPhotoLabel];
[_toolBar addSubview:_doneButton];
[_toolBar addSubview:_originalPhotoButton];
[_toolBar addSubview:_numberImageView];
[_toolBar addSubview:_numberLabel];
[self.view addSubview:_toolBar];
}
- (void)configCollectionView {
_layout = [[UICollectionViewFlowLayout alloc] init];
_layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout];
_collectionView.backgroundColor = [UIColor blackColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.pagingEnabled = YES;
_collectionView.scrollsToTop = NO;
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.contentOffset = CGPointMake(0, 0);
_collectionView.contentSize = CGSizeMake(self.models.count * (self.view.tz_width + 20), 0);
[self.view addSubview:_collectionView];
[_collectionView registerClass:[TZPhotoPreviewCell class] forCellWithReuseIdentifier:@"TZPhotoPreviewCell"];
[_collectionView registerClass:[TZVideoPreviewCell class] forCellWithReuseIdentifier:@"TZVideoPreviewCell"];
[_collectionView registerClass:[TZGifPreviewCell class] forCellWithReuseIdentifier:@"TZGifPreviewCell"];
}
- (void)configCropView {
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (_tzImagePickerVc.maxImagesCount <= 1 && _tzImagePickerVc.allowCrop) {
[_cropView removeFromSuperview];
[_cropBgView removeFromSuperview];
_cropBgView = [UIView new];
_cropBgView.userInteractionEnabled = NO;
_cropBgView.frame = self.view.bounds;
_cropBgView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_cropBgView];
[TZImageCropManager overlayClippingWithView:_cropBgView cropRect:_tzImagePickerVc.cropRect containerView:self.view needCircleCrop:_tzImagePickerVc.needCircleCrop];
_cropView = [UIView new];
_cropView.userInteractionEnabled = NO;
_cropView.frame = _tzImagePickerVc.cropRect;
_cropView.backgroundColor = [UIColor clearColor];
_cropView.layer.borderColor = [UIColor whiteColor].CGColor;
_cropView.layer.borderWidth = 1.0;
if (_tzImagePickerVc.needCircleCrop) {
_cropView.layer.cornerRadius = _tzImagePickerVc.cropRect.size.width / 2;
_cropView.clipsToBounds = YES;
}
[self.view addSubview:_cropView];
if (_tzImagePickerVc.cropViewSettingBlock) {
_tzImagePickerVc.cropViewSettingBlock(_cropView);
}
[self.view bringSubviewToFront:_naviBar];
[self.view bringSubviewToFront:_toolBar];
}
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
CGFloat statusBarHeight = [TZCommonTools tz_statusBarHeight];
CGFloat statusBarHeightInterval = statusBarHeight - 20;
CGFloat naviBarHeight = statusBarHeight + _tzImagePickerVc.navigationBar.tz_height;
_naviBar.frame = CGRectMake(0, 0, self.view.tz_width, naviBarHeight);
_backButton.frame = CGRectMake(10, 10 + statusBarHeightInterval, 44, 44);
_selectButton.frame = CGRectMake(self.view.tz_width - 56, 10 + statusBarHeightInterval, 44, 44);
_indexLabel.frame = _selectButton.frame;
_layout.itemSize = CGSizeMake(self.view.tz_width + 20, self.view.tz_height);
_layout.minimumInteritemSpacing = 0;
_layout.minimumLineSpacing = 0;
_collectionView.frame = CGRectMake(-10, 0, self.view.tz_width + 20, self.view.tz_height);
[_collectionView setCollectionViewLayout:_layout];
if (_offsetItemCount > 0) {
CGFloat offsetX = _offsetItemCount * _layout.itemSize.width;
[_collectionView setContentOffset:CGPointMake(offsetX, 0)];
}
if (_tzImagePickerVc.allowCrop) {
[_collectionView reloadData];
}
CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44;
CGFloat toolBarTop = self.view.tz_height - toolBarHeight;
_toolBar.frame = CGRectMake(0, toolBarTop, self.view.tz_width, toolBarHeight);
if (_tzImagePickerVc.allowPickingOriginalPhoto) {
CGFloat fullImageWidth = [_tzImagePickerVc.fullImageBtnTitleStr tz_calculateSizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} maxSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].width;
_originalPhotoButton.frame = CGRectMake(0, 0, fullImageWidth + 56, 44);
_originalPhotoLabel.frame = CGRectMake(fullImageWidth + 42, 0, 80, 44);
}
[_doneButton sizeToFit];
_doneButton.frame = CGRectMake(self.view.tz_width - _doneButton.tz_width - 12, 0, _doneButton.tz_width, 44);
_numberImageView.frame = CGRectMake(_doneButton.tz_left - 24 - 5, 10, 24, 24);
_numberLabel.frame = _numberImageView.frame;
[self configCropView];
}
#pragma mark - Notification
- (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti {
_offsetItemCount = _collectionView.contentOffset.x / _layout.itemSize.width;
}
#pragma mark - Click Event
- (void)select:(UIButton *)selectButton {
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
TZAssetModel *model = _models[_currentIndex];
if (!selectButton.isSelected) {
// 1. select:check if over the maxImagesCount / 选择照片,检查是否超过了最大个数的限制
if (_tzImagePickerVc.selectedModels.count >= _tzImagePickerVc.maxImagesCount) {
NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a maximum of %zd photos"], _tzImagePickerVc.maxImagesCount];
[_tzImagePickerVc showAlertWithTitle:title];
return;
// 2. if not over the maxImagesCount / 如果没有超过最大个数限制
} else {
[_tzImagePickerVc addSelectedModel:model];
if (self.photos) {
[_tzImagePickerVc.selectedAssets addObject:_assetsTemp[_currentIndex]];
[self.photos addObject:_photosTemp[_currentIndex]];
}
if (model.type == TZAssetModelMediaTypeVideo && !_tzImagePickerVc.allowPickingMultipleVideo) {
[_tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Select the video when in multi state, we will handle the video as a photo"]];
}
}
} else {
NSArray *selectedModels = [NSArray arrayWithArray:_tzImagePickerVc.selectedModels];
for (TZAssetModel *model_item in selectedModels) {
if ([[[TZImageManager manager] getAssetIdentifier:model.asset] isEqualToString:[[TZImageManager manager] getAssetIdentifier:model_item.asset]]) {
// 1.6.7版本更新:防止有多个一样的model,一次性被移除了
NSArray *selectedModelsTmp = [NSArray arrayWithArray:_tzImagePickerVc.selectedModels];
for (NSInteger i = 0; i < selectedModelsTmp.count; i++) {
TZAssetModel *model = selectedModelsTmp[i];
if ([model isEqual:model_item]) {
[_tzImagePickerVc removeSelectedModel:model];
// [_tzImagePickerVc.selectedModels removeObjectAtIndex:i];
break;
}
}
if (self.photos) {
// 1.6.7版本更新:防止有多个一样的asset,一次性被移除了
NSArray *selectedAssetsTmp = [NSArray arrayWithArray:_tzImagePickerVc.selectedAssets];
for (NSInteger i = 0; i < selectedAssetsTmp.count; i++) {
id asset = selectedAssetsTmp[i];
if ([asset isEqual:_assetsTemp[_currentIndex]]) {
[_tzImagePickerVc.selectedAssets removeObjectAtIndex:i];
break;
}
}
// [_tzImagePickerVc.selectedAssets removeObject:_assetsTemp[_currentIndex]];
[self.photos removeObject:_photosTemp[_currentIndex]];
}
break;
}
}
}
model.isSelected = !selectButton.isSelected;
[self refreshNaviBarAndBottomBarState];
if (model.isSelected) {
[UIView showOscillatoryAnimationWithLayer:selectButton.imageView.layer type:TZOscillatoryAnimationToBigger];
}
[UIView showOscillatoryAnimationWithLayer:_numberImageView.layer type:TZOscillatoryAnimationToSmaller];
}
- (void)backButtonClick {
if (self.navigationController.childViewControllers.count < 2) {
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
return;
}
[self.navigationController popViewControllerAnimated:YES];
if (self.backButtonClickBlock) {
self.backButtonClickBlock(_isSelectOriginalPhoto);
}
}
- (void)doneButtonClick {
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
// 如果图片正在从iCloud同步中,提醒用户
if (_progress > 0 && _progress < 1 && (_selectButton.isSelected || !_tzImagePickerVc.selectedModels.count )) {
_alertView = [_tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Synchronizing photos from iCloud"]];
return;
}
// 如果没有选中过照片 点击确定时选中当前预览的照片
if (_tzImagePickerVc.selectedModels.count == 0 && _tzImagePickerVc.minImagesCount <= 0) {
TZAssetModel *model = _models[_currentIndex];
[_tzImagePickerVc addSelectedModel:model];
}
if (_tzImagePickerVc.allowCrop) { // 裁剪状态
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:_currentIndex inSection:0];
TZPhotoPreviewCell *cell = (TZPhotoPreviewCell *)[_collectionView cellForItemAtIndexPath:indexPath];
UIImage *cropedImage = [TZImageCropManager cropImageView:cell.previewView.imageView toRect:_tzImagePickerVc.cropRect zoomScale:cell.previewView.scrollView.zoomScale containerView:self.view];
if (_tzImagePickerVc.needCircleCrop) {
cropedImage = [TZImageCropManager circularClipImage:cropedImage];
}
if (self.doneButtonClickBlockCropMode) {
TZAssetModel *model = _models[_currentIndex];
self.doneButtonClickBlockCropMode(cropedImage,model.asset);
}
} else if (self.doneButtonClickBlock) { // 非裁剪状态
self.doneButtonClickBlock(_isSelectOriginalPhoto);
}
if (self.doneButtonClickBlockWithPreviewType) {
self.doneButtonClickBlockWithPreviewType(self.photos,_tzImagePickerVc.selectedAssets,self.isSelectOriginalPhoto);
}
}
- (void)originalPhotoButtonClick {
_originalPhotoButton.selected = !_originalPhotoButton.isSelected;
_isSelectOriginalPhoto = _originalPhotoButton.isSelected;
_originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;
if (_isSelectOriginalPhoto) {
[self showPhotoBytes];
if (!_selectButton.isSelected) {
// 如果当前已选择照片张数 < 最大可选张数 && 最大可选张数大于1,就选中该张图
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (_tzImagePickerVc.selectedModels.count < _tzImagePickerVc.maxImagesCount && _tzImagePickerVc.showSelectBtn) {
[self select:_selectButton];
}
}
}
}
- (void)didTapPreviewCell {
self.isHideNaviBar = !self.isHideNaviBar;
_naviBar.hidden = self.isHideNaviBar;
_toolBar.hidden = self.isHideNaviBar;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offSetWidth = scrollView.contentOffset.x;
offSetWidth = offSetWidth + ((self.view.tz_width + 20) * 0.5);
NSInteger currentIndex = offSetWidth / (self.view.tz_width + 20);
if (currentIndex < _models.count && _currentIndex != currentIndex) {
_currentIndex = currentIndex;
[self refreshNaviBarAndBottomBarState];
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"photoPreviewCollectionViewDidScroll" object:nil];
}
#pragma mark - UICollectionViewDataSource && Delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _models.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
TZAssetModel *model = _models[indexPath.row];
TZAssetPreviewCell *cell;
__weak typeof(self) weakSelf = self;
if (_tzImagePickerVc.allowPickingMultipleVideo && model.type == TZAssetModelMediaTypeVideo) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZVideoPreviewCell" forIndexPath:indexPath];
} else if (_tzImagePickerVc.allowPickingMultipleVideo && model.type == TZAssetModelMediaTypePhotoGif && _tzImagePickerVc.allowPickingGif) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZGifPreviewCell" forIndexPath:indexPath];
} else {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZPhotoPreviewCell" forIndexPath:indexPath];
TZPhotoPreviewCell *photoPreviewCell = (TZPhotoPreviewCell *)cell;
photoPreviewCell.cropRect = _tzImagePickerVc.cropRect;
photoPreviewCell.allowCrop = _tzImagePickerVc.allowCrop;
__weak typeof(_tzImagePickerVc) weakTzImagePickerVc = _tzImagePickerVc;
__weak typeof(_collectionView) weakCollectionView = _collectionView;
__weak typeof(photoPreviewCell) weakCell = photoPreviewCell;
[photoPreviewCell setImageProgressUpdateBlock:^(double progress) {
__strong typeof(weakSelf) strongSelf = weakSelf;
__strong typeof(weakTzImagePickerVc) strongTzImagePickerVc = weakTzImagePickerVc;
__strong typeof(weakCollectionView) strongCollectionView = weakCollectionView;
__strong typeof(weakCell) strongCell = weakCell;
strongSelf.progress = progress;
if (progress >= 1) {
if (strongSelf.isSelectOriginalPhoto) [strongSelf showPhotoBytes];
if (strongSelf.alertView && [strongCollectionView.visibleCells containsObject:strongCell]) {
[strongTzImagePickerVc hideAlertView:strongSelf.alertView];
strongSelf.alertView = nil;
[strongSelf doneButtonClick];
}
}
}];
}
cell.model = model;
[cell setSingleTapGestureBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf didTapPreviewCell];
}];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if ([cell isKindOfClass:[TZPhotoPreviewCell class]]) {
[(TZPhotoPreviewCell *)cell recoverSubviews];
}
}
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if ([cell isKindOfClass:[TZPhotoPreviewCell class]]) {
[(TZPhotoPreviewCell *)cell recoverSubviews];
} else if ([cell isKindOfClass:[TZVideoPreviewCell class]]) {
[(TZVideoPreviewCell *)cell pausePlayerAndShowNaviBar];
}
}
#pragma mark - Private Method
- (void)dealloc {
// NSLog(@"%@ dealloc",NSStringFromClass(self.class));
}
- (void)refreshNaviBarAndBottomBarState {
TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController;
TZAssetModel *model = _models[_currentIndex];
_selectButton.selected = model.isSelected;
[self refreshSelectButtonImageViewContentMode];
if (_selectButton.isSelected && _tzImagePickerVc.showSelectedIndex) {
NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset];
NSString *index = [NSString stringWithFormat:@"%zd", [_tzImagePickerVc.selectedAssetIds indexOfObject:assetId] + 1];
_indexLabel.text = index;
_indexLabel.hidden = NO;
} else {
_indexLabel.hidden = YES;
}
_numberLabel.text = [NSString stringWithFormat:@"%zd",_tzImagePickerVc.selectedModels.count];
_numberImageView.hidden = (_tzImagePickerVc.selectedModels.count <= 0 || _isHideNaviBar || _isCropImage);
_numberLabel.hidden = (_tzImagePickerVc.selectedModels.count <= 0 || _isHideNaviBar || _isCropImage);
_originalPhotoButton.selected = _isSelectOriginalPhoto;
_originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;
if (_isSelectOriginalPhoto) [self showPhotoBytes];
// If is previewing video, hide original photo button
// 如果正在预览的是视频,隐藏原图按钮
if (!_isHideNaviBar) {
if (model.type == TZAssetModelMediaTypeVideo) {
_originalPhotoButton.hidden = YES;
_originalPhotoLabel.hidden = YES;
} else {
_originalPhotoButton.hidden = NO;
if (_isSelectOriginalPhoto) _originalPhotoLabel.hidden = NO;
}
}
_doneButton.hidden = NO;
_selectButton.hidden = !_tzImagePickerVc.showSelectBtn;
// 让宽度/高度小于 最小可选照片尺寸 的图片不能选中
if (![[TZImageManager manager] isPhotoSelectableWithAsset:model.asset]) {
_numberLabel.hidden = YES;
_numberImageView.hidden = YES;
_selectButton.hidden = YES;
_originalPhotoButton.hidden = YES;
_originalPhotoLabel.hidden = YES;
_doneButton.hidden = YES;
}
}
- (void)refreshSelectButtonImageViewContentMode {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self->_selectButton.imageView.image.size.width <= 27) {
self->_selectButton.imageView.contentMode = UIViewContentModeCenter;
} else {
self->_selectButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
}
});
}
- (void)showPhotoBytes {
[[TZImageManager manager] getPhotosBytesWithArray:@[_models[_currentIndex]] completion:^(NSString *totalBytes) {
self->_originalPhotoLabel.text = [NSString stringWithFormat:@"(%@)",totalBytes];
}];
}
@end
//
// TZProgressView.h
// TZImagePickerController
//
// Created by ttouch on 2016/12/6.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TZProgressView : UIView
@property (nonatomic, assign) double progress;
@end
//
// TZProgressView.m
// TZImagePickerController
//
// Created by ttouch on 2016/12/6.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZProgressView.h"
@interface TZProgressView ()
@property (nonatomic, strong) CAShapeLayer *progressLayer;
@end
@implementation TZProgressView
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor];
_progressLayer = [CAShapeLayer layer];
_progressLayer.fillColor = [[UIColor clearColor] CGColor];
_progressLayer.strokeColor = [[UIColor whiteColor] CGColor];
_progressLayer.opacity = 1;
_progressLayer.lineCap = kCALineCapRound;
_progressLayer.lineWidth = 5;
[_progressLayer setShadowColor:[UIColor blackColor].CGColor];
[_progressLayer setShadowOffset:CGSizeMake(1, 1)];
[_progressLayer setShadowOpacity:0.5];
[_progressLayer setShadowRadius:2];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2);
CGFloat radius = rect.size.width / 2;
CGFloat startA = - M_PI_2;
CGFloat endA = - M_PI_2 + M_PI * 2 * _progress;
_progressLayer.frame = self.bounds;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
_progressLayer.path =[path CGPath];
[_progressLayer removeFromSuperlayer];
[self.layer addSublayer:_progressLayer];
}
- (void)setProgress:(double)progress {
_progress = progress;
[self setNeedsDisplay];
}
@end
//
// TZVideoPlayerController.h
// TZImagePickerController
//
// Created by 谭真 on 16/1/5.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAssetModel;
@interface TZVideoPlayerController : UIViewController
@property (nonatomic, strong) TZAssetModel *model;
@end
\ No newline at end of file
//
// TZVideoPlayerController.m
// TZImagePickerController
//
// Created by 谭真 on 16/1/5.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZVideoPlayerController.h"
#import <MediaPlayer/MediaPlayer.h>
#import "UIView+Layout.h"
#import "TZImageManager.h"
#import "TZAssetModel.h"
#import "TZImagePickerController.h"
#import "TZPhotoPreviewController.h"
@interface TZVideoPlayerController () {
AVPlayer *_player;
AVPlayerLayer *_playerLayer;
UIButton *_playButton;
UIImage *_cover;
UIView *_toolBar;
UIButton *_doneButton;
UIProgressView *_progress;
UIStatusBarStyle _originStatusBarStyle;
}
@property (assign, nonatomic) BOOL needShowStatusBar;
@end
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@implementation TZVideoPlayerController
- (void)viewDidLoad {
[super viewDidLoad];
self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden;
self.view.backgroundColor = [UIColor blackColor];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
self.navigationItem.title = tzImagePickerVc.previewBtnTitleStr;
}
[self configMoviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
[UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
}
- (void)configMoviePlayer {
[[TZImageManager manager] getPhotoWithAsset:_model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (!isDegraded && photo) {
self->_cover = photo;
self->_doneButton.enabled = YES;
}
}];
[[TZImageManager manager] getVideoWithAsset:_model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_player = [AVPlayer playerWithPlayerItem:playerItem];
self->_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self->_player];
self->_playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:self->_playerLayer];
[self addProgressObserver];
[self configPlayButton];
[self configBottomToolBar];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self->_player.currentItem];
});
}];
}
/// Show progress,do it next time / 给播放器添加进度更新,下次加上
- (void)addProgressObserver{
AVPlayerItem *playerItem = _player.currentItem;
UIProgressView *progress = _progress;
[_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds([playerItem duration]);
if (current) {
[progress setProgress:(current/total) animated:YES];
}
}];
}
- (void)configPlayButton {
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted];
[_playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_playButton];
}
- (void)configBottomToolBar {
_toolBar = [[UIView alloc] initWithFrame:CGRectZero];
CGFloat rgb = 34 / 255.0;
_toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7];
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
if (!_cover) {
_doneButton.enabled = NO;
}
[_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
[_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal];
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal];
} else {
[_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal];
[_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal];
}
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorDisabled forState:UIControlStateDisabled];
[_toolBar addSubview:_doneButton];
[self.view addSubview:_toolBar];
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat statusBarHeight = [TZCommonTools tz_statusBarHeight];
CGFloat statusBarAndNaviBarHeight = statusBarHeight + self.navigationController.navigationBar.tz_height;
_playerLayer.frame = self.view.bounds;
CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44;
_toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight);
_doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44);
_playButton.frame = CGRectMake(0, statusBarAndNaviBarHeight, self.view.tz_width, self.view.tz_height - statusBarAndNaviBarHeight - toolBarHeight);
}
#pragma mark - Click Event
- (void)playButtonClick {
CMTime currentTime = _player.currentItem.currentTime;
CMTime durationTime = _player.currentItem.duration;
if (_player.rate == 0.0f) {
if (currentTime.value == durationTime.value) [_player.currentItem seekToTime:CMTimeMake(0, 1)];
[_player play];
[self.navigationController setNavigationBarHidden:YES];
_toolBar.hidden = YES;
[_playButton setImage:nil forState:UIControlStateNormal];
if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES;
} else {
[self pausePlayerAndShowNaviBar];
}
}
- (void)doneButtonClick {
if (self.navigationController) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
if (imagePickerVc.autoDismiss) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
} else {
[self callDelegateMethod];
}
} else {
[self dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
}
}
- (void)callDelegateMethod {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingVideo:sourceAssets:)]) {
[imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingVideo:_cover sourceAssets:_model.asset];
}
if (imagePickerVc.didFinishPickingVideoHandle) {
imagePickerVc.didFinishPickingVideoHandle(_cover,_model.asset);
}
}
#pragma mark - Notification Method
- (void)pausePlayerAndShowNaviBar {
[_player pause];
_toolBar.hidden = NO;
[self.navigationController setNavigationBarHidden:NO];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
if (self.needShowStatusBar && iOS7Later) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma clang diagnostic pop
@end
//
// UIView+Layout.h
//
// Created by 谭真 on 15/2/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TZOscillatoryAnimationToBigger,
TZOscillatoryAnimationToSmaller,
} TZOscillatoryAnimationType;
@interface UIView (Layout)
@property (nonatomic) CGFloat tz_left; ///< Shortcut for frame.origin.x.
@property (nonatomic) CGFloat tz_top; ///< Shortcut for frame.origin.y
@property (nonatomic) CGFloat tz_right; ///< Shortcut for frame.origin.x + frame.size.width
@property (nonatomic) CGFloat tz_bottom; ///< Shortcut for frame.origin.y + frame.size.height
@property (nonatomic) CGFloat tz_width; ///< Shortcut for frame.size.width.
@property (nonatomic) CGFloat tz_height; ///< Shortcut for frame.size.height.
@property (nonatomic) CGFloat tz_centerX; ///< Shortcut for center.x
@property (nonatomic) CGFloat tz_centerY; ///< Shortcut for center.y
@property (nonatomic) CGPoint tz_origin; ///< Shortcut for frame.origin.
@property (nonatomic) CGSize tz_size; ///< Shortcut for frame.size.
+ (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(TZOscillatoryAnimationType)type;
@end
//
// UIView+Layout.m
//
// Created by 谭真 on 15/2/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "UIView+Layout.h"
@implementation UIView (Layout)
- (CGFloat)tz_left {
return self.frame.origin.x;
}
- (void)setTz_left:(CGFloat)x {
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)tz_top {
return self.frame.origin.y;
}
- (void)setTz_top:(CGFloat)y {
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)tz_right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setTz_right:(CGFloat)right {
CGRect frame = self.frame;
frame.origin.x = right - frame.size.width;
self.frame = frame;
}
- (CGFloat)tz_bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setTz_bottom:(CGFloat)bottom {
CGRect frame = self.frame;
frame.origin.y = bottom - frame.size.height;
self.frame = frame;
}
- (CGFloat)tz_width {
return self.frame.size.width;
}
- (void)setTz_width:(CGFloat)width {
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)tz_height {
return self.frame.size.height;
}
- (void)setTz_height:(CGFloat)height {
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)tz_centerX {
return self.center.x;
}
- (void)setTz_centerX:(CGFloat)centerX {
self.center = CGPointMake(centerX, self.center.y);
}
- (CGFloat)tz_centerY {
return self.center.y;
}
- (void)setTz_centerY:(CGFloat)centerY {
self.center = CGPointMake(self.center.x, centerY);
}
- (CGPoint)tz_origin {
return self.frame.origin;
}
- (void)setTz_origin:(CGPoint)origin {
CGRect frame = self.frame;
frame.origin = origin;
self.frame = frame;
}
- (CGSize)tz_size {
return self.frame.size;
}
- (void)setTz_size:(CGSize)size {
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
+ (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(TZOscillatoryAnimationType)type{
NSNumber *animationScale1 = type == TZOscillatoryAnimationToBigger ? @(1.15) : @(0.5);
NSNumber *animationScale2 = type == TZOscillatoryAnimationToBigger ? @(0.92) : @(1.15);
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
[layer setValue:animationScale1 forKeyPath:@"transform.scale"];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
[layer setValue:animationScale2 forKeyPath:@"transform.scale"];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
[layer setValue:@(1.0) forKeyPath:@"transform.scale"];
} completion:nil];
}];
}];
}
@end
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!