Commit e69396e3194e1955a4342113964d82795bbf1ec9
1 parent
506347e9
提交创建
Showing
71 changed files
with
74 additions
and
4767 deletions
Too many changes to show.
To preserve performance only 71 of 109 files are displayed.
.gitignore
0 → 100644
| 1 | +# OS X | |
| 2 | +.DS_Store | |
| 3 | + | |
| 4 | +# Xcode | |
| 5 | +build/ | |
| 6 | +Products/ | |
| 7 | +*.pbxuser | |
| 8 | +!default.pbxuser | |
| 9 | +*.mode1v3 | |
| 10 | +!default.mode1v3 | |
| 11 | +*.mode2v3 | |
| 12 | +!default.mode2v3 | |
| 13 | +*.perspectivev3 | |
| 14 | +!default.perspectivev3 | |
| 15 | +xcuserdata/ | |
| 16 | +*.xccheckout | |
| 17 | +profile | |
| 18 | +*.moved-aside | |
| 19 | +DerivedData | |
| 20 | +*.hmap | |
| 21 | +*.ipa | |
| 22 | + | |
| 23 | +# Bundler | |
| 24 | +.bundle | |
| 25 | + | |
| 26 | +# Add this line if you want to avoid checking in source code from Carthage dependencies. | |
| 27 | +# Carthage/Checkouts | |
| 28 | + | |
| 29 | +Carthage/Build | |
| 30 | + | |
| 31 | +# We recommend against adding the Pods directory to your .gitignore. However | |
| 32 | +# you should judge for yourself, the pros and cons are mentioned at: | |
| 33 | +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control | |
| 34 | +# | |
| 35 | +# Note: if you ignore the Pods directory, make sure to uncomment | |
| 36 | +# `pod install` in .travis.yml | |
| 37 | +# | |
| 38 | +# Pods/ | ... | ... |
CNLiveImagePickerController.podspec
| 1 | 1 | # |
| 2 | -# Be sure to run `pod spec lint CNLiveImagePickerController.podspec' to ensure this is a | |
| 3 | -# valid spec and to remove all comments including this before submitting the spec. | |
| 2 | +# Be sure to run `pod lib lint CNLiveImagePickerController.podspec' to ensure this is a | |
| 3 | +# valid spec before submitting. | |
| 4 | 4 | # |
| 5 | -# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html | |
| 6 | -# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ | |
| 5 | +# Any lines starting with a # are optional, but their use is encouraged | |
| 6 | +# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html | |
| 7 | 7 | # |
| 8 | 8 | |
| 9 | 9 | Pod::Spec.new do |s| |
| 10 | - | |
| 11 | -# | |
| 12 | - # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 13 | - # | |
| 14 | - # These will help people to find your library, and whilst it | |
| 15 | - # can feel like a chore to fill in it's definitely to your advantage. The | |
| 16 | - # summary should be tweet-length, and the description more in depth. | |
| 17 | - # | |
| 18 | - | |
| 19 | - s.name = "CNLiveImagePickerController" # 项目名称 | |
| 20 | - s.version = "0.0.2" # 版本号 与 你仓库的 标签号 对应 | |
| 21 | - s.summary = "相册选择基于TZImagePickerController" # 项目简介 | |
| 22 | - | |
| 23 | - # This description is used to generate tags and improve search results. | |
| 24 | - # * Think: What does it do? Why did you write it? What is the focus? | |
| 25 | - # * Try to keep it short, snappy and to the point. | |
| 26 | - # * Write the description between the DESC delimiters below. | |
| 27 | - # * Finally, don't worry about the indent, CocoaPods strips it! | |
| 28 | - s.description = <<-DESC | |
| 29 | - 1.相册选择图片,视频 | |
| 30 | - 2.更改了相册适配问题 | |
| 31 | - DESC | |
| 32 | - | |
| 33 | - s.homepage = "http://bj.gitlab.cnlive.com/ios-team/CNLiveImagePickerController" | |
| 34 | - | |
| 35 | - # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" | |
| 36 | - | |
| 37 | - | |
| 38 | - # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 39 | - # | |
| 40 | - # Licensing your code is important. See http://choosealicense.com for more info. | |
| 41 | - # CocoaPods will detect a license file if there is a named LICENSE* | |
| 42 | - # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. | |
| 43 | - # | |
| 44 | - | |
| 45 | - s.license = "MIT" # 开源证书 | |
| 46 | - # s.license = { :type => "MIT", :file => "FILE_LICENSE" } | |
| 47 | - | |
| 48 | - | |
| 49 | - # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 50 | - # | |
| 51 | - # Specify the authors of the library, with email addresses. Email addresses | |
| 52 | - # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also | |
| 53 | - # accepts just a name if you'd rather not provide an email address. | |
| 54 | - # | |
| 55 | - # Specify a social_media_url where others can refer to, for example a twitter | |
| 56 | - # profile URL. | |
| 57 | - # | |
| 58 | - | |
| 59 | - s.author = { "梁星国" => "liangxingguo@cnlive.com" } | |
| 60 | - # Or just: s.author = "梁星国" | |
| 61 | - # s.authors = { "梁星国" => "jyyjkt@qq.com" } | |
| 62 | - # s.social_media_url = "http://twitter.com/梁星国" | |
| 63 | - | |
| 64 | - # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 65 | - # | |
| 66 | - # If this Pod runs only on iOS or OS X, then specify the platform and | |
| 67 | - # the deployment target. You can optionally include the target after the platform. | |
| 68 | - # | |
| 69 | - | |
| 70 | - # s.platform = :ios | |
| 71 | - s.platform = :ios, "9.0" | |
| 72 | - | |
| 73 | - # When using multiple platforms | |
| 74 | - # s.ios.deployment_target = "5.0" | |
| 75 | - # s.osx.deployment_target = "10.7" | |
| 76 | - # s.watchos.deployment_target = "2.0" | |
| 77 | - # s.tvos.deployment_target = "9.0" | |
| 78 | - | |
| 79 | - | |
| 80 | - # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 81 | - # | |
| 82 | - # Specify the location from where the source should be retrieved. | |
| 83 | - # Supports git, hg, bzr, svn and HTTP. | |
| 84 | - # | |
| 85 | - | |
| 86 | - s.source = { :git => "http://bj.gitlab.cnlive.com/ios-team/CNLiveImagePickerController.git", :tag => "#{s.version}" } #你的仓库地址 | |
| 87 | - | |
| 88 | - | |
| 89 | - # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 90 | - # | |
| 91 | - # CocoaPods is smart about how it includes source code. For source files | |
| 92 | - # giving a folder will include any swift, h, m, mm, c & cpp files. | |
| 93 | - # For header files it will include any header in the folder. | |
| 94 | - # Not including the public_header_files will make all headers public. | |
| 95 | - # | |
| 96 | - | |
| 97 | - s.source_files = "CNLiveImagePickerController/*" | |
| 98 | - s.exclude_files = "Classes/Exclude" | |
| 99 | - | |
| 100 | - # s.public_header_files = "Classes/**/*.h" | |
| 101 | - | |
| 102 | - | |
| 103 | - # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 104 | - # | |
| 105 | - # A list of resources included with the Pod. These are copied into the | |
| 106 | - # target bundle with a build phase script. Anything else will be cleaned. | |
| 107 | - # You can preserve files from being cleaned, please don't preserve | |
| 108 | - # non-essential files like tests, examples and documentation. | |
| 109 | - # | |
| 110 | - | |
| 111 | - # s.resource = "icon.png" | |
| 112 | - # s.resources = "Resources/*.png" | |
| 113 | - | |
| 114 | - # s.preserve_paths = "FilesToSave", "MoreFilesToSave" | |
| 115 | - | |
| 116 | - | |
| 117 | - # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 118 | - # | |
| 119 | - # Link your library with frameworks, or libraries. Libraries do not include | |
| 120 | - # the lib prefix of their name. | |
| 121 | - # | |
| 122 | - | |
| 123 | - # s.framework = "SomeFramework" | |
| 124 | - s.frameworks = "UIKit", "Foundation" #支持的框架 | |
| 125 | - | |
| 126 | - # s.library = "iconv" | |
| 127 | - # s.libraries = "iconv", "xml2" | |
| 128 | - | |
| 129 | - | |
| 130 | - # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | |
| 131 | - # | |
| 132 | - # If your library depends on compiler flags you can set them in the xcconfig hash | |
| 133 | - # where they will only apply to your library. If you depend on other Podspecs | |
| 134 | - # you can include multiple dependencies to ensure it works. | |
| 135 | - | |
| 136 | - s.requires_arc = true | |
| 137 | - | |
| 138 | - # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } | |
| 139 | - # s.dependency "JSONKit", "~> 1.4" | |
| 140 | - | |
| 10 | + s.name = 'CNLiveImagePickerController' | |
| 11 | + s.version = '0.1.0' | |
| 12 | + s.summary = 'A short description of CNLiveImagePickerController.' | |
| 13 | + | |
| 14 | +# This description is used to generate tags and improve search results. | |
| 15 | +# * Think: What does it do? Why did you write it? What is the focus? | |
| 16 | +# * Try to keep it short, snappy and to the point. | |
| 17 | +# * Write the description between the DESC delimiters below. | |
| 18 | +# * Finally, don't worry about the indent, CocoaPods strips it! | |
| 19 | + | |
| 20 | + s.description = <<-DESC | |
| 21 | +TODO: Add long description of the pod here. | |
| 22 | + DESC | |
| 23 | + | |
| 24 | + s.homepage = 'https://github.com/jyyjkt/CNLiveImagePickerController' | |
| 25 | + # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' | |
| 26 | + s.license = { :type => 'MIT', :file => 'LICENSE' } | |
| 27 | + s.author = { 'jyyjkt' => 'jyyjkt@qq.com' } | |
| 28 | + s.source = { :git => 'https://github.com/jyyjkt/CNLiveImagePickerController.git', :tag => s.version.to_s } | |
| 29 | + # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>' | |
| 30 | + | |
| 31 | + s.ios.deployment_target = '8.0' | |
| 32 | + | |
| 33 | + s.source_files = 'CNLiveImagePickerController/Classes/**/*' | |
| 34 | + | |
| 35 | + # s.resource_bundles = { | |
| 36 | + # 'CNLiveImagePickerController' => ['CNLiveImagePickerController/Assets/*.png'] | |
| 37 | + # } | |
| 38 | + | |
| 39 | + # s.public_header_files = 'Pod/Classes/**/*.h' | |
| 40 | + # s.frameworks = 'UIKit', 'MapKit' | |
| 41 | + # s.dependency 'AFNetworking', '~> 2.3' | |
| 141 | 42 | end | ... | ... |
CNLiveImagePickerController/Assets/.gitkeep
0 → 100644
CNLiveImagePickerController/CNEditVideoController.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// ZLEditVideoController.h | |
| 3 | -// ZLPhotoBrowser | |
| 4 | -// | |
| 5 | -// Created by long on 2017/9/15. | |
| 6 | -// Copyright © 2017年 long. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <UIKit/UIKit.h> | |
| 10 | - | |
| 11 | -@class TZAssetModel; | |
| 12 | - | |
| 13 | -@interface CNEditVideoController : UIViewController | |
| 14 | - | |
| 15 | -@property (nonatomic, strong) TZAssetModel *model; | |
| 16 | - | |
| 17 | -@property (nonatomic, strong) NSMutableArray *models; ///< All photo models / 所有图片模型数组 | |
| 18 | -@property (nonatomic, strong) NSMutableArray *photos; ///< All photos / 所有图片数组 | |
| 19 | -@property (nonatomic, assign) NSInteger currentIndex; | |
| 20 | -@property (nonatomic, assign) BOOL isCropImage; | |
| 21 | - | |
| 22 | -/// Return the new selected photos / 返回最新的选中图片数组 | |
| 23 | -@property (nonatomic, copy) void (^backButtonEditClickBlock)(BOOL isSelectOriginalPhoto); | |
| 24 | -@property (nonatomic, copy) void (^doneButtonEditClickBlock)(BOOL isSelectOriginalPhoto); | |
| 25 | - | |
| 26 | - | |
| 27 | -@end |
CNLiveImagePickerController/CNEditVideoController.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// ZLEditVideoController.m | |
| 3 | -// ZLPhotoBrowser | |
| 4 | -// | |
| 5 | -// Created by long on 2017/9/15. | |
| 6 | -// Copyright © 2017年 long. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "CNEditVideoController.h" | |
| 10 | -#import <AVFoundation/AVFoundation.h> | |
| 11 | -#import "TZAssetModel.h" | |
| 12 | -#import <objc/runtime.h> | |
| 13 | -#import <AssetsLibrary/AssetsLibrary.h> | |
| 14 | -#import "TZImagePickerController.h" | |
| 15 | -#import "TZPhotoPreviewCell.h" | |
| 16 | -#import "TZImageCropManager.h" | |
| 17 | - | |
| 18 | -#define kItemWidth kItemHeight * 2/3 | |
| 19 | -#define kItemHeight 50 | |
| 20 | -#define kAPPName @"网++" | |
| 21 | - | |
| 22 | -#pragma mark - 显示帧数的CNEditFrameView | |
| 23 | - | |
| 24 | -@interface CNEditVideoCell : UICollectionViewCell | |
| 25 | - | |
| 26 | -@property (nonatomic, strong) UIImageView *imageView; | |
| 27 | - | |
| 28 | -@end | |
| 29 | - | |
| 30 | -@implementation CNEditVideoCell | |
| 31 | - | |
| 32 | -- (UIImageView *)imageView | |
| 33 | -{ | |
| 34 | - if (!_imageView) { | |
| 35 | - _imageView = [[UIImageView alloc] init]; | |
| 36 | - _imageView.frame = self.bounds; | |
| 37 | - _imageView.contentMode = UIViewContentModeScaleAspectFill; | |
| 38 | - _imageView.clipsToBounds = YES; | |
| 39 | - [self.contentView addSubview:_imageView]; | |
| 40 | - } | |
| 41 | - return _imageView; | |
| 42 | -} | |
| 43 | - | |
| 44 | -@end | |
| 45 | - | |
| 46 | -#pragma mark - 协议编辑帧数协议CNEditFrameViewDelegate | |
| 47 | -@protocol CNEditFrameViewDelegate <NSObject> | |
| 48 | - | |
| 49 | -- (void)editViewValidRectChanged;//有效范围更变 | |
| 50 | - | |
| 51 | -- (void)editViewValidRectEndChanged;//有效范围更变结束 | |
| 52 | - | |
| 53 | -@end | |
| 54 | - | |
| 55 | -#pragma mark - 编辑时视图CNEditFrameView | |
| 56 | -@interface CNEditFrameView : UIView | |
| 57 | -{ | |
| 58 | - UIImageView *_leftView;//左视图 | |
| 59 | - UIImageView *_rightView;//有视图 | |
| 60 | -} | |
| 61 | - | |
| 62 | -@property (nonatomic, assign) CGRect validRect; | |
| 63 | -@property (nonatomic, weak) id<CNEditFrameViewDelegate> delegate; | |
| 64 | - | |
| 65 | -@end | |
| 66 | - | |
| 67 | -@implementation CNEditFrameView | |
| 68 | - | |
| 69 | -- (instancetype)init | |
| 70 | -{ | |
| 71 | - self = [super init]; | |
| 72 | - if (self) { | |
| 73 | - [self setupUI]; | |
| 74 | - } | |
| 75 | - return self; | |
| 76 | -} | |
| 77 | - | |
| 78 | -- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event | |
| 79 | -{ | |
| 80 | - //扩大下有效范围 | |
| 81 | - CGRect left = _leftView.frame; | |
| 82 | - left.origin.x -= kItemWidth/2; | |
| 83 | - left.size.width += kItemWidth/2; | |
| 84 | - CGRect right = _rightView.frame; | |
| 85 | - right.size.width += kItemWidth/2; | |
| 86 | - | |
| 87 | - if (CGRectContainsPoint(left, point)) { | |
| 88 | - return _leftView; | |
| 89 | - } | |
| 90 | - if (CGRectContainsPoint(right, point)) { | |
| 91 | - return _rightView; | |
| 92 | - } | |
| 93 | - return nil; | |
| 94 | -} | |
| 95 | - | |
| 96 | -- (void)setupUI | |
| 97 | -{ | |
| 98 | - self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5]; | |
| 99 | - self.layer.borderWidth = 2; | |
| 100 | - self.layer.borderColor = [UIColor clearColor].CGColor; | |
| 101 | - | |
| 102 | - _leftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"video_edit_left"]]; | |
| 103 | - _leftView.userInteractionEnabled = YES; | |
| 104 | - _leftView.tag = 0; | |
| 105 | - UIPanGestureRecognizer *lg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; | |
| 106 | - [_leftView addGestureRecognizer:lg]; | |
| 107 | - [self addSubview:_leftView]; | |
| 108 | - | |
| 109 | - _rightView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"video_edit_right"]]; | |
| 110 | - _rightView.userInteractionEnabled = YES; | |
| 111 | - _rightView.tag = 1; | |
| 112 | - UIPanGestureRecognizer *rg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; | |
| 113 | - [_rightView addGestureRecognizer:rg]; | |
| 114 | - [self addSubview:_rightView]; | |
| 115 | -} | |
| 116 | - | |
| 117 | -- (void)panAction:(UIGestureRecognizer *)pan //滑动收拾实现 | |
| 118 | -{ | |
| 119 | - self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:.4].CGColor; | |
| 120 | - CGPoint point = [pan locationInView:self]; | |
| 121 | - | |
| 122 | - CGRect rct = self.validRect; | |
| 123 | - | |
| 124 | - const CGFloat W = self.frame.size.width; | |
| 125 | - CGFloat minX = 0; | |
| 126 | - CGFloat maxX = W; | |
| 127 | - | |
| 128 | - switch (pan.view.tag) { | |
| 129 | - case 0: { | |
| 130 | - //left视图响应 | |
| 131 | - maxX = rct.origin.x + rct.size.width - kItemWidth; | |
| 132 | - | |
| 133 | - point.x = MAX(minX, MIN(point.x, maxX)); | |
| 134 | - point.y = 0; | |
| 135 | - | |
| 136 | - rct.size.width -= (point.x - rct.origin.x); | |
| 137 | - rct.origin.x = point.x; | |
| 138 | - } | |
| 139 | - break; | |
| 140 | - | |
| 141 | - case 1: | |
| 142 | - { | |
| 143 | - //right视图响应 | |
| 144 | - minX = rct.origin.x + kItemWidth/2; | |
| 145 | - maxX = W - kItemWidth/2; | |
| 146 | - | |
| 147 | - point.x = MAX(minX, MIN(point.x, maxX)); | |
| 148 | - point.y = 0; | |
| 149 | - | |
| 150 | - rct.size.width = (point.x - rct.origin.x + kItemWidth/2); | |
| 151 | - } | |
| 152 | - break; | |
| 153 | - } | |
| 154 | - | |
| 155 | - switch (pan.state) { | |
| 156 | - case UIGestureRecognizerStateBegan: | |
| 157 | - case UIGestureRecognizerStateChanged: | |
| 158 | - if (self.delegate && [self.delegate respondsToSelector:@selector(editViewValidRectChanged)]) { | |
| 159 | - [self.delegate editViewValidRectChanged];//滑动响应实现 | |
| 160 | - } | |
| 161 | - break; | |
| 162 | - | |
| 163 | - case UIGestureRecognizerStateEnded: | |
| 164 | - case UIGestureRecognizerStateCancelled: | |
| 165 | - self.layer.borderColor = [UIColor clearColor].CGColor; | |
| 166 | - if (self.delegate && [self.delegate respondsToSelector:@selector(editViewValidRectEndChanged)]) { | |
| 167 | - [self.delegate editViewValidRectEndChanged];//滑动结束响应实现 | |
| 168 | - } | |
| 169 | - break; | |
| 170 | - | |
| 171 | - default: | |
| 172 | - break; | |
| 173 | - } | |
| 174 | - | |
| 175 | - | |
| 176 | - self.validRect = rct; | |
| 177 | -} | |
| 178 | - | |
| 179 | -- (void)setValidRect:(CGRect)validRect//根据范围 | |
| 180 | -{ | |
| 181 | - _validRect = validRect; | |
| 182 | - _leftView.frame = CGRectMake(validRect.origin.x - kItemWidth/4, 0, kItemWidth/2, kItemHeight); | |
| 183 | - _rightView.frame = CGRectMake(validRect.origin.x+validRect.size.width-kItemWidth/4, 0, kItemWidth/2, kItemHeight); | |
| 184 | - | |
| 185 | - [self setNeedsDisplay]; | |
| 186 | -} | |
| 187 | - | |
| 188 | -- (void)drawRect:(CGRect)rect | |
| 189 | -{ | |
| 190 | - CGContextRef context = UIGraphicsGetCurrentContext(); | |
| 191 | - | |
| 192 | - CGContextClearRect(context, self.validRect); | |
| 193 | - | |
| 194 | - CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); | |
| 195 | - CGContextSetLineWidth(context, 4.0); | |
| 196 | - | |
| 197 | - CGPoint topPoints[2]; | |
| 198 | - topPoints[0] = CGPointMake(self.validRect.origin.x, 0); | |
| 199 | - topPoints[1] = CGPointMake(self.validRect.origin.x+self.validRect.size.width, 0); | |
| 200 | - | |
| 201 | - CGPoint bottomPoints[2]; | |
| 202 | - bottomPoints[0] = CGPointMake(self.validRect.origin.x, kItemHeight); | |
| 203 | - bottomPoints[1] = CGPointMake(self.validRect.origin.x+self.validRect.size.width, kItemHeight); | |
| 204 | - | |
| 205 | - CGContextAddLines(context, topPoints, 2); | |
| 206 | - CGContextAddLines(context, bottomPoints, 2); | |
| 207 | - | |
| 208 | - CGContextDrawPath(context, kCGPathStroke); | |
| 209 | -} | |
| 210 | - | |
| 211 | -@end | |
| 212 | - | |
| 213 | - | |
| 214 | -#pragma mark - 编辑视频控制器CNEditVideoController | |
| 215 | -@interface CNEditVideoController () <UIScrollViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate, CNEditFrameViewDelegate> | |
| 216 | -{ | |
| 217 | - UIView *_bottomView; | |
| 218 | - UIButton *_cancelBtn; | |
| 219 | - UIButton *_doneBtn; | |
| 220 | - | |
| 221 | - NSTimer *_timer; | |
| 222 | - | |
| 223 | - //下方collectionview偏移量 | |
| 224 | - CGFloat _offsetX; | |
| 225 | - BOOL _orientationChanged; | |
| 226 | - | |
| 227 | - UIView *_indicatorLine; | |
| 228 | - | |
| 229 | - AVAsset *_avAsset; | |
| 230 | - | |
| 231 | - NSTimeInterval _interval; | |
| 232 | - | |
| 233 | - NSInteger _measureCount; | |
| 234 | - NSOperationQueue *_queue; | |
| 235 | - NSMutableDictionary<NSString *, UIImage *> *_imageCache; | |
| 236 | - NSMutableDictionary<NSString *, NSBlockOperation *> *_opCache; | |
| 237 | -} | |
| 238 | - | |
| 239 | -@property (nonatomic, strong) AVPlayerLayer *playerLayer; | |
| 240 | -@property (nonatomic, strong) UICollectionView *collectionView; | |
| 241 | -@property (nonatomic, strong) CNEditFrameView *editView; | |
| 242 | -@property (nonatomic, strong) AVAssetImageGenerator *generator; | |
| 243 | - | |
| 244 | -@end | |
| 245 | - | |
| 246 | -@implementation CNEditVideoController | |
| 247 | - | |
| 248 | -- (void)dealloc | |
| 249 | -{ | |
| 250 | - [_queue cancelAllOperations]; | |
| 251 | - [self stopTimer]; | |
| 252 | - [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 253 | -// NSLog(@"---- %s", __FUNCTION__); | |
| 254 | -} | |
| 255 | - | |
| 256 | -- (AVAssetImageGenerator *)generator | |
| 257 | -{ | |
| 258 | - if (!_generator) { | |
| 259 | - _generator = [[AVAssetImageGenerator alloc] initWithAsset:_avAsset]; | |
| 260 | - _generator.maximumSize = CGSizeMake(kItemWidth*4, kItemHeight*4); | |
| 261 | - _generator.appliesPreferredTrackTransform = YES; | |
| 262 | - _generator.requestedTimeToleranceBefore = kCMTimeZero; | |
| 263 | - _generator.requestedTimeToleranceAfter = kCMTimeZero; | |
| 264 | - _generator.apertureMode = AVAssetImageGeneratorApertureModeProductionAperture; | |
| 265 | - } | |
| 266 | - return _generator; | |
| 267 | -} | |
| 268 | - | |
| 269 | -- (void)viewDidLoad { | |
| 270 | - [super viewDidLoad]; | |
| 271 | - [self setupUI]; | |
| 272 | - [self analysisAssetImages]; | |
| 273 | - | |
| 274 | - _queue = [[NSOperationQueue alloc] init]; | |
| 275 | - _queue.maxConcurrentOperationCount = 3; | |
| 276 | - | |
| 277 | - _imageCache = [NSMutableDictionary dictionary]; | |
| 278 | - _opCache = [NSMutableDictionary dictionary]; | |
| 279 | - | |
| 280 | - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationChanged:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; | |
| 281 | - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appResignActive) name:UIApplicationWillResignActiveNotification object:nil]; | |
| 282 | - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; | |
| 283 | -} | |
| 284 | - | |
| 285 | -- (void)viewWillAppear:(BOOL)animated | |
| 286 | -{ | |
| 287 | - [super viewWillAppear:animated]; | |
| 288 | - [UIApplication sharedApplication].statusBarHidden = YES; | |
| 289 | - self.navigationController.navigationBar.hidden = YES; | |
| 290 | -} | |
| 291 | - | |
| 292 | -- (void)viewWillDisappear:(BOOL)animated | |
| 293 | -{ | |
| 294 | - [super viewWillDisappear:animated]; | |
| 295 | - self.navigationController.navigationBar.hidden = NO; | |
| 296 | -} | |
| 297 | - | |
| 298 | -- (void)viewDidLayoutSubviews | |
| 299 | -{ | |
| 300 | - [super viewDidLayoutSubviews]; | |
| 301 | - | |
| 302 | - UIEdgeInsets inset = UIEdgeInsetsZero; | |
| 303 | - if (@available(iOS 11, *)) { | |
| 304 | - inset = self.view.safeAreaInsets; | |
| 305 | - } | |
| 306 | - | |
| 307 | - self.playerLayer.frame = CGRectMake(15, inset.top>0?inset.top:30, [UIScreen mainScreen].bounds.size.width-30, [UIScreen mainScreen].bounds.size.height-160-inset.bottom); | |
| 308 | - | |
| 309 | - self.editView.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width-kItemWidth*10)/2, [UIScreen mainScreen].bounds.size.height-100-inset.bottom, kItemWidth*10, kItemHeight); | |
| 310 | - self.editView.validRect = self.editView.bounds; | |
| 311 | - self.collectionView.frame = CGRectMake(inset.left, [UIScreen mainScreen].bounds.size.height-100-inset.bottom, [UIScreen mainScreen].bounds.size.width-inset.left-inset.right, kItemHeight); | |
| 312 | - | |
| 313 | - CGFloat leftOffset = (([UIScreen mainScreen].bounds.size.width-kItemWidth*10)/2-inset.left); | |
| 314 | - CGFloat rightOffset = (([UIScreen mainScreen].bounds.size.width-kItemWidth*10)/2-inset.right); | |
| 315 | - [self.collectionView setContentInset:UIEdgeInsetsMake(0, leftOffset, 0, rightOffset)]; | |
| 316 | - [self.collectionView setContentOffset:CGPointMake(_offsetX-leftOffset, 0)]; | |
| 317 | - | |
| 318 | - CGFloat bottomViewH = 44; | |
| 319 | - CGFloat bottomBtnH = 30; | |
| 320 | - _bottomView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-bottomViewH-inset.bottom, [UIScreen mainScreen].bounds.size.width, kItemHeight); | |
| 321 | - _cancelBtn.frame = CGRectMake(10+inset.left, 7, 60, bottomBtnH); | |
| 322 | - _doneBtn.frame = CGRectMake([UIScreen mainScreen].bounds.size.width-70-inset.right, 7, 60, bottomBtnH); | |
| 323 | - | |
| 324 | - [self.playerLayer setBackgroundColor:[UIColor clearColor].CGColor]; | |
| 325 | -} | |
| 326 | - | |
| 327 | -#pragma mark - notifies | |
| 328 | -//设备旋转 | |
| 329 | -- (void)deviceOrientationChanged:(NSNotification *)notify | |
| 330 | -{ | |
| 331 | - _offsetX = self.collectionView.contentOffset.x + self.collectionView.contentInset.left; | |
| 332 | - _orientationChanged = YES; | |
| 333 | -} | |
| 334 | - | |
| 335 | -- (void)appResignActive | |
| 336 | -{ | |
| 337 | - [self stopTimer]; | |
| 338 | -} | |
| 339 | - | |
| 340 | -- (void)appBecomeActive | |
| 341 | -{ | |
| 342 | - [self startTimer]; | |
| 343 | -} | |
| 344 | - | |
| 345 | -- (void)setupUI | |
| 346 | -{ | |
| 347 | - //禁用返回手势 | |
| 348 | - if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { | |
| 349 | - self.navigationController.interactivePopGestureRecognizer.delegate = nil; | |
| 350 | - self.navigationController.interactivePopGestureRecognizer.enabled = NO; | |
| 351 | - } | |
| 352 | - | |
| 353 | - self.view.backgroundColor = [UIColor blackColor]; | |
| 354 | - | |
| 355 | - self.playerLayer = [[AVPlayerLayer alloc] init]; | |
| 356 | - [self.view.layer addSublayer:self.playerLayer]; | |
| 357 | - | |
| 358 | - UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; | |
| 359 | - layout.itemSize = CGSizeMake(kItemWidth, kItemHeight); | |
| 360 | - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; | |
| 361 | - layout.minimumInteritemSpacing = 0; | |
| 362 | - layout.minimumLineSpacing = 0; | |
| 363 | - | |
| 364 | - self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; | |
| 365 | - self.collectionView.delegate = self; | |
| 366 | - self.collectionView.dataSource = self; | |
| 367 | - self.collectionView.backgroundColor = [UIColor clearColor]; | |
| 368 | - self.collectionView.showsHorizontalScrollIndicator = NO; | |
| 369 | - [self.collectionView registerClass:CNEditVideoCell.class forCellWithReuseIdentifier:@"CNEditVideoCell"]; | |
| 370 | - | |
| 371 | - [self.view addSubview:self.collectionView]; | |
| 372 | - | |
| 373 | - [self creatBottomView]; | |
| 374 | - | |
| 375 | - self.editView = [[CNEditFrameView alloc] init]; | |
| 376 | - self.editView.delegate = self; | |
| 377 | - [self.view addSubview:self.editView]; | |
| 378 | - | |
| 379 | - _indicatorLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2, kItemHeight)]; | |
| 380 | -// _indicatorLine.backgroundColor = [RGBOF(0x0BBE06) colorWithAlphaComponent:.7]; | |
| 381 | -} | |
| 382 | - | |
| 383 | -- (void)creatBottomView | |
| 384 | -{ | |
| 385 | - //下方视图 | |
| 386 | - _bottomView = [[UIView alloc] init]; | |
| 387 | - _bottomView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.7]; | |
| 388 | - [self.view addSubview:_bottomView]; | |
| 389 | - | |
| 390 | - _cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 391 | - _cancelBtn.titleLabel.font = [UIFont systemFontOfSize:15]; | |
| 392 | - [_cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 393 | - [_cancelBtn setTitle:@"取消" forState:UIControlStateNormal]; | |
| 394 | - [_cancelBtn addTarget:self action:@selector(cancelBtn_click) forControlEvents:UIControlEventTouchUpInside]; | |
| 395 | - [_bottomView addSubview:_cancelBtn]; | |
| 396 | - | |
| 397 | - _doneBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 398 | - [_doneBtn setTitle:@"完成" forState:UIControlStateNormal]; | |
| 399 | - [_doneBtn setBackgroundColor:[UIColor clearColor]]; | |
| 400 | -// [_doneBtn setTitleColor:RGBOF(0x0BBE06) forState:UIControlStateNormal]; | |
| 401 | - _doneBtn.titleLabel.font = [UIFont systemFontOfSize:15]; | |
| 402 | - _doneBtn.layer.masksToBounds = YES; | |
| 403 | - _doneBtn.layer.cornerRadius = 3.0f; | |
| 404 | - [_doneBtn addTarget:self action:@selector(btnDone_click) forControlEvents:UIControlEventTouchUpInside]; | |
| 405 | - [_bottomView addSubview:_doneBtn]; | |
| 406 | -} | |
| 407 | - | |
| 408 | -#pragma mark - 解析视频每一帧图片 | |
| 409 | -- (void)analysisAssetImages | |
| 410 | -{ | |
| 411 | - NSTimeInterval duration = 0.0; | |
| 412 | - if ([self.model.asset isKindOfClass:[PHAsset class]]) { | |
| 413 | - PHAsset *asset = self.model.asset; | |
| 414 | - duration = asset.duration; | |
| 415 | - }else if([self.model.asset isKindOfClass:[ALAsset class]]){ | |
| 416 | - duration = [[self.model.asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | |
| 417 | - } | |
| 418 | - | |
| 419 | - TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 420 | - _interval = _tzImagePickerVc.maxEditVideoTime/10.0; | |
| 421 | - _measureCount = (NSInteger)(duration / _interval); | |
| 422 | - ///播放视频的 | |
| 423 | - if ([self.model.asset isKindOfClass:[PHAsset class]]) {//PHAsset | |
| 424 | - __weak typeof(self) weakSelf = self; | |
| 425 | - [[PHCachingImageManager defaultManager] requestPlayerItemForVideo:self.model.asset options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) { | |
| 426 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 427 | - __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 428 | - if (!playerItem) return; | |
| 429 | - AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; | |
| 430 | - strongSelf.playerLayer.player = player; | |
| 431 | - [strongSelf startTimer]; | |
| 432 | - }); | |
| 433 | - | |
| 434 | - }]; | |
| 435 | - | |
| 436 | - ///截取视频帧数 | |
| 437 | - PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init]; | |
| 438 | - options.version = PHVideoRequestOptionsVersionOriginal; | |
| 439 | - options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; | |
| 440 | - options.networkAccessAllowed = YES; | |
| 441 | - [[PHImageManager defaultManager] requestAVAssetForVideo:self.model.asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { | |
| 442 | - __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 443 | - strongSelf->_avAsset = asset; | |
| 444 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 445 | - [strongSelf.collectionView reloadData]; | |
| 446 | - }); | |
| 447 | - }]; | |
| 448 | - | |
| 449 | - }else if([self.model.asset isKindOfClass:[ALAsset class]]){//ALAsset | |
| 450 | - ///播放视频的 | |
| 451 | - ALAsset *alAsset = (ALAsset *)self.model.asset; | |
| 452 | - ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation]; | |
| 453 | - NSString *uti = [defaultRepresentation UTI]; | |
| 454 | - NSURL *videoURL = [[alAsset valueForProperty:ALAssetPropertyURLs] valueForKey:uti]; | |
| 455 | - AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL]; | |
| 456 | - AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; | |
| 457 | - self.playerLayer.player = player; | |
| 458 | - [self startTimer]; | |
| 459 | - | |
| 460 | - ///截取视频帧数 | |
| 461 | - NSURL *videoURL_al =[self.model.asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs | |
| 462 | - AVAsset *videoAsset = [AVURLAsset assetWithURL:videoURL]; | |
| 463 | - _avAsset = videoAsset ; | |
| 464 | - [self.collectionView reloadData]; | |
| 465 | - | |
| 466 | - } | |
| 467 | - | |
| 468 | -} | |
| 469 | - | |
| 470 | -#pragma mark - action | |
| 471 | -- (void)cancelBtn_click | |
| 472 | -{ | |
| 473 | - [self stopTimer]; | |
| 474 | - | |
| 475 | - if (self.backButtonEditClickBlock) { | |
| 476 | - TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 477 | - self.backButtonEditClickBlock(_tzImagePickerVc.isSelectOriginalPhoto); | |
| 478 | - } | |
| 479 | - UIViewController *vc = [self.navigationController popViewControllerAnimated:NO]; | |
| 480 | - if (!vc) { | |
| 481 | - [self dismissViewControllerAnimated:YES completion:nil]; | |
| 482 | - } | |
| 483 | -} | |
| 484 | - | |
| 485 | -- (void)btnDone_click | |
| 486 | -{ | |
| 487 | - [self stopTimer]; | |
| 488 | - | |
| 489 | - TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 490 | - [_tzImagePickerVc showProgressHUD]; | |
| 491 | - | |
| 492 | - [self exportEditVideoForAsset:_avAsset range:[self getTimeRange] complete:^(BOOL isSuc, id asset) { | |
| 493 | - [_tzImagePickerVc hideProgressHUD]; | |
| 494 | - if (isSuc) { | |
| 495 | - TZAssetModel *editModel = [TZAssetModel modelWithAsset:asset type:TZAssetModelMediaTypeVideo]; | |
| 496 | - | |
| 497 | - // 如果没有选中过照片 点击确定时选中当前预览的照片 | |
| 498 | - if (_tzImagePickerVc.selectedModels.count == 0 && _tzImagePickerVc.minImagesCount <= 0) { | |
| 499 | - [_tzImagePickerVc addSelectedModel:editModel]; | |
| 500 | - } | |
| 501 | - | |
| 502 | - if (self.doneButtonEditClickBlock) { | |
| 503 | - self.doneButtonEditClickBlock(_tzImagePickerVc.isSelectOriginalPhoto); | |
| 504 | - } | |
| 505 | - [_tzImagePickerVc hideProgressHUD]; | |
| 506 | - }else{ | |
| 507 | - [_tzImagePickerVc showAlertWithTitle:@"编辑失败"]; | |
| 508 | - } | |
| 509 | - | |
| 510 | - }]; | |
| 511 | -} | |
| 512 | -#pragma mark - 截取视频 | |
| 513 | - | |
| 514 | -- (void)exportEditVideoForAsset:(AVAsset *)asset range:(CMTimeRange)range complete:(void (^)(BOOL, id))complete | |
| 515 | -{ | |
| 516 | - [self export:asset range:range presetName:AVAssetExportPresetPassthrough renderSize:CGSizeZero imageSize:CGSizeZero effectImage:nil birthRate:0 velocity:0 complete:^(NSString *exportFilePath, NSError *error) { | |
| 517 | - if (!error) { | |
| 518 | - [self saveVideoToAblum:[NSURL fileURLWithPath:exportFilePath] completion:^(BOOL isSuc, id asset) { | |
| 519 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 520 | - if (complete) complete(isSuc, asset); | |
| 521 | - }); | |
| 522 | - }]; | |
| 523 | - } else { | |
| 524 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 525 | - if (complete) complete(NO, nil); | |
| 526 | - }); | |
| 527 | - } | |
| 528 | - }]; | |
| 529 | -} | |
| 530 | - | |
| 531 | -///保存视频到相册 | |
| 532 | -- (void)saveVideoToAblum:(NSURL *)url completion:(void (^)(BOOL, id))completion | |
| 533 | -{ | |
| 534 | - if (iOS8Later) { | |
| 535 | - PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; | |
| 536 | - if (status == PHAuthorizationStatusDenied) { | |
| 537 | - if (completion) completion(NO, nil); | |
| 538 | - } else if (status == PHAuthorizationStatusRestricted) { | |
| 539 | - if (completion) completion(NO, nil); | |
| 540 | - } else { | |
| 541 | - __block PHObjectPlaceholder *placeholderAsset=nil; | |
| 542 | - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | |
| 543 | - PHAssetChangeRequest *newAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url]; | |
| 544 | - placeholderAsset = newAssetRequest.placeholderForCreatedAsset; | |
| 545 | - } completionHandler:^(BOOL success, NSError * _Nullable error) { | |
| 546 | - if (!success) { | |
| 547 | - if (completion) completion(NO, nil); | |
| 548 | - return; | |
| 549 | - } | |
| 550 | - PHAsset *asset = [self getAssetFromlocalIdentifier:placeholderAsset.localIdentifier]; | |
| 551 | - PHAssetCollection *desCollection = [self getDestinationCollection]; | |
| 552 | - if (!desCollection) completion(NO, nil); | |
| 553 | - | |
| 554 | - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | |
| 555 | - [[PHAssetCollectionChangeRequest changeRequestForAssetCollection:desCollection] addAssets:@[asset]]; | |
| 556 | - } completionHandler:^(BOOL success, NSError * _Nullable error) { | |
| 557 | - if (completion) completion(success, asset); | |
| 558 | - }]; | |
| 559 | - }]; | |
| 560 | - } | |
| 561 | - }else{ | |
| 562 | - | |
| 563 | - ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init]; | |
| 564 | - [assetLibrary writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { | |
| 565 | - if (error) { | |
| 566 | - NSLog(@"保存视频出错:%@",error.localizedDescription); | |
| 567 | - if (completion) { | |
| 568 | - completion(NO,nil); | |
| 569 | - } | |
| 570 | - } else { | |
| 571 | - [assetLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) { | |
| 572 | - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 573 | - if (completion) { | |
| 574 | - completion(YES,asset); | |
| 575 | - } | |
| 576 | - }); | |
| 577 | - } failureBlock:^(NSError *error) { | |
| 578 | - | |
| 579 | - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 580 | - if (completion) { | |
| 581 | - completion(NO,nil); | |
| 582 | - } | |
| 583 | - }); | |
| 584 | - | |
| 585 | - }]; | |
| 586 | - } | |
| 587 | - }]; | |
| 588 | - | |
| 589 | - } | |
| 590 | - | |
| 591 | -} | |
| 592 | - | |
| 593 | -- (PHAsset *)getAssetFromlocalIdentifier:(NSString *)localIdentifier{ | |
| 594 | - if(localIdentifier == nil){ | |
| 595 | - NSLog(@"Cannot get asset from localID because it is nil"); | |
| 596 | - return nil; | |
| 597 | - } | |
| 598 | - PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; | |
| 599 | - if(result.count){ | |
| 600 | - return result[0]; | |
| 601 | - } | |
| 602 | - return nil; | |
| 603 | -} | |
| 604 | - | |
| 605 | -//获取自定义相册 | |
| 606 | -- (PHAssetCollection *)getDestinationCollection | |
| 607 | -{ | |
| 608 | - //找是否已经创建自定义相册 | |
| 609 | - PHFetchResult<PHAssetCollection *> *collectionResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; | |
| 610 | - for (PHAssetCollection *collection in collectionResult) { | |
| 611 | - if ([collection.localizedTitle isEqualToString:kAPPName]) { | |
| 612 | - return collection; | |
| 613 | - } | |
| 614 | - } | |
| 615 | - //新建自定义相册 | |
| 616 | - __block NSString *collectionId = nil; | |
| 617 | - NSError *error = nil; | |
| 618 | - [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ | |
| 619 | - collectionId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:kAPPName].placeholderForCreatedAssetCollection.localIdentifier; | |
| 620 | - } error:&error]; | |
| 621 | - if (error) { | |
| 622 | - return nil; | |
| 623 | - } | |
| 624 | - return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[collectionId] options:nil].lastObject; | |
| 625 | -} | |
| 626 | - | |
| 627 | -- (void)export:(AVAsset *)asset range:(CMTimeRange)range presetName:(NSString *)presetName renderSize:(CGSize)renderSize imageSize:(CGSize)imageSize effectImage:(UIImage *)effectImage birthRate:(NSInteger)birthRate velocity:(CGFloat)velocity complete:(void (^)(NSString *exportFilePath, NSError *error))complete | |
| 628 | -{ | |
| 629 | - NSString *exportFilePath = [self getVideoExportFilePath]; | |
| 630 | - | |
| 631 | - AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:presetName]; | |
| 632 | - | |
| 633 | - NSURL *exportFileUrl = [NSURL fileURLWithPath:exportFilePath]; | |
| 634 | - | |
| 635 | - exportSession.outputURL = exportFileUrl; | |
| 636 | - exportSession.outputFileType = AVFileTypeMPEG4; | |
| 637 | - exportSession.timeRange = range; | |
| 638 | - // exportSession.shouldOptimizeForNetworkUse = YES; | |
| 639 | - if (!CGSizeEqualToSize(renderSize, CGSizeZero)) { | |
| 640 | - AVMutableVideoComposition *com = [self getVideoComposition:asset renderSize:renderSize imageSize:imageSize effectImage:effectImage birthRate:birthRate velocity:velocity]; | |
| 641 | - if (!com) { | |
| 642 | - if (complete) { | |
| 643 | - complete(nil, [NSError errorWithDomain:@"视频裁剪导出失败" code:-1 userInfo:@{@"message": @"视频对象格式可能有错误,没有检测到视频通道"}]); | |
| 644 | - } | |
| 645 | - return; | |
| 646 | - } | |
| 647 | - exportSession.videoComposition = com; | |
| 648 | - } | |
| 649 | - | |
| 650 | - [exportSession exportAsynchronouslyWithCompletionHandler:^{ | |
| 651 | - BOOL suc = NO; | |
| 652 | - switch ([exportSession status]) { | |
| 653 | - case AVAssetExportSessionStatusFailed: | |
| 654 | - NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]); | |
| 655 | - break; | |
| 656 | - case AVAssetExportSessionStatusCancelled: | |
| 657 | - NSLog(@"Export canceled"); | |
| 658 | - break; | |
| 659 | - | |
| 660 | - case AVAssetExportSessionStatusCompleted:{ | |
| 661 | - NSLog(@"Export completed"); | |
| 662 | - suc = YES; | |
| 663 | - } | |
| 664 | - break; | |
| 665 | - | |
| 666 | - default: | |
| 667 | - NSLog(@"Export other"); | |
| 668 | - break; | |
| 669 | - } | |
| 670 | - | |
| 671 | - if (complete) { | |
| 672 | - complete(suc?exportFilePath:nil, suc?nil:exportSession.error); | |
| 673 | - if (!suc) { | |
| 674 | - [exportSession cancelExport]; | |
| 675 | - } | |
| 676 | - } | |
| 677 | - }]; | |
| 678 | -} | |
| 679 | - | |
| 680 | -- (NSString *)getVideoExportFilePath | |
| 681 | -{ | |
| 682 | - NSString *format = @"mp4"; | |
| 683 | - | |
| 684 | - NSString *exportFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self getUniqueStrByUUID], format]]; | |
| 685 | - | |
| 686 | - return exportFilePath; | |
| 687 | -} | |
| 688 | - | |
| 689 | -- (NSString *)getUniqueStrByUUID | |
| 690 | -{ | |
| 691 | - CFUUIDRef uuidObj = CFUUIDCreate(nil);//create a new UUID | |
| 692 | - | |
| 693 | - //get the string representation of the UUID | |
| 694 | - CFStringRef uuidString = CFUUIDCreateString(nil, uuidObj); | |
| 695 | - | |
| 696 | - NSString *str = [NSString stringWithString:(__bridge NSString *)uuidString]; | |
| 697 | - | |
| 698 | - CFRelease(uuidObj); | |
| 699 | - CFRelease(uuidString); | |
| 700 | - | |
| 701 | - return [str lowercaseString]; | |
| 702 | -} | |
| 703 | - | |
| 704 | - | |
| 705 | -- (AVMutableVideoComposition *)getVideoComposition:(AVAsset *)asset renderSize:(CGSize)renderSize imageSize:(CGSize)imageSize effectImage:(UIImage *)effectImage birthRate:(NSInteger)birthRate velocity:(CGFloat)velocity | |
| 706 | -{ | |
| 707 | - AVMutableComposition *composition = [AVMutableComposition composition]; | |
| 708 | - //视频通道 | |
| 709 | - AVMutableCompositionTrack *assetVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; | |
| 710 | - //音频通道 | |
| 711 | - AVMutableCompositionTrack *assetAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; | |
| 712 | - | |
| 713 | - NSError *error = nil; | |
| 714 | - //裁剪时长 | |
| 715 | - CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration); | |
| 716 | - | |
| 717 | - AVAssetTrack *videoTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;//视频 | |
| 718 | - AVAssetTrack *audioTrack = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject;//音频 | |
| 719 | - | |
| 720 | - if (!videoTrack) { | |
| 721 | - return nil; | |
| 722 | - } | |
| 723 | - [assetVideoTrack insertTimeRange:timeRange ofTrack:videoTrack atTime:kCMTimeZero error:&error]; | |
| 724 | - NSLog(@"%@", error); | |
| 725 | - if (audioTrack) { | |
| 726 | - [assetAudioTrack insertTimeRange:timeRange ofTrack:audioTrack atTime:kCMTimeZero error:&error]; | |
| 727 | - NSLog(@"%@", error); | |
| 728 | - } | |
| 729 | - | |
| 730 | - if (error) { | |
| 731 | - return nil; | |
| 732 | - } | |
| 733 | - | |
| 734 | - AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; | |
| 735 | - instruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration); | |
| 736 | - | |
| 737 | - //处理视频旋转 | |
| 738 | - AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:assetVideoTrack]; | |
| 739 | - [layerInstruction setOpacity:0.0 atTime:assetVideoTrack.timeRange.duration]; | |
| 740 | - //视频旋转,获取视频旋转角度,然后旋转对应角度,保持视频方向正确 | |
| 741 | - CGFloat degree = [self getVideoDegree:videoTrack]; | |
| 742 | - CGSize naturalSize = assetVideoTrack.naturalSize; | |
| 743 | - | |
| 744 | - CGAffineTransform mixedTransform = CGAffineTransformIdentity; | |
| 745 | - //处理renderSize,不能大于视频宽高 | |
| 746 | - CGFloat videoWidth = (degree==0 || degree==M_PI) ? naturalSize.width : naturalSize.height; | |
| 747 | - CGFloat videoHeight = (degree==0 || degree==M_PI) ? naturalSize.height : naturalSize.width; | |
| 748 | - CGSize cropSize = CGSizeMake(MIN(videoWidth, renderSize.width), MIN(videoHeight, renderSize.height)); | |
| 749 | - CGFloat x, y; | |
| 750 | - if (degree == M_PI_2) { | |
| 751 | - //顺时针 90° | |
| 752 | - CGAffineTransform t = CGAffineTransformMakeTranslation(naturalSize.height,.0); | |
| 753 | - CGAffineTransform t1 = CGAffineTransformRotate(t, M_PI_2); | |
| 754 | - //x为正向下 y为正向左 | |
| 755 | - x = -(videoHeight-cropSize.height)/2; | |
| 756 | - y = (videoWidth-cropSize.width)/2; | |
| 757 | - mixedTransform = CGAffineTransformTranslate(t1, x, y); | |
| 758 | - } else if (degree == M_PI) { | |
| 759 | - //顺时针 180° | |
| 760 | - CGAffineTransform t = CGAffineTransformMakeTranslation(naturalSize.width, naturalSize.height); | |
| 761 | - CGAffineTransform t1 = CGAffineTransformRotate(t, M_PI); | |
| 762 | - //x为正向左 y为正向上 | |
| 763 | - x = (videoWidth-cropSize.width)/2; | |
| 764 | - y = (videoHeight-cropSize.height)/2; | |
| 765 | - mixedTransform = CGAffineTransformTranslate(t1, x, y); | |
| 766 | - } else if (degree == (M_PI_2 * 3)) { | |
| 767 | - //顺时针 270° | |
| 768 | - CGAffineTransform t = CGAffineTransformMakeTranslation(.0, naturalSize.width); | |
| 769 | - CGAffineTransform t1 = CGAffineTransformRotate(t, M_PI_2*3); | |
| 770 | - //x为正向上 y为正向右 | |
| 771 | - x = (videoHeight-cropSize.height)/2; | |
| 772 | - y = -(videoWidth-cropSize.width)/2; | |
| 773 | - mixedTransform = CGAffineTransformTranslate(t1, x, y); | |
| 774 | - } else { | |
| 775 | - //x为正向右 y为正向下 | |
| 776 | - x = -(videoWidth-cropSize.width)/2; | |
| 777 | - y = -(videoHeight-cropSize.height)/2; | |
| 778 | - mixedTransform = CGAffineTransformMakeTranslation(x, y); | |
| 779 | - } | |
| 780 | - | |
| 781 | - [layerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | |
| 782 | - | |
| 783 | - //管理所有需要处理的视频 | |
| 784 | - AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; | |
| 785 | - videoComposition.frameDuration = CMTimeMake(1, 30); | |
| 786 | - videoComposition.renderScale = 1; | |
| 787 | - videoComposition.renderSize = cropSize; | |
| 788 | - | |
| 789 | - instruction.layerInstructions = @[layerInstruction]; | |
| 790 | - videoComposition.instructions = @[instruction]; | |
| 791 | - | |
| 792 | - return videoComposition; | |
| 793 | -} | |
| 794 | - | |
| 795 | -- (CGFloat)getVideoDegree:(AVAssetTrack *)videoTrack | |
| 796 | -{ | |
| 797 | - CGAffineTransform tf = videoTrack.preferredTransform; | |
| 798 | - | |
| 799 | - CGFloat degree = 0; | |
| 800 | - if (tf.b == 1.0 && tf.c == -1.0) { | |
| 801 | - degree = M_PI_2; | |
| 802 | - } else if (tf.a == -1.0 && tf.d == -1.0) { | |
| 803 | - degree = M_PI; | |
| 804 | - } else if (tf.b == -1.0 && tf.c == 1.0) { | |
| 805 | - degree = M_PI_2 * 3; | |
| 806 | - } | |
| 807 | - return degree; | |
| 808 | -} | |
| 809 | - | |
| 810 | - | |
| 811 | - | |
| 812 | -#pragma mark - timer | |
| 813 | -- (void)startTimer | |
| 814 | -{ | |
| 815 | - [self stopTimer]; | |
| 816 | - | |
| 817 | - CGFloat duration = _interval * self.editView.validRect.size.width / (kItemWidth); | |
| 818 | - _timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(playPartVideo:) userInfo:nil repeats:YES]; | |
| 819 | - [_timer fire]; | |
| 820 | - [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; | |
| 821 | - | |
| 822 | - _indicatorLine.frame = CGRectMake(self.editView.validRect.origin.x, 0, 4, kItemHeight); | |
| 823 | - [self.editView addSubview:_indicatorLine]; | |
| 824 | - [UIView animateWithDuration:duration delay:.0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear animations:^{ | |
| 825 | - _indicatorLine.frame = CGRectMake(CGRectGetMaxX(self.editView.validRect)-2, 0, 2, kItemHeight); | |
| 826 | - } completion:nil]; | |
| 827 | -} | |
| 828 | - | |
| 829 | -- (void)stopTimer | |
| 830 | -{ | |
| 831 | - [_timer invalidate]; | |
| 832 | - _timer = nil; | |
| 833 | - [_indicatorLine removeFromSuperview]; | |
| 834 | - [self.playerLayer.player pause]; | |
| 835 | -} | |
| 836 | - | |
| 837 | -- (CMTime)getStartTime | |
| 838 | -{ | |
| 839 | - CGRect rect = [self.collectionView convertRect:self.editView.validRect fromView:self.editView]; | |
| 840 | - CGFloat s = MAX(0, _interval * rect.origin.x / (kItemWidth)); | |
| 841 | - return CMTimeMakeWithSeconds(s, self.playerLayer.player.currentTime.timescale); | |
| 842 | -} | |
| 843 | - | |
| 844 | -- (CMTimeRange)getTimeRange | |
| 845 | -{ | |
| 846 | - CMTime start = [self getStartTime]; | |
| 847 | - CGFloat d = _interval * self.editView.validRect.size.width / (kItemWidth); | |
| 848 | - CMTime duration = CMTimeMakeWithSeconds(d, self.playerLayer.player.currentTime.timescale); | |
| 849 | - return CMTimeRangeMake(start, duration); | |
| 850 | -} | |
| 851 | - | |
| 852 | -- (void)playPartVideo:(NSTimer *)timer | |
| 853 | -{ | |
| 854 | - [self.playerLayer.player play]; | |
| 855 | - [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; | |
| 856 | -} | |
| 857 | - | |
| 858 | -#pragma mark - edit view delegate | |
| 859 | -- (void)editViewValidRectChanged | |
| 860 | -{ | |
| 861 | - [self stopTimer]; | |
| 862 | - [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; | |
| 863 | -} | |
| 864 | - | |
| 865 | -- (void)editViewValidRectEndChanged | |
| 866 | -{ | |
| 867 | - [self startTimer]; | |
| 868 | -} | |
| 869 | - | |
| 870 | -#pragma mark - scroll view delegate | |
| 871 | -- (void)scrollViewDidScroll:(UIScrollView *)scrollView | |
| 872 | -{ | |
| 873 | - if (!self.playerLayer.player || _orientationChanged) { | |
| 874 | - _orientationChanged = NO; | |
| 875 | - return; | |
| 876 | - } | |
| 877 | - [self stopTimer]; | |
| 878 | - [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; | |
| 879 | -} | |
| 880 | - | |
| 881 | -- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate | |
| 882 | -{ | |
| 883 | - if (!decelerate) { | |
| 884 | - [self startTimer]; | |
| 885 | - } | |
| 886 | -} | |
| 887 | - | |
| 888 | -- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView | |
| 889 | -{ | |
| 890 | - [self startTimer]; | |
| 891 | -} | |
| 892 | - | |
| 893 | -#pragma mark - collection view data sources | |
| 894 | -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section | |
| 895 | -{ | |
| 896 | - return _measureCount; | |
| 897 | -} | |
| 898 | - | |
| 899 | -- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath | |
| 900 | -{ | |
| 901 | - CNEditVideoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CNEditVideoCell" forIndexPath:indexPath]; | |
| 902 | - | |
| 903 | - UIImage *image = _imageCache[@(indexPath.row).stringValue]; | |
| 904 | - if (image) { | |
| 905 | - cell.imageView.image = image; | |
| 906 | - } | |
| 907 | - | |
| 908 | - return cell; | |
| 909 | -} | |
| 910 | - | |
| 911 | -static const char _CNOperationCellKey; | |
| 912 | -- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath | |
| 913 | -{ | |
| 914 | - if (!_avAsset) return; | |
| 915 | - | |
| 916 | - if (_imageCache[@(indexPath.row).stringValue] || _opCache[@(indexPath.row).stringValue]) { | |
| 917 | - return; | |
| 918 | - } | |
| 919 | - | |
| 920 | - NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ | |
| 921 | - NSInteger row = indexPath.row; | |
| 922 | - NSInteger i = row * _interval; | |
| 923 | - | |
| 924 | - CMTime time = CMTimeMake((i+0.35) * _avAsset.duration.timescale, _avAsset.duration.timescale); | |
| 925 | - | |
| 926 | - NSError *error = nil; | |
| 927 | - CGImageRef cgImg = [self.generator copyCGImageAtTime:time actualTime:NULL error:&error]; | |
| 928 | - if (!error && cgImg) { | |
| 929 | - UIImage *image = [UIImage imageWithCGImage:cgImg]; | |
| 930 | - CGImageRelease(cgImg); | |
| 931 | - | |
| 932 | - [_imageCache setValue:image forKey:@(row).stringValue]; | |
| 933 | - | |
| 934 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 935 | - | |
| 936 | - NSIndexPath *nowIndexPath = [collectionView indexPathForCell:cell]; | |
| 937 | - if (row == nowIndexPath.row) { | |
| 938 | - [(CNEditVideoCell *)cell imageView].image = image; | |
| 939 | - } else { | |
| 940 | - UIImage *cacheImage = _imageCache[@(nowIndexPath.row).stringValue]; | |
| 941 | - if (cacheImage) { | |
| 942 | - [(CNEditVideoCell *)cell imageView].image = cacheImage; | |
| 943 | - } | |
| 944 | - } | |
| 945 | - }); | |
| 946 | - [_opCache removeObjectForKey:@(row).stringValue]; | |
| 947 | - } | |
| 948 | - objc_removeAssociatedObjects(cell); | |
| 949 | - }]; | |
| 950 | - [_queue addOperation:op]; | |
| 951 | - [_opCache setValue:op forKey:@(indexPath.row).stringValue]; | |
| 952 | - | |
| 953 | - objc_setAssociatedObject(cell, &_CNOperationCellKey, op, OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
| 954 | -} | |
| 955 | - | |
| 956 | -- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath | |
| 957 | -{ | |
| 958 | - NSBlockOperation *op = objc_getAssociatedObject(cell, &_CNOperationCellKey); | |
| 959 | - if (op) { | |
| 960 | - [op cancel]; | |
| 961 | - objc_removeAssociatedObjects(cell); | |
| 962 | - [_opCache removeObjectForKey:@(indexPath.row).stringValue]; | |
| 963 | - } | |
| 964 | -} | |
| 965 | - | |
| 966 | -- (void)didReceiveMemoryWarning { | |
| 967 | - [super didReceiveMemoryWarning]; | |
| 968 | - // Dispose of any resources that can be recreated. | |
| 969 | -} | |
| 970 | - | |
| 971 | -/* | |
| 972 | -#pragma mark - Navigation | |
| 973 | - | |
| 974 | -// In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 975 | -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 976 | - // Get the new view controller using [segue destinationViewController]. | |
| 977 | - // Pass the selected object to the new view controller. | |
| 978 | -} | |
| 979 | -*/ | |
| 980 | - | |
| 981 | -@end |
CNLiveImagePickerController/CNImagePickerCommonDefine.h deleted
100644 → 0
| 1 | -// | |
| 2 | -// CNImagePickerCommonDefine.h | |
| 3 | -// CNLiveImagePickerControllerExample | |
| 4 | -// | |
| 5 | -// Created by 梁星国 on 2018/10/25. | |
| 6 | -// Copyright © 2018年 梁星国. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#ifndef CNImagePickerCommonDefine_h | |
| 10 | -#define CNImagePickerCommonDefine_h | |
| 11 | - | |
| 12 | - | |
| 13 | -//////////////////////////////////////////////////////// | |
| 14 | -//////////// Color | |
| 15 | -//////////////////////////////////////////////////////// | |
| 16 | - | |
| 17 | -#define IP_RGBOF(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \ | |
| 18 | -green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \ | |
| 19 | -blue:((float)(rgbValue & 0xFF))/255.0 \ | |
| 20 | -alpha:1.0] | |
| 21 | - | |
| 22 | -#define IP_RGBA_OF(rgbValue) [UIColor colorWithRed:((float)(((rgbValue) & 0xFF000000) >> 24))/255.0 \ | |
| 23 | -green:((float)(((rgbValue) & 0x00FF0000) >> 16))/255.0 \ | |
| 24 | -blue:((float)(rgbValue & 0x0000FF00) >> 8)/255.0 \ | |
| 25 | -alpha:((float)(rgbValue & 0x000000FF))/255.0] | |
| 26 | - | |
| 27 | -#define IP_RGBAOF(v, a) [UIColor colorWithRed:((float)(((v) & 0xFF0000) >> 16))/255.0 \ | |
| 28 | -green:((float)(((v) & 0x00FF00) >> 8))/255.0 \ | |
| 29 | -blue:((float)(v & 0x0000FF))/255.0 \ | |
| 30 | -alpha:a] | |
| 31 | - | |
| 32 | - | |
| 33 | -//////////////////////////////////////////////////////// | |
| 34 | -//////////// 发送图片,视频限制 | |
| 35 | -//////////////////////////////////////////////////////// | |
| 36 | -#define kFriendsCircle_limit_photo 20*1024.0*1024.0 //朋友圈限制图片20M | |
| 37 | -#define kFirendsCircle_limit_video 30*1024.0*1024.0 //朋友圈限制视频30M | |
| 38 | -#define kTarentoComment_limit_photo 10*1024.0*1024.0 //达人评论10M | |
| 39 | -#define kSweepCode_limit_phoho 10*1024.0*1024.0 //扫码10M限制 | |
| 40 | -#define kChat_limit_photo 20*1024.0*1024.0 //聊天限制图片20M | |
| 41 | -#define kChat_limit_video 20*1024.0*1024.0 //聊天限制视频20M | |
| 42 | - | |
| 43 | -#define kChat_limit_video_length 5*60 //聊天限制视频5分钟 | |
| 44 | -#define kFriendsCircle_limit_video_maxEdit 5*60 //朋友圈限制视频5分钟 | |
| 45 | -#define kFriendsCircle_limit_video_max 10*60 //朋友圈限制最大视频编辑10分钟 | |
| 46 | - | |
| 47 | - | |
| 48 | - | |
| 49 | -//////////////////////////////////////////////////////// | |
| 50 | -//////////// SCreen | |
| 51 | -//////////////////////////////////////////////////////// | |
| 52 | -#define kIPScreenWidth ([UIScreen mainScreen].bounds.size.width > [UIScreen mainScreen].bounds.size.height ? [UIScreen mainScreen].bounds.size.height : [UIScreen mainScreen].bounds.size.width) | |
| 53 | -#define kIPScreenHeight ([UIScreen mainScreen].bounds.size.width > [UIScreen mainScreen].bounds.size.height ? [UIScreen mainScreen].bounds.size.width : [UIScreen mainScreen].bounds.size.height) | |
| 54 | - | |
| 55 | - | |
| 56 | -//判断iPHoneXr | |
| 57 | -#define IP_Device_Is_iPhoneXS_XR ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(828, 1792), [[UIScreen mainScreen] currentMode].size) : NO) | |
| 58 | - | |
| 59 | -//判断iPHoneX、iPHoneXs | |
| 60 | -#define IP_Device_Is_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) | |
| 61 | -#define IP_Device_Is_iPhoneXS ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) | |
| 62 | - | |
| 63 | -//判断iPhoneXs Max | |
| 64 | -#define IP_Device_Is_iPhoneXS_MAX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2688), [[UIScreen mainScreen] currentMode].size) : NO) | |
| 65 | - | |
| 66 | -#define IP_IS_IPhoneX_All (CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(375, 812)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(812, 375)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(414, 896)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(896, 414))) | |
| 67 | - | |
| 68 | - | |
| 69 | -#define kIPStatusHeight (int)[UIApplication sharedApplication].statusBarFrame.size.height //x44 非x20 | |
| 70 | -#define kIPNavigationBarHeight ((int)[UIApplication sharedApplication].statusBarFrame.size.height+44) //x88 非x64 | |
| 71 | -#define kIPVerticalNaviBarHeight ((IP_IS_IPhoneX_All > 0) ? 88 : 44) | |
| 72 | -#define kIPVerticalTabBarTotalHeight ((IP_IS_IPhoneX_All > 0) ? 83 : 49) | |
| 73 | -#define kIPVerticalBottomSafeHeight ((IP_IS_IPhoneX_All > 0) ? 34 : 0) | |
| 74 | -#define kIPVerticalStatusHeight ((IP_IS_IPhoneX_All > 0) ? 44 : 20) | |
| 75 | -#define kIPVerticalTopHeight ((IP_IS_IPhoneX_All > 0) ? 24 : 0) | |
| 76 | -#define kIPHorizontalTopNaviBarHeight 32 | |
| 77 | -#define kIPHorizontalBottomHeight ((IP_IS_IPhoneX_All > 0) ? 21 : 0) | |
| 78 | -#define kIPHorizontalLeftHeight ((IP_IS_IPhoneX_All > 0) ? 44 : 0) | |
| 79 | -#define kIPHorizontalRightHeight ((IP_IS_IPhoneX_All > 0) ? 44 : 0) | |
| 80 | -#define kIPHorizontalTopHeight ((IP_IS_IPhoneX_All > 0) ? 24 : 0) | |
| 81 | - | |
| 82 | -#endif /* CNImagePickerCommonDefine_h */ |
CNLiveImagePickerController/CNImagePickerController.h deleted
100644 → 0
| 1 | -// | |
| 2 | -// CNTZImagePickerControllerViewController.h | |
| 3 | -// CNLiveNetAdd | |
| 4 | -// | |
| 5 | -// Created by 梁星国 on 2018/8/22. | |
| 6 | -// Copyright © 2018年 cnlive. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "TZImagePickerController.h" | |
| 10 | - | |
| 11 | -@interface CNImagePickerController : TZImagePickerController | |
| 12 | - | |
| 13 | -/** | |
| 14 | - 配置 | |
| 15 | - */ | |
| 16 | -- (void)configTZImagePicker; | |
| 17 | - | |
| 18 | -@end |
CNLiveImagePickerController/CNImagePickerController.m deleted
100644 → 0
| 1 | -// | |
| 2 | -// CNTZImagePickerControllerViewController.m | |
| 3 | -// CNLiveNetAdd | |
| 4 | -// | |
| 5 | -// Created by 梁星国 on 2018/8/22. | |
| 6 | -// Copyright © 2018年 cnlive. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "CNImagePickerController.h" | |
| 10 | -#import "CNImagePickerCommonDefine.h" | |
| 11 | -#import "UIImage+CNImagePicker.h" | |
| 12 | - | |
| 13 | -@interface CNImagePickerController () | |
| 14 | - | |
| 15 | -@end | |
| 16 | - | |
| 17 | -@implementation CNImagePickerController | |
| 18 | - | |
| 19 | -- (void)viewDidLoad { | |
| 20 | - [super viewDidLoad]; | |
| 21 | - | |
| 22 | -} | |
| 23 | - | |
| 24 | -/** | |
| 25 | - 配置 详情看TZImagePickerController | |
| 26 | - */ | |
| 27 | -- (void)configTZImagePicker{ | |
| 28 | - | |
| 29 | -#pragma mark - 五类个性化设置,这些参数都可以不传,此时会走默认设置 | |
| 30 | - // 1.设置目前已经选中的图片数组 | |
| 31 | - self.allowTakePicture = NO; // 在内部显示拍照按钮 | |
| 32 | - self.allowTakeVideo = NO;// 在内部拍摄按钮 | |
| 33 | - self.videoMaximumDuration = 10; // 视频最大拍摄时间 | |
| 34 | - [self setUiImagePickerControllerSettingBlock:^(UIImagePickerController *imagePickerController) { | |
| 35 | - imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;//拍摄分辨率 | |
| 36 | - }]; | |
| 37 | - self.iconThemeColor = IP_RGBOF(0x00BBE06);// 主体颜色 | |
| 38 | - self.showPhotoCannotSelectLayer = YES;//达到上限是否有浮层 | |
| 39 | - self.cannotSelectLayerColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];//设置达到上限的浮层 | |
| 40 | - | |
| 41 | - self.photoDefImage = [UIImage imageNamedFromMyBundle:@"photo_cell_normal"]; | |
| 42 | - self.photoSelImage = [UIImage ip_imageWithColor:IP_RGBOF(0x0BBE06) size:CGSizeMake(22, 22) radius:11]; | |
| 43 | - self.photoOriginDefImage = [UIImage imageNamedFromMyBundle:@"photo_original_nor"]; | |
| 44 | - | |
| 45 | - // 3. 设置是否可以选择视频/图片/原图/gif | |
| 46 | - self.allowPickingVideo = YES;// 显示视频 | |
| 47 | - self.allowPickingImage = YES;// 显示图片 | |
| 48 | - self.allowPickingOriginalPhoto = YES; //显示原图 | |
| 49 | - self.allowPickingGif = NO;// 显示GIF | |
| 50 | - self.allowPickingMultipleVideo = YES; // 是否可以多选视频 | |
| 51 | - | |
| 52 | - // 4. 照片排列按修改时间升序 | |
| 53 | - self.sortAscendingByModificationDate = YES; | |
| 54 | - | |
| 55 | - // imagePickerVc.minImagesCount = 3; | |
| 56 | - // imagePickerVc.alwaysEnableDoneBtn = YES; | |
| 57 | - | |
| 58 | - // imagePickerVc.minPhotoWidthSelectable = 3000; | |
| 59 | - // imagePickerVc.minPhotoHeightSelectable = 2000; | |
| 60 | - | |
| 61 | - /// 5. Single selection mode, valid when maxImagesCount = 1 | |
| 62 | - /// 5. 单选模式,maxImagesCount为1时才生效 | |
| 63 | - self.showSelectBtn = NO; | |
| 64 | - self.allowCrop = NO; | |
| 65 | - self.needCircleCrop = NO; | |
| 66 | - // 设置横屏下的裁剪尺寸 | |
| 67 | - // imagePickerVc.cropRectLandscape = CGRectMake((self.view.tz_height - widthHeight) / 2, left, widthHeight, widthHeight); | |
| 68 | - /* | |
| 69 | - [imagePickerVc setCropViewSettingBlock:^(UIView *cropView) { | |
| 70 | - cropView.layer.borderColor = [UIColor redColor].CGColor; | |
| 71 | - cropView.layer.borderWidth = 2.0; | |
| 72 | - }];*/ | |
| 73 | - | |
| 74 | - //imagePickerVc.allowPreview = NO; | |
| 75 | - // 自定义导航栏上的返回按钮 | |
| 76 | - /* | |
| 77 | - [imagePickerVc setNavLeftBarButtonSettingBlock:^(UIButton *leftButton){ | |
| 78 | - [leftButton setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal]; | |
| 79 | - [leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, -10, 0, 20)]; | |
| 80 | - }]; | |
| 81 | - imagePickerVc.delegate = self; | |
| 82 | - */ | |
| 83 | - | |
| 84 | - self.statusBarStyle = UIStatusBarStyleDefault;//状态栏黑色 | |
| 85 | - | |
| 86 | - self.showSelectedIndex = YES; // 设置是否显示图片序号 | |
| 87 | - | |
| 88 | - self.naviBgColor = IP_RGBOF(0xFFFFFF);//导航栏颜色 | |
| 89 | - self.naviTitleColor = IP_RGBOF(0x282828);//相册title | |
| 90 | - self.naviTitleFont = [UIFont systemFontOfSize:18];//title文字大小 | |
| 91 | - [self setNavLeftBarButtonSettingBlock:^(UIButton *leftButton) {//设置返回按钮图标 | |
| 92 | - [leftButton setImage:[UIImage imageNamedFromMyBundle:@"photoback"] forState:UIControlStateNormal];//返回按钮图片 | |
| 93 | - [leftButton setTitle:@"返回" forState:UIControlStateNormal];//设置文字大小 | |
| 94 | - [leftButton setTitleColor:IP_RGBOF(0x282828) forState:UIControlStateNormal];//设置文字颜色 | |
| 95 | - leftButton.titleLabel.font = [UIFont systemFontOfSize:15];//设置文字大小 | |
| 96 | - leftButton.imageEdgeInsets = UIEdgeInsetsMake(0, -7, 0, +7);//设置图片边缘 | |
| 97 | - }]; | |
| 98 | - self.barItemTextColor = IP_RGBOF(0x282828);//设置barItem文字颜色 | |
| 99 | - self.barItemTextFont = [UIFont systemFontOfSize:15]; | |
| 100 | - | |
| 101 | - // 设置首选语言 / Set preferred language | |
| 102 | - // imagePickerVc.preferredLanguage = @"zh-Hans"; | |
| 103 | - | |
| 104 | - // 设置languageBundle以使用其它语言 / Set languageBundle to use other language | |
| 105 | - // imagePickerVc.languageBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"tz-ru" ofType:@"lproj"]]; | |
| 106 | - | |
| 107 | - /// 【自定义各页面/组件的样式】在界面初始化/组件setModel完成后调用,允许外界修改样式等 | |
| 108 | - //相册 | |
| 109 | - [self setPhotoPickerPageUIConfigBlock:^(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine) { | |
| 110 | - | |
| 111 | - //底部栏 | |
| 112 | - [bottomToolBar setBackgroundColor:IP_RGBOF(0xFFFFFF)]; | |
| 113 | - | |
| 114 | - //预览 | |
| 115 | - previewButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 116 | - [previewButton setTitleColor:IP_RGBOF(0xB3B3B3) forState:UIControlStateDisabled];//禁用 | |
| 117 | - [previewButton setTitleColor:IP_RGBOF(0x000000) forState:UIControlStateNormal];//默认 | |
| 118 | - | |
| 119 | - //原图按钮 | |
| 120 | - [originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_normal"] forState:UIControlStateDisabled];//禁用 | |
| 121 | - [originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_normal"] forState:UIControlStateNormal];//默认 | |
| 122 | - [originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_select"] forState:UIControlStateSelected];//选中 | |
| 123 | - originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 124 | - [originalPhotoButton setTitleColor:IP_RGBOF(0xB3B3B3) forState:UIControlStateDisabled]; | |
| 125 | - [originalPhotoButton setTitleColor:IP_RGBOF(0x000000) forState:UIControlStateNormal]; | |
| 126 | - | |
| 127 | - //原图文字提示 | |
| 128 | - originalPhotoLabel.font = [UIFont systemFontOfSize:16]; | |
| 129 | - originalPhotoLabel.textColor = IP_RGBOF(0xB3B3B3); | |
| 130 | - originalPhotoLabel.hidden = YES; | |
| 131 | - | |
| 132 | - //选中提示 | |
| 133 | - numberImageView = [UIImageView new]; | |
| 134 | - numberLabel = [UILabel new]; | |
| 135 | - | |
| 136 | - //完成按钮 | |
| 137 | - doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | |
| 138 | - [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | |
| 139 | - [doneButton setTitleColor:IP_RGBAOF(0xFFFFFF, 0.5) forState:UIControlStateDisabled]; | |
| 140 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | |
| 141 | - | |
| 142 | - //颜色绘制成图片 | |
| 143 | - UIImage *dis_image = [UIImage ip_imageWithColor:IP_RGBAOF(0x0BBE06, 0.5)]; | |
| 144 | - [doneButton setBackgroundImage:dis_image forState:UIControlStateDisabled]; | |
| 145 | - UIImage *nor_image = [UIImage ip_imageWithColor:IP_RGBOF(0x0BBE06)]; | |
| 146 | - [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | |
| 147 | - doneButton.layer.masksToBounds = YES; | |
| 148 | - doneButton.layer.cornerRadius = 5; | |
| 149 | - | |
| 150 | - //分割线 | |
| 151 | - [divideLine setBackgroundColor:IP_RGBOF(0xDEDEDE)]; | |
| 152 | - | |
| 153 | - }]; | |
| 154 | - | |
| 155 | - //图片预览 | |
| 156 | - [self setPhotoPreviewPageUIConfigBlock:^(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel) { | |
| 157 | - | |
| 158 | - //返回按钮 | |
| 159 | - [backButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_back"] forState:UIControlStateNormal]; | |
| 160 | - //选择按钮 | |
| 161 | - [selectButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_nor"] forState:UIControlStateNormal]; | |
| 162 | - [selectButton setImage:[UIImage ip_imageWithColor:IP_RGBOF(0x0BBE06) size:CGSizeMake(28, 28) radius:14] forState:UIControlStateSelected]; | |
| 163 | - [selectButton setTitle:@"" forState:UIControlStateDisabled]; | |
| 164 | - | |
| 165 | - //原图按钮 | |
| 166 | - [originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_normal"] forState:UIControlStateDisabled];//禁用 | |
| 167 | - [originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_normal"] forState:UIControlStateNormal];//默认 | |
| 168 | - [originalPhotoButton setImage:[UIImage imageNamedFromMyBundle:@"photo_original_select"] forState:UIControlStateSelected];//选中 | |
| 169 | - originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 170 | - [originalPhotoButton setTitleColor:IP_RGBOF(0xB3B3B3) forState:UIControlStateDisabled]; | |
| 171 | - [originalPhotoButton setTitleColor:IP_RGBAOF(0xFFFFFF, 0.7) forState:UIControlStateNormal]; | |
| 172 | - //原图内容隐藏 | |
| 173 | - originalPhotoLabel.hidden = YES; | |
| 174 | - | |
| 175 | - //完成按钮 | |
| 176 | - doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | |
| 177 | - [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | |
| 178 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | |
| 179 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | |
| 180 | - | |
| 181 | - //颜色绘制成图片 | |
| 182 | - UIImage *dis_image = [UIImage ip_imageWithColor:IP_RGBAOF(0x0BBE06, 0.5)]; | |
| 183 | - [doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | |
| 184 | - UIImage *nor_image = [UIImage ip_imageWithColor:IP_RGBOF(0x0BBE06)]; | |
| 185 | - [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | |
| 186 | - doneButton.layer.masksToBounds = YES; | |
| 187 | - doneButton.layer.cornerRadius = 5; | |
| 188 | - }]; | |
| 189 | - | |
| 190 | - //视频预览 | |
| 191 | - [self setVideoPreviewPageUIConfigBlock:^(UIButton *playButton, UIView *toolBar, UIButton *doneButton) { | |
| 192 | - | |
| 193 | - //完成按钮 | |
| 194 | - doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | |
| 195 | - [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | |
| 196 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | |
| 197 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | |
| 198 | - | |
| 199 | - //颜色绘制成图片 | |
| 200 | - UIImage *dis_image = [UIImage ip_imageWithColor:IP_RGBAOF(0x0BBE06, 0.5)]; | |
| 201 | - [doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | |
| 202 | - UIImage *nor_image = [UIImage ip_imageWithColor:IP_RGBOF(0x0BBE06)]; | |
| 203 | - [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | |
| 204 | - doneButton.layer.masksToBounds = YES; | |
| 205 | - doneButton.layer.cornerRadius = 5; | |
| 206 | - | |
| 207 | - }]; | |
| 208 | - | |
| 209 | - //gif预览 | |
| 210 | - [self setGifPreviewPageUIConfigBlock:^(UIView *toolBar, UIButton *doneButton) { | |
| 211 | - | |
| 212 | - //完成按钮 | |
| 213 | - doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | |
| 214 | - [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | |
| 215 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | |
| 216 | - [doneButton setTitleColor:IP_RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | |
| 217 | - | |
| 218 | - //颜色绘制成图片 | |
| 219 | - UIImage *dis_image = [UIImage ip_imageWithColor:IP_RGBAOF(0x0BBE06, 0.5)]; | |
| 220 | - [doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | |
| 221 | - UIImage *nor_image = [UIImage ip_imageWithColor:IP_RGBOF(0x0BBE06)]; | |
| 222 | - [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | |
| 223 | - doneButton.layer.masksToBounds = YES; | |
| 224 | - doneButton.layer.cornerRadius = 5; | |
| 225 | - | |
| 226 | - }]; | |
| 227 | - //相册cell | |
| 228 | - [self setAssetCellDidSetModelBlock:^(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView) { | |
| 229 | - | |
| 230 | - | |
| 231 | - }]; | |
| 232 | - //相簿 | |
| 233 | - [self setAlbumCellDidSetModelBlock:^(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel) { | |
| 234 | - [posterImageView setBackgroundColor:[UIColor blueColor]]; | |
| 235 | - [posterImageView setBackgroundColor:[UIColor blueColor]]; | |
| 236 | - | |
| 237 | - }]; | |
| 238 | - | |
| 239 | - | |
| 240 | - /// 【自定义各页面/组件的frame】在界面viewDidLayoutSubviews/组件layoutSubviews后调用,允许外界修改frame等 | |
| 241 | - //相册 | |
| 242 | - [self setPhotoPickerPageDidLayoutSubviewsBlock:^(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine) { | |
| 243 | - | |
| 244 | - CGRect frame = bottomToolBar.frame; | |
| 245 | - doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15 - (kIPVerticalBottomSafeHeight /2), 60, 30); | |
| 246 | - | |
| 247 | - CGRect originalPhotoLabelFrame = originalPhotoLabel.frame; | |
| 248 | - CGFloat originalX = (bottomToolBar.frame.size.width - originalPhotoLabelFrame.size.width) /2; | |
| 249 | - CGFloat originalY = originalPhotoLabelFrame.origin.y; | |
| 250 | - originalPhotoButton.frame = CGRectMake(originalX, originalY, originalPhotoLabelFrame.size.width, originalPhotoLabelFrame.size.height); | |
| 251 | - | |
| 252 | - | |
| 253 | - }]; | |
| 254 | - //图片预览 | |
| 255 | - [self setPhotoPreviewPageDidLayoutSubviewsBlock:^(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel) { | |
| 256 | - | |
| 257 | - CGRect frame = toolBar.frame; | |
| 258 | - doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15 - (kIPVerticalBottomSafeHeight /2), 60, 30); | |
| 259 | - | |
| 260 | - }]; | |
| 261 | - //视频预览 | |
| 262 | - [self setVideoPreviewPageDidLayoutSubviewsBlock:^(UIButton *playButton, UIView *toolBar, UIButton *doneButton) { | |
| 263 | - | |
| 264 | - CGRect frame = toolBar.frame; | |
| 265 | - doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15 - (kIPVerticalBottomSafeHeight /2), 60, 30); | |
| 266 | - | |
| 267 | - }]; | |
| 268 | - //gif预览 | |
| 269 | - [self setGifPreviewPageDidLayoutSubviewsBlock:^(UIView *toolBar, UIButton *doneButton) { | |
| 270 | - | |
| 271 | - CGRect frame = toolBar.frame; | |
| 272 | - doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15 - (kIPVerticalBottomSafeHeight /2), 60, 30); | |
| 273 | - | |
| 274 | - }]; | |
| 275 | - //相册cell | |
| 276 | - | |
| 277 | - [self setAssetCellDidLayoutSubviewsBlock:^(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView) { | |
| 278 | - }]; | |
| 279 | - //相簿 | |
| 280 | - [self setAlbumCellDidLayoutSubviewsBlock:^(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel) { | |
| 281 | - | |
| 282 | - }]; | |
| 283 | - | |
| 284 | - //设置图片个数 | |
| 285 | - self.photoNumberIconImage = [UIImage new]; | |
| 286 | - | |
| 287 | -} | |
| 288 | - | |
| 289 | -- (void)didReceiveMemoryWarning { | |
| 290 | - [super didReceiveMemoryWarning]; | |
| 291 | - // Dispose of any resources that can be recreated. | |
| 292 | -} | |
| 293 | - | |
| 294 | - | |
| 295 | -@end |
CNLiveImagePickerController/CNImagePickerManager.h deleted
100644 → 0
| 1 | -// | |
| 2 | -// CNImagePickerManager.h | |
| 3 | -// CNLiveNetAdd | |
| 4 | -// | |
| 5 | -// Created by 梁星国 on 2018/10/8. | |
| 6 | -// Copyright © 2018年 cnlive. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <UIKit/UIKit.h> | |
| 10 | -#import <Foundation/Foundation.h> | |
| 11 | -#import "CNImagePickerController.h" | |
| 12 | - | |
| 13 | - | |
| 14 | -typedef void (^didFinishPickingPhotosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto);//选着图片返回 | |
| 15 | - | |
| 16 | -//typedef void (^failureBlock)(NSMutableArray* errorArray, NSError *error);//失败数组,错误 | |
| 17 | - | |
| 18 | -typedef NS_ENUM(NSUInteger, CNImagePickerEnterType) { | |
| 19 | - CNImagePickerEnterTypeFriendsCircle = 0,//朋友圈 | |
| 20 | - CNImagePickerEnterTypeComment = 1,//评论 | |
| 21 | - CNImagePickerEnterTypeSweepCode = 2,//扫码 | |
| 22 | - CNImagePickerEnterTypeChat = 3,//聊天 | |
| 23 | - CNImagePickerEnterTypeOther = 4,//其他 | |
| 24 | - CNImagePickerEnterTypeWitness = 5, //目击者 | |
| 25 | - | |
| 26 | -}; | |
| 27 | -@interface CNImagePickerManager : NSObject | |
| 28 | - | |
| 29 | - | |
| 30 | - | |
| 31 | -/** | |
| 32 | - 打开相册选择图片 | |
| 33 | - | |
| 34 | - @param maxImagesCount 最大图片 | |
| 35 | - @param columnNumber 列个数 | |
| 36 | - @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | |
| 37 | - @param pushPhotoPickerVc 是否跳转到相册 | |
| 38 | - @param isSelectOriginalPhoto 是否原图 | |
| 39 | - @param selectedAssets 选择图片 | |
| 40 | - @param enterType 进入方式 | |
| 41 | - @param pickingPhotosHandle 选择结束回调 | |
| 42 | - */ | |
| 43 | -+ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto selectedAssets:(NSMutableArray *)selectedAssets enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle; | |
| 44 | - | |
| 45 | -/** | |
| 46 | - 预览图片 | |
| 47 | - | |
| 48 | - @param maxImagesCount 最大限制 | |
| 49 | - @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | |
| 50 | - @param selectedAssets 选中Assets | |
| 51 | - @param selectedPhotos 选中Photos | |
| 52 | - @param index 选中第几个 | |
| 53 | - @param isSelectOriginalPhoto 是否原图 | |
| 54 | - @param enterType 进入方式 | |
| 55 | - @param pickingPhotosHandle 回调参数 | |
| 56 | - */ | |
| 57 | -+ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate selectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle; | |
| 58 | - | |
| 59 | - | |
| 60 | -/** | |
| 61 | - 预览单个视频 | |
| 62 | - | |
| 63 | - @param selectedAsset 选择视频 | |
| 64 | - @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | |
| 65 | - */ | |
| 66 | -+ (void)ImagePickerWithSelectedAsset:(id)selectedAsset delegate:(id<TZImagePickerControllerDelegate>)delegate; | |
| 67 | - | |
| 68 | - | |
| 69 | -/** | |
| 70 | - 保存图片 | |
| 71 | - | |
| 72 | - @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | |
| 73 | - @param image 图片 | |
| 74 | - @param location 地点 | |
| 75 | - @param success 成功 | |
| 76 | - @param failure 失败 | |
| 77 | - | |
| 78 | - */ | |
| 79 | -+(void)ImageManagerSavePhotoWithDelegate:(id<TZImagePickerControllerDelegate>)delegate image:(UIImage *)image location:(CLLocation *)location success:(void (^)(TZAssetModel *assetModel))success failure:(void (^)(NSError *error))failure; | |
| 80 | - | |
| 81 | - | |
| 82 | -/** | |
| 83 | - 判断Asset类型 | |
| 84 | - | |
| 85 | - @param asset asset | |
| 86 | - @return 返回类型 | |
| 87 | - */ | |
| 88 | -+ (TZAssetModelMediaType)getAssetType:(id)asset; | |
| 89 | - | |
| 90 | - | |
| 91 | -@end | |
| 92 | - |
CNLiveImagePickerController/CNImagePickerManager.m deleted
100644 → 0
| 1 | -// | |
| 2 | -// CNImagePickerManager.m | |
| 3 | -// CNLiveNetAdd | |
| 4 | -// | |
| 5 | -// Created by 梁星国 on 2018/10/8. | |
| 6 | -// Copyright © 2018年 cnlive. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "CNImagePickerManager.h" | |
| 10 | -#import <AssetsLibrary/AssetsLibrary.h> | |
| 11 | -#import "CNImagePickerCommonDefine.h" | |
| 12 | - | |
| 13 | -@implementation CNImagePickerManager | |
| 14 | - | |
| 15 | -/// 打开相册选择图片 | |
| 16 | -+ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto selectedAssets:(NSMutableArray *)selectedAssets enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle{ | |
| 17 | - | |
| 18 | - if (![delegate isKindOfClass:[UIViewController class]]) { | |
| 19 | - return; | |
| 20 | - } | |
| 21 | - UIViewController *vc = (UIViewController *)delegate; | |
| 22 | - | |
| 23 | - CNImagePickerController *imagePickerVc = [[CNImagePickerController alloc] initWithMaxImagesCount:maxImagesCount columnNumber:columnNumber delegate:delegate pushPhotoPickerVc:pushPhotoPickerVc]; | |
| 24 | - [imagePickerVc configTZImagePicker];//基本配置 | |
| 25 | - //特殊配置 | |
| 26 | - switch (enterType) { | |
| 27 | - case CNImagePickerEnterTypeFriendsCircle: | |
| 28 | - imagePickerVc.maxEditVideoTime = kFriendsCircle_limit_video_maxEdit;//编辑长度 | |
| 29 | - imagePickerVc.maxVideoDuration = kFriendsCircle_limit_video_max;//编辑视频最大长度 | |
| 30 | - // 3. 设置是否可以混选选择视频/图片/原图 | |
| 31 | - imagePickerVc.allowPickingMultipleVideo = NO; | |
| 32 | - break; | |
| 33 | - case CNImagePickerEnterTypeComment: | |
| 34 | - | |
| 35 | - break; | |
| 36 | - case CNImagePickerEnterTypeSweepCode: | |
| 37 | - | |
| 38 | - break; | |
| 39 | - case CNImagePickerEnterTypeChat: | |
| 40 | - imagePickerVc.maxEditVideoTime = kFriendsCircle_limit_video_maxEdit;//编辑长度 | |
| 41 | - imagePickerVc.maxVideoDuration = kChat_limit_video_length;//编辑视频最大长度 | |
| 42 | - | |
| 43 | - break; | |
| 44 | - case CNImagePickerEnterTypeWitness: | |
| 45 | - imagePickerVc.allowPickingImage = NO;// 显示图片 | |
| 46 | - imagePickerVc.allowPickingMultipleVideo = NO; | |
| 47 | - imagePickerVc.maxEditVideoTime = kFriendsCircle_limit_video_maxEdit;//编辑长度 | |
| 48 | - imagePickerVc.maxVideoDuration = kFriendsCircle_limit_video_max;//编辑视频最大长度 | |
| 49 | - break; | |
| 50 | - default://其他 | |
| 51 | - break; | |
| 52 | - } | |
| 53 | - | |
| 54 | - imagePickerVc.isSelectOriginalPhoto = isSelectOriginalPhoto;//是否选择原图 | |
| 55 | - // 1.设置目前已经选中的图片数组 | |
| 56 | - imagePickerVc.selectedAssets = selectedAssets; // 目前已经选中的图片数组 | |
| 57 | - /// 5. Single selection mode, valid when maxImagesCount = 1 | |
| 58 | - /// 5. 单选模式,maxImagesCount为1时才生效 | |
| 59 | - // 设置竖屏下的裁剪尺寸 | |
| 60 | - NSInteger left = 30; | |
| 61 | - NSInteger widthHeight = vc.view.frame.size.width - 2 * left; | |
| 62 | - NSInteger top = (vc.view.frame.size.height - widthHeight) / 2; | |
| 63 | - imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight); | |
| 64 | - // 你可以通过block或者代理,来得到用户选择的照片. | |
| 65 | - [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) { | |
| 66 | - // 回调参数 | |
| 67 | - !pickingPhotosHandle ? : pickingPhotosHandle(photos, assets, isSelectOriginalPhoto); | |
| 68 | - | |
| 69 | - }]; | |
| 70 | - | |
| 71 | - [vc presentViewController:imagePickerVc animated:YES completion:nil]; | |
| 72 | -} | |
| 73 | - | |
| 74 | -/// 预览图片 | |
| 75 | -+ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate selectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle { | |
| 76 | - | |
| 77 | - if (![delegate isKindOfClass:[UIViewController class]]) { | |
| 78 | - return; | |
| 79 | - } | |
| 80 | - UIViewController *vc = (UIViewController *)delegate; | |
| 81 | - | |
| 82 | - CNImagePickerController *imagePickerVc = [[CNImagePickerController alloc] initWithSelectedAssets:selectedAssets selectedPhotos:selectedPhotos index:index]; | |
| 83 | - [imagePickerVc configTZImagePicker];//基本配置 | |
| 84 | - //特殊配置 | |
| 85 | - switch (enterType) { | |
| 86 | - case CNImagePickerEnterTypeFriendsCircle: | |
| 87 | - imagePickerVc.allowPickingGif = NO; | |
| 88 | - imagePickerVc.allowPickingOriginalPhoto = YES; | |
| 89 | - imagePickerVc.allowPickingMultipleVideo = NO; | |
| 90 | - break; | |
| 91 | - case CNImagePickerEnterTypeComment: | |
| 92 | -#pragma mark - TODO:根据不同设置 | |
| 93 | - break; | |
| 94 | - case CNImagePickerEnterTypeSweepCode: | |
| 95 | - | |
| 96 | - break; | |
| 97 | - case CNImagePickerEnterTypeChat: | |
| 98 | - | |
| 99 | - break; | |
| 100 | - case CNImagePickerEnterTypeOther: | |
| 101 | - break; | |
| 102 | - default://其他 | |
| 103 | - break; | |
| 104 | - } | |
| 105 | - | |
| 106 | - imagePickerVc.maxImagesCount = maxImagesCount; | |
| 107 | - imagePickerVc.isSelectOriginalPhoto = isSelectOriginalPhoto; | |
| 108 | - | |
| 109 | - [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) { | |
| 110 | - // 回调参数 | |
| 111 | - !pickingPhotosHandle ? : pickingPhotosHandle(photos, assets, isSelectOriginalPhoto); | |
| 112 | - | |
| 113 | - }]; | |
| 114 | - [vc presentViewController:imagePickerVc animated:YES completion:nil]; | |
| 115 | - | |
| 116 | -} | |
| 117 | - | |
| 118 | -/// 预览单个视频 | |
| 119 | -+ (void)ImagePickerWithSelectedAsset:(id)selectedAsset delegate:(id<TZImagePickerControllerDelegate>)delegate { | |
| 120 | - | |
| 121 | - if (![delegate isKindOfClass:[UIViewController class]]) { | |
| 122 | - return; | |
| 123 | - } | |
| 124 | - UIViewController *vc = (UIViewController *)delegate; | |
| 125 | - | |
| 126 | - TZVideoPlayerController *videoVC = [[TZVideoPlayerController alloc] init]; | |
| 127 | - TZAssetModel *model = [TZAssetModel modelWithAsset:selectedAsset type:TZAssetModelMediaTypeVideo timeLength:@""]; | |
| 128 | - videoVC.isOther = YES;//(LXG)相册外进入 | |
| 129 | - videoVC.model = model; | |
| 130 | - [vc presentViewController:videoVC animated:YES completion:nil]; | |
| 131 | - | |
| 132 | -} | |
| 133 | - | |
| 134 | -/// 返回类型 | |
| 135 | -+ (TZAssetModelMediaType)getAssetType:(id)asset { | |
| 136 | - | |
| 137 | - TZAssetModelMediaType type = TZAssetModelMediaTypePhoto; | |
| 138 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 139 | - PHAsset *phAsset = (PHAsset *)asset; | |
| 140 | - if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo; | |
| 141 | - else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio; | |
| 142 | - else if (phAsset.mediaType == PHAssetMediaTypeImage) { | |
| 143 | - if (iOS9_1Later) { | |
| 144 | - if (@available(iOS 9.1, *)) { | |
| 145 | - if (phAsset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) | |
| 146 | - { | |
| 147 | - type = TZAssetModelMediaTypeLivePhoto; | |
| 148 | - } | |
| 149 | - } else { | |
| 150 | - // Fallback on earlier versions | |
| 151 | - } | |
| 152 | - } | |
| 153 | - // Gif | |
| 154 | - if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) { | |
| 155 | - type = TZAssetModelMediaTypePhotoGif; | |
| 156 | - } | |
| 157 | - } | |
| 158 | - } else { | |
| 159 | - if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) { | |
| 160 | - type = TZAssetModelMediaTypeVideo; | |
| 161 | - } | |
| 162 | - } | |
| 163 | - return type; | |
| 164 | -} | |
| 165 | - | |
| 166 | - | |
| 167 | -+(void)ImageManagerSavePhotoWithDelegate:(id<TZImagePickerControllerDelegate>)delegate image:(UIImage *)image location:(CLLocation *)location success:(void (^)(TZAssetModel *assetModel))success failure:(void (^)(NSError *error))failure { | |
| 168 | - | |
| 169 | - TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:delegate]; | |
| 170 | - tzImagePickerVc.sortAscendingByModificationDate = YES; | |
| 171 | - | |
| 172 | - [tzImagePickerVc showProgressHUD]; | |
| 173 | - // save photo and get asset / 保存图片,获取到asset | |
| 174 | - [[TZImageManager manager] savePhotoWithImage:image location:location completion:^(NSError *error){ | |
| 175 | - | |
| 176 | - if (error) { | |
| 177 | - [tzImagePickerVc hideProgressHUD]; | |
| 178 | - failure(error); | |
| 179 | - NSLog(@"图片保存失败 %@",error); | |
| 180 | - } else { | |
| 181 | - | |
| 182 | - [[TZImageManager manager] getCameraRollAlbum:NO allowPickingImage:YES needFetchAssets:YES completion:^(TZAlbumModel *model) { | |
| 183 | - [[TZImageManager manager] getAssetsFromFetchResult:model.result allowPickingVideo:NO allowPickingImage:YES completion:^(NSArray<TZAssetModel *> *models) { | |
| 184 | - [tzImagePickerVc hideProgressHUD]; | |
| 185 | - TZAssetModel *assetModel = [models firstObject]; | |
| 186 | - if (tzImagePickerVc.sortAscendingByModificationDate) { | |
| 187 | - assetModel = [models lastObject]; | |
| 188 | - } | |
| 189 | - success(assetModel); | |
| 190 | - }]; | |
| 191 | - | |
| 192 | - }]; | |
| 193 | - } | |
| 194 | - }]; | |
| 195 | - | |
| 196 | -} | |
| 197 | - | |
| 198 | - | |
| 199 | - | |
| 200 | - | |
| 201 | - | |
| 202 | - | |
| 203 | - | |
| 204 | - | |
| 205 | - | |
| 206 | -@end |
CNLiveImagePickerController/Classes/.gitkeep
0 → 100644
CNLiveImagePickerController/Classes/ReplaceMe.m
0 → 100644
CNLiveImagePickerController/NSBundle+TZImagePicker.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// NSBundle+TZImagePicker.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 16/08/18. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <UIKit/UIKit.h> | |
| 10 | - | |
| 11 | -@interface NSBundle (TZImagePicker) | |
| 12 | - | |
| 13 | -+ (NSBundle *)tz_imagePickerBundle; | |
| 14 | - | |
| 15 | -+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value; | |
| 16 | -+ (NSString *)tz_localizedStringForKey:(NSString *)key; | |
| 17 | - | |
| 18 | -@end | |
| 19 | - |
CNLiveImagePickerController/NSBundle+TZImagePicker.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// NSBundle+TZImagePicker.m | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 16/08/18. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "NSBundle+TZImagePicker.h" | |
| 10 | -#import "TZImagePickerController.h" | |
| 11 | - | |
| 12 | -@implementation NSBundle (TZImagePicker) | |
| 13 | - | |
| 14 | -+ (NSBundle *)tz_imagePickerBundle { | |
| 15 | - NSBundle *bundle = [NSBundle bundleForClass:[TZImagePickerController class]]; | |
| 16 | - NSURL *url = [bundle URLForResource:@"TZImagePickerController" withExtension:@"bundle"]; | |
| 17 | - bundle = [NSBundle bundleWithURL:url]; | |
| 18 | - return bundle; | |
| 19 | -} | |
| 20 | - | |
| 21 | -+ (NSString *)tz_localizedStringForKey:(NSString *)key { | |
| 22 | - return [self tz_localizedStringForKey:key value:@""]; | |
| 23 | -} | |
| 24 | - | |
| 25 | -+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value { | |
| 26 | - NSBundle *bundle = [TZImagePickerConfig sharedInstance].languageBundle; | |
| 27 | - NSString *value1 = [bundle localizedStringForKey:key value:value table:nil]; | |
| 28 | - return value1; | |
| 29 | -} | |
| 30 | - | |
| 31 | -@end |
CNLiveImagePickerController/TZAssetCell.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZAssetCell.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 15/12/24. | |
| 6 | -// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <UIKit/UIKit.h> | |
| 10 | -#import <Photos/Photos.h> | |
| 11 | - | |
| 12 | -typedef enum : NSUInteger { | |
| 13 | - TZAssetCellTypePhoto = 0, | |
| 14 | - TZAssetCellTypeLivePhoto, | |
| 15 | - TZAssetCellTypePhotoGif, | |
| 16 | - TZAssetCellTypeVideo, | |
| 17 | - TZAssetCellTypeAudio, | |
| 18 | -} TZAssetCellType; | |
| 19 | - | |
| 20 | -@class TZAssetModel; | |
| 21 | -@interface TZAssetCell : UICollectionViewCell | |
| 22 | -@property (weak, nonatomic) UIButton *selectPhotoButton; | |
| 23 | -@property (weak, nonatomic) UIButton *cannotSelectLayerButton; | |
| 24 | -@property (nonatomic, strong) TZAssetModel *model; | |
| 25 | -@property (assign, nonatomic) NSInteger index; | |
| 26 | -@property (nonatomic, copy) void (^didSelectPhotoBlock)(BOOL); | |
| 27 | -@property (nonatomic, assign) TZAssetCellType type; | |
| 28 | -@property (nonatomic, assign) BOOL allowPickingGif; | |
| 29 | -@property (nonatomic, assign) BOOL allowPickingMultipleVideo; | |
| 30 | -@property (nonatomic, copy) NSString *representedAssetIdentifier; | |
| 31 | -@property (nonatomic, assign) int32_t imageRequestID; | |
| 32 | - | |
| 33 | -@property (nonatomic, strong) UIImage *photoSelImage; | |
| 34 | -@property (nonatomic, strong) UIImage *photoDefImage; | |
| 35 | - | |
| 36 | -@property (nonatomic, assign) BOOL showSelectBtn; | |
| 37 | -@property (assign, nonatomic) BOOL allowPreview; | |
| 38 | -@property (assign, nonatomic) BOOL useCachedImage; | |
| 39 | - | |
| 40 | -@property (nonatomic, copy) void (^assetCellDidSetModelBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | |
| 41 | -@property (nonatomic, copy) void (^assetCellDidLayoutSubviewsBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | |
| 42 | -@end | |
| 43 | - | |
| 44 | - | |
| 45 | -@class TZAlbumModel; | |
| 46 | -@interface TZAlbumCell : UITableViewCell | |
| 47 | -@property (nonatomic, strong) TZAlbumModel *model; | |
| 48 | -@property (weak, nonatomic) UIButton *selectedCountButton; | |
| 49 | - | |
| 50 | -@property (nonatomic, copy) void (^albumCellDidSetModelBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | |
| 51 | -@property (nonatomic, copy) void (^albumCellDidLayoutSubviewsBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | |
| 52 | -@end | |
| 53 | - | |
| 54 | - | |
| 55 | -@interface TZAssetCameraCell : UICollectionViewCell | |
| 56 | -@property (nonatomic, strong) UIImageView *imageView; | |
| 57 | -@end |
CNLiveImagePickerController/TZAssetCell.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZAssetCell.m | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 15/12/24. | |
| 6 | -// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "TZAssetCell.h" | |
| 10 | -#import "TZAssetModel.h" | |
| 11 | -#import "UIView+Layout.h" | |
| 12 | -#import "TZImageManager.h" | |
| 13 | -#import "TZImagePickerController.h" | |
| 14 | -#import "TZProgressView.h" | |
| 15 | - | |
| 16 | -@interface TZAssetCell () | |
| 17 | -@property (weak, nonatomic) UIImageView *imageView; // The photo / 照片 | |
| 18 | -@property (weak, nonatomic) UIImageView *selectImageView; | |
| 19 | -@property (weak, nonatomic) UILabel *indexLabel; | |
| 20 | -@property (weak, nonatomic) UIView *bottomView; | |
| 21 | -@property (weak, nonatomic) UILabel *timeLength; | |
| 22 | -@property (strong, nonatomic) UITapGestureRecognizer *tapGesture; | |
| 23 | - | |
| 24 | -@property (nonatomic, weak) UIImageView *videoImgView; | |
| 25 | -@property (nonatomic, strong) TZProgressView *progressView; | |
| 26 | -@property (nonatomic, assign) int32_t bigImageRequestID; | |
| 27 | -@end | |
| 28 | - | |
| 29 | -@implementation TZAssetCell | |
| 30 | - | |
| 31 | -- (void)setModel:(TZAssetModel *)model { | |
| 32 | - _model = model; | |
| 33 | - if (iOS8Later) { | |
| 34 | - self.representedAssetIdentifier = [[TZImageManager manager] getAssetIdentifier:model.asset]; | |
| 35 | - } | |
| 36 | - if (self.useCachedImage && model.cachedImage) { | |
| 37 | - self.imageView.image = model.cachedImage; | |
| 38 | - } else { | |
| 39 | - self.model.cachedImage = nil; | |
| 40 | - int32_t imageRequestID = [[TZImageManager manager] getPhotoWithAsset:model.asset photoWidth:self.tz_width completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 41 | - // Set the cell's thumbnail image if it's still showing the same asset. | |
| 42 | - if (!iOS8Later) { | |
| 43 | - self.imageView.image = photo; | |
| 44 | - self.model.cachedImage = photo; | |
| 45 | - [self hideProgressView]; | |
| 46 | - return; | |
| 47 | - } | |
| 48 | - if ([self.representedAssetIdentifier isEqualToString:[[TZImageManager manager] getAssetIdentifier:model.asset]]) { | |
| 49 | - self.imageView.image = photo; | |
| 50 | - self.model.cachedImage = photo; | |
| 51 | - } else { | |
| 52 | - // NSLog(@"this cell is showing other asset"); | |
| 53 | - [[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID]; | |
| 54 | - } | |
| 55 | - if (!isDegraded) { | |
| 56 | - [self hideProgressView]; | |
| 57 | - self.imageRequestID = 0; | |
| 58 | - } | |
| 59 | - } progressHandler:nil networkAccessAllowed:NO]; | |
| 60 | - if (imageRequestID && self.imageRequestID && imageRequestID != self.imageRequestID) { | |
| 61 | - [[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID]; | |
| 62 | - // NSLog(@"cancelImageRequest %d",self.imageRequestID); | |
| 63 | - } | |
| 64 | - self.imageRequestID = imageRequestID; | |
| 65 | - } | |
| 66 | - self.selectPhotoButton.selected = model.isSelected; | |
| 67 | - self.selectImageView.image = self.selectPhotoButton.isSelected ? self.photoSelImage : self.photoDefImage; | |
| 68 | - self.indexLabel.hidden = !self.selectPhotoButton.isSelected; | |
| 69 | - | |
| 70 | - self.type = (NSInteger)model.type; | |
| 71 | - // 让宽度/高度小于 最小可选照片尺寸 的图片不能选中 | |
| 72 | - if (![[TZImageManager manager] isPhotoSelectableWithAsset:model.asset]) { | |
| 73 | - if (_selectImageView.hidden == NO) { | |
| 74 | - self.selectPhotoButton.hidden = YES; | |
| 75 | - _selectImageView.hidden = YES; | |
| 76 | - } | |
| 77 | - } | |
| 78 | - // 如果用户选中了该图片,提前获取一下大图 | |
| 79 | - if (model.isSelected) { | |
| 80 | - [self requestBigImage]; | |
| 81 | - } else { | |
| 82 | - [self cancelBigImageRequest]; | |
| 83 | - } | |
| 84 | - if (model.needOscillatoryAnimation) { | |
| 85 | - [UIView showOscillatoryAnimationWithLayer:self.selectImageView.layer type:TZOscillatoryAnimationToBigger]; | |
| 86 | - } | |
| 87 | - model.needOscillatoryAnimation = NO; | |
| 88 | - [self setNeedsLayout]; | |
| 89 | - | |
| 90 | - if (self.assetCellDidSetModelBlock) { | |
| 91 | - self.assetCellDidSetModelBlock(self, _imageView, _selectImageView, _indexLabel, _bottomView, _timeLength, _videoImgView); | |
| 92 | - } | |
| 93 | -} | |
| 94 | - | |
| 95 | -- (void)setIndex:(NSInteger)index { | |
| 96 | - _index = index; | |
| 97 | - self.indexLabel.text = [NSString stringWithFormat:@"%zd", index]; | |
| 98 | - [self.contentView bringSubviewToFront:self.indexLabel]; | |
| 99 | -} | |
| 100 | - | |
| 101 | -- (void)setShowSelectBtn:(BOOL)showSelectBtn { | |
| 102 | - _showSelectBtn = showSelectBtn; | |
| 103 | - BOOL selectable = [[TZImageManager manager] isPhotoSelectableWithAsset:self.model.asset]; | |
| 104 | - if (!self.selectPhotoButton.hidden) { | |
| 105 | - self.selectPhotoButton.hidden = !showSelectBtn || !selectable; | |
| 106 | - } | |
| 107 | - if (!self.selectImageView.hidden) { | |
| 108 | - self.selectImageView.hidden = !showSelectBtn || !selectable; | |
| 109 | - } | |
| 110 | -} | |
| 111 | - | |
| 112 | -- (void)setType:(TZAssetCellType)type { | |
| 113 | - _type = type; | |
| 114 | - if (type == TZAssetCellTypePhoto || type == TZAssetCellTypeLivePhoto || (type == TZAssetCellTypePhotoGif && !self.allowPickingGif) || self.allowPickingMultipleVideo) { | |
| 115 | - _selectImageView.hidden = NO; | |
| 116 | - _selectPhotoButton.hidden = NO; | |
| 117 | - _bottomView.hidden = YES; | |
| 118 | - } else { // Video of Gif | |
| 119 | - _selectImageView.hidden = YES; | |
| 120 | - _selectPhotoButton.hidden = YES; | |
| 121 | - } | |
| 122 | - | |
| 123 | - if (type == TZAssetCellTypeVideo) { | |
| 124 | - self.bottomView.hidden = NO; | |
| 125 | - self.timeLength.text = _model.timeLength; | |
| 126 | - self.videoImgView.hidden = NO; | |
| 127 | - _timeLength.tz_left = self.videoImgView.tz_right; | |
| 128 | - _timeLength.textAlignment = NSTextAlignmentRight; | |
| 129 | - } else if (type == TZAssetCellTypePhotoGif && self.allowPickingGif) { | |
| 130 | - self.bottomView.hidden = NO; | |
| 131 | - self.timeLength.text = @"GIF"; | |
| 132 | - self.videoImgView.hidden = YES; | |
| 133 | - _timeLength.tz_left = 5; | |
| 134 | - _timeLength.textAlignment = NSTextAlignmentLeft; | |
| 135 | - } | |
| 136 | -} | |
| 137 | - | |
| 138 | -- (void)setAllowPreview:(BOOL)allowPreview { | |
| 139 | - _allowPreview = allowPreview; | |
| 140 | - if (allowPreview) { | |
| 141 | - _imageView.userInteractionEnabled = NO; | |
| 142 | - _tapGesture.enabled = NO; | |
| 143 | - } else { | |
| 144 | - _imageView.userInteractionEnabled = YES; | |
| 145 | - _tapGesture.enabled = YES; | |
| 146 | - } | |
| 147 | -} | |
| 148 | - | |
| 149 | -- (void)selectPhotoButtonClick:(UIButton *)sender { | |
| 150 | - if (self.didSelectPhotoBlock) { | |
| 151 | - self.didSelectPhotoBlock(sender.isSelected); | |
| 152 | - } | |
| 153 | - self.selectImageView.image = sender.isSelected ? self.photoSelImage : self.photoDefImage; | |
| 154 | - if (sender.isSelected) { | |
| 155 | - if (![TZImagePickerConfig sharedInstance].showSelectedIndex && ![TZImagePickerConfig sharedInstance].showPhotoCannotSelectLayer) { | |
| 156 | - [UIView showOscillatoryAnimationWithLayer:_selectImageView.layer type:TZOscillatoryAnimationToBigger]; | |
| 157 | - } | |
| 158 | - // 用户选中了该图片,提前获取一下大图 | |
| 159 | - [self requestBigImage]; | |
| 160 | - } else { // 取消选中,取消大图的获取 | |
| 161 | - [self cancelBigImageRequest]; | |
| 162 | - } | |
| 163 | -} | |
| 164 | - | |
| 165 | -/// 只在单选状态且allowPreview为NO时会有该事件 | |
| 166 | -- (void)didTapImageView { | |
| 167 | - if (self.didSelectPhotoBlock) { | |
| 168 | - self.didSelectPhotoBlock(NO); | |
| 169 | - } | |
| 170 | -} | |
| 171 | - | |
| 172 | -- (void)hideProgressView { | |
| 173 | - if (_progressView) { | |
| 174 | - self.progressView.hidden = YES; | |
| 175 | - self.imageView.alpha = 1.0; | |
| 176 | - } | |
| 177 | -} | |
| 178 | - | |
| 179 | -- (void)requestBigImage { | |
| 180 | - if (_bigImageRequestID) { | |
| 181 | - [[PHImageManager defaultManager] cancelImageRequest:_bigImageRequestID]; | |
| 182 | - } | |
| 183 | - | |
| 184 | - _bigImageRequestID = [[TZImageManager manager] requestImageDataForAsset:_model.asset completion:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | |
| 185 | - [self hideProgressView]; | |
| 186 | - } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 187 | - if (self.model.isSelected) { | |
| 188 | - progress = progress > 0.02 ? progress : 0.02;; | |
| 189 | - self.progressView.progress = progress; | |
| 190 | - self.progressView.hidden = NO; | |
| 191 | - self.imageView.alpha = 0.4; | |
| 192 | - if (progress >= 1) { | |
| 193 | - [self hideProgressView]; | |
| 194 | - } | |
| 195 | - } else { | |
| 196 | - *stop = YES; | |
| 197 | - [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; | |
| 198 | - [self cancelBigImageRequest]; | |
| 199 | - } | |
| 200 | - }]; | |
| 201 | -} | |
| 202 | - | |
| 203 | -- (void)cancelBigImageRequest { | |
| 204 | - if (_bigImageRequestID) { | |
| 205 | - [[PHImageManager defaultManager] cancelImageRequest:_bigImageRequestID]; | |
| 206 | - } | |
| 207 | - [self hideProgressView]; | |
| 208 | -} | |
| 209 | - | |
| 210 | -#pragma mark - Lazy load | |
| 211 | - | |
| 212 | -- (UIButton *)selectPhotoButton { | |
| 213 | - if (_selectPhotoButton == nil) { | |
| 214 | - UIButton *selectPhotoButton = [[UIButton alloc] init]; | |
| 215 | - [selectPhotoButton addTarget:self action:@selector(selectPhotoButtonClick:) forControlEvents:UIControlEventTouchUpInside]; | |
| 216 | - [self.contentView addSubview:selectPhotoButton]; | |
| 217 | - _selectPhotoButton = selectPhotoButton; | |
| 218 | - } | |
| 219 | - return _selectPhotoButton; | |
| 220 | -} | |
| 221 | - | |
| 222 | -- (UIImageView *)imageView { | |
| 223 | - if (_imageView == nil) { | |
| 224 | - UIImageView *imageView = [[UIImageView alloc] init]; | |
| 225 | - imageView.contentMode = UIViewContentModeScaleAspectFill; | |
| 226 | - imageView.clipsToBounds = YES; | |
| 227 | - [self.contentView addSubview:imageView]; | |
| 228 | - _imageView = imageView; | |
| 229 | - | |
| 230 | - _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapImageView)]; | |
| 231 | - [_imageView addGestureRecognizer:_tapGesture]; | |
| 232 | - } | |
| 233 | - return _imageView; | |
| 234 | -} | |
| 235 | - | |
| 236 | -- (UIImageView *)selectImageView { | |
| 237 | - if (_selectImageView == nil) { | |
| 238 | - UIImageView *selectImageView = [[UIImageView alloc] init]; | |
| 239 | - selectImageView.contentMode = UIViewContentModeCenter; | |
| 240 | - selectImageView.clipsToBounds = YES; | |
| 241 | - [self.contentView addSubview:selectImageView]; | |
| 242 | - _selectImageView = selectImageView; | |
| 243 | - } | |
| 244 | - return _selectImageView; | |
| 245 | -} | |
| 246 | - | |
| 247 | -- (UIView *)bottomView { | |
| 248 | - if (_bottomView == nil) { | |
| 249 | - UIView *bottomView = [[UIView alloc] init]; | |
| 250 | - static NSInteger rgb = 0; | |
| 251 | - bottomView.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.8]; | |
| 252 | - [self.contentView addSubview:bottomView]; | |
| 253 | - _bottomView = bottomView; | |
| 254 | - } | |
| 255 | - return _bottomView; | |
| 256 | -} | |
| 257 | - | |
| 258 | -- (UIButton *)cannotSelectLayerButton { | |
| 259 | - if (_cannotSelectLayerButton == nil) { | |
| 260 | - UIButton *cannotSelectLayerButton = [[UIButton alloc] init]; | |
| 261 | - [self.contentView addSubview:cannotSelectLayerButton]; | |
| 262 | - _cannotSelectLayerButton = cannotSelectLayerButton; | |
| 263 | - } | |
| 264 | - return _cannotSelectLayerButton; | |
| 265 | -} | |
| 266 | - | |
| 267 | -- (UIImageView *)videoImgView { | |
| 268 | - if (_videoImgView == nil) { | |
| 269 | - UIImageView *videoImgView = [[UIImageView alloc] init]; | |
| 270 | - [videoImgView setImage:[UIImage imageNamedFromMyBundle:@"VideoSendIcon"]]; | |
| 271 | - [self.bottomView addSubview:videoImgView]; | |
| 272 | - _videoImgView = videoImgView; | |
| 273 | - } | |
| 274 | - return _videoImgView; | |
| 275 | -} | |
| 276 | - | |
| 277 | -- (UILabel *)timeLength { | |
| 278 | - if (_timeLength == nil) { | |
| 279 | - UILabel *timeLength = [[UILabel alloc] init]; | |
| 280 | - timeLength.font = [UIFont boldSystemFontOfSize:11]; | |
| 281 | - timeLength.textColor = [UIColor whiteColor]; | |
| 282 | - timeLength.textAlignment = NSTextAlignmentRight; | |
| 283 | - [self.bottomView addSubview:timeLength]; | |
| 284 | - _timeLength = timeLength; | |
| 285 | - } | |
| 286 | - return _timeLength; | |
| 287 | -} | |
| 288 | - | |
| 289 | -- (UILabel *)indexLabel { | |
| 290 | - if (_indexLabel == nil) { | |
| 291 | - UILabel *indexLabel = [[UILabel alloc] init]; | |
| 292 | - indexLabel.font = [UIFont systemFontOfSize:14]; | |
| 293 | - indexLabel.textColor = [UIColor whiteColor]; | |
| 294 | - indexLabel.textAlignment = NSTextAlignmentCenter; | |
| 295 | - [self.contentView addSubview:indexLabel]; | |
| 296 | - _indexLabel = indexLabel; | |
| 297 | - } | |
| 298 | - return _indexLabel; | |
| 299 | -} | |
| 300 | - | |
| 301 | -- (TZProgressView *)progressView { | |
| 302 | - if (_progressView == nil) { | |
| 303 | - _progressView = [[TZProgressView alloc] init]; | |
| 304 | - _progressView.hidden = YES; | |
| 305 | - [self addSubview:_progressView]; | |
| 306 | - } | |
| 307 | - return _progressView; | |
| 308 | -} | |
| 309 | - | |
| 310 | -- (void)layoutSubviews { | |
| 311 | - [super layoutSubviews]; | |
| 312 | - _cannotSelectLayerButton.frame = self.bounds; | |
| 313 | - if (self.allowPreview) { | |
| 314 | - _selectPhotoButton.frame = CGRectMake(self.tz_width - 44, 0, 44, 44); | |
| 315 | - } else { | |
| 316 | - _selectPhotoButton.frame = self.bounds; | |
| 317 | - } | |
| 318 | - _selectImageView.frame = CGRectMake(self.tz_width - 27, 3, 24, 24); | |
| 319 | - if (_selectImageView.image.size.width <= 27) { | |
| 320 | - _selectImageView.contentMode = UIViewContentModeCenter; | |
| 321 | - } else { | |
| 322 | - _selectImageView.contentMode = UIViewContentModeScaleAspectFit; | |
| 323 | - } | |
| 324 | - _indexLabel.frame = _selectImageView.frame; | |
| 325 | - _imageView.frame = CGRectMake(0, 0, self.tz_width, self.tz_height); | |
| 326 | - | |
| 327 | - static CGFloat progressWH = 20; | |
| 328 | - CGFloat progressXY = (self.tz_width - progressWH) / 2; | |
| 329 | - _progressView.frame = CGRectMake(progressXY, progressXY, progressWH, progressWH); | |
| 330 | - | |
| 331 | - _bottomView.frame = CGRectMake(0, self.tz_height - 17, self.tz_width, 17); | |
| 332 | - _videoImgView.frame = CGRectMake(8, 0, 17, 17); | |
| 333 | - _timeLength.frame = CGRectMake(self.videoImgView.tz_right, 0, self.tz_width - self.videoImgView.tz_right - 5, 17); | |
| 334 | - | |
| 335 | - self.type = (NSInteger)self.model.type; | |
| 336 | - self.showSelectBtn = self.showSelectBtn; | |
| 337 | - | |
| 338 | - [self.contentView bringSubviewToFront:_bottomView]; | |
| 339 | - [self.contentView bringSubviewToFront:_cannotSelectLayerButton]; | |
| 340 | - [self.contentView bringSubviewToFront:_selectPhotoButton]; | |
| 341 | - [self.contentView bringSubviewToFront:_selectImageView]; | |
| 342 | - [self.contentView bringSubviewToFront:_indexLabel]; | |
| 343 | - | |
| 344 | - if (self.assetCellDidLayoutSubviewsBlock) { | |
| 345 | - self.assetCellDidLayoutSubviewsBlock(self, _imageView, _selectImageView, _indexLabel, _bottomView, _timeLength, _videoImgView); | |
| 346 | - } | |
| 347 | -} | |
| 348 | - | |
| 349 | -@end | |
| 350 | - | |
| 351 | -@interface TZAlbumCell () | |
| 352 | -@property (weak, nonatomic) UIImageView *posterImageView; | |
| 353 | -@property (weak, nonatomic) UILabel *titleLabel; | |
| 354 | -@end | |
| 355 | - | |
| 356 | -@implementation TZAlbumCell | |
| 357 | - | |
| 358 | -- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { | |
| 359 | - self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; | |
| 360 | - self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; | |
| 361 | - return self; | |
| 362 | -} | |
| 363 | - | |
| 364 | -- (void)setModel:(TZAlbumModel *)model { | |
| 365 | - _model = model; | |
| 366 | - | |
| 367 | - NSMutableAttributedString *nameString = [[NSMutableAttributedString alloc] initWithString:model.name attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor blackColor]}]; | |
| 368 | - NSAttributedString *countString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" (%zd)",model.count] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor lightGrayColor]}]; | |
| 369 | - [nameString appendAttributedString:countString]; | |
| 370 | - self.titleLabel.attributedText = nameString; | |
| 371 | - [[TZImageManager manager] getPostImageWithAlbumModel:model completion:^(UIImage *postImage) { | |
| 372 | - self.posterImageView.image = postImage; | |
| 373 | - }]; | |
| 374 | - if (model.selectedCount) { | |
| 375 | - self.selectedCountButton.hidden = NO; | |
| 376 | - [self.selectedCountButton setTitle:[NSString stringWithFormat:@"%zd",model.selectedCount] forState:UIControlStateNormal]; | |
| 377 | - } else { | |
| 378 | - self.selectedCountButton.hidden = YES; | |
| 379 | - } | |
| 380 | - | |
| 381 | - if (self.albumCellDidSetModelBlock) { | |
| 382 | - self.albumCellDidSetModelBlock(self, _posterImageView, _titleLabel); | |
| 383 | - } | |
| 384 | -} | |
| 385 | - | |
| 386 | -/// For fitting iOS6 | |
| 387 | -- (void)layoutSubviews { | |
| 388 | - if (iOS7Later) [super layoutSubviews]; | |
| 389 | - _selectedCountButton.frame = CGRectMake(self.tz_width - 24 - 30, 23, 24, 24); | |
| 390 | - NSInteger titleHeight = ceil(self.titleLabel.font.lineHeight); | |
| 391 | - self.titleLabel.frame = CGRectMake(80, (self.tz_height - titleHeight) / 2, self.tz_width - 80 - 50, titleHeight); | |
| 392 | - self.posterImageView.frame = CGRectMake(0, 0, 70, 70); | |
| 393 | - | |
| 394 | - if (self.albumCellDidLayoutSubviewsBlock) { | |
| 395 | - self.albumCellDidLayoutSubviewsBlock(self, _posterImageView, _titleLabel); | |
| 396 | - } | |
| 397 | -} | |
| 398 | - | |
| 399 | -- (void)layoutSublayersOfLayer:(CALayer *)layer { | |
| 400 | - if (iOS7Later) [super layoutSublayersOfLayer:layer]; | |
| 401 | -} | |
| 402 | - | |
| 403 | -#pragma mark - Lazy load | |
| 404 | - | |
| 405 | -- (UIImageView *)posterImageView { | |
| 406 | - if (_posterImageView == nil) { | |
| 407 | - UIImageView *posterImageView = [[UIImageView alloc] init]; | |
| 408 | - posterImageView.contentMode = UIViewContentModeScaleAspectFill; | |
| 409 | - posterImageView.clipsToBounds = YES; | |
| 410 | - [self.contentView addSubview:posterImageView]; | |
| 411 | - _posterImageView = posterImageView; | |
| 412 | - } | |
| 413 | - return _posterImageView; | |
| 414 | -} | |
| 415 | - | |
| 416 | -- (UILabel *)titleLabel { | |
| 417 | - if (_titleLabel == nil) { | |
| 418 | - UILabel *titleLabel = [[UILabel alloc] init]; | |
| 419 | - titleLabel.font = [UIFont boldSystemFontOfSize:17]; | |
| 420 | - titleLabel.textColor = [UIColor blackColor]; | |
| 421 | - titleLabel.textAlignment = NSTextAlignmentLeft; | |
| 422 | - [self.contentView addSubview:titleLabel]; | |
| 423 | - _titleLabel = titleLabel; | |
| 424 | - } | |
| 425 | - return _titleLabel; | |
| 426 | -} | |
| 427 | - | |
| 428 | -- (UIButton *)selectedCountButton { | |
| 429 | - if (_selectedCountButton == nil) { | |
| 430 | - UIButton *selectedCountButton = [[UIButton alloc] init]; | |
| 431 | - selectedCountButton.layer.cornerRadius = 12; | |
| 432 | - selectedCountButton.clipsToBounds = YES; | |
| 433 | - selectedCountButton.backgroundColor = [UIColor redColor]; | |
| 434 | - [selectedCountButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 435 | - selectedCountButton.titleLabel.font = [UIFont systemFontOfSize:15]; | |
| 436 | - [self.contentView addSubview:selectedCountButton]; | |
| 437 | - _selectedCountButton = selectedCountButton; | |
| 438 | - } | |
| 439 | - return _selectedCountButton; | |
| 440 | -} | |
| 441 | - | |
| 442 | -@end | |
| 443 | - | |
| 444 | - | |
| 445 | - | |
| 446 | -@implementation TZAssetCameraCell | |
| 447 | - | |
| 448 | -- (instancetype)initWithFrame:(CGRect)frame { | |
| 449 | - self = [super initWithFrame:frame]; | |
| 450 | - if (self) { | |
| 451 | - self.backgroundColor = [UIColor whiteColor]; | |
| 452 | - _imageView = [[UIImageView alloc] init]; | |
| 453 | - _imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500]; | |
| 454 | - _imageView.contentMode = UIViewContentModeScaleAspectFill; | |
| 455 | - [self.contentView addSubview:_imageView]; | |
| 456 | - self.clipsToBounds = YES; | |
| 457 | - } | |
| 458 | - return self; | |
| 459 | -} | |
| 460 | - | |
| 461 | -- (void)layoutSubviews { | |
| 462 | - [super layoutSubviews]; | |
| 463 | - _imageView.frame = self.bounds; | |
| 464 | -} | |
| 465 | - | |
| 466 | -@end |
CNLiveImagePickerController/TZAssetModel.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZAssetModel.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 15/12/24. | |
| 6 | -// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <Foundation/Foundation.h> | |
| 10 | -#import <UIKit/UIKit.h> | |
| 11 | - | |
| 12 | -typedef enum : NSUInteger { | |
| 13 | - TZAssetModelMediaTypePhoto = 0, | |
| 14 | - TZAssetModelMediaTypeLivePhoto, | |
| 15 | - TZAssetModelMediaTypePhotoGif, | |
| 16 | - TZAssetModelMediaTypeVideo, | |
| 17 | - TZAssetModelMediaTypeAudio | |
| 18 | -} TZAssetModelMediaType; | |
| 19 | - | |
| 20 | -@class PHAsset; | |
| 21 | -@interface TZAssetModel : NSObject | |
| 22 | - | |
| 23 | -@property (nonatomic, strong) id asset; ///< PHAsset or ALAsset | |
| 24 | -@property (nonatomic, assign) BOOL isSelected; ///< The select status of a photo, default is No | |
| 25 | -@property (nonatomic, assign) TZAssetModelMediaType type; | |
| 26 | -@property (assign, nonatomic) BOOL needOscillatoryAnimation; | |
| 27 | -@property (nonatomic, copy) NSString *timeLength; | |
| 28 | -@property (strong, nonatomic) UIImage *cachedImage; | |
| 29 | - | |
| 30 | -/// Init a photo dataModel With a asset | |
| 31 | -/// 用一个PHAsset/ALAsset实例,初始化一个照片模型 | |
| 32 | -+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type; | |
| 33 | -+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength; | |
| 34 | - | |
| 35 | -@end | |
| 36 | - | |
| 37 | - | |
| 38 | -@class PHFetchResult; | |
| 39 | -@interface TZAlbumModel : NSObject | |
| 40 | - | |
| 41 | -@property (nonatomic, strong) NSString *name; ///< The album name | |
| 42 | -@property (nonatomic, assign) NSInteger count; ///< Count of photos the album contain | |
| 43 | -@property (nonatomic, strong) id result; ///< PHFetchResult<PHAsset> or ALAssetsGroup<ALAsset> | |
| 44 | - | |
| 45 | -@property (nonatomic, strong) NSArray *models; | |
| 46 | -@property (nonatomic, strong) NSArray *selectedModels; | |
| 47 | -@property (nonatomic, assign) NSUInteger selectedCount; | |
| 48 | - | |
| 49 | -@property (nonatomic, assign) BOOL isCameraRoll; | |
| 50 | - | |
| 51 | -- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets; | |
| 52 | - | |
| 53 | -@end |
CNLiveImagePickerController/TZAssetModel.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZAssetModel.m | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 15/12/24. | |
| 6 | -// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "TZAssetModel.h" | |
| 10 | -#import "TZImageManager.h" | |
| 11 | - | |
| 12 | -@implementation TZAssetModel | |
| 13 | - | |
| 14 | -+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type{ | |
| 15 | - TZAssetModel *model = [[TZAssetModel alloc] init]; | |
| 16 | - model.asset = asset; | |
| 17 | - model.isSelected = NO; | |
| 18 | - model.type = type; | |
| 19 | - return model; | |
| 20 | -} | |
| 21 | - | |
| 22 | -+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength { | |
| 23 | - TZAssetModel *model = [self modelWithAsset:asset type:type]; | |
| 24 | - model.timeLength = timeLength; | |
| 25 | - return model; | |
| 26 | -} | |
| 27 | - | |
| 28 | -@end | |
| 29 | - | |
| 30 | - | |
| 31 | - | |
| 32 | -@implementation TZAlbumModel | |
| 33 | - | |
| 34 | -- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets { | |
| 35 | - _result = result; | |
| 36 | - if (needFetchAssets) { | |
| 37 | - [[TZImageManager manager] getAssetsFromFetchResult:result completion:^(NSArray<TZAssetModel *> *models) { | |
| 38 | - self->_models = models; | |
| 39 | - if (self->_selectedModels) { | |
| 40 | - [self checkSelectedModels]; | |
| 41 | - } | |
| 42 | - }]; | |
| 43 | - } | |
| 44 | -} | |
| 45 | - | |
| 46 | -- (void)setSelectedModels:(NSArray *)selectedModels { | |
| 47 | - _selectedModels = selectedModels; | |
| 48 | - if (_models) { | |
| 49 | - [self checkSelectedModels]; | |
| 50 | - } | |
| 51 | -} | |
| 52 | - | |
| 53 | -- (void)checkSelectedModels { | |
| 54 | - self.selectedCount = 0; | |
| 55 | - NSMutableArray *selectedAssets = [NSMutableArray array]; | |
| 56 | - for (TZAssetModel *model in _selectedModels) { | |
| 57 | - [selectedAssets addObject:model.asset]; | |
| 58 | - } | |
| 59 | - for (TZAssetModel *model in _models) { | |
| 60 | - if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) { | |
| 61 | - self.selectedCount ++; | |
| 62 | - } | |
| 63 | - } | |
| 64 | -} | |
| 65 | - | |
| 66 | -- (NSString *)name { | |
| 67 | - if (_name) { | |
| 68 | - return _name; | |
| 69 | - } | |
| 70 | - return @""; | |
| 71 | -} | |
| 72 | - | |
| 73 | -@end |
CNLiveImagePickerController/TZGifPhotoPreviewController.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZGifPhotoPreviewController.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by ttouch on 2016/12/13. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import <UIKit/UIKit.h> | |
| 10 | - | |
| 11 | -@class TZAssetModel; | |
| 12 | -@interface TZGifPhotoPreviewController : UIViewController | |
| 13 | - | |
| 14 | -@property (nonatomic, strong) TZAssetModel *model; | |
| 15 | - | |
| 16 | -@end |
CNLiveImagePickerController/TZGifPhotoPreviewController.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZGifPhotoPreviewController.m | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by ttouch on 2016/12/13. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "TZGifPhotoPreviewController.h" | |
| 10 | -#import "TZImagePickerController.h" | |
| 11 | -#import "TZAssetModel.h" | |
| 12 | -#import "UIView+Layout.h" | |
| 13 | -#import "TZPhotoPreviewCell.h" | |
| 14 | -#import "TZImageManager.h" | |
| 15 | - | |
| 16 | -#pragma clang diagnostic push | |
| 17 | -#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 18 | - | |
| 19 | -@interface TZGifPhotoPreviewController () { | |
| 20 | - UIView *_toolBar; | |
| 21 | - UIButton *_doneButton; | |
| 22 | - UIProgressView *_progress; | |
| 23 | - | |
| 24 | - TZPhotoPreviewView *_previewView; | |
| 25 | - | |
| 26 | - UIStatusBarStyle _originStatusBarStyle; | |
| 27 | -} | |
| 28 | -@end | |
| 29 | - | |
| 30 | -@implementation TZGifPhotoPreviewController | |
| 31 | - | |
| 32 | -- (void)viewDidLoad { | |
| 33 | - [super viewDidLoad]; | |
| 34 | - self.view.backgroundColor = [UIColor blackColor]; | |
| 35 | - TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 36 | - if (tzImagePickerVc) { | |
| 37 | - self.navigationItem.title = [NSString stringWithFormat:@"GIF %@",tzImagePickerVc.previewBtnTitleStr]; | |
| 38 | - } | |
| 39 | - [self configPreviewView]; | |
| 40 | - [self configBottomToolBar]; | |
| 41 | -} | |
| 42 | - | |
| 43 | -- (void)viewWillAppear:(BOOL)animated { | |
| 44 | - [super viewWillAppear:animated]; | |
| 45 | - _originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; | |
| 46 | - [UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque; | |
| 47 | -} | |
| 48 | - | |
| 49 | -- (void)viewWillDisappear:(BOOL)animated { | |
| 50 | - [super viewWillDisappear:animated]; | |
| 51 | - [UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle; | |
| 52 | -} | |
| 53 | - | |
| 54 | -- (void)configPreviewView { | |
| 55 | - _previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero]; | |
| 56 | - _previewView.model = self.model; | |
| 57 | - __weak typeof(self) weakSelf = self; | |
| 58 | - [_previewView setSingleTapGestureBlock:^{ | |
| 59 | - __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 60 | - [strongSelf signleTapAction]; | |
| 61 | - }]; | |
| 62 | - [self.view addSubview:_previewView]; | |
| 63 | -} | |
| 64 | - | |
| 65 | -- (void)configBottomToolBar { | |
| 66 | - _toolBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 67 | - CGFloat rgb = 34 / 255.0; | |
| 68 | - _toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7]; | |
| 69 | - | |
| 70 | - _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 71 | - _doneButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 72 | - [_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 73 | - TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 74 | - if (tzImagePickerVc) { | |
| 75 | - [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal]; | |
| 76 | - [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal]; | |
| 77 | - } else { | |
| 78 | - [_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal]; | |
| 79 | - [_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal]; | |
| 80 | - } | |
| 81 | - [_toolBar addSubview:_doneButton]; | |
| 82 | - | |
| 83 | - UILabel *byteLabel = [[UILabel alloc] init]; | |
| 84 | - byteLabel.textColor = [UIColor whiteColor]; | |
| 85 | - byteLabel.font = [UIFont systemFontOfSize:13]; | |
| 86 | - byteLabel.frame = CGRectMake(10, 0, 100, 44); | |
| 87 | - [[TZImageManager manager] getPhotosBytesWithArray:@[_model] completion:^(NSString *totalBytes) { | |
| 88 | - byteLabel.text = totalBytes; | |
| 89 | - }]; | |
| 90 | - [_toolBar addSubview:byteLabel]; | |
| 91 | - | |
| 92 | - [self.view addSubview:_toolBar]; | |
| 93 | - | |
| 94 | - if (tzImagePickerVc.gifPreviewPageUIConfigBlock) { | |
| 95 | - tzImagePickerVc.gifPreviewPageUIConfigBlock(_toolBar, _doneButton); | |
| 96 | - } | |
| 97 | -} | |
| 98 | - | |
| 99 | -#pragma mark - Layout | |
| 100 | - | |
| 101 | -- (void)viewDidLayoutSubviews { | |
| 102 | - [super viewDidLayoutSubviews]; | |
| 103 | - | |
| 104 | - _previewView.frame = self.view.bounds; | |
| 105 | - _previewView.scrollView.frame = self.view.bounds; | |
| 106 | - CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44; | |
| 107 | - _toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight); | |
| 108 | - _doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44); | |
| 109 | - | |
| 110 | - TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 111 | - if (tzImagePickerVc.gifPreviewPageDidLayoutSubviewsBlock) { | |
| 112 | - tzImagePickerVc.gifPreviewPageDidLayoutSubviewsBlock(_toolBar, _doneButton); | |
| 113 | - } | |
| 114 | -} | |
| 115 | - | |
| 116 | -#pragma mark - Click Event | |
| 117 | - | |
| 118 | -- (void)signleTapAction { | |
| 119 | - _toolBar.hidden = !_toolBar.isHidden; | |
| 120 | - [self.navigationController setNavigationBarHidden:_toolBar.isHidden]; | |
| 121 | - TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 122 | - if (iOS7Later) { | |
| 123 | - if (_toolBar.isHidden) { | |
| 124 | - [UIApplication sharedApplication].statusBarHidden = YES; | |
| 125 | - } else if (tzImagePickerVc.needShowStatusBar) { | |
| 126 | - [UIApplication sharedApplication].statusBarHidden = NO; | |
| 127 | - } | |
| 128 | - } | |
| 129 | -} | |
| 130 | - | |
| 131 | -- (void)doneButtonClick { | |
| 132 | - if (self.navigationController) { | |
| 133 | - TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 134 | - if (imagePickerVc.autoDismiss) { | |
| 135 | - [self.navigationController dismissViewControllerAnimated:YES completion:^{ | |
| 136 | - [self callDelegateMethod]; | |
| 137 | - }]; | |
| 138 | - } else { | |
| 139 | - [self callDelegateMethod]; | |
| 140 | - } | |
| 141 | - } else { | |
| 142 | - [self dismissViewControllerAnimated:YES completion:^{ | |
| 143 | - [self callDelegateMethod]; | |
| 144 | - }]; | |
| 145 | - } | |
| 146 | -} | |
| 147 | - | |
| 148 | -- (void)callDelegateMethod { | |
| 149 | - TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 150 | - UIImage *animatedImage = _previewView.imageView.image; | |
| 151 | - if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingGifImage:sourceAssets:)]) { | |
| 152 | - [imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingGifImage:animatedImage sourceAssets:_model.asset]; | |
| 153 | - } | |
| 154 | - if (imagePickerVc.didFinishPickingGifImageHandle) { | |
| 155 | - imagePickerVc.didFinishPickingGifImageHandle(animatedImage,_model.asset); | |
| 156 | - } | |
| 157 | -} | |
| 158 | - | |
| 159 | -#pragma clang diagnostic pop | |
| 160 | - | |
| 161 | -@end |
CNLiveImagePickerController/TZImageCropManager.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZImageCropManager.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 2016/12/5. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// 图片裁剪管理类 | |
| 8 | - | |
| 9 | -#import <Foundation/Foundation.h> | |
| 10 | -#import <UIKit/UIKit.h> | |
| 11 | - | |
| 12 | -@interface TZImageCropManager : NSObject | |
| 13 | - | |
| 14 | -/// 裁剪框背景的处理 | |
| 15 | -+ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop; | |
| 16 | - | |
| 17 | -/* | |
| 18 | - 1.7.2 为了解决多位同学对于图片裁剪的需求,我这两天有空便在研究图片裁剪 | |
| 19 | - 幸好有tuyou的PhotoTweaks库做参考,裁剪的功能实现起来简单许多 | |
| 20 | - 该方法和其内部引用的方法基本来自于tuyou的PhotoTweaks库,我做了稍许删减和修改 | |
| 21 | - 感谢tuyou同学在github开源了优秀的裁剪库PhotoTweaks,表示感谢 | |
| 22 | - PhotoTweaks库的github链接:https://github.com/itouch2/PhotoTweaks | |
| 23 | - */ | |
| 24 | -/// 获得裁剪后的图片 | |
| 25 | -+ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView; | |
| 26 | - | |
| 27 | -/// 获取圆形图片 | |
| 28 | -+ (UIImage *)circularClipImage:(UIImage *)image; | |
| 29 | - | |
| 30 | -@end | |
| 31 | - | |
| 32 | - | |
| 33 | -/// 该分类的代码来自SDWebImage:https://github.com/rs/SDWebImage | |
| 34 | -/// 为了防止冲突,我将分类名字和方法名字做了修改 | |
| 35 | -@interface UIImage (TZGif) | |
| 36 | - | |
| 37 | -+ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data; | |
| 38 | - | |
| 39 | -@end |
CNLiveImagePickerController/TZImageCropManager.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZImageCropManager.m | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 2016/12/5. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "TZImageCropManager.h" | |
| 10 | -#import "UIView+Layout.h" | |
| 11 | -#import <ImageIO/ImageIO.h> | |
| 12 | -#import "TZImageManager.h" | |
| 13 | -#import "TZImagePickerController.h" | |
| 14 | - | |
| 15 | -@implementation TZImageCropManager | |
| 16 | - | |
| 17 | -/// 裁剪框背景的处理 | |
| 18 | -+ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop { | |
| 19 | - UIBezierPath *path= [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds]; | |
| 20 | - CAShapeLayer *layer = [CAShapeLayer layer]; | |
| 21 | - if (needCircleCrop) { // 圆形裁剪框 | |
| 22 | - [path appendPath:[UIBezierPath bezierPathWithArcCenter:containerView.center radius:cropRect.size.width / 2 startAngle:0 endAngle: 2 * M_PI clockwise:NO]]; | |
| 23 | - } else { // 矩形裁剪框 | |
| 24 | - [path appendPath:[UIBezierPath bezierPathWithRect:cropRect]]; | |
| 25 | - } | |
| 26 | - layer.path = path.CGPath; | |
| 27 | - layer.fillRule = kCAFillRuleEvenOdd; | |
| 28 | - layer.fillColor = [[UIColor blackColor] CGColor]; | |
| 29 | - layer.opacity = 0.5; | |
| 30 | - [view.layer addSublayer:layer]; | |
| 31 | -} | |
| 32 | - | |
| 33 | -/// 获得裁剪后的图片 | |
| 34 | -+ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView { | |
| 35 | - CGAffineTransform transform = CGAffineTransformIdentity; | |
| 36 | - // 平移的处理 | |
| 37 | - CGRect imageViewRect = [imageView convertRect:imageView.bounds toView:containerView]; | |
| 38 | - CGPoint point = CGPointMake(imageViewRect.origin.x + imageViewRect.size.width / 2, imageViewRect.origin.y + imageViewRect.size.height / 2); | |
| 39 | - CGFloat xMargin = containerView.tz_width - CGRectGetMaxX(rect) - rect.origin.x; | |
| 40 | - CGPoint zeroPoint = CGPointMake((CGRectGetWidth(containerView.frame) - xMargin) / 2, containerView.center.y); | |
| 41 | - CGPoint translation = CGPointMake(point.x - zeroPoint.x, point.y - zeroPoint.y); | |
| 42 | - transform = CGAffineTransformTranslate(transform, translation.x, translation.y); | |
| 43 | - // 缩放的处理 | |
| 44 | - transform = CGAffineTransformScale(transform, zoomScale, zoomScale); | |
| 45 | - | |
| 46 | - CGImageRef imageRef = [self newTransformedImage:transform | |
| 47 | - sourceImage:imageView.image.CGImage | |
| 48 | - sourceSize:imageView.image.size | |
| 49 | - outputWidth:rect.size.width * [UIScreen mainScreen].scale | |
| 50 | - cropSize:rect.size | |
| 51 | - imageViewSize:imageView.frame.size]; | |
| 52 | - UIImage *cropedImage = [UIImage imageWithCGImage:imageRef]; | |
| 53 | - cropedImage = [[TZImageManager manager] fixOrientation:cropedImage]; | |
| 54 | - CGImageRelease(imageRef); | |
| 55 | - return cropedImage; | |
| 56 | -} | |
| 57 | - | |
| 58 | -+ (CGImageRef)newTransformedImage:(CGAffineTransform)transform sourceImage:(CGImageRef)sourceImage sourceSize:(CGSize)sourceSize outputWidth:(CGFloat)outputWidth cropSize:(CGSize)cropSize imageViewSize:(CGSize)imageViewSize { | |
| 59 | - CGImageRef source = [self newScaledImage:sourceImage toSize:sourceSize]; | |
| 60 | - | |
| 61 | - CGFloat aspect = cropSize.height/cropSize.width; | |
| 62 | - CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect); | |
| 63 | - | |
| 64 | - CGContextRef context = CGBitmapContextCreate(NULL, outputSize.width, outputSize.height, CGImageGetBitsPerComponent(source), 0, CGImageGetColorSpace(source), CGImageGetBitmapInfo(source)); | |
| 65 | - CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); | |
| 66 | - CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height)); | |
| 67 | - | |
| 68 | - CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width / cropSize.width, outputSize.height / cropSize.height); | |
| 69 | - uiCoords = CGAffineTransformTranslate(uiCoords, cropSize.width/2.0, cropSize.height / 2.0); | |
| 70 | - uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0); | |
| 71 | - CGContextConcatCTM(context, uiCoords); | |
| 72 | - | |
| 73 | - CGContextConcatCTM(context, transform); | |
| 74 | - CGContextScaleCTM(context, 1.0, -1.0); | |
| 75 | - | |
| 76 | - CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2, -imageViewSize.height/2.0, imageViewSize.width, imageViewSize.height), source); | |
| 77 | - CGImageRef resultRef = CGBitmapContextCreateImage(context); | |
| 78 | - CGContextRelease(context); | |
| 79 | - CGImageRelease(source); | |
| 80 | - return resultRef; | |
| 81 | -} | |
| 82 | - | |
| 83 | -+ (CGImageRef)newScaledImage:(CGImageRef)source toSize:(CGSize)size { | |
| 84 | - CGSize srcSize = size; | |
| 85 | - CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); | |
| 86 | - CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); | |
| 87 | - CGColorSpaceRelease(rgbColorSpace); | |
| 88 | - | |
| 89 | - CGContextSetInterpolationQuality(context, kCGInterpolationNone); | |
| 90 | - CGContextTranslateCTM(context, size.width/2, size.height/2); | |
| 91 | - | |
| 92 | - CGContextDrawImage(context, CGRectMake(-srcSize.width/2, -srcSize.height/2, srcSize.width, srcSize.height), source); | |
| 93 | - | |
| 94 | - CGImageRef resultRef = CGBitmapContextCreateImage(context); | |
| 95 | - CGContextRelease(context); | |
| 96 | - return resultRef; | |
| 97 | -} | |
| 98 | - | |
| 99 | -/// 获取圆形图片 | |
| 100 | -+ (UIImage *)circularClipImage:(UIImage *)image { | |
| 101 | - UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale); | |
| 102 | - | |
| 103 | - CGContextRef ctx = UIGraphicsGetCurrentContext(); | |
| 104 | - CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); | |
| 105 | - CGContextAddEllipseInRect(ctx, rect); | |
| 106 | - CGContextClip(ctx); | |
| 107 | - | |
| 108 | - [image drawInRect:rect]; | |
| 109 | - UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext(); | |
| 110 | - | |
| 111 | - UIGraphicsEndImageContext(); | |
| 112 | - return circleImage; | |
| 113 | -} | |
| 114 | - | |
| 115 | -@end | |
| 116 | - | |
| 117 | - | |
| 118 | -@implementation UIImage (TZGif) | |
| 119 | - | |
| 120 | -+ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data { | |
| 121 | - if (!data) { | |
| 122 | - return nil; | |
| 123 | - } | |
| 124 | - | |
| 125 | - CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); | |
| 126 | - | |
| 127 | - size_t count = CGImageSourceGetCount(source); | |
| 128 | - | |
| 129 | - UIImage *animatedImage; | |
| 130 | - | |
| 131 | - if (count <= 1) { | |
| 132 | - animatedImage = [[UIImage alloc] initWithData:data]; | |
| 133 | - } | |
| 134 | - else { | |
| 135 | - // images数组过大时内存会飙升,在这里限制下最大count | |
| 136 | - NSInteger maxCount = [TZImagePickerConfig sharedInstance].gifPreviewMaxImagesCount ?: 200; | |
| 137 | - NSInteger interval = MAX((count + maxCount / 2) / maxCount, 1); | |
| 138 | - | |
| 139 | - NSMutableArray *images = [NSMutableArray array]; | |
| 140 | - | |
| 141 | - NSTimeInterval duration = 0.0f; | |
| 142 | - | |
| 143 | - for (size_t i = 0; i < count; i+=interval) { | |
| 144 | - CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); | |
| 145 | - if (!image) { | |
| 146 | - continue; | |
| 147 | - } | |
| 148 | - | |
| 149 | - duration += [self sd_frameDurationAtIndex:i source:source] * MIN(interval, 3); | |
| 150 | - | |
| 151 | - [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; | |
| 152 | - | |
| 153 | - CGImageRelease(image); | |
| 154 | - } | |
| 155 | - | |
| 156 | - if (!duration) { | |
| 157 | - duration = (1.0f / 10.0f) * count; | |
| 158 | - } | |
| 159 | - | |
| 160 | - animatedImage = [UIImage animatedImageWithImages:images duration:duration]; | |
| 161 | - } | |
| 162 | - | |
| 163 | - CFRelease(source); | |
| 164 | - | |
| 165 | - return animatedImage; | |
| 166 | -} | |
| 167 | - | |
| 168 | -+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { | |
| 169 | - float frameDuration = 0.1f; | |
| 170 | - CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); | |
| 171 | - NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; | |
| 172 | - NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; | |
| 173 | - | |
| 174 | - NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; | |
| 175 | - if (delayTimeUnclampedProp) { | |
| 176 | - frameDuration = [delayTimeUnclampedProp floatValue]; | |
| 177 | - } | |
| 178 | - else { | |
| 179 | - | |
| 180 | - NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; | |
| 181 | - if (delayTimeProp) { | |
| 182 | - frameDuration = [delayTimeProp floatValue]; | |
| 183 | - } | |
| 184 | - } | |
| 185 | - | |
| 186 | - // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. | |
| 187 | - // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify | |
| 188 | - // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> | |
| 189 | - // for more information. | |
| 190 | - | |
| 191 | - if (frameDuration < 0.011f) { | |
| 192 | - frameDuration = 0.100f; | |
| 193 | - } | |
| 194 | - | |
| 195 | - CFRelease(cfFrameProperties); | |
| 196 | - return frameDuration; | |
| 197 | -} | |
| 198 | - | |
| 199 | -@end |
CNLiveImagePickerController/TZImageManager.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZImageManager.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 16/1/4. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// 图片资源获取管理类 | |
| 8 | - | |
| 9 | -#import <Foundation/Foundation.h> | |
| 10 | -#import <UIKit/UIKit.h> | |
| 11 | -#import <AVFoundation/AVFoundation.h> | |
| 12 | -#import <Photos/Photos.h> | |
| 13 | -#import "TZAssetModel.h" | |
| 14 | - | |
| 15 | -@class TZAlbumModel,TZAssetModel; | |
| 16 | -@protocol TZImagePickerControllerDelegate; | |
| 17 | -@interface TZImageManager : NSObject | |
| 18 | - | |
| 19 | -@property (nonatomic, strong) PHCachingImageManager *cachingImageManager; | |
| 20 | - | |
| 21 | -+ (instancetype)manager NS_SWIFT_NAME(default()); | |
| 22 | -+ (void)deallocManager; | |
| 23 | - | |
| 24 | -@property (weak, nonatomic) id<TZImagePickerControllerDelegate> pickerDelegate; | |
| 25 | - | |
| 26 | -#pragma mark - ADD: 原有逻辑-会话id(LXG) | |
| 27 | - | |
| 28 | -/// 导出路径 | |
| 29 | -@property (nonatomic, copy) NSString *outputPath; | |
| 30 | - | |
| 31 | -@property (nonatomic, assign) BOOL shouldFixOrientation; | |
| 32 | - | |
| 33 | -/// Default is 600px / 默认600像素宽 | |
| 34 | -@property (nonatomic, assign) CGFloat photoPreviewMaxWidth; | |
| 35 | -/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽 | |
| 36 | -@property (nonatomic, assign) CGFloat photoWidth; | |
| 37 | - | |
| 38 | -/// Default is 4, Use in photos collectionView in TZPhotoPickerController | |
| 39 | -/// 默认4列, TZPhotoPickerController中的照片collectionView | |
| 40 | -@property (nonatomic, assign) NSInteger columnNumber; | |
| 41 | - | |
| 42 | -/// Sort photos ascending by modificationDate,Default is YES | |
| 43 | -/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个 | |
| 44 | -@property (nonatomic, assign) BOOL sortAscendingByModificationDate; | |
| 45 | - | |
| 46 | -/// Minimum selectable photo width, Default is 0 | |
| 47 | -/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中 | |
| 48 | -@property (nonatomic, assign) NSInteger minPhotoWidthSelectable; | |
| 49 | -@property (nonatomic, assign) NSInteger minPhotoHeightSelectable; | |
| 50 | -@property (nonatomic, assign) BOOL hideWhenCanNotSelect; | |
| 51 | - | |
| 52 | -/// Return YES if Authorized 返回YES如果得到了授权 | |
| 53 | -- (BOOL)authorizationStatusAuthorized; | |
| 54 | -+ (NSInteger)authorizationStatus; | |
| 55 | -- (void)requestAuthorizationWithCompletion:(void (^)(void))completion; | |
| 56 | - | |
| 57 | -/// Get Album 获得相册/相册数组 | |
| 58 | -- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion; | |
| 59 | -- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *models))completion; | |
| 60 | - | |
| 61 | -/// Get Assets 获得Asset数组 | |
| 62 | -- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *models))completion; | |
| 63 | -- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *models))completion; | |
| 64 | -- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *model))completion; | |
| 65 | - | |
| 66 | -/// Get photo 获得照片 | |
| 67 | -- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *postImage))completion; | |
| 68 | - | |
| 69 | -- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion; | |
| 70 | -- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion; | |
| 71 | -- (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; | |
| 72 | -- (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; | |
| 73 | -- (int32_t)requestImageDataForAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler; | |
| 74 | - | |
| 75 | -/// Get full Image 获取原图 | |
| 76 | -/// 如下两个方法completion一般会调多次,一般会先返回缩略图,再返回原图(详见方法内部使用的系统API的说明),如果info[PHImageResultIsDegradedKey] 为 YES,则表明当前返回的是缩略图,否则是原图。 | |
| 77 | -- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion; | |
| 78 | -- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion; | |
| 79 | -// 该方法中,completion只会走一次 | |
| 80 | -- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion; | |
| 81 | -- (void)getOriginalPhotoDataWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion; | |
| 82 | - | |
| 83 | -/// Save photo 保存照片 | |
| 84 | -- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion; | |
| 85 | -- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion; | |
| 86 | - | |
| 87 | -/// Save video 保存视频 | |
| 88 | -- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion; | |
| 89 | -- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion; | |
| 90 | - | |
| 91 | -/// Get video 获得视频 | |
| 92 | -- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem * playerItem, NSDictionary * info))completion; | |
| 93 | -- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion; | |
| 94 | - | |
| 95 | -/// Export video 导出视频 presetName: 预设名字,默认值是AVAssetExportPreset640x480 | |
| 96 | -- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure; | |
| 97 | -- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure; | |
| 98 | -/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success: | |
| 99 | -- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion __attribute__((deprecated("Use -getVideoOutputPathWithAsset:failure:success:"))); | |
| 100 | - | |
| 101 | -/// Get photo bytes 获得一组照片的大小 | |
| 102 | -- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion; | |
| 103 | - | |
| 104 | -/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset | |
| 105 | -- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset; | |
| 106 | - | |
| 107 | -- (NSString *)getAssetIdentifier:(id)asset; | |
| 108 | -- (BOOL)isCameraRollAlbum:(id)metadata; | |
| 109 | - | |
| 110 | -/// 检查照片大小是否满足最小要求 | |
| 111 | -- (BOOL)isPhotoSelectableWithAsset:(id)asset; | |
| 112 | -- (CGSize)photoSizeWithAsset:(id)asset; | |
| 113 | - | |
| 114 | -/// 修正图片转向 | |
| 115 | -- (UIImage *)fixOrientation:(UIImage *)aImage; | |
| 116 | - | |
| 117 | -/// 获取asset的资源类型 | |
| 118 | -- (TZAssetModelMediaType)getAssetType:(id)asset; | |
| 119 | -/// 缩放图片至新尺寸 | |
| 120 | -- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size; | |
| 121 | - | |
| 122 | -/// 判断asset是否是视频 | |
| 123 | -- (BOOL)isVideo:(id)asset; | |
| 124 | - | |
| 125 | -@end | |
| 126 | - | |
| 127 | -//@interface TZSortDescriptor : NSSortDescriptor | |
| 128 | -// | |
| 129 | -//@end |
CNLiveImagePickerController/TZImageManager.m deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZImageManager.m | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 16/1/4. | |
| 6 | -// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | -// | |
| 8 | - | |
| 9 | -#import "TZImageManager.h" | |
| 10 | -#import <AssetsLibrary/AssetsLibrary.h> | |
| 11 | -#import "TZAssetModel.h" | |
| 12 | -#import "TZImagePickerController.h" | |
| 13 | - | |
| 14 | -@interface TZImageManager () | |
| 15 | -#pragma clang diagnostic push | |
| 16 | -#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 17 | -@property (nonatomic, strong) ALAssetsLibrary *assetLibrary; | |
| 18 | -@end | |
| 19 | - | |
| 20 | -@implementation TZImageManager | |
| 21 | - | |
| 22 | -CGSize AssetGridThumbnailSize; | |
| 23 | -CGFloat TZScreenWidth; | |
| 24 | -CGFloat TZScreenScale; | |
| 25 | - | |
| 26 | -static TZImageManager *manager; | |
| 27 | -static dispatch_once_t onceToken; | |
| 28 | - | |
| 29 | -+ (instancetype)manager { | |
| 30 | - dispatch_once(&onceToken, ^{ | |
| 31 | - manager = [[self alloc] init]; | |
| 32 | - if (iOS8Later) { | |
| 33 | - // manager.cachingImageManager = [[PHCachingImageManager alloc] init]; | |
| 34 | - // manager.cachingImageManager.allowsCachingHighQualityImages = YES; | |
| 35 | - } | |
| 36 | - | |
| 37 | - [manager configTZScreenWidth]; | |
| 38 | - }); | |
| 39 | - return manager; | |
| 40 | -} | |
| 41 | - | |
| 42 | -+ (void)deallocManager { | |
| 43 | - onceToken = 0; | |
| 44 | - manager = nil; | |
| 45 | -} | |
| 46 | - | |
| 47 | -- (void)setPhotoWidth:(CGFloat)photoWidth { | |
| 48 | - _photoWidth = photoWidth; | |
| 49 | - TZScreenWidth = photoWidth / 2; | |
| 50 | -} | |
| 51 | - | |
| 52 | -- (void)setColumnNumber:(NSInteger)columnNumber { | |
| 53 | - [self configTZScreenWidth]; | |
| 54 | - | |
| 55 | - _columnNumber = columnNumber; | |
| 56 | - CGFloat margin = 4; | |
| 57 | - CGFloat itemWH = (TZScreenWidth - 2 * margin - 4) / columnNumber - margin; | |
| 58 | - AssetGridThumbnailSize = CGSizeMake(itemWH * TZScreenScale, itemWH * TZScreenScale); | |
| 59 | -} | |
| 60 | - | |
| 61 | -- (void)configTZScreenWidth { | |
| 62 | - TZScreenWidth = [UIScreen mainScreen].bounds.size.width; | |
| 63 | - // 测试发现,如果scale在plus真机上取到3.0,内存会增大特别多。故这里写死成2.0 | |
| 64 | - TZScreenScale = 2.0; | |
| 65 | - if (TZScreenWidth > 700) { | |
| 66 | - TZScreenScale = 1.5; | |
| 67 | - } | |
| 68 | -} | |
| 69 | - | |
| 70 | -- (ALAssetsLibrary *)assetLibrary { | |
| 71 | - if (_assetLibrary == nil) _assetLibrary = [[ALAssetsLibrary alloc] init]; | |
| 72 | - return _assetLibrary; | |
| 73 | -} | |
| 74 | - | |
| 75 | -/// Return YES if Authorized 返回YES如果得到了授权 | |
| 76 | -- (BOOL)authorizationStatusAuthorized { | |
| 77 | - NSInteger status = [self.class authorizationStatus]; | |
| 78 | - if (status == 0) { | |
| 79 | - /** | |
| 80 | - * 当某些情况下AuthorizationStatus == AuthorizationStatusNotDetermined时,无法弹出系统首次使用的授权alertView,系统应用设置里亦没有相册的设置,此时将无法使用,故作以下操作,弹出系统首次使用的授权alertView | |
| 81 | - */ | |
| 82 | - [self requestAuthorizationWithCompletion:nil]; | |
| 83 | - } | |
| 84 | - | |
| 85 | - return status == 3; | |
| 86 | -} | |
| 87 | - | |
| 88 | -+ (NSInteger)authorizationStatus { | |
| 89 | - if (iOS8Later) { | |
| 90 | - return [PHPhotoLibrary authorizationStatus]; | |
| 91 | - } else { | |
| 92 | - return [ALAssetsLibrary authorizationStatus]; | |
| 93 | - } | |
| 94 | - return NO; | |
| 95 | -} | |
| 96 | - | |
| 97 | -- (void)requestAuthorizationWithCompletion:(void (^)(void))completion { | |
| 98 | - void (^callCompletionBlock)(void) = ^(){ | |
| 99 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 100 | - if (completion) { | |
| 101 | - completion(); | |
| 102 | - } | |
| 103 | - }); | |
| 104 | - }; | |
| 105 | - | |
| 106 | - if (iOS8Later) { | |
| 107 | - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
| 108 | - [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { | |
| 109 | - callCompletionBlock(); | |
| 110 | - }]; | |
| 111 | - }); | |
| 112 | - } else { | |
| 113 | - [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { | |
| 114 | - callCompletionBlock(); | |
| 115 | - } failureBlock:^(NSError *error) { | |
| 116 | - callCompletionBlock(); | |
| 117 | - }]; | |
| 118 | - } | |
| 119 | -} | |
| 120 | - | |
| 121 | -#pragma mark - Get Album | |
| 122 | - | |
| 123 | -/// Get Album 获得相册/相册数组 | |
| 124 | -- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion { | |
| 125 | - __block TZAlbumModel *model; | |
| 126 | - if (iOS8Later) { | |
| 127 | - PHFetchOptions *option = [[PHFetchOptions alloc] init]; | |
| 128 | - if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; | |
| 129 | - if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", | |
| 130 | - PHAssetMediaTypeVideo]; | |
| 131 | - // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]]; | |
| 132 | - if (!self.sortAscendingByModificationDate) { | |
| 133 | - option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]]; | |
| 134 | - } | |
| 135 | - PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; | |
| 136 | - for (PHAssetCollection *collection in smartAlbums) { | |
| 137 | - // 有可能是PHCollectionList类的的对象,过滤掉 | |
| 138 | - if (![collection isKindOfClass:[PHAssetCollection class]]) continue; | |
| 139 | - // 过滤空相册 | |
| 140 | - if (collection.estimatedAssetCount <= 0) continue; | |
| 141 | - if ([self isCameraRollAlbum:collection]) { | |
| 142 | - PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option]; | |
| 143 | - model = [self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets]; | |
| 144 | - if (completion) completion(model); | |
| 145 | - break; | |
| 146 | - } | |
| 147 | - } | |
| 148 | - } else { | |
| 149 | - [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { | |
| 150 | - if ([group numberOfAssets] < 1) return; | |
| 151 | - if ([self isCameraRollAlbum:group]) { | |
| 152 | - NSString *name = [group valueForProperty:ALAssetsGroupPropertyName]; | |
| 153 | - model = [self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets]; | |
| 154 | - if (completion) completion(model); | |
| 155 | - *stop = YES; | |
| 156 | - } | |
| 157 | - } failureBlock:nil]; | |
| 158 | - } | |
| 159 | -} | |
| 160 | - | |
| 161 | -- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *))completion{ | |
| 162 | - NSMutableArray *albumArr = [NSMutableArray array]; | |
| 163 | - if (iOS8Later) { | |
| 164 | - PHFetchOptions *option = [[PHFetchOptions alloc] init]; | |
| 165 | - if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; | |
| 166 | - if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", | |
| 167 | - PHAssetMediaTypeVideo]; | |
| 168 | - // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]]; | |
| 169 | - if (!self.sortAscendingByModificationDate) { | |
| 170 | - option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]]; | |
| 171 | - } | |
| 172 | - // 我的照片流 1.6.10重新加入.. | |
| 173 | - PHFetchResult *myPhotoStreamAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil]; | |
| 174 | - PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; | |
| 175 | - PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil]; | |
| 176 | - PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil]; | |
| 177 | - PHFetchResult *sharedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumCloudShared options:nil]; | |
| 178 | - NSArray *allAlbums = @[myPhotoStreamAlbum,smartAlbums,topLevelUserCollections,syncedAlbums,sharedAlbums]; | |
| 179 | - for (PHFetchResult *fetchResult in allAlbums) { | |
| 180 | - for (PHAssetCollection *collection in fetchResult) { | |
| 181 | - // 有可能是PHCollectionList类的的对象,过滤掉 | |
| 182 | - if (![collection isKindOfClass:[PHAssetCollection class]]) continue; | |
| 183 | - // 过滤空相册 | |
| 184 | - if (collection.estimatedAssetCount <= 0) continue; | |
| 185 | - PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option]; | |
| 186 | - if (fetchResult.count < 1) continue; | |
| 187 | - | |
| 188 | - if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) { | |
| 189 | - if (![self.pickerDelegate isAlbumCanSelect:collection.localizedTitle result:fetchResult]) { | |
| 190 | - continue; | |
| 191 | - } | |
| 192 | - } | |
| 193 | - | |
| 194 | - if (collection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumAllHidden) continue; | |
| 195 | - if (collection.assetCollectionSubtype == 1000000201) continue; //『最近删除』相册 | |
| 196 | - if ([self isCameraRollAlbum:collection]) { | |
| 197 | - [albumArr insertObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0]; | |
| 198 | - } else { | |
| 199 | - [albumArr addObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:NO needFetchAssets:needFetchAssets]]; | |
| 200 | - } | |
| 201 | - } | |
| 202 | - } | |
| 203 | - if (completion && albumArr.count > 0) completion(albumArr); | |
| 204 | - } else { | |
| 205 | - [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { | |
| 206 | - if (group == nil) { | |
| 207 | - if (completion && albumArr.count > 0) completion(albumArr); | |
| 208 | - } | |
| 209 | - if ([group numberOfAssets] < 1) return; | |
| 210 | - NSString *name = [group valueForProperty:ALAssetsGroupPropertyName]; | |
| 211 | - | |
| 212 | - if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) { | |
| 213 | - if (![self.pickerDelegate isAlbumCanSelect:name result:group]) { | |
| 214 | - return; | |
| 215 | - } | |
| 216 | - } | |
| 217 | - | |
| 218 | - if ([self isCameraRollAlbum:group]) { | |
| 219 | - [albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0]; | |
| 220 | - } else if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupPhotoStream) { | |
| 221 | - if (albumArr.count) { | |
| 222 | - [albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets] atIndex:1]; | |
| 223 | - } else { | |
| 224 | - [albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]]; | |
| 225 | - } | |
| 226 | - } else { | |
| 227 | - [albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]]; | |
| 228 | - } | |
| 229 | - } failureBlock:nil]; | |
| 230 | - } | |
| 231 | -} | |
| 232 | - | |
| 233 | -#pragma mark - Get Assets | |
| 234 | - | |
| 235 | -/// Get Assets 获得照片数组 | |
| 236 | -- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *))completion { | |
| 237 | - TZImagePickerConfig *config = [TZImagePickerConfig sharedInstance]; | |
| 238 | - return [self getAssetsFromFetchResult:result allowPickingVideo:config.allowPickingVideo allowPickingImage:config.allowPickingImage completion:completion]; | |
| 239 | -} | |
| 240 | - | |
| 241 | -- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *))completion { | |
| 242 | - NSMutableArray *photoArr = [NSMutableArray array]; | |
| 243 | - if ([result isKindOfClass:[PHFetchResult class]]) { | |
| 244 | - PHFetchResult *fetchResult = (PHFetchResult *)result; | |
| 245 | - [fetchResult enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | |
| 246 | - TZAssetModel *model = [self assetModelWithAsset:obj allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | |
| 247 | - if (model) { | |
| 248 | - [photoArr addObject:model]; | |
| 249 | - } | |
| 250 | - }]; | |
| 251 | - if (completion) completion(photoArr); | |
| 252 | - } else if ([result isKindOfClass:[ALAssetsGroup class]]) { | |
| 253 | - ALAssetsGroup *group = (ALAssetsGroup *)result; | |
| 254 | - if (allowPickingImage && allowPickingVideo) { | |
| 255 | - [group setAssetsFilter:[ALAssetsFilter allAssets]]; | |
| 256 | - } else if (allowPickingVideo) { | |
| 257 | - [group setAssetsFilter:[ALAssetsFilter allVideos]]; | |
| 258 | - } else if (allowPickingImage) { | |
| 259 | - [group setAssetsFilter:[ALAssetsFilter allPhotos]]; | |
| 260 | - } | |
| 261 | - ALAssetsGroupEnumerationResultsBlock resultBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) { | |
| 262 | - if (result == nil) { | |
| 263 | - if (completion) completion(photoArr); | |
| 264 | - } | |
| 265 | - TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | |
| 266 | - if (model) { | |
| 267 | - [photoArr addObject:model]; | |
| 268 | - } | |
| 269 | - }; | |
| 270 | - if (self.sortAscendingByModificationDate) { | |
| 271 | - [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { | |
| 272 | - if (resultBlock) { resultBlock(result,index,stop); } | |
| 273 | - }]; | |
| 274 | - } else { | |
| 275 | - [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { | |
| 276 | - if (resultBlock) {resultBlock(result,index,stop); } | |
| 277 | - }]; | |
| 278 | - } | |
| 279 | - } | |
| 280 | -} | |
| 281 | - | |
| 282 | -/// Get asset at index 获得下标为index的单个照片 | |
| 283 | -/// if index beyond bounds, return nil in callback 如果索引越界, 在回调中返回 nil | |
| 284 | -- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *))completion { | |
| 285 | - if ([result isKindOfClass:[PHFetchResult class]]) { | |
| 286 | - PHFetchResult *fetchResult = (PHFetchResult *)result; | |
| 287 | - PHAsset *asset; | |
| 288 | - @try { | |
| 289 | - asset = fetchResult[index]; | |
| 290 | - } | |
| 291 | - @catch (NSException* e) { | |
| 292 | - if (completion) completion(nil); | |
| 293 | - return; | |
| 294 | - } | |
| 295 | - TZAssetModel *model = [self assetModelWithAsset:asset allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | |
| 296 | - if (completion) completion(model); | |
| 297 | - } else if ([result isKindOfClass:[ALAssetsGroup class]]) { | |
| 298 | - ALAssetsGroup *group = (ALAssetsGroup *)result; | |
| 299 | - if (allowPickingImage && allowPickingVideo) { | |
| 300 | - [group setAssetsFilter:[ALAssetsFilter allAssets]]; | |
| 301 | - } else if (allowPickingVideo) { | |
| 302 | - [group setAssetsFilter:[ALAssetsFilter allVideos]]; | |
| 303 | - } else if (allowPickingImage) { | |
| 304 | - [group setAssetsFilter:[ALAssetsFilter allPhotos]]; | |
| 305 | - } | |
| 306 | - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index]; | |
| 307 | - @try { | |
| 308 | - [group enumerateAssetsAtIndexes:indexSet options:NSEnumerationConcurrent usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { | |
| 309 | - if (!result) return; | |
| 310 | - TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | |
| 311 | - if (completion) completion(model); | |
| 312 | - }]; | |
| 313 | - } | |
| 314 | - @catch (NSException* e) { | |
| 315 | - if (completion) completion(nil); | |
| 316 | - } | |
| 317 | - } | |
| 318 | -} | |
| 319 | - | |
| 320 | -- (TZAssetModel *)assetModelWithAsset:(id)asset allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage { | |
| 321 | - BOOL canSelect = YES; | |
| 322 | - if ([self.pickerDelegate respondsToSelector:@selector(isAssetCanSelect:)]) { | |
| 323 | - canSelect = [self.pickerDelegate isAssetCanSelect:asset]; | |
| 324 | - } | |
| 325 | - if (!canSelect) return nil; | |
| 326 | - | |
| 327 | - TZAssetModel *model; | |
| 328 | - TZAssetModelMediaType type = [self getAssetType:asset]; | |
| 329 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 330 | - if (!allowPickingVideo && type == TZAssetModelMediaTypeVideo) return nil; | |
| 331 | - if (!allowPickingImage && type == TZAssetModelMediaTypePhoto) return nil; | |
| 332 | - if (!allowPickingImage && type == TZAssetModelMediaTypePhotoGif) return nil; | |
| 333 | - | |
| 334 | - PHAsset *phAsset = (PHAsset *)asset; | |
| 335 | - if (self.hideWhenCanNotSelect) { | |
| 336 | - // 过滤掉尺寸不满足要求的图片 | |
| 337 | - if (![self isPhotoSelectableWithAsset:phAsset]) { | |
| 338 | - return nil; | |
| 339 | - } | |
| 340 | - } | |
| 341 | - NSString *timeLength = type == TZAssetModelMediaTypeVideo ? [NSString stringWithFormat:@"%0.0f",phAsset.duration] : @""; | |
| 342 | - timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue]; | |
| 343 | - model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength]; | |
| 344 | - } else { | |
| 345 | - if (!allowPickingVideo){ | |
| 346 | - model = [TZAssetModel modelWithAsset:asset type:type]; | |
| 347 | - return model; | |
| 348 | - } | |
| 349 | - /// Allow picking video | |
| 350 | - if (type == TZAssetModelMediaTypeVideo) { | |
| 351 | - NSTimeInterval duration = [[asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | |
| 352 | - NSString *timeLength = [NSString stringWithFormat:@"%0.0f",duration]; | |
| 353 | - timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue]; | |
| 354 | - model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength]; | |
| 355 | - } else { | |
| 356 | - if (self.hideWhenCanNotSelect) { | |
| 357 | - // 过滤掉尺寸不满足要求的图片 | |
| 358 | - if (![self isPhotoSelectableWithAsset:asset]) { | |
| 359 | - return nil; | |
| 360 | - } | |
| 361 | - } | |
| 362 | - model = [TZAssetModel modelWithAsset:asset type:type]; | |
| 363 | - } | |
| 364 | - } | |
| 365 | - return model; | |
| 366 | -} | |
| 367 | - | |
| 368 | -- (TZAssetModelMediaType)getAssetType:(id)asset { | |
| 369 | - TZAssetModelMediaType type = TZAssetModelMediaTypePhoto; | |
| 370 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 371 | - PHAsset *phAsset = (PHAsset *)asset; | |
| 372 | - if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo; | |
| 373 | - else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio; | |
| 374 | - else if (phAsset.mediaType == PHAssetMediaTypeImage) { | |
| 375 | - if (@available(iOS 9.1, *)) { | |
| 376 | - // if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) type = TZAssetModelMediaTypeLivePhoto; | |
| 377 | - } | |
| 378 | - // Gif | |
| 379 | - if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) { | |
| 380 | - type = TZAssetModelMediaTypePhotoGif; | |
| 381 | - } | |
| 382 | - } | |
| 383 | - } else { | |
| 384 | - if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) { | |
| 385 | - type = TZAssetModelMediaTypeVideo; | |
| 386 | - } | |
| 387 | - } | |
| 388 | - return type; | |
| 389 | -} | |
| 390 | - | |
| 391 | -- (NSString *)getNewTimeFromDurationSecond:(NSInteger)duration { | |
| 392 | - NSString *newTime; | |
| 393 | - if (duration < 10) { | |
| 394 | - newTime = [NSString stringWithFormat:@"0:0%zd",duration]; | |
| 395 | - } else if (duration < 60) { | |
| 396 | - newTime = [NSString stringWithFormat:@"0:%zd",duration]; | |
| 397 | - } else { | |
| 398 | - NSInteger min = duration / 60; | |
| 399 | - NSInteger sec = duration - (min * 60); | |
| 400 | - if (sec < 10) { | |
| 401 | - newTime = [NSString stringWithFormat:@"%zd:0%zd",min,sec]; | |
| 402 | - } else { | |
| 403 | - newTime = [NSString stringWithFormat:@"%zd:%zd",min,sec]; | |
| 404 | - } | |
| 405 | - } | |
| 406 | - return newTime; | |
| 407 | -} | |
| 408 | - | |
| 409 | -/// Get photo bytes 获得一组照片的大小 | |
| 410 | -- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion { | |
| 411 | - if (!photos || !photos.count) { | |
| 412 | - if (completion) completion(@"0B"); | |
| 413 | - return; | |
| 414 | - } | |
| 415 | - __block NSInteger dataLength = 0; | |
| 416 | - __block NSInteger assetCount = 0; | |
| 417 | - for (NSInteger i = 0; i < photos.count; i++) { | |
| 418 | - TZAssetModel *model = photos[i]; | |
| 419 | - if ([model.asset isKindOfClass:[PHAsset class]]) { | |
| 420 | - PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; | |
| 421 | - options.resizeMode = PHImageRequestOptionsResizeModeFast; | |
| 422 | - options.networkAccessAllowed = YES; | |
| 423 | - if (model.type == TZAssetModelMediaTypePhotoGif) { | |
| 424 | - options.version = PHImageRequestOptionsVersionOriginal; | |
| 425 | - } | |
| 426 | - [[PHImageManager defaultManager] requestImageDataForAsset:model.asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | |
| 427 | - if (model.type != TZAssetModelMediaTypeVideo) dataLength += imageData.length; | |
| 428 | - assetCount ++; | |
| 429 | - if (assetCount >= photos.count) { | |
| 430 | - NSString *bytes = [self getBytesFromDataLength:dataLength]; | |
| 431 | - if (completion) completion(bytes); | |
| 432 | - } | |
| 433 | - }]; | |
| 434 | - } else if ([model.asset isKindOfClass:[ALAsset class]]) { | |
| 435 | - ALAssetRepresentation *representation = [model.asset defaultRepresentation]; | |
| 436 | - if (model.type != TZAssetModelMediaTypeVideo) dataLength += (NSInteger)representation.size; | |
| 437 | - if (i >= photos.count - 1) { | |
| 438 | - NSString *bytes = [self getBytesFromDataLength:dataLength]; | |
| 439 | - if (completion) completion(bytes); | |
| 440 | - } | |
| 441 | - } | |
| 442 | - } | |
| 443 | -} | |
| 444 | - | |
| 445 | -- (NSString *)getBytesFromDataLength:(NSInteger)dataLength { | |
| 446 | - NSString *bytes; | |
| 447 | - if (dataLength >= 0.1 * (1024 * 1024)) { | |
| 448 | - bytes = [NSString stringWithFormat:@"%0.1fM",dataLength/1024/1024.0]; | |
| 449 | - } else if (dataLength >= 1024) { | |
| 450 | - bytes = [NSString stringWithFormat:@"%0.0fK",dataLength/1024.0]; | |
| 451 | - } else { | |
| 452 | - bytes = [NSString stringWithFormat:@"%zdB",dataLength]; | |
| 453 | - } | |
| 454 | - return bytes; | |
| 455 | -} | |
| 456 | - | |
| 457 | -#pragma mark - Get Photo | |
| 458 | - | |
| 459 | -/// Get photo 获得照片本身 | |
| 460 | -- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *, NSDictionary *, BOOL isDegraded))completion { | |
| 461 | - CGFloat fullScreenWidth = TZScreenWidth; | |
| 462 | - if (fullScreenWidth > _photoPreviewMaxWidth) { | |
| 463 | - fullScreenWidth = _photoPreviewMaxWidth; | |
| 464 | - } | |
| 465 | - return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:nil networkAccessAllowed:YES]; | |
| 466 | -} | |
| 467 | - | |
| 468 | -- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion { | |
| 469 | - return [self getPhotoWithAsset:asset photoWidth:photoWidth completion:completion progressHandler:nil networkAccessAllowed:YES]; | |
| 470 | -} | |
| 471 | - | |
| 472 | -- (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 { | |
| 473 | - CGFloat fullScreenWidth = TZScreenWidth; | |
| 474 | - if (fullScreenWidth > _photoPreviewMaxWidth) { | |
| 475 | - fullScreenWidth = _photoPreviewMaxWidth; | |
| 476 | - } | |
| 477 | - return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:progressHandler networkAccessAllowed:networkAccessAllowed]; | |
| 478 | -} | |
| 479 | - | |
| 480 | -- (int32_t)requestImageDataForAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler { | |
| 481 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 482 | - PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; | |
| 483 | - options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 484 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 485 | - if (progressHandler) { | |
| 486 | - progressHandler(progress, error, stop, info); | |
| 487 | - } | |
| 488 | - }); | |
| 489 | - }; | |
| 490 | - options.networkAccessAllowed = YES; | |
| 491 | - options.resizeMode = PHImageRequestOptionsResizeModeFast; | |
| 492 | - int32_t imageRequestID = [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | |
| 493 | - if (completion) completion(imageData,dataUTI,orientation,info); | |
| 494 | - }]; | |
| 495 | - return imageRequestID; | |
| 496 | - } else if ([asset isKindOfClass:[ALAsset class]]) { | |
| 497 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 498 | - dispatch_async(dispatch_get_global_queue(0,0), ^{ | |
| 499 | - ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | |
| 500 | - CGImageRef fullScrennImageRef = [assetRep fullScreenImage]; | |
| 501 | - UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp]; | |
| 502 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 503 | - if (completion) completion(UIImageJPEGRepresentation(fullScrennImage, 0.83), nil, UIImageOrientationUp, nil); | |
| 504 | - }); | |
| 505 | - }); | |
| 506 | - } | |
| 507 | - return 0; | |
| 508 | -} | |
| 509 | - | |
| 510 | -- (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 { | |
| 511 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 512 | - CGSize imageSize; | |
| 513 | - if (photoWidth < TZScreenWidth && photoWidth < _photoPreviewMaxWidth) { | |
| 514 | - imageSize = AssetGridThumbnailSize; | |
| 515 | - } else { | |
| 516 | - PHAsset *phAsset = (PHAsset *)asset; | |
| 517 | - CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight; | |
| 518 | - CGFloat pixelWidth = photoWidth * TZScreenScale * 1.5; | |
| 519 | - // 超宽图片 | |
| 520 | - if (aspectRatio > 1.8) { | |
| 521 | - pixelWidth = pixelWidth * aspectRatio; | |
| 522 | - } | |
| 523 | - // 超高图片 | |
| 524 | - if (aspectRatio < 0.2) { | |
| 525 | - pixelWidth = pixelWidth * 0.5; | |
| 526 | - } | |
| 527 | - CGFloat pixelHeight = pixelWidth / aspectRatio; | |
| 528 | - imageSize = CGSizeMake(pixelWidth, pixelHeight); | |
| 529 | - } | |
| 530 | - | |
| 531 | - __block UIImage *image; | |
| 532 | - // 修复获取图片时出现的瞬间内存过高问题 | |
| 533 | - // 下面两行代码,来自hsjcom,他的github是:https://github.com/hsjcom 表示感谢 | |
| 534 | - PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; | |
| 535 | - option.resizeMode = PHImageRequestOptionsResizeModeFast; | |
| 536 | - int32_t imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) { | |
| 537 | - if (result) { | |
| 538 | - image = result; | |
| 539 | - } | |
| 540 | - BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); | |
| 541 | - if (downloadFinined && result) { | |
| 542 | - result = [self fixOrientation:result]; | |
| 543 | - if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]); | |
| 544 | - } | |
| 545 | - // Download image from iCloud / 从iCloud下载图片 | |
| 546 | - if ([info objectForKey:PHImageResultIsInCloudKey] && !result && networkAccessAllowed) { | |
| 547 | - PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; | |
| 548 | - options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 549 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 550 | - if (progressHandler) { | |
| 551 | - progressHandler(progress, error, stop, info); | |
| 552 | - } | |
| 553 | - }); | |
| 554 | - }; | |
| 555 | - options.networkAccessAllowed = YES; | |
| 556 | - options.resizeMode = PHImageRequestOptionsResizeModeFast; | |
| 557 | - [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | |
| 558 | - UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1]; | |
| 559 | - if (![TZImagePickerConfig sharedInstance].notScaleImage) { | |
| 560 | - resultImage = [self scaleImage:resultImage toSize:imageSize]; | |
| 561 | - } | |
| 562 | - if (!resultImage) { | |
| 563 | - resultImage = image; | |
| 564 | - } | |
| 565 | - resultImage = [self fixOrientation:resultImage]; | |
| 566 | - if (completion) completion(resultImage,info,NO); | |
| 567 | - }]; | |
| 568 | - } | |
| 569 | - }]; | |
| 570 | - return imageRequestID; | |
| 571 | - } else if ([asset isKindOfClass:[ALAsset class]]) { | |
| 572 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 573 | - dispatch_async(dispatch_get_global_queue(0,0), ^{ | |
| 574 | - CGImageRef thumbnailImageRef = alAsset.thumbnail; | |
| 575 | - UIImage *thumbnailImage = [UIImage imageWithCGImage:thumbnailImageRef scale:2.0 orientation:UIImageOrientationUp]; | |
| 576 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 577 | - if (completion) completion(thumbnailImage,nil,YES); | |
| 578 | - | |
| 579 | - if (photoWidth == TZScreenWidth || photoWidth == self->_photoPreviewMaxWidth) { | |
| 580 | - dispatch_async(dispatch_get_global_queue(0,0), ^{ | |
| 581 | - ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | |
| 582 | - CGImageRef fullScrennImageRef = [assetRep fullScreenImage]; | |
| 583 | - UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp]; | |
| 584 | - | |
| 585 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 586 | - if (completion) completion(fullScrennImage,nil,NO); | |
| 587 | - }); | |
| 588 | - }); | |
| 589 | - } | |
| 590 | - }); | |
| 591 | - }); | |
| 592 | - } | |
| 593 | - return 0; | |
| 594 | -} | |
| 595 | - | |
| 596 | -/// Get postImage / 获取封面图 | |
| 597 | -- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *))completion { | |
| 598 | - if (iOS8Later) { | |
| 599 | - id asset = [model.result lastObject]; | |
| 600 | - if (!self.sortAscendingByModificationDate) { | |
| 601 | - asset = [model.result firstObject]; | |
| 602 | - } | |
| 603 | - [[TZImageManager manager] getPhotoWithAsset:asset photoWidth:80 completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 604 | - if (completion) completion(photo); | |
| 605 | - }]; | |
| 606 | - } else { | |
| 607 | - ALAssetsGroup *group = model.result; | |
| 608 | - UIImage *postImage = [UIImage imageWithCGImage:group.posterImage]; | |
| 609 | - if (completion) completion(postImage); | |
| 610 | - } | |
| 611 | -} | |
| 612 | - | |
| 613 | -/// Get Original Photo / 获取原图 | |
| 614 | -- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion { | |
| 615 | - [self getOriginalPhotoWithAsset:asset newCompletion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 616 | - if (completion) { | |
| 617 | - completion(photo,info); | |
| 618 | - } | |
| 619 | - }]; | |
| 620 | -} | |
| 621 | - | |
| 622 | -- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion { | |
| 623 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 624 | - PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init]; | |
| 625 | - option.networkAccessAllowed = YES; | |
| 626 | - option.resizeMode = PHImageRequestOptionsResizeModeFast; | |
| 627 | - [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage *result, NSDictionary *info) { | |
| 628 | - BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); | |
| 629 | - if (downloadFinined && result) { | |
| 630 | - result = [self fixOrientation:result]; | |
| 631 | - BOOL isDegraded = [[info objectForKey:PHImageResultIsDegradedKey] boolValue]; | |
| 632 | - if (completion) completion(result,info,isDegraded); | |
| 633 | - } | |
| 634 | - }]; | |
| 635 | - } else if ([asset isKindOfClass:[ALAsset class]]) { | |
| 636 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 637 | - ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | |
| 638 | - | |
| 639 | - dispatch_async(dispatch_get_global_queue(0,0), ^{ | |
| 640 | - CGImageRef originalImageRef = [assetRep fullResolutionImage]; | |
| 641 | - UIImage *originalImage = [UIImage imageWithCGImage:originalImageRef scale:1.0 orientation:UIImageOrientationUp]; | |
| 642 | - | |
| 643 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 644 | - if (completion) completion(originalImage,nil,NO); | |
| 645 | - }); | |
| 646 | - }); | |
| 647 | - } | |
| 648 | -} | |
| 649 | - | |
| 650 | -- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion { | |
| 651 | - [self getOriginalPhotoDataWithAsset:asset progressHandler:nil completion:completion]; | |
| 652 | -} | |
| 653 | - | |
| 654 | -- (void)getOriginalPhotoDataWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion { | |
| 655 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 656 | - PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; | |
| 657 | - option.networkAccessAllowed = YES; | |
| 658 | - if ([[asset valueForKey:@"filename"] hasSuffix:@"GIF"]) { | |
| 659 | - // if version isn't PHImageRequestOptionsVersionOriginal, the gif may cann't play | |
| 660 | - option.version = PHImageRequestOptionsVersionOriginal; | |
| 661 | - } | |
| 662 | - [option setProgressHandler:progressHandler]; | |
| 663 | - option.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; | |
| 664 | - [[PHImageManager defaultManager] requestImageDataForAsset:asset options:option resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | |
| 665 | - BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); | |
| 666 | - if (downloadFinined && imageData) { | |
| 667 | - if (completion) completion(imageData,info,NO); | |
| 668 | - } | |
| 669 | - }]; | |
| 670 | - } else if ([asset isKindOfClass:[ALAsset class]]) { | |
| 671 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 672 | - ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | |
| 673 | - Byte *imageBuffer = (Byte *)malloc(assetRep.size); | |
| 674 | - NSUInteger bufferSize = [assetRep getBytes:imageBuffer fromOffset:0.0 length:assetRep.size error:nil]; | |
| 675 | - NSData *imageData = [NSData dataWithBytesNoCopy:imageBuffer length:bufferSize freeWhenDone:YES]; | |
| 676 | - if (completion) completion(imageData,nil,NO); | |
| 677 | - } | |
| 678 | -} | |
| 679 | - | |
| 680 | -#pragma mark - Save photo | |
| 681 | - | |
| 682 | -- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion { | |
| 683 | - [self savePhotoWithImage:image location:nil completion:completion]; | |
| 684 | -} | |
| 685 | - | |
| 686 | -- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion { | |
| 687 | - if (iOS8Later) { | |
| 688 | - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | |
| 689 | - if (@available(iOS 9, *)) { | |
| 690 | - NSData *data = UIImageJPEGRepresentation(image, 0.9); | |
| 691 | - PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init]; | |
| 692 | - options.shouldMoveFile = YES; | |
| 693 | - PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; | |
| 694 | - [request addResourceWithType:PHAssetResourceTypePhoto data:data options:options]; | |
| 695 | - if (location) { | |
| 696 | - request.location = location; | |
| 697 | - } | |
| 698 | - request.creationDate = [NSDate date]; | |
| 699 | - } else { | |
| 700 | - PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image]; | |
| 701 | - if (location) { | |
| 702 | - request.location = location; | |
| 703 | - } | |
| 704 | - request.creationDate = [NSDate date]; | |
| 705 | - } | |
| 706 | - } completionHandler:^(BOOL success, NSError *error) { | |
| 707 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 708 | - if (success && completion) { | |
| 709 | - completion(nil); | |
| 710 | - } else if (error) { | |
| 711 | - NSLog(@"保存照片出错:%@",error.localizedDescription); | |
| 712 | - if (completion) { | |
| 713 | - completion(error); | |
| 714 | - } | |
| 715 | - } | |
| 716 | - }); | |
| 717 | - }]; | |
| 718 | - } else { | |
| 719 | - [self.assetLibrary writeImageToSavedPhotosAlbum:image.CGImage orientation:[self orientationFromImage:image] completionBlock:^(NSURL *assetURL, NSError *error) { | |
| 720 | - if (error) { | |
| 721 | - NSLog(@"保存图片失败:%@",error.localizedDescription); | |
| 722 | - if (completion) { | |
| 723 | - completion(error); | |
| 724 | - } | |
| 725 | - } else { | |
| 726 | - // 多给系统0.5秒的时间,让系统去更新相册数据 | |
| 727 | - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 728 | - if (completion) { | |
| 729 | - completion(nil); | |
| 730 | - } | |
| 731 | - }); | |
| 732 | - } | |
| 733 | - }]; | |
| 734 | - } | |
| 735 | -} | |
| 736 | - | |
| 737 | -#pragma mark - Save video | |
| 738 | - | |
| 739 | -- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion { | |
| 740 | - [self saveVideoWithUrl:url location:nil completion:completion]; | |
| 741 | -} | |
| 742 | - | |
| 743 | -- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion { | |
| 744 | - if (iOS8Later) { | |
| 745 | - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | |
| 746 | - if (@available(iOS 9, *)) { | |
| 747 | - PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init]; | |
| 748 | - options.shouldMoveFile = YES; | |
| 749 | - PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; | |
| 750 | - [request addResourceWithType:PHAssetResourceTypeVideo fileURL:url options:options]; | |
| 751 | - if (location) { | |
| 752 | - request.location = location; | |
| 753 | - } | |
| 754 | - request.creationDate = [NSDate date]; | |
| 755 | - } else { | |
| 756 | - PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url]; | |
| 757 | - if (location) { | |
| 758 | - request.location = location; | |
| 759 | - } | |
| 760 | - request.creationDate = [NSDate date]; | |
| 761 | - } | |
| 762 | - } completionHandler:^(BOOL success, NSError *error) { | |
| 763 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 764 | - if (success && completion) { | |
| 765 | - completion(nil); | |
| 766 | - } else if (error) { | |
| 767 | - NSLog(@"保存视频出错:%@",error.localizedDescription); | |
| 768 | - if (completion) { | |
| 769 | - completion(error); | |
| 770 | - } | |
| 771 | - } | |
| 772 | - }); | |
| 773 | - }]; | |
| 774 | - } else { | |
| 775 | - [self.assetLibrary writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { | |
| 776 | - if (error) { | |
| 777 | - NSLog(@"保存视频出错:%@",error.localizedDescription); | |
| 778 | - if (completion) { | |
| 779 | - completion(error); | |
| 780 | - } | |
| 781 | - } else { | |
| 782 | - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 783 | - if (completion) { | |
| 784 | - completion(nil); | |
| 785 | - } | |
| 786 | - }); | |
| 787 | - } | |
| 788 | - }]; | |
| 789 | - } | |
| 790 | -} | |
| 791 | - | |
| 792 | -#pragma mark - Get Video | |
| 793 | - | |
| 794 | -/// Get Video / 获取视频 | |
| 795 | -- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem *, NSDictionary *))completion { | |
| 796 | - [self getVideoWithAsset:asset progressHandler:nil completion:completion]; | |
| 797 | -} | |
| 798 | - | |
| 799 | -- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion { | |
| 800 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 801 | - PHVideoRequestOptions *option = [[PHVideoRequestOptions alloc] init]; | |
| 802 | - option.networkAccessAllowed = YES; | |
| 803 | - option.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 804 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 805 | - if (progressHandler) { | |
| 806 | - progressHandler(progress, error, stop, info); | |
| 807 | - } | |
| 808 | - }); | |
| 809 | - }; | |
| 810 | - [[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:option resultHandler:^(AVPlayerItem *playerItem, NSDictionary *info) { | |
| 811 | - if (completion) completion(playerItem,info); | |
| 812 | - }]; | |
| 813 | - } else if ([asset isKindOfClass:[ALAsset class]]) { | |
| 814 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 815 | - ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation]; | |
| 816 | - NSString *uti = [defaultRepresentation UTI]; | |
| 817 | - NSURL *videoURL = [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:uti]; | |
| 818 | - AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL]; | |
| 819 | - if (completion && playerItem) completion(playerItem,nil); | |
| 820 | - } | |
| 821 | -} | |
| 822 | - | |
| 823 | -#pragma mark - Export video | |
| 824 | - | |
| 825 | -/// Export Video / 导出视频 | |
| 826 | -- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { | |
| 827 | - [self getVideoOutputPathWithAsset:asset presetName:AVAssetExportPreset640x480 success:success failure:failure]; | |
| 828 | -} | |
| 829 | - | |
| 830 | -- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { | |
| 831 | - if ([asset isKindOfClass:[PHAsset class]]) { | |
| 832 | - PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init]; | |
| 833 | - options.version = PHVideoRequestOptionsVersionOriginal; | |
| 834 | - options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; | |
| 835 | - options.networkAccessAllowed = YES; | |
| 836 | - [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){ | |
| 837 | - // NSLog(@"Info:\n%@",info); | |
| 838 | - AVURLAsset *videoAsset = (AVURLAsset*)avasset; | |
| 839 | - // NSLog(@"AVAsset URL: %@",myAsset.URL); | |
| 840 | - [self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure]; | |
| 841 | - }]; | |
| 842 | - } else if ([asset isKindOfClass:[ALAsset class]]) { | |
| 843 | - NSURL *videoURL =[asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs | |
| 844 | - AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil]; | |
| 845 | - [self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure]; | |
| 846 | - } | |
| 847 | -} | |
| 848 | - | |
| 849 | -/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success: | |
| 850 | -- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion { | |
| 851 | - [self getVideoOutputPathWithAsset:asset success:completion failure:nil]; | |
| 852 | -} | |
| 853 | - | |
| 854 | -- (void)startExportVideoWithVideoAsset:(AVURLAsset *)videoAsset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { | |
| 855 | - // Find compatible presets by video asset. | |
| 856 | - NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset]; | |
| 857 | - | |
| 858 | - // Begin to compress video | |
| 859 | - // Now we just compress to low resolution if it supports | |
| 860 | - // If you need to upload to the server, but server does't support to upload by streaming, | |
| 861 | - // You can compress the resolution to lower. Or you can support more higher resolution. | |
| 862 | - if ([presets containsObject:presetName]) { | |
| 863 | - AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:presetName]; | |
| 864 | - | |
| 865 | - NSDateFormatter *formater = [[NSDateFormatter alloc] init]; | |
| 866 | - [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss-SSS"]; | |
| 867 | -#pragma mark - ADD: 原有逻辑-根据会话id取得path(LXG) | |
| 868 | - NSString *outputPath = @""; | |
| 869 | - if (self.outputPath.length <= 0) { | |
| 870 | - outputPath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/output-%@.mp4", [formater stringFromDate:[NSDate date]]]; | |
| 871 | - }else { | |
| 872 | - NSString *filaName = [NSString stringWithFormat:@"output-%@_fileType=videoType.mp4",[formater stringFromDate:[NSDate date]]]; | |
| 873 | - outputPath = [self.outputPath stringByAppendingString:filaName]; | |
| 874 | - } | |
| 875 | - | |
| 876 | - NSLog(@"video outputPath = %@",outputPath); | |
| 877 | - | |
| 878 | - session.outputURL = [NSURL fileURLWithPath:outputPath]; | |
| 879 | - | |
| 880 | - // Optimize for network use. | |
| 881 | - session.shouldOptimizeForNetworkUse = true; | |
| 882 | - | |
| 883 | - NSArray *supportedTypeArray = session.supportedFileTypes; | |
| 884 | - if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) { | |
| 885 | - session.outputFileType = AVFileTypeMPEG4; | |
| 886 | - } else if (supportedTypeArray.count == 0) { | |
| 887 | - if (failure) { | |
| 888 | - failure(@"该视频类型暂不支持导出", nil); | |
| 889 | - } | |
| 890 | - NSLog(@"No supported file types 视频类型暂不支持导出"); | |
| 891 | - return; | |
| 892 | - } else { | |
| 893 | - session.outputFileType = [supportedTypeArray objectAtIndex:0]; | |
| 894 | - } | |
| 895 | - | |
| 896 | - if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"]]) { | |
| 897 | - [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"] withIntermediateDirectories:YES attributes:nil error:nil]; | |
| 898 | - } | |
| 899 | - | |
| 900 | - if ([TZImagePickerConfig sharedInstance].needFixComposition) { | |
| 901 | - AVMutableVideoComposition *videoComposition = [self fixedCompositionWithAsset:videoAsset]; | |
| 902 | - if (videoComposition.renderSize.width) { | |
| 903 | - // 修正视频转向 | |
| 904 | - session.videoComposition = videoComposition; | |
| 905 | - } | |
| 906 | - } | |
| 907 | - | |
| 908 | - // Begin to export video to the output path asynchronously. | |
| 909 | -#pragma mark - ADD: 原有逻辑-会话id置空(LXG) | |
| 910 | - [session exportAsynchronouslyWithCompletionHandler:^(void) { | |
| 911 | - dispatch_async(dispatch_get_main_queue(), ^{ | |
| 912 | - | |
| 913 | - switch (session.status) { | |
| 914 | - case AVAssetExportSessionStatusUnknown: { | |
| 915 | - NSLog(@"AVAssetExportSessionStatusUnknown"); | |
| 916 | - } break; | |
| 917 | - case AVAssetExportSessionStatusWaiting: { | |
| 918 | - NSLog(@"AVAssetExportSessionStatusWaiting"); | |
| 919 | - } break; | |
| 920 | - case AVAssetExportSessionStatusExporting: { | |
| 921 | - NSLog(@"AVAssetExportSessionStatusExporting"); | |
| 922 | - } break; | |
| 923 | - case AVAssetExportSessionStatusCompleted: { | |
| 924 | - NSLog(@"AVAssetExportSessionStatusCompleted"); | |
| 925 | - if (success) { | |
| 926 | - success(outputPath); | |
| 927 | - } | |
| 928 | - } break; | |
| 929 | - case AVAssetExportSessionStatusFailed: { | |
| 930 | - NSLog(@"AVAssetExportSessionStatusFailed"); | |
| 931 | - if (failure) { | |
| 932 | - failure(@"视频导出失败", session.error); | |
| 933 | - } | |
| 934 | - } break; | |
| 935 | - case AVAssetExportSessionStatusCancelled: { | |
| 936 | - NSLog(@"AVAssetExportSessionStatusCancelled"); | |
| 937 | - if (failure) { | |
| 938 | - failure(@"导出任务已被取消", nil); | |
| 939 | - } | |
| 940 | - } break; | |
| 941 | - default: | |
| 942 | - NSLog(@"未知错误"); | |
| 943 | - break; | |
| 944 | - } | |
| 945 | - }); | |
| 946 | - }]; | |
| 947 | - } else { | |
| 948 | - if (failure) { | |
| 949 | - NSString *errorMessage = [NSString stringWithFormat:@"当前设备不支持该预设:%@", presetName]; | |
| 950 | - failure(errorMessage, nil); | |
| 951 | - } | |
| 952 | - } | |
| 953 | -} | |
| 954 | - | |
| 955 | -/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset | |
| 956 | -- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset { | |
| 957 | - if (iOS8Later) { | |
| 958 | - return [assets containsObject:asset]; | |
| 959 | - } else { | |
| 960 | - NSMutableArray *selectedAssetUrls = [NSMutableArray array]; | |
| 961 | - for (ALAsset *asset_item in assets) { | |
| 962 | - [selectedAssetUrls addObject:[asset_item valueForProperty:ALAssetPropertyURLs]]; | |
| 963 | - } | |
| 964 | - return [selectedAssetUrls containsObject:[asset valueForProperty:ALAssetPropertyURLs]]; | |
| 965 | - } | |
| 966 | -} | |
| 967 | - | |
| 968 | -- (BOOL)isCameraRollAlbum:(id)metadata { | |
| 969 | - if ([metadata isKindOfClass:[PHAssetCollection class]]) { | |
| 970 | - NSString *versionStr = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@""]; | |
| 971 | - if (versionStr.length <= 1) { | |
| 972 | - versionStr = [versionStr stringByAppendingString:@"00"]; | |
| 973 | - } else if (versionStr.length <= 2) { | |
| 974 | - versionStr = [versionStr stringByAppendingString:@"0"]; | |
| 975 | - } | |
| 976 | - CGFloat version = versionStr.floatValue; | |
| 977 | - // 目前已知8.0.0 ~ 8.0.2系统,拍照后的图片会保存在最近添加中 | |
| 978 | - if (version >= 800 && version <= 802) { | |
| 979 | - return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumRecentlyAdded; | |
| 980 | - } else { | |
| 981 | - return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumUserLibrary; | |
| 982 | - } | |
| 983 | - } | |
| 984 | - if ([metadata isKindOfClass:[ALAssetsGroup class]]) { | |
| 985 | - ALAssetsGroup *group = metadata; | |
| 986 | - return ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupSavedPhotos); | |
| 987 | - } | |
| 988 | - | |
| 989 | - return NO; | |
| 990 | -} | |
| 991 | - | |
| 992 | -- (NSString *)getAssetIdentifier:(id)asset { | |
| 993 | - if (iOS8Later) { | |
| 994 | - PHAsset *phAsset = (PHAsset *)asset; | |
| 995 | - return phAsset.localIdentifier; | |
| 996 | - } else { | |
| 997 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 998 | - NSURL *assetUrl = [alAsset valueForProperty:ALAssetPropertyAssetURL]; | |
| 999 | - return assetUrl.absoluteString; | |
| 1000 | - } | |
| 1001 | -} | |
| 1002 | - | |
| 1003 | -/// 检查照片大小是否满足最小要求 | |
| 1004 | -- (BOOL)isPhotoSelectableWithAsset:(id)asset { | |
| 1005 | - CGSize photoSize = [self photoSizeWithAsset:asset]; | |
| 1006 | - if (self.minPhotoWidthSelectable > photoSize.width || self.minPhotoHeightSelectable > photoSize.height) { | |
| 1007 | - return NO; | |
| 1008 | - } | |
| 1009 | - return YES; | |
| 1010 | -} | |
| 1011 | - | |
| 1012 | -- (CGSize)photoSizeWithAsset:(id)asset { | |
| 1013 | - if (iOS8Later) { | |
| 1014 | - PHAsset *phAsset = (PHAsset *)asset; | |
| 1015 | - return CGSizeMake(phAsset.pixelWidth, phAsset.pixelHeight); | |
| 1016 | - } else { | |
| 1017 | - ALAsset *alAsset = (ALAsset *)asset; | |
| 1018 | - return alAsset.defaultRepresentation.dimensions; | |
| 1019 | - } | |
| 1020 | -} | |
| 1021 | - | |
| 1022 | -#pragma mark - Private Method | |
| 1023 | - | |
| 1024 | -- (TZAlbumModel *)modelWithResult:(id)result name:(NSString *)name isCameraRoll:(BOOL)isCameraRoll needFetchAssets:(BOOL)needFetchAssets { | |
| 1025 | - TZAlbumModel *model = [[TZAlbumModel alloc] init]; | |
| 1026 | - [model setResult:result needFetchAssets:needFetchAssets]; | |
| 1027 | - model.name = name; | |
| 1028 | - model.isCameraRoll = isCameraRoll; | |
| 1029 | - if ([result isKindOfClass:[PHFetchResult class]]) { | |
| 1030 | - PHFetchResult *fetchResult = (PHFetchResult *)result; | |
| 1031 | - model.count = fetchResult.count; | |
| 1032 | - } else if ([result isKindOfClass:[ALAssetsGroup class]]) { | |
| 1033 | - ALAssetsGroup *group = (ALAssetsGroup *)result; | |
| 1034 | - model.count = [group numberOfAssets]; | |
| 1035 | - } | |
| 1036 | - return model; | |
| 1037 | -} | |
| 1038 | - | |
| 1039 | -/// 缩放图片至新尺寸 | |
| 1040 | -- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size { | |
| 1041 | - if (image.size.width > size.width) { | |
| 1042 | - UIGraphicsBeginImageContext(size); | |
| 1043 | - [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; | |
| 1044 | - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); | |
| 1045 | - UIGraphicsEndImageContext(); | |
| 1046 | - return newImage; | |
| 1047 | - | |
| 1048 | - /* 好像不怎么管用:https://mp.weixin.qq.com/s/CiqMlEIp1Ir2EJSDGgMooQ | |
| 1049 | - CGFloat maxPixelSize = MAX(size.width, size.height); | |
| 1050 | - CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)UIImageJPEGRepresentation(image, 0.9), nil); | |
| 1051 | - NSDictionary *options = @{(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways:(__bridge id)kCFBooleanTrue, | |
| 1052 | - (__bridge id)kCGImageSourceThumbnailMaxPixelSize:[NSNumber numberWithFloat:maxPixelSize] | |
| 1053 | - }; | |
| 1054 | - CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); | |
| 1055 | - UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:2 orientation:image.imageOrientation]; | |
| 1056 | - CGImageRelease(imageRef); | |
| 1057 | - CFRelease(sourceRef); | |
| 1058 | - return newImage; | |
| 1059 | - */ | |
| 1060 | - } else { | |
| 1061 | - return image; | |
| 1062 | - } | |
| 1063 | -} | |
| 1064 | - | |
| 1065 | -/// 判断asset是否是视频 | |
| 1066 | -- (BOOL)isVideo:(id)asset { | |
| 1067 | - if (iOS8Later) { | |
| 1068 | - PHAsset *phAsset = asset; | |
| 1069 | - return phAsset.mediaType == PHAssetMediaTypeVideo; | |
| 1070 | - } else { | |
| 1071 | - ALAsset *alAsset = asset; | |
| 1072 | - NSString *alAssetType = [[alAsset valueForProperty:ALAssetPropertyType] stringValue]; | |
| 1073 | - return [alAssetType isEqualToString:ALAssetTypeVideo]; | |
| 1074 | - } | |
| 1075 | -} | |
| 1076 | - | |
| 1077 | -- (ALAssetOrientation)orientationFromImage:(UIImage *)image { | |
| 1078 | - NSInteger orientation = image.imageOrientation; | |
| 1079 | - return orientation; | |
| 1080 | -} | |
| 1081 | - | |
| 1082 | -/// 获取优化后的视频转向信息 | |
| 1083 | -- (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset { | |
| 1084 | - AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; | |
| 1085 | - // 视频转向 | |
| 1086 | - int degrees = [self degressFromVideoFileWithAsset:videoAsset]; | |
| 1087 | - if (degrees != 0) { | |
| 1088 | - CGAffineTransform translateToCenter; | |
| 1089 | - CGAffineTransform mixedTransform; | |
| 1090 | - videoComposition.frameDuration = CMTimeMake(1, 30); | |
| 1091 | - | |
| 1092 | - NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo]; | |
| 1093 | - AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; | |
| 1094 | - | |
| 1095 | - AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; | |
| 1096 | - roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]); | |
| 1097 | - AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; | |
| 1098 | - | |
| 1099 | - if (degrees == 90) { | |
| 1100 | - // 顺时针旋转90° | |
| 1101 | - translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0); | |
| 1102 | - mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2); | |
| 1103 | - videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width); | |
| 1104 | - [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | |
| 1105 | - } else if(degrees == 180){ | |
| 1106 | - // 顺时针旋转180° | |
| 1107 | - translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height); | |
| 1108 | - mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI); | |
| 1109 | - videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height); | |
| 1110 | - [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | |
| 1111 | - } else if(degrees == 270){ | |
| 1112 | - // 顺时针旋转270° | |
| 1113 | - translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width); | |
| 1114 | - mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0); | |
| 1115 | - videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width); | |
| 1116 | - [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | |
| 1117 | - } | |
| 1118 | - | |
| 1119 | - roateInstruction.layerInstructions = @[roateLayerInstruction]; | |
| 1120 | - // 加入视频方向信息 | |
| 1121 | - videoComposition.instructions = @[roateInstruction]; | |
| 1122 | - } | |
| 1123 | - return videoComposition; | |
| 1124 | -} | |
| 1125 | - | |
| 1126 | -/// 获取视频角度 | |
| 1127 | -- (int)degressFromVideoFileWithAsset:(AVAsset *)asset { | |
| 1128 | - int degress = 0; | |
| 1129 | - NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; | |
| 1130 | - if([tracks count] > 0) { | |
| 1131 | - AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; | |
| 1132 | - CGAffineTransform t = videoTrack.preferredTransform; | |
| 1133 | - if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){ | |
| 1134 | - // Portrait | |
| 1135 | - degress = 90; | |
| 1136 | - } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){ | |
| 1137 | - // PortraitUpsideDown | |
| 1138 | - degress = 270; | |
| 1139 | - } else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){ | |
| 1140 | - // LandscapeRight | |
| 1141 | - degress = 0; | |
| 1142 | - } else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){ | |
| 1143 | - // LandscapeLeft | |
| 1144 | - degress = 180; | |
| 1145 | - } | |
| 1146 | - } | |
| 1147 | - return degress; | |
| 1148 | -} | |
| 1149 | - | |
| 1150 | -/// 修正图片转向 | |
| 1151 | -- (UIImage *)fixOrientation:(UIImage *)aImage { | |
| 1152 | - if (!self.shouldFixOrientation) return aImage; | |
| 1153 | - | |
| 1154 | - // No-op if the orientation is already correct | |
| 1155 | - if (aImage.imageOrientation == UIImageOrientationUp) | |
| 1156 | - return aImage; | |
| 1157 | - | |
| 1158 | - // We need to calculate the proper transformation to make the image upright. | |
| 1159 | - // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored. | |
| 1160 | - CGAffineTransform transform = CGAffineTransformIdentity; | |
| 1161 | - | |
| 1162 | - switch (aImage.imageOrientation) { | |
| 1163 | - case UIImageOrientationDown: | |
| 1164 | - case UIImageOrientationDownMirrored: | |
| 1165 | - transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height); | |
| 1166 | - transform = CGAffineTransformRotate(transform, M_PI); | |
| 1167 | - break; | |
| 1168 | - | |
| 1169 | - case UIImageOrientationLeft: | |
| 1170 | - case UIImageOrientationLeftMirrored: | |
| 1171 | - transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); | |
| 1172 | - transform = CGAffineTransformRotate(transform, M_PI_2); | |
| 1173 | - break; | |
| 1174 | - | |
| 1175 | - case UIImageOrientationRight: | |
| 1176 | - case UIImageOrientationRightMirrored: | |
| 1177 | - transform = CGAffineTransformTranslate(transform, 0, aImage.size.height); | |
| 1178 | - transform = CGAffineTransformRotate(transform, -M_PI_2); | |
| 1179 | - break; | |
| 1180 | - default: | |
| 1181 | - break; | |
| 1182 | - } | |
| 1183 | - | |
| 1184 | - switch (aImage.imageOrientation) { | |
| 1185 | - case UIImageOrientationUpMirrored: | |
| 1186 | - case UIImageOrientationDownMirrored: | |
| 1187 | - transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); | |
| 1188 | - transform = CGAffineTransformScale(transform, -1, 1); | |
| 1189 | - break; | |
| 1190 | - | |
| 1191 | - case UIImageOrientationLeftMirrored: | |
| 1192 | - case UIImageOrientationRightMirrored: | |
| 1193 | - transform = CGAffineTransformTranslate(transform, aImage.size.height, 0); | |
| 1194 | - transform = CGAffineTransformScale(transform, -1, 1); | |
| 1195 | - break; | |
| 1196 | - default: | |
| 1197 | - break; | |
| 1198 | - } | |
| 1199 | - | |
| 1200 | - // Now we draw the underlying CGImage into a new context, applying the transform | |
| 1201 | - // calculated above. | |
| 1202 | - CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height, | |
| 1203 | - CGImageGetBitsPerComponent(aImage.CGImage), 0, | |
| 1204 | - CGImageGetColorSpace(aImage.CGImage), | |
| 1205 | - CGImageGetBitmapInfo(aImage.CGImage)); | |
| 1206 | - CGContextConcatCTM(ctx, transform); | |
| 1207 | - switch (aImage.imageOrientation) { | |
| 1208 | - case UIImageOrientationLeft: | |
| 1209 | - case UIImageOrientationLeftMirrored: | |
| 1210 | - case UIImageOrientationRight: | |
| 1211 | - case UIImageOrientationRightMirrored: | |
| 1212 | - // Grr... | |
| 1213 | - CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage); | |
| 1214 | - break; | |
| 1215 | - | |
| 1216 | - default: | |
| 1217 | - CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage); | |
| 1218 | - break; | |
| 1219 | - } | |
| 1220 | - | |
| 1221 | - // And now we just create a new UIImage from the drawing context | |
| 1222 | - CGImageRef cgimg = CGBitmapContextCreateImage(ctx); | |
| 1223 | - UIImage *img = [UIImage imageWithCGImage:cgimg]; | |
| 1224 | - CGContextRelease(ctx); | |
| 1225 | - CGImageRelease(cgimg); | |
| 1226 | - return img; | |
| 1227 | -} | |
| 1228 | - | |
| 1229 | -#pragma clang diagnostic pop | |
| 1230 | - | |
| 1231 | -@end | |
| 1232 | - | |
| 1233 | - | |
| 1234 | -//@implementation TZSortDescriptor | |
| 1235 | -// | |
| 1236 | -//- (id)reversedSortDescriptor { | |
| 1237 | -// return [NSNumber numberWithBool:![TZImageManager manager].sortAscendingByModificationDate]; | |
| 1238 | -//} | |
| 1239 | -// | |
| 1240 | -//@end |
CNLiveImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlay@2x.png deleted
100755 → 0
3.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlayHL@2x.png deleted
100755 → 0
3.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/Root.plist deleted
100755 → 0
| 1 | -<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 3 | -<plist version="1.0"> | |
| 4 | -<dict> | |
| 5 | - <key>StringsTable</key> | |
| 6 | - <string>Root</string> | |
| 7 | - <key>PreferenceSpecifiers</key> | |
| 8 | - <array> | |
| 9 | - <dict> | |
| 10 | - <key>Type</key> | |
| 11 | - <string>PSGroupSpecifier</string> | |
| 12 | - <key>Title</key> | |
| 13 | - <string>Group</string> | |
| 14 | - </dict> | |
| 15 | - <dict> | |
| 16 | - <key>Type</key> | |
| 17 | - <string>PSTextFieldSpecifier</string> | |
| 18 | - <key>Title</key> | |
| 19 | - <string>Name</string> | |
| 20 | - <key>Key</key> | |
| 21 | - <string>name_preference</string> | |
| 22 | - <key>DefaultValue</key> | |
| 23 | - <string></string> | |
| 24 | - <key>IsSecure</key> | |
| 25 | - <false/> | |
| 26 | - <key>KeyboardType</key> | |
| 27 | - <string>Alphabet</string> | |
| 28 | - <key>AutocapitalizationType</key> | |
| 29 | - <string>None</string> | |
| 30 | - <key>AutocorrectionType</key> | |
| 31 | - <string>No</string> | |
| 32 | - </dict> | |
| 33 | - <dict> | |
| 34 | - <key>Type</key> | |
| 35 | - <string>PSToggleSwitchSpecifier</string> | |
| 36 | - <key>Title</key> | |
| 37 | - <string>Enabled</string> | |
| 38 | - <key>Key</key> | |
| 39 | - <string>enabled_preference</string> | |
| 40 | - <key>DefaultValue</key> | |
| 41 | - <true/> | |
| 42 | - </dict> | |
| 43 | - <dict> | |
| 44 | - <key>Type</key> | |
| 45 | - <string>PSSliderSpecifier</string> | |
| 46 | - <key>Key</key> | |
| 47 | - <string>slider_preference</string> | |
| 48 | - <key>DefaultValue</key> | |
| 49 | - <real>0.5</real> | |
| 50 | - <key>MinimumValue</key> | |
| 51 | - <integer>0</integer> | |
| 52 | - <key>MaximumValue</key> | |
| 53 | - <integer>1</integer> | |
| 54 | - <key>MinimumValueImage</key> | |
| 55 | - <string></string> | |
| 56 | - <key>MaximumValueImage</key> | |
| 57 | - <string></string> | |
| 58 | - </dict> | |
| 59 | - </array> | |
| 60 | -</dict> | |
| 61 | -</plist> |
CNLiveImagePickerController/TZImagePickerController.bundle/VideoSendIcon@2x.png deleted
100755 → 0
223 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/en.lproj/Localizable.strings deleted
100755 → 0
CNLiveImagePickerController/TZImagePickerController.bundle/fb_quxiao@2x.png deleted
100755 → 0
1.16 KB
CNLiveImagePickerController/TZImagePickerController.bundle/navi_back@2x.png deleted
100755 → 0
116 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/ph_select@2x.png deleted
100755 → 0
1.31 KB
CNLiveImagePickerController/TZImagePickerController.bundle/ph_unselect@2x.png deleted
100755 → 0
902 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_cell_normal.png deleted
100644 → 0
641 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_cell_normal@2x.png deleted
100644 → 0
1.35 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_cell_normal@3x.png deleted
100644 → 0
2.57 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_def_photoPickerVc@2x.png deleted
100755 → 0
1.13 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_def_previewVc@2x.png deleted
100755 → 0
1.13 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_number_icon@2x.png deleted
100755 → 0
501 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_back.png deleted
100644 → 0
234 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_back@2x.png deleted
100644 → 0
384 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_back@3x.png deleted
100644 → 0
726 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_def@2x.png deleted
100755 → 0
1.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_nor.png deleted
100644 → 0
1.04 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_nor@2x.png deleted
100644 → 0
2.12 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_nor@3x.png deleted
100644 → 0
6.27 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_normal.png deleted
100644 → 0
453 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_normal@2x.png deleted
100644 → 0
902 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_normal@3x.png deleted
100644 → 0
1.77 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_sel@2x.png deleted
100755 → 0
620 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_select.png deleted
100644 → 0
713 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_select@2x.png deleted
100644 → 0
1.31 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_select@3x.png deleted
100644 → 0
3.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_sel_photoPickerVc@2x.png deleted
100755 → 0
1006 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_sel_previewVc@2x.png deleted
100755 → 0
1.16 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photoback.png deleted
100644 → 0
1.01 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photoback@2x.png deleted
100644 → 0
1.16 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photoback@3x.png deleted
100644 → 0
1.23 KB
CNLiveImagePickerController/TZImagePickerController.bundle/preview_number_icon@2x.png deleted
100755 → 0
501 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/preview_original_def@2x.png deleted
100755 → 0
392 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/takePicture80@2x.png deleted
100755 → 0
1.25 KB
CNLiveImagePickerController/TZImagePickerController.bundle/takePicture@2x.png deleted
100755 → 0
2.98 KB
CNLiveImagePickerController/TZImagePickerController.bundle/upload_selection1@2x.png deleted
100755 → 0
1.35 KB
CNLiveImagePickerController/TZImagePickerController.bundle/vi.lproj/Localizable.strings deleted
100755 → 0
| 1 | -"OK" = "Xác nhận"; | |
| 2 | -"Back" = "Quay lại"; | |
| 3 | -"Done" = "Hoàn thành"; | |
| 4 | -"Sorry" = "Xin lỗi"; | |
| 5 | -"Cancel" = "Hủy"; | |
| 6 | -"Setting" = "Cài đặt"; | |
| 7 | -"Photos" = "Hình"; | |
| 8 | -"Videos" = "Clip"; | |
| 9 | -"Preview" = "Xem trước"; | |
| 10 | -"Full image" = "Hình gốc"; | |
| 11 | -"Processing..." = "Đang xử lý..."; | |
| 12 | -"Can not use camera" = "Máy chụp hình không khả dụng"; | |
| 13 | -"Synchronizing photos from iCloud" = "Đang đồng bộ hình ảnh từ ICloud"; | |
| 14 | -"Can not choose both video and photo" = "Trong lúc chọn hình ảnh không cùng lúc chọn video"; | |
| 15 | -"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"; | |
| 16 | -"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."; | |
| 17 | -"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."; | |
| 18 | - | |
| 19 | -"Select a maximum of %zd photos" = "Bạn chỉ được chọn nhiều nhất %zd tấm hình"; | |
| 20 | -"Select a minimum of %zd photos" = "Chọn ít nhất %zd tấm hình"; | |
| 21 | -"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."; | |
| 22 | -"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"; |
CNLiveImagePickerController/TZImagePickerController.bundle/video_edit_left@2x.png deleted
100644 → 0
203 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/video_edit_left@3x.png deleted
100644 → 0
514 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/video_edit_right@2x.png deleted
100644 → 0
208 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/video_edit_right@3x.png deleted
100644 → 0
502 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/zh-Hans.lproj/Localizable.strings deleted
100755 → 0
CNLiveImagePickerController/TZImagePickerController.bundle/zh-Hant.lproj/Localizable.strings deleted
100755 → 0
CNLiveImagePickerController/TZImagePickerController.h deleted
100755 → 0
| 1 | -// | |
| 2 | -// TZImagePickerController.h | |
| 3 | -// TZImagePickerController | |
| 4 | -// | |
| 5 | -// Created by 谭真 on 15/12/24. | |
| 6 | -// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | -// version 2.2.6 - 2018.08.21 | |
| 8 | -// 更多信息,请前往项目的github地址:https://github.com/banchichen/TZImagePickerController | |
| 9 | - | |
| 10 | -/* | |
| 11 | - 经过测试,比起xib的方式,把TZAssetCell改用纯代码的方式来写,滑动帧数明显提高了(约提高10帧左右) | |
| 12 | - | |
| 13 | - 最初发现这个问题并修复的是@小鱼周凌宇同学,她的博客地址: http://zhoulingyu.com/ | |
| 14 | - 表示感谢~ | |
| 15 | - | |
| 16 | - 原来xib确实会导致性能问题啊...大家也要注意了... | |
| 17 | - */ | |
| 18 | - | |
| 19 | -#import <UIKit/UIKit.h> | |
| 20 | -#import "TZAssetModel.h" | |
| 21 | -#import "NSBundle+TZImagePicker.h" | |
| 22 | -#import "TZImageManager.h" | |
| 23 | -#import "TZVideoPlayerController.h" | |
| 24 | -#import "TZGifPhotoPreviewController.h" | |
| 25 | -#import "TZLocationManager.h" | |
| 26 | -#import "TZPhotoPreviewController.h" | |
| 27 | - | |
| 28 | -#define iOS7Later ([UIDevice currentDevice].systemVersion.floatValue >= 7.0f) | |
| 29 | -#define iOS8Later ([UIDevice currentDevice].systemVersion.floatValue >= 8.0f) | |
| 30 | -/// | |
| 31 | -#define iOS9Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.0f) | |
| 32 | -#define iOS9_1Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.1f) | |
| 33 | - | |
| 34 | -@class TZAlbumCell, TZAssetCell; | |
| 35 | -@protocol TZImagePickerControllerDelegate; | |
| 36 | -@interface TZImagePickerController : UINavigationController | |
| 37 | - | |
| 38 | -#pragma mark - | |
| 39 | -/// Use this init method / 用这个初始化方法 | |
| 40 | -- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate; | |
| 41 | -- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate; | |
| 42 | -- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc; | |
| 43 | -/// This init method just for previewing photos / 用这个初始化方法以预览图片 | |
| 44 | -- (instancetype)initWithSelectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index; | |
| 45 | -/// This init method for crop photo / 用这个初始化方法以裁剪图片 | |
| 46 | -- (instancetype)initCropTypeWithAsset:(id)asset photo:(UIImage *)photo completion:(void (^)(UIImage *cropImage,id asset))completion; | |
| 47 | - | |
| 48 | -#pragma mark - | |
| 49 | - | |
| 50 | -#pragma mark - ADD:视频编辑(LXG) | |
| 51 | -///** | |
| 52 | -// 是否允许编辑视频,选择一张时候才允许编辑,默认NO | |
| 53 | -// */ | |
| 54 | -//@property (nonatomic, assign) BOOL allowEditVideo; | |
| 55 | - | |
| 56 | -/** | |
| 57 | - 编辑视频时最大裁剪时间,单位:秒,默认10s 且最低10s | |
| 58 | - | |
| 59 | - @discussion 当该参数为10s时,所选视频时长必须大于等于10s才允许进行编辑 | |
| 60 | - */ | |
| 61 | -@property (nonatomic, assign) NSInteger maxEditVideoTime; | |
| 62 | - | |
| 63 | -/** | |
| 64 | - 允许选择视频的最大时长,单位:秒, 默认 120s | |
| 65 | - */ | |
| 66 | -@property (nonatomic, assign) NSInteger maxVideoDuration; | |
| 67 | - | |
| 68 | -/// Default is 9 / 默认最大可选9张图片 | |
| 69 | -@property (nonatomic, assign) NSInteger maxImagesCount; | |
| 70 | - | |
| 71 | -/// The minimum count photos user must pick, Default is 0 | |
| 72 | -/// 最小照片必选张数,默认是0 | |
| 73 | -@property (nonatomic, assign) NSInteger minImagesCount; | |
| 74 | - | |
| 75 | -/// Always enale the done button, not require minimum 1 photo be picked | |
| 76 | -/// 让完成按钮一直可以点击,无须最少选择一张图片 | |
| 77 | -@property (nonatomic, assign) BOOL alwaysEnableDoneBtn; | |
| 78 | - | |
| 79 | -/// Sort photos ascending by modificationDate,Default is YES | |
| 80 | -/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个 | |
| 81 | -@property (nonatomic, assign) BOOL sortAscendingByModificationDate; | |
| 82 | - | |
| 83 | -/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽 | |
| 84 | -@property (nonatomic, assign) CGFloat photoWidth; | |
| 85 | - | |
| 86 | -/// Default is 600px / 默认600像素宽 | |
| 87 | -@property (nonatomic, assign) CGFloat photoPreviewMaxWidth; | |
| 88 | - | |
| 89 | -/// Default is 15, While fetching photo, HUD will dismiss automatic if timeout; | |
| 90 | -/// 超时时间,默认为15秒,当取图片时间超过15秒还没有取成功时,会自动dismiss HUD; | |
| 91 | -@property (nonatomic, assign) NSInteger timeout; | |
| 92 | - | |
| 93 | -/// Default is YES, if set NO, the original photo button will hide. user can't picking original photo. | |
| 94 | -/// 默认为YES,如果设置为NO,原图按钮将隐藏,用户不能选择发送原图 | |
| 95 | -@property (nonatomic, assign) BOOL allowPickingOriginalPhoto; | |
| 96 | - | |
| 97 | -/// Default is YES, if set NO, user can't picking video. | |
| 98 | -/// 默认为YES,如果设置为NO,用户将不能选择视频 | |
| 99 | -@property (nonatomic, assign) BOOL allowPickingVideo; | |
| 100 | -/// Default is NO / 默认为NO,为YES时可以多选视频/gif/图片,和照片共享最大可选张数maxImagesCount的限制 | |
| 101 | -@property (nonatomic, assign) BOOL allowPickingMultipleVideo; | |
| 102 | - | |
| 103 | -/// Default is NO, if set YES, user can picking gif image. | |
| 104 | -/// 默认为NO,如果设置为YES,用户可以选择gif图片 | |
| 105 | -@property (nonatomic, assign) BOOL allowPickingGif; | |
| 106 | - | |
| 107 | -/// Default is YES, if set NO, user can't picking image. | |
| 108 | -/// 默认为YES,如果设置为NO,用户将不能选择发送图片 | |
| 109 | -@property (nonatomic, assign) BOOL allowPickingImage; | |
| 110 | - | |
| 111 | -/// Default is YES, if set NO, user can't take picture. | |
| 112 | -/// 默认为YES,如果设置为NO, 用户将不能拍摄照片 | |
| 113 | -@property (nonatomic, assign) BOOL allowTakePicture; | |
| 114 | -@property (nonatomic, assign) BOOL allowCameraLocation; | |
| 115 | - | |
| 116 | -/// Default is YES, if set NO, user can't take video. | |
| 117 | -/// 默认为YES,如果设置为NO, 用户将不能拍摄视频 | |
| 118 | -@property(nonatomic, assign) BOOL allowTakeVideo; | |
| 119 | -/// Default value is 10 minutes / 视频最大拍摄时间,默认是10分钟,单位是秒 | |
| 120 | -@property (assign, nonatomic) NSTimeInterval videoMaximumDuration; | |
| 121 | -/// Customizing UIImagePickerController's other properties, such as videoQuality / 定制UIImagePickerController的其它属性,比如视频拍摄质量videoQuality | |
| 122 | -@property (nonatomic, copy) void(^uiImagePickerControllerSettingBlock)(UIImagePickerController *imagePickerController); | |
| 123 | - | |
| 124 | -/// 首选语言,如果设置了就用该语言,不设则取当前系统语言。 | |
| 125 | -/// 由于目前只支持中文、繁体中文、英文、越南语。故该属性只支持zh-Hans、zh-Hant、en、vi四种值,其余值无效。 | |
| 126 | -@property (copy, nonatomic) NSString *preferredLanguage; | |
| 127 | - | |
| 128 | -/// 语言bundle,preferredLanguage变化时languageBundle会变化 | |
| 129 | -/// 可通过手动设置bundle,让选择器支持新的的语言(需要在设置preferredLanguage后设置languageBundle)。欢迎提交PR把语言文件提交上来~ | |
| 130 | -@property (strong, nonatomic) NSBundle *languageBundle; | |
| 131 | - | |
| 132 | -/// Default is YES, if set NO, user can't preview photo. | |
| 133 | -/// 默认为YES,如果设置为NO,预览按钮将隐藏,用户将不能去预览照片 | |
| 134 | -@property (nonatomic, assign) BOOL allowPreview; | |
| 135 | - | |
| 136 | -/// Default is YES, if set NO, the picker don't dismiss itself. | |
| 137 | -/// 默认为YES,如果设置为NO, 选择器将不会自己dismiss | |
| 138 | -@property(nonatomic, assign) BOOL autoDismiss; | |
| 139 | - | |
| 140 | -/// Default is NO, if set YES, in the delegate method the photos and infos will be nil, only assets hava value. | |
| 141 | -/// 默认为NO,如果设置为YES,代理方法里photos和infos会是nil,只返回assets | |
| 142 | -@property (assign, nonatomic) BOOL onlyReturnAsset; | |
| 143 | - | |
| 144 | -/// Default is NO, if set YES, will show the image's selected index. | |
| 145 | -/// 默认为NO,如果设置为YES,会显示照片的选中序号 | |
| 146 | -@property (assign, nonatomic) BOOL showSelectedIndex; | |
| 147 | - | |
| 148 | -/// 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. | |
| 149 | -/// 默认是NO,如果设置为YES,当照片选择张数达到maxImagesCount时,其它照片会显示颜色为cannotSelectLayerColor的浮层 | |
| 150 | -@property (assign, nonatomic) BOOL showPhotoCannotSelectLayer; | |
| 151 | -/// Default is white color with 0.8 alpha; | |
| 152 | -@property (strong, nonatomic) UIColor *cannotSelectLayerColor; | |
| 153 | - | |
| 154 | -/// Default is No, if set YES, the result photo will not be scaled to photoWidth pixel width. The photoWidth default is 828px | |
| 155 | -/// 默认是NO,如果设置为YES,内部不会缩放图片到photoWidth像素宽 | |
| 156 | -@property (assign, nonatomic) BOOL notScaleImage; | |
| 157 | - | |
| 158 | -/// 默认是NO,如果设置为YES,导出视频时会修正转向(慎重设为YES,可能导致部分安卓下拍的视频导出失败) | |
| 159 | -@property (assign, nonatomic) BOOL needFixComposition; | |
| 160 | - | |
| 161 | -/// The photos user have selected | |
| 162 | -/// 用户选中过的图片数组 | |
| 163 | -@property (nonatomic, strong) NSMutableArray *selectedAssets; | |
| 164 | -@property (nonatomic, strong) NSMutableArray<TZAssetModel *> *selectedModels; | |
| 165 | -@property (nonatomic, strong) NSMutableArray *selectedAssetIds; | |
| 166 | -- (void)addSelectedModel:(TZAssetModel *)model; | |
| 167 | -- (void)removeSelectedModel:(TZAssetModel *)model; | |
| 168 | - | |
| 169 | -/// Minimum selectable photo width, Default is 0 | |
| 170 | -/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中 | |
| 171 | -@property (nonatomic, assign) NSInteger minPhotoWidthSelectable; | |
| 172 | -@property (nonatomic, assign) NSInteger minPhotoHeightSelectable; | |
| 173 | -/// Hide the photo what can not be selected, Default is NO | |
| 174 | -/// 隐藏不可以选中的图片,默认是NO,不推荐将其设置为YES | |
| 175 | -@property (nonatomic, assign) BOOL hideWhenCanNotSelect; | |
| 176 | -/// Deprecated, Use statusBarStyle (顶部statusBar 是否为系统默认的黑色,默认为NO) | |
| 177 | -@property (nonatomic, assign) BOOL isStatusBarDefault __attribute__((deprecated("Use -statusBarStyle."))); | |
| 178 | -/// statusBar的样式,默认为UIStatusBarStyleLightContent | |
| 179 | -@property (assign, nonatomic) UIStatusBarStyle statusBarStyle; | |
| 180 | - | |
| 181 | -#pragma mark - | |
| 182 | -/// Single selection mode, valid when maxImagesCount = 1 | |
| 183 | -/// 单选模式,maxImagesCount为1时才生效 | |
| 184 | -@property (nonatomic, assign) BOOL showSelectBtn; ///< 在单选模式下,照片列表页中,显示选择按钮,默认为NO | |
| 185 | -@property (nonatomic, assign) BOOL allowCrop; ///< 允许裁剪,默认为YES,showSelectBtn为NO才生效 | |
| 186 | -@property (nonatomic, assign) CGRect cropRect; ///< 裁剪框的尺寸 | |
| 187 | -@property (nonatomic, assign) CGRect cropRectPortrait; ///< 裁剪框的尺寸(竖屏) | |
| 188 | -@property (nonatomic, assign) CGRect cropRectLandscape; ///< 裁剪框的尺寸(横屏) | |
| 189 | -@property (nonatomic, assign) BOOL needCircleCrop; ///< 需要圆形裁剪框 | |
| 190 | -@property (nonatomic, assign) NSInteger circleCropRadius; ///< 圆形裁剪框半径大小 | |
| 191 | -@property (nonatomic, copy) void (^cropViewSettingBlock)(UIView *cropView); ///< 自定义裁剪框的其他属性 | |
| 192 | -@property (nonatomic, copy) void (^navLeftBarButtonSettingBlock)(UIButton *leftButton); ///< 自定义返回按钮样式及其属性 | |
| 193 | - | |
| 194 | -/// 【自定义各页面/组件的样式】在界面初始化/组件setModel完成后调用,允许外界修改样式等 | |
| 195 | -@property (nonatomic, copy) void (^photoPickerPageUIConfigBlock)(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine); | |
| 196 | -@property (nonatomic, copy) void (^photoPreviewPageUIConfigBlock)(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel); | |
| 197 | -@property (nonatomic, copy) void (^videoPreviewPageUIConfigBlock)(UIButton *playButton, UIView *toolBar, UIButton *doneButton); | |
| 198 | -@property (nonatomic, copy) void (^gifPreviewPageUIConfigBlock)(UIView *toolBar, UIButton *doneButton); | |
| 199 | -@property (nonatomic, copy) void (^assetCellDidSetModelBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | |
| 200 | -@property (nonatomic, copy) void (^albumCellDidSetModelBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | |
| 201 | -/// 【自定义各页面/组件的frame】在界面viewDidLayoutSubviews/组件layoutSubviews后调用,允许外界修改frame等 | |
| 202 | -@property (nonatomic, copy) void (^photoPickerPageDidLayoutSubviewsBlock)(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine); | |
| 203 | -@property (nonatomic, copy) void (^photoPreviewPageDidLayoutSubviewsBlock)(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel); | |
| 204 | -@property (nonatomic, copy) void (^videoPreviewPageDidLayoutSubviewsBlock)(UIButton *playButton, UIView *toolBar, UIButton *doneButton); | |
| 205 | -@property (nonatomic, copy) void (^gifPreviewPageDidLayoutSubviewsBlock)(UIView *toolBar, UIButton *doneButton); | |
| 206 | -@property (nonatomic, copy) void (^assetCellDidLayoutSubviewsBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | |
| 207 | -@property (nonatomic, copy) void (^albumCellDidLayoutSubviewsBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | |
| 208 | - | |
| 209 | -#pragma mark - | |
| 210 | -- (id)showAlertWithTitle:(NSString *)title; | |
| 211 | -- (void)hideAlertView:(id)alertView; | |
| 212 | -- (void)showProgressHUD; | |
| 213 | -- (void)hideProgressHUD; | |
| 214 | -@property (nonatomic, assign) BOOL isSelectOriginalPhoto; | |
| 215 | -@property (assign, nonatomic) BOOL needShowStatusBar; | |
| 216 | - | |
| 217 | -#pragma mark - | |
| 218 | -@property (nonatomic, copy) NSString *takePictureImageName __attribute__((deprecated("Use -takePictureImage."))); | |
| 219 | -@property (nonatomic, copy) NSString *photoSelImageName __attribute__((deprecated("Use -photoSelImage."))); | |
| 220 | -@property (nonatomic, copy) NSString *photoDefImageName __attribute__((deprecated("Use -photoDefImage."))); | |
| 221 | -@property (nonatomic, copy) NSString *photoOriginSelImageName __attribute__((deprecated("Use -photoOriginSelImage."))); | |
| 222 | -@property (nonatomic, copy) NSString *photoOriginDefImageName __attribute__((deprecated("Use -photoOriginDefImage."))); | |
| 223 | -@property (nonatomic, copy) NSString *photoPreviewOriginDefImageName __attribute__((deprecated("Use -photoPreviewOriginDefImage."))); | |
| 224 | -@property (nonatomic, copy) NSString *photoNumberIconImageName __attribute__((deprecated("Use -photoNumberIconImage."))); | |
| 225 | -@property (nonatomic, strong) UIImage *takePictureImage; | |
| 226 | -@property (nonatomic, strong) UIImage *photoSelImage; | |
| 227 | -@property (nonatomic, strong) UIImage *photoDefImage; | |
| 228 | -@property (nonatomic, strong) UIImage *photoOriginSelImage; | |
| 229 | -@property (nonatomic, strong) UIImage *photoOriginDefImage; | |
| 230 | -@property (nonatomic, strong) UIImage *photoPreviewOriginDefImage; | |
| 231 | -@property (nonatomic, strong) UIImage *photoNumberIconImage; | |
| 232 | - | |
| 233 | -#pragma mark - | |
| 234 | -/// Appearance / 外观颜色 + 按钮文字 | |
| 235 | -@property (nonatomic, strong) UIColor *oKButtonTitleColorNormal; | |
| 236 | -@property (nonatomic, strong) UIColor *oKButtonTitleColorDisabled; | |
| 237 | -@property (nonatomic, strong) UIColor *naviBgColor; | |
| 238 | -@property (nonatomic, strong) UIColor *naviTitleColor; | |
| 239 | -@property (nonatomic, strong) UIFont *naviTitleFont; | |
| 240 | -@property (nonatomic, strong) UIColor *barItemTextColor; | |
| 241 | -@property (nonatomic, strong) UIFont *barItemTextFont; | |
| 242 | -@property (nonatomic, strong) UIColor *editBtnTitleColor;///LXG编辑按钮title颜色 | |
| 243 | - | |
| 244 | -@property (nonatomic, copy) NSString *doneBtnTitleStr; | |
| 245 | -@property (nonatomic, copy) NSString *cancelBtnTitleStr; | |
| 246 | -@property (nonatomic, copy) NSString *previewBtnTitleStr; | |
| 247 | -@property (nonatomic, copy) NSString *fullImageBtnTitleStr; | |
| 248 | -@property (nonatomic, copy) NSString *settingBtnTitleStr; | |
| 249 | -@property (nonatomic, copy) NSString *processHintStr; | |
| 250 | -@property (nonatomic, copy) NSString *editBtnTitleStr;///LXG编辑按钮 | |
| 251 | - | |
| 252 | - | |
| 253 | - | |
| 254 | -/// 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. If you need it, please set it as soon as possible | |
| 255 | -/// icon主题色,默认是微信的绿色,值是r:31 g:185 b:34。目前仅支持showSelectedIndex为YES时的图片选中icon。如需要,请尽早设置它。 | |
| 256 | -@property (strong, nonatomic) UIColor *iconThemeColor; | |
| 257 | - | |
| 258 | -#pragma mark - | |
| 259 | -- (void)cancelButtonClick; | |
| 260 | - | |
| 261 | -// The picker should dismiss itself; when it dismissed these handle will be called. | |
| 262 | -// You can also set autoDismiss to NO, then the picker don't dismiss itself. | |
| 263 | -// If isOriginalPhoto is YES, user picked the original photo. | |
| 264 | -// You can get original photo with asset, by the method [[TZImageManager manager] getOriginalPhotoWithAsset:completion:]. | |
| 265 | -// The UIImage Object in photos default width is 828px, you can set it by photoWidth property. | |
| 266 | -// 这个照片选择器会自己dismiss,当选择器dismiss的时候,会执行下面的handle | |
| 267 | -// 你也可以设置autoDismiss属性为NO,选择器就不会自己dismis了 | |
| 268 | -// 如果isSelectOriginalPhoto为YES,表明用户选择了原图 | |
| 269 | -// 你可以通过一个asset获得原图,通过这个方法:[[TZImageManager manager] getOriginalPhotoWithAsset:completion:] | |
| 270 | -// photos数组里的UIImage对象,默认是828像素宽,你可以通过设置photoWidth属性的值来改变它 | |
| 271 | -@property (nonatomic, copy) void (^didFinishPickingPhotosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto); | |
| 272 | -@property (nonatomic, copy) void (^didFinishPickingPhotosWithInfosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto,NSArray<NSDictionary *> *infos); | |
| 273 | -@property (nonatomic, copy) void (^imagePickerControllerDidCancelHandle)(void); | |
| 274 | - | |
| 275 | -// If user picking a video, this handle will be called. | |
| 276 | -// If system version > iOS8,asset is kind of PHAsset class, else is ALAsset class. | |
| 277 | -// 如果用户选择了一个视频,下面的handle会被执行 | |
| 278 | -// 如果系统版本大于iOS8,asset是PHAsset类的对象,否则是ALAsset类的对象 | |
| 279 | -@property (nonatomic, copy) void (^didFinishPickingVideoHandle)(UIImage *coverImage,id asset); | |
| 280 | - | |
| 281 | -// If user picking a gif image, this callback will be called. | |
| 282 | -// 如果用户选择了一个gif图片,下面的handle会被执行 | |
| 283 | -@property (nonatomic, copy) void (^didFinishPickingGifImageHandle)(UIImage *animatedImage,id sourceAssets); | |
| 284 | - | |
| 285 | -@property (nonatomic, weak) id<TZImagePickerControllerDelegate> pickerDelegate; | |
| 286 | - | |
| 287 | -@end | |
| 288 | - | |
| 289 | - | |
| 290 | -@protocol TZImagePickerControllerDelegate <NSObject> | |
| 291 | -@optional | |
| 292 | -// The picker should dismiss itself; when it dismissed these handle will be called. | |
| 293 | -// You can also set autoDismiss to NO, then the picker don't dismiss itself. | |
| 294 | -// If isOriginalPhoto is YES, user picked the original photo. | |
| 295 | -// You can get original photo with asset, by the method [[TZImageManager manager] getOriginalPhotoWithAsset:completion:]. | |
| 296 | -// The UIImage Object in photos default width is 828px, you can set it by photoWidth property. | |
| 297 | -// 这个照片选择器会自己dismiss,当选择器dismiss的时候,会执行下面的handle | |
| 298 | -// 你也可以设置autoDismiss属性为NO,选择器就不会自己dismis了 | |
| 299 | -// 如果isSelectOriginalPhoto为YES,表明用户选择了原图 | |
| 300 | -// 你可以通过一个asset获得原图,通过这个方法:[[TZImageManager manager] getOriginalPhotoWithAsset:completion:] | |
| 301 | -// photos数组里的UIImage对象,默认是828像素宽,你可以通过设置photoWidth属性的值来改变它 | |
| 302 | -- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto; | |
| 303 | -- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray<NSDictionary *> *)infos; | |
| 304 | -//- (void)imagePickerControllerDidCancel:(TZImagePickerController *)picker __attribute__((deprecated("Use -tz_imagePickerControllerDidCancel:."))); | |
| 305 | -- (void)tz_imagePickerControllerDidCancel:(TZImagePickerController *)picker; | |
| 306 | - | |
| 307 | -// If user picking a video, this callback will be called. | |
| 308 | -// If system version > iOS8,asset is kind of PHAsset class, else is ALAsset class. | |
| 309 | -// 如果用户选择了一个视频,下面的handle会被执行 | |
| 310 | -// 如果系统版本大于iOS8,asset是PHAsset类的对象,否则是ALAsset类的对象 | |
| 311 | -- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset; | |
| 312 | - | |
| 313 | -// If user picking a gif image, this callback will be called. | |
| 314 | -// 如果用户选择了一个gif图片,下面的handle会被执行 | |
| 315 | -- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingGifImage:(UIImage *)animatedImage sourceAssets:(id)asset; | |
| 316 | - | |
| 317 | -// Decide album show or not't | |
| 318 | -// 决定相册显示与否 albumName:相册名字 result:相册原始数据 | |
| 319 | -- (BOOL)isAlbumCanSelect:(NSString *)albumName result:(id)result; | |
| 320 | - | |
| 321 | -// Decide asset show or not't | |
| 322 | -// 决定照片显示与否 | |
| 323 | -- (BOOL)isAssetCanSelect:(id)asset; | |
| 324 | -@end | |
| 325 | - | |
| 326 | - | |
| 327 | -@interface TZAlbumPickerController : UIViewController | |
| 328 | -@property (nonatomic, assign) NSInteger columnNumber; | |
| 329 | -@property (assign, nonatomic) BOOL isFirstAppear; | |
| 330 | -- (void)configTableView; | |
| 331 | -@end | |
| 332 | - | |
| 333 | - | |
| 334 | -@interface UIImage (MyBundle) | |
| 335 | -+ (UIImage *)imageNamedFromMyBundle:(NSString *)name; | |
| 336 | -@end | |
| 337 | - | |
| 338 | - | |
| 339 | -@interface NSString (TzExtension) | |
| 340 | -- (BOOL)tz_containsString:(NSString *)string; | |
| 341 | -- (CGSize)tz_calculateSizeWithAttributes:(NSDictionary *)attributes maxSize:(CGSize)maxSize; | |
| 342 | -@end | |
| 343 | - | |
| 344 | - | |
| 345 | -@interface TZCommonTools : NSObject | |
| 346 | -+ (BOOL)tz_isIPhoneX; | |
| 347 | -+ (CGFloat)tz_statusBarHeight; | |
| 348 | -// 获得Info.plist数据字典 | |
| 349 | -+ (NSDictionary *)tz_getInfoDictionary; | |
| 350 | -@end | |
| 351 | - | |
| 352 | - | |
| 353 | -@interface TZImagePickerConfig : NSObject | |
| 354 | -+ (instancetype)sharedInstance; | |
| 355 | -@property (copy, nonatomic) NSString *preferredLanguage; | |
| 356 | -@property(nonatomic, assign) BOOL allowPickingImage; | |
| 357 | -@property (nonatomic, assign) BOOL allowPickingVideo; | |
| 358 | -@property (strong, nonatomic) NSBundle *languageBundle; | |
| 359 | -/// 默认是200,如果一个GIF过大,里面图片个数可能超过1000,会导致内存飙升而崩溃 | |
| 360 | -@property (assign, nonatomic) NSInteger gifPreviewMaxImagesCount; | |
| 361 | -@property (assign, nonatomic) BOOL showSelectedIndex; | |
| 362 | -@property (assign, nonatomic) BOOL showPhotoCannotSelectLayer; | |
| 363 | -@property (assign, nonatomic) BOOL notScaleImage; | |
| 364 | -@property (assign, nonatomic) BOOL needFixComposition; | |
| 365 | -@end |