Commit 41fcf581 cgx

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

1 个父辈 7facae2d
正在显示 61 个修改的文件 包含 1812 行增加4 行删除
......@@ -85,5 +85,9 @@
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>NSCameraUsageDescription</key>
<string>拍照</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>相册</string>
</dict>
</plist>
#import <UIKit/UIKit.h>
/// 意见反馈图片自定义cell
@interface FeedImageCollectionCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIImageView *addIcon;
@property (nonatomic, strong) UIButton *deleteBtn;
@property (nonatomic, strong) id asset;
@end
#import "FeedImageCollectionCell.h"
#import <Photos/Photos.h>
@implementation FeedImageCollectionCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.imageView];
[self addSubview:self.deleteBtn];
[self.imageView addSubview:self.addIcon];
[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.bottom.equalTo(self);
make.top.equalTo(self).offset(15);
make.right.equalTo(self).offset(-15);
make.width.height.equalTo(@77);
}];
[self.deleteBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.top.equalTo(self);
make.width.height.equalTo(@30);
}];
[self.addIcon mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.equalTo(@30);
make.center.equalTo(self.imageView);
}];
}
return self;
}
- (void)setAsset:(id)asset {
_asset = asset;
if ([asset isKindOfClass:[PHAsset class]]) {
// PHAsset *phAsset = asset;
}
}
#pragma mark - lazy
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [UIImageView new];
_imageView.dk_backgroundColorPicker = DKColorPickerWithColors(ColorFromHex(0xf0f0f0), CornerViewDarkColor, DSWhite);
_imageView.contentMode = UIViewContentModeScaleToFill;
[_imageView cornerRadius:12];
}
return _imageView;
}
- (UIImageView *)addIcon {
if (!_addIcon) {
_addIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"add_img"]];
}
return _addIcon;
}
- (UIButton *)deleteBtn {
if (!_deleteBtn) {
_deleteBtn = [UIButton new];
[_deleteBtn setImage:[UIImage imageNamed:@"image_delete"] forState:UIControlStateNormal];
}
return _deleteBtn;
}
@end
......@@ -8,8 +8,15 @@
#import "FeedbackController.h"
#import "MyFeedListController.h"
#import "FeedbackRequestModel.h"
#import "TZImagePickerController.h"
#import "FeedImageCollectionCell.h"
@interface FeedbackController () <MyFeedListControllerDelegate, UITextViewDelegate>
// 最大图片上传数量
static int MaxUpdateImgCount = 3;
// 相册显示列数
static int AlbumColumnCount = 4;
@interface FeedbackController () <MyFeedListControllerDelegate, UITextViewDelegate, TZImagePickerControllerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UINavigationControllerDelegate>
@property (strong, nonatomic) UILabel *redLab;
@property (assign, nonatomic) int unreadCount;
@property (strong, nonatomic) UITextView *feedTV;
......@@ -17,7 +24,11 @@
@property (strong, nonatomic) UIButton *commitBtn;
@end
@implementation FeedbackController
@implementation FeedbackController {
NSMutableArray *_selectedPhotos;
NSMutableArray *_selectedAssets;
BOOL _isSelectOriginalPhoto;
}
- (void)viewDidLoad {
[super viewDidLoad];
......@@ -25,6 +36,9 @@
self.navigationItem.title = @"意见反馈";
self.view.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
_selectedPhotos = [NSMutableArray array];
_selectedAssets = [NSMutableArray array];
[self setNaviRightItem];
[self.view addSubview:self.feedTV];
......@@ -93,6 +107,152 @@
}];
}
- (void)deleteBtnClik:(UIButton *)sender {
[_selectedPhotos removeObjectAtIndex:sender.tag];
[_selectedAssets removeObjectAtIndex:sender.tag];
[self.imgCollectionView performBatchUpdates:^{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:sender.tag inSection:0];
[self.imgCollectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
[self.imgCollectionView reloadData];
}];
}
#pragma mark - UICollectionViewDataSource && UICollectionViewDelegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _selectedPhotos.count + 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
FeedImageCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"FeedImageCollectionCell" forIndexPath:indexPath];
if (indexPath.row == 0) {
cell.imageView.image = nil;
cell.addIcon.hidden = NO;
cell.deleteBtn.hidden = YES;
} else {
cell.imageView.image = _selectedPhotos[indexPath.row-1];
cell.asset = _selectedAssets[indexPath.row-1];
cell.addIcon.hidden = YES;
cell.deleteBtn.hidden = NO;
cell.deleteBtn.tag = indexPath.row - 1;
[cell.deleteBtn addTarget:self action:@selector(deleteBtnClik:) forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) { // 打开相册
[self pushTZImagePickerController];
} else { // 预览照片或者视频
id asset = _selectedAssets[(indexPath.row - 1)];
BOOL isVideo = NO;
if ([asset isKindOfClass:[PHAsset class]]) {
PHAsset *phAsset = asset;
isVideo = phAsset.mediaType == PHAssetMediaTypeVideo;
}
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithSelectedAssets:_selectedAssets selectedPhotos:_selectedPhotos index:(indexPath.row - 1)];
imagePickerVc.maxImagesCount = MaxUpdateImgCount;
imagePickerVc.allowPickingOriginalPhoto = YES;
imagePickerVc.allowPickingMultipleVideo =NO;
imagePickerVc.showSelectedIndex = YES;
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
WS(weakSelf);
[imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
self->_selectedPhotos = [NSMutableArray arrayWithArray:photos];
self->_selectedAssets = [NSMutableArray arrayWithArray:assets];
self->_isSelectOriginalPhoto = isSelectOriginalPhoto;
[weakSelf.imgCollectionView reloadData];
}];
[self presentViewController:imagePickerVc animated:YES completion:nil];
}
}
#pragma mark - TZImagePickerController
- (void)pushTZImagePickerController {
if (MaxUpdateImgCount <= 0) { return; }
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:MaxUpdateImgCount columnNumber:AlbumColumnCount delegate:self pushPhotoPickerVc:NO];
imagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
// 设置目前已经选中的图片数组
if (MaxUpdateImgCount > 1) { imagePickerVc.selectedAssets = _selectedAssets; }
// 在内部显示拍照按钮
imagePickerVc.allowTakePicture = YES;
imagePickerVc.iconThemeColor = ColorFromRGBA(31.0, 185.0, 34.0, 1.0);
imagePickerVc.showPhotoCannotSelectLayer = YES;
imagePickerVc.cannotSelectLayerColor = [DSWhite colorWithAlphaComponent:0.8];
// 设置是否可以选择视频/图片/原图
imagePickerVc.allowPickingVideo = NO;
imagePickerVc.allowPickingImage = YES;
imagePickerVc.allowPickingOriginalPhoto = YES;
imagePickerVc.allowPickingGif = NO;
imagePickerVc.allowPickingOriginalPhoto = NO;
// 是否可以多选视频
imagePickerVc.allowPickingMultipleVideo = NO;
// 照片排列按修改时间升序
imagePickerVc.sortAscendingByModificationDate = YES;
imagePickerVc.showSelectBtn = NO;
imagePickerVc.allowCrop = NO;
imagePickerVc.needCircleCrop = NO;
// 设置竖屏下的裁剪尺寸
NSInteger left = 30;
NSInteger widthHeight = self.view.width - 2 * left;
NSInteger top = (self.view.height - widthHeight) / 2;
imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
// 设置是否显示图片序号
imagePickerVc.showSelectedIndex = YES;
// 你可以通过block或者代理,来得到用户选择的照片.
[imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
}];
[self presentViewController:imagePickerVc animated:YES completion:nil];
}
#pragma mark - TZImagePickerControllerDelegate
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray<NSDictionary *> *)infos {
_selectedPhotos = [NSMutableArray arrayWithArray:photos];
_selectedAssets = [NSMutableArray arrayWithArray:assets];
_isSelectOriginalPhoto = isSelectOriginalPhoto;
[self.imgCollectionView reloadData];
// 1.打印图片名字
[self printAssetsName:assets];
// 2.图片位置信息
for (PHAsset *phAsset in assets) {
DSLog(@"location:%@", phAsset.location);
}
}
// 如果用户选择了一个gif图片,下面的handle会被执行
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingGifImage:(UIImage *)animatedImage sourceAssets:(id)asset {
_selectedPhotos = [NSMutableArray arrayWithArray:@[animatedImage]];
_selectedAssets = [NSMutableArray arrayWithArray:@[asset]];
[self.imgCollectionView reloadData];
}
// 决定相册显示与否
- (BOOL)isAlbumCanSelect:(NSString *)albumName result:(id)result {
return YES;
}
// 决定asset显示与否
- (BOOL)isAssetCanSelect:(id)asset {
return YES;
}
#pragma mark - Private
- (void)printAssetsName:(NSArray *)assets {
/// 打印图片名字
NSString *fileName;
for (id asset in assets) {
if ([asset isKindOfClass:[PHAsset class]]) {
PHAsset *phAsset = (PHAsset *)asset;
fileName = [phAsset valueForKey:@"filename"];
}
}
}
#pragma mark - lazy
- (UITextView *)feedTV {
if (!_feedTV) {
......@@ -108,8 +268,20 @@
- (UICollectionView *)imgCollectionView {
if (!_imgCollectionView) {
UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(92, 92);
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 0;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_imgCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_imgCollectionView.showsHorizontalScrollIndicator = NO;
_imgCollectionView.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
_imgCollectionView.dataSource = self;
_imgCollectionView.delegate = self;
_imgCollectionView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
[_imgCollectionView registerClass:[FeedImageCollectionCell class] forCellWithReuseIdentifier:@"FeedImageCollectionCell"];
}
return _imgCollectionView;
}
......@@ -163,7 +335,7 @@
make.top.equalTo(self.feedTV.mas_bottom).offset(26);
make.left.equalTo(@15);
make.right.equalTo(@-15);
make.height.equalTo(@77);
make.height.equalTo(@92);
}];
[self.commitBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.imgCollectionView.mas_bottom).offset(60);
......
{
"images" : [
{
"filename" : "add_img.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "add_img@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "add_img@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "image_delete.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "image_delete@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "image_delete@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// NSBundle+TZImagePicker.h
// TZImagePickerController
//
// Created by 谭真 on 16/08/18.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NSBundle (TZImagePicker)
+ (NSBundle *)tz_imagePickerBundle;
+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value;
+ (NSString *)tz_localizedStringForKey:(NSString *)key;
@end
//
// NSBundle+TZImagePicker.m
// TZImagePickerController
//
// Created by 谭真 on 16/08/18.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "NSBundle+TZImagePicker.h"
#import "TZImagePickerController.h"
@implementation NSBundle (TZImagePicker)
+ (NSBundle *)tz_imagePickerBundle {
NSBundle *bundle = [NSBundle bundleForClass:[TZImagePickerController class]];
NSURL *url = [bundle URLForResource:@"TZImagePickerController" withExtension:@"bundle"];
bundle = [NSBundle bundleWithURL:url];
return bundle;
}
+ (NSString *)tz_localizedStringForKey:(NSString *)key {
return [self tz_localizedStringForKey:key value:@""];
}
+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value {
NSBundle *bundle = [TZImagePickerConfig sharedInstance].languageBundle;
NSString *value1 = [bundle localizedStringForKey:key value:value table:nil];
return value1;
}
@end
//
// TZAssetCell.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <Photos/Photos.h>
typedef enum : NSUInteger {
TZAssetCellTypePhoto = 0,
TZAssetCellTypeLivePhoto,
TZAssetCellTypePhotoGif,
TZAssetCellTypeVideo,
TZAssetCellTypeAudio,
} TZAssetCellType;
@class TZAssetModel;
@interface TZAssetCell : UICollectionViewCell
@property (weak, nonatomic) UIButton *selectPhotoButton;
@property (weak, nonatomic) UIButton *cannotSelectLayerButton;
@property (nonatomic, strong) TZAssetModel *model;
@property (assign, nonatomic) NSInteger index;
@property (nonatomic, copy) void (^didSelectPhotoBlock)(BOOL);
@property (nonatomic, assign) TZAssetCellType type;
@property (nonatomic, assign) BOOL allowPickingGif;
@property (nonatomic, assign) BOOL allowPickingMultipleVideo;
@property (nonatomic, copy) NSString *representedAssetIdentifier;
@property (nonatomic, assign) int32_t imageRequestID;
@property (nonatomic, strong) UIImage *photoSelImage;
@property (nonatomic, strong) UIImage *photoDefImage;
@property (nonatomic, assign) BOOL showSelectBtn;
@property (assign, nonatomic) BOOL allowPreview;
@property (assign, nonatomic) BOOL useCachedImage;
@end
@class TZAlbumModel;
@interface TZAlbumCell : UITableViewCell
@property (nonatomic, strong) TZAlbumModel *model;
@property (weak, nonatomic) UIButton *selectedCountButton;
@end
@interface TZAssetCameraCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *imageView;
@end
//
// TZAssetModel.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TZAssetModelMediaTypePhoto = 0,
TZAssetModelMediaTypeLivePhoto,
TZAssetModelMediaTypePhotoGif,
TZAssetModelMediaTypeVideo,
TZAssetModelMediaTypeAudio
} TZAssetModelMediaType;
@class PHAsset;
@interface TZAssetModel : NSObject
@property (nonatomic, strong) id asset; ///< PHAsset or ALAsset
@property (nonatomic, assign) BOOL isSelected; ///< The select status of a photo, default is No
@property (nonatomic, assign) TZAssetModelMediaType type;
@property (assign, nonatomic) BOOL needOscillatoryAnimation;
@property (nonatomic, copy) NSString *timeLength;
@property (strong, nonatomic) UIImage *cachedImage;
/// Init a photo dataModel With a asset
/// 用一个PHAsset/ALAsset实例,初始化一个照片模型
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type;
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength;
@end
@class PHFetchResult;
@interface TZAlbumModel : NSObject
@property (nonatomic, strong) NSString *name; ///< The album name
@property (nonatomic, assign) NSInteger count; ///< Count of photos the album contain
@property (nonatomic, strong) id result; ///< PHFetchResult<PHAsset> or ALAssetsGroup<ALAsset>
@property (nonatomic, strong) NSArray *models;
@property (nonatomic, strong) NSArray *selectedModels;
@property (nonatomic, assign) NSUInteger selectedCount;
@property (nonatomic, assign) BOOL isCameraRoll;
- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets;
@end
//
// TZAssetModel.m
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "TZAssetModel.h"
#import "TZImageManager.h"
@implementation TZAssetModel
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type{
TZAssetModel *model = [[TZAssetModel alloc] init];
model.asset = asset;
model.isSelected = NO;
model.type = type;
return model;
}
+ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength {
TZAssetModel *model = [self modelWithAsset:asset type:type];
model.timeLength = timeLength;
return model;
}
@end
@implementation TZAlbumModel
- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets {
_result = result;
if (needFetchAssets) {
[[TZImageManager manager] getAssetsFromFetchResult:result completion:^(NSArray<TZAssetModel *> *models) {
self->_models = models;
if (self->_selectedModels) {
[self checkSelectedModels];
}
}];
}
}
- (void)setSelectedModels:(NSArray *)selectedModels {
_selectedModels = selectedModels;
if (_models) {
[self checkSelectedModels];
}
}
- (void)checkSelectedModels {
self.selectedCount = 0;
NSMutableArray *selectedAssets = [NSMutableArray array];
for (TZAssetModel *model in _selectedModels) {
[selectedAssets addObject:model.asset];
}
for (TZAssetModel *model in _models) {
if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) {
self.selectedCount ++;
}
}
}
- (NSString *)name {
if (_name) {
return _name;
}
return @"";
}
@end
//
// TZGifPhotoPreviewController.h
// TZImagePickerController
//
// Created by ttouch on 2016/12/13.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAssetModel;
@interface TZGifPhotoPreviewController : UIViewController
@property (nonatomic, strong) TZAssetModel *model;
@end
//
// TZGifPhotoPreviewController.m
// TZImagePickerController
//
// Created by ttouch on 2016/12/13.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZGifPhotoPreviewController.h"
#import "TZImagePickerController.h"
#import "TZAssetModel.h"
#import "UIView+Layout.h"
#import "TZPhotoPreviewCell.h"
#import "TZImageManager.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@interface TZGifPhotoPreviewController () {
UIView *_toolBar;
UIButton *_doneButton;
UIProgressView *_progress;
TZPhotoPreviewView *_previewView;
UIStatusBarStyle _originStatusBarStyle;
}
@end
@implementation TZGifPhotoPreviewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
self.navigationItem.title = [NSString stringWithFormat:@"GIF %@",tzImagePickerVc.previewBtnTitleStr];
}
[self configPreviewView];
[self configBottomToolBar];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
[UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
}
- (void)configPreviewView {
_previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero];
_previewView.model = self.model;
__weak typeof(self) weakSelf = self;
[_previewView setSingleTapGestureBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf signleTapAction];
}];
[self.view addSubview:_previewView];
}
- (void)configBottomToolBar {
_toolBar = [[UIView alloc] initWithFrame:CGRectZero];
CGFloat rgb = 34 / 255.0;
_toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7];
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
[_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
[_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal];
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal];
} else {
[_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal];
[_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal];
}
[_toolBar addSubview:_doneButton];
UILabel *byteLabel = [[UILabel alloc] init];
byteLabel.textColor = [UIColor whiteColor];
byteLabel.font = [UIFont systemFontOfSize:13];
byteLabel.frame = CGRectMake(10, 0, 100, 44);
[[TZImageManager manager] getPhotosBytesWithArray:@[_model] completion:^(NSString *totalBytes) {
byteLabel.text = totalBytes;
}];
[_toolBar addSubview:byteLabel];
[self.view addSubview:_toolBar];
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_previewView.frame = self.view.bounds;
_previewView.scrollView.frame = self.view.bounds;
CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44;
_toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight);
_doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44);
}
#pragma mark - Click Event
- (void)signleTapAction {
_toolBar.hidden = !_toolBar.isHidden;
[self.navigationController setNavigationBarHidden:_toolBar.isHidden];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (iOS7Later) {
if (_toolBar.isHidden) {
[UIApplication sharedApplication].statusBarHidden = YES;
} else if (tzImagePickerVc.needShowStatusBar) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
}
}
- (void)doneButtonClick {
if (self.navigationController) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
if (imagePickerVc.autoDismiss) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
} else {
[self callDelegateMethod];
}
} else {
[self dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
}
}
- (void)callDelegateMethod {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
UIImage *animatedImage = _previewView.imageView.image;
if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingGifImage:sourceAssets:)]) {
[imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingGifImage:animatedImage sourceAssets:_model.asset];
}
if (imagePickerVc.didFinishPickingGifImageHandle) {
imagePickerVc.didFinishPickingGifImageHandle(animatedImage,_model.asset);
}
}
#pragma clang diagnostic pop
@end
//
// TZImageCropManager.h
// TZImagePickerController
//
// Created by 谭真 on 2016/12/5.
// Copyright © 2016年 谭真. All rights reserved.
// 图片裁剪管理类
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface TZImageCropManager : NSObject
/// 裁剪框背景的处理
+ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop;
/*
1.7.2 为了解决多位同学对于图片裁剪的需求,我这两天有空便在研究图片裁剪
幸好有tuyou的PhotoTweaks库做参考,裁剪的功能实现起来简单许多
该方法和其内部引用的方法基本来自于tuyou的PhotoTweaks库,我做了稍许删减和修改
感谢tuyou同学在github开源了优秀的裁剪库PhotoTweaks,表示感谢
PhotoTweaks库的github链接:https://github.com/itouch2/PhotoTweaks
*/
/// 获得裁剪后的图片
+ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView;
/// 获取圆形图片
+ (UIImage *)circularClipImage:(UIImage *)image;
@end
/// 该分类的代码来自SDWebImage:https://github.com/rs/SDWebImage
/// 为了防止冲突,我将分类名字和方法名字做了修改
@interface UIImage (TZGif)
+ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data;
@end
//
// TZImageCropManager.m
// TZImagePickerController
//
// Created by 谭真 on 2016/12/5.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZImageCropManager.h"
#import "UIView+Layout.h"
#import <ImageIO/ImageIO.h>
#import "TZImageManager.h"
#import "TZImagePickerController.h"
@implementation TZImageCropManager
/// 裁剪框背景的处理
+ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop {
UIBezierPath *path= [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds];
CAShapeLayer *layer = [CAShapeLayer layer];
if (needCircleCrop) { // 圆形裁剪框
[path appendPath:[UIBezierPath bezierPathWithArcCenter:containerView.center radius:cropRect.size.width / 2 startAngle:0 endAngle: 2 * M_PI clockwise:NO]];
} else { // 矩形裁剪框
[path appendPath:[UIBezierPath bezierPathWithRect:cropRect]];
}
layer.path = path.CGPath;
layer.fillRule = kCAFillRuleEvenOdd;
layer.fillColor = [[UIColor blackColor] CGColor];
layer.opacity = 0.5;
[view.layer addSublayer:layer];
}
/// 获得裁剪后的图片
+ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView {
CGAffineTransform transform = CGAffineTransformIdentity;
// 平移的处理
CGRect imageViewRect = [imageView convertRect:imageView.bounds toView:containerView];
CGPoint point = CGPointMake(imageViewRect.origin.x + imageViewRect.size.width / 2, imageViewRect.origin.y + imageViewRect.size.height / 2);
CGFloat xMargin = containerView.tz_width - CGRectGetMaxX(rect) - rect.origin.x;
CGPoint zeroPoint = CGPointMake((CGRectGetWidth(containerView.frame) - xMargin) / 2, containerView.center.y);
CGPoint translation = CGPointMake(point.x - zeroPoint.x, point.y - zeroPoint.y);
transform = CGAffineTransformTranslate(transform, translation.x, translation.y);
// 缩放的处理
transform = CGAffineTransformScale(transform, zoomScale, zoomScale);
CGImageRef imageRef = [self newTransformedImage:transform
sourceImage:imageView.image.CGImage
sourceSize:imageView.image.size
outputWidth:rect.size.width * [UIScreen mainScreen].scale
cropSize:rect.size
imageViewSize:imageView.frame.size];
UIImage *cropedImage = [UIImage imageWithCGImage:imageRef];
cropedImage = [[TZImageManager manager] fixOrientation:cropedImage];
CGImageRelease(imageRef);
return cropedImage;
}
+ (CGImageRef)newTransformedImage:(CGAffineTransform)transform sourceImage:(CGImageRef)sourceImage sourceSize:(CGSize)sourceSize outputWidth:(CGFloat)outputWidth cropSize:(CGSize)cropSize imageViewSize:(CGSize)imageViewSize {
CGImageRef source = [self newScaledImage:sourceImage toSize:sourceSize];
CGFloat aspect = cropSize.height/cropSize.width;
CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect);
CGContextRef context = CGBitmapContextCreate(NULL, outputSize.width, outputSize.height, CGImageGetBitsPerComponent(source), 0, CGImageGetColorSpace(source), CGImageGetBitmapInfo(source));
CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height));
CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width / cropSize.width, outputSize.height / cropSize.height);
uiCoords = CGAffineTransformTranslate(uiCoords, cropSize.width/2.0, cropSize.height / 2.0);
uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0);
CGContextConcatCTM(context, uiCoords);
CGContextConcatCTM(context, transform);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2, -imageViewSize.height/2.0, imageViewSize.width, imageViewSize.height), source);
CGImageRef resultRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGImageRelease(source);
return resultRef;
}
+ (CGImageRef)newScaledImage:(CGImageRef)source toSize:(CGSize)size {
CGSize srcSize = size;
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(rgbColorSpace);
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
CGContextTranslateCTM(context, size.width/2, size.height/2);
CGContextDrawImage(context, CGRectMake(-srcSize.width/2, -srcSize.height/2, srcSize.width, srcSize.height), source);
CGImageRef resultRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return resultRef;
}
/// 获取圆形图片
+ (UIImage *)circularClipImage:(UIImage *)image {
UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextAddEllipseInRect(ctx, rect);
CGContextClip(ctx);
[image drawInRect:rect];
UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return circleImage;
}
@end
@implementation UIImage (TZGif)
+ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage;
if (count <= 1) {
animatedImage = [[UIImage alloc] initWithData:data];
}
else {
// images数组过大时内存会飙升,在这里限制下最大count
NSInteger maxCount = [TZImagePickerConfig sharedInstance].gifPreviewMaxImagesCount ?: 200;
NSInteger interval = MAX((count + maxCount / 2) / maxCount, 1);
NSMutableArray *images = [NSMutableArray array];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i+=interval) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
if (!image) {
continue;
}
duration += [self sd_frameDurationAtIndex:i source:source] * MIN(interval, 3);
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
CGImageRelease(image);
}
if (!duration) {
duration = (1.0f / 10.0f) * count;
}
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
CFRelease(source);
return animatedImage;
}
+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
float frameDuration = 0.1f;
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
if (delayTimeUnclampedProp) {
frameDuration = [delayTimeUnclampedProp floatValue];
}
else {
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
if (delayTimeProp) {
frameDuration = [delayTimeProp floatValue];
}
}
// Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
// We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
// a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
// for more information.
if (frameDuration < 0.011f) {
frameDuration = 0.100f;
}
CFRelease(cfFrameProperties);
return frameDuration;
}
@end
//
// TZImageManager.h
// TZImagePickerController
//
// Created by 谭真 on 16/1/4.
// Copyright © 2016年 谭真. All rights reserved.
// 图片资源获取管理类
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
#import "TZAssetModel.h"
@class TZAlbumModel,TZAssetModel;
@protocol TZImagePickerControllerDelegate;
@interface TZImageManager : NSObject
@property (nonatomic, strong) PHCachingImageManager *cachingImageManager;
+ (instancetype)manager NS_SWIFT_NAME(default());
+ (void)deallocManager;
@property (weak, nonatomic) id<TZImagePickerControllerDelegate> pickerDelegate;
@property (nonatomic, assign) BOOL shouldFixOrientation;
/// Default is 600px / 默认600像素宽
@property (nonatomic, assign) CGFloat photoPreviewMaxWidth;
/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽
@property (nonatomic, assign) CGFloat photoWidth;
/// Default is 4, Use in photos collectionView in TZPhotoPickerController
/// 默认4列, TZPhotoPickerController中的照片collectionView
@property (nonatomic, assign) NSInteger columnNumber;
/// Sort photos ascending by modificationDate,Default is YES
/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个
@property (nonatomic, assign) BOOL sortAscendingByModificationDate;
/// Minimum selectable photo width, Default is 0
/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中
@property (nonatomic, assign) NSInteger minPhotoWidthSelectable;
@property (nonatomic, assign) NSInteger minPhotoHeightSelectable;
@property (nonatomic, assign) BOOL hideWhenCanNotSelect;
/// Return YES if Authorized 返回YES如果得到了授权
- (BOOL)authorizationStatusAuthorized;
+ (NSInteger)authorizationStatus;
- (void)requestAuthorizationWithCompletion:(void (^)(void))completion;
/// Get Album 获得相册/相册数组
- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion;
- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *models))completion;
/// Get Assets 获得Asset数组
- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *models))completion;
- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *models))completion;
- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *model))completion;
/// Get photo 获得照片
- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *postImage))completion;
- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion;
- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion;
- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
/// Get full Image 获取原图
/// 如下两个方法completion一般会调多次,一般会先返回缩略图,再返回原图(详见方法内部使用的系统API的说明),如果info[PHImageResultIsDegradedKey] 为 YES,则表明当前返回的是缩略图,否则是原图。
- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion;
- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion;
// 该方法中,completion只会走一次
- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion;
/// Save photo 保存照片
- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion;
- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion;
/// Save video 保存视频
- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion;
- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion;
/// Get video 获得视频
- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem * playerItem, NSDictionary * info))completion;
- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion;
/// Export video 导出视频 presetName: 预设名字,默认值是AVAssetExportPreset640x480
- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure;
- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure;
/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success:
- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion __attribute__((deprecated("Use -getVideoOutputPathWithAsset:failure:success:")));
/// Get photo bytes 获得一组照片的大小
- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion;
/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset
- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset;
- (NSString *)getAssetIdentifier:(id)asset;
- (BOOL)isCameraRollAlbum:(id)metadata;
/// 检查照片大小是否满足最小要求
- (BOOL)isPhotoSelectableWithAsset:(id)asset;
- (CGSize)photoSizeWithAsset:(id)asset;
/// 修正图片转向
- (UIImage *)fixOrientation:(UIImage *)aImage;
/// 获取asset的资源类型
- (TZAssetModelMediaType)getAssetType:(id)asset;
/// 缩放图片至新尺寸
- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size;
@end
//@interface TZSortDescriptor : NSSortDescriptor
//
//@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StringsTable</key>
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Group</string>
</dict>
<dict>
<key>Type</key>
<string>PSTextFieldSpecifier</string>
<key>Title</key>
<string>Name</string>
<key>Key</key>
<string>name_preference</string>
<key>DefaultValue</key>
<string></string>
<key>IsSecure</key>
<false/>
<key>KeyboardType</key>
<string>Alphabet</string>
<key>AutocapitalizationType</key>
<string>None</string>
<key>AutocorrectionType</key>
<string>No</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Enabled</string>
<key>Key</key>
<string>enabled_preference</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSSliderSpecifier</string>
<key>Key</key>
<string>slider_preference</string>
<key>DefaultValue</key>
<real>0.5</real>
<key>MinimumValue</key>
<integer>0</integer>
<key>MaximumValue</key>
<integer>1</integer>
<key>MinimumValueImage</key>
<string></string>
<key>MaximumValueImage</key>
<string></string>
</dict>
</array>
</dict>
</plist>
"OK" = "Xác nhận";
"Back" = "Quay lại";
"Done" = "Hoàn thành";
"Sorry" = "Xin lỗi";
"Cancel" = "Hủy";
"Setting" = "Cài đặt";
"Photos" = "Hình";
"Videos" = "Clip";
"Preview" = "Xem trước";
"Full image" = "Hình gốc";
"Processing..." = "Đang xử lý...";
"Can not use camera" = "Máy chụp hình không khả dụng";
"Synchronizing photos from iCloud" = "Đang đồng bộ hình ảnh từ ICloud";
"Can not choose both video and photo" = "Trong lúc chọn hình ảnh không cùng lúc chọn video";
"Can not choose both photo and GIF" = "Trong lúc chọn hình ảnh không cùng lúc chọn hình GIF";
"Select the video when in multi state, we will handle the video as a photo" = "Chọn hình ảnh cùng video, video sẽ bị mặc nhận thành hình ảnh và gửi đi.";
"Can not jump to the privacy settings page, please go to the settings page by self, thank you" = "Không thể chuyển tự động qua trang cài đặt riêng tư, bạn hãy thoát ra cà điều chỉnh lại, cám ơn bạn.";
"Select a maximum of %zd photos" = "Bạn chỉ được chọn nhiều nhất %zd tấm hình";
"Select a minimum of %zd photos" = "Chọn ít nhất %zd tấm hình";
"Allow %@ to access your album in \"Settings -> Privacy -> Photos\"" = "Vui lòng tại mục iPhone \" Cài đặt – quyền riêng tư - Ảnh\" mở quyền cho phép %@ truy cập ảnh.";
"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\"" = "Vui lòng tại mục iPhone \" Cài đặt – quyền riêng tư - Ảnh\" mở quyền cho phép %@ truy cập máy ảnh";
//
// TZLocationManager.h
// TZImagePickerController
//
// Created by 谭真 on 2017/06/03.
// Copyright © 2017年 谭真. All rights reserved.
// 定位管理类
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
@interface TZLocationManager : NSObject
+ (instancetype)manager;
/// 开始定位
- (void)startLocation;
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock;
- (void)startLocationWithGeocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock;
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock geocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock;
@end
//
// TZLocationManager.m
// TZImagePickerController
//
// Created by 谭真 on 2017/06/03.
// Copyright © 2017年 谭真. All rights reserved.
// 定位管理类
#import "TZLocationManager.h"
#import "TZImagePickerController.h"
@interface TZLocationManager ()<CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager;
/// 定位成功的回调block
@property (nonatomic, copy) void (^successBlock)(NSArray<CLLocation *> *);
/// 编码成功的回调block
@property (nonatomic, copy) void (^geocodeBlock)(NSArray *geocodeArray);
/// 定位失败的回调block
@property (nonatomic, copy) void (^failureBlock)(NSError *error);
@end
@implementation TZLocationManager
+ (instancetype)manager {
static TZLocationManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
manager.locationManager = [[CLLocationManager alloc] init];
manager.locationManager.delegate = manager;
if (iOS8Later) {
[manager.locationManager requestWhenInUseAuthorization];
}
});
return manager;
}
- (void)startLocation {
[self startLocationWithSuccessBlock:nil failureBlock:nil geocoderBlock:nil];
}
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock {
[self startLocationWithSuccessBlock:successBlock failureBlock:failureBlock geocoderBlock:nil];
}
- (void)startLocationWithGeocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock {
[self startLocationWithSuccessBlock:nil failureBlock:nil geocoderBlock:geocoderBlock];
}
- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock geocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock {
[self.locationManager startUpdatingLocation];
_successBlock = successBlock;
_geocodeBlock = geocoderBlock;
_failureBlock = failureBlock;
}
#pragma mark - CLLocationManagerDelegate
/// 地理位置发生改变时触发
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
[manager stopUpdatingLocation];
if (_successBlock) {
_successBlock(locations);
}
if (_geocodeBlock && locations.count) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:[locations firstObject] completionHandler:^(NSArray *array, NSError *error) {
self->_geocodeBlock(array);
}];
}
}
/// 定位失败回调方法
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(@"定位失败, 错误: %@",error);
switch([error code]) {
case kCLErrorDenied: { // 用户禁止了定位权限
} break;
default: break;
}
if (_failureBlock) {
_failureBlock(error);
}
}
@end
//
// TZPhotoPickerController.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAlbumModel;
@interface TZPhotoPickerController : UIViewController
@property (nonatomic, assign) BOOL isFirstAppear;
@property (nonatomic, assign) NSInteger columnNumber;
@property (nonatomic, strong) TZAlbumModel *model;
@end
@interface TZCollectionView : UICollectionView
@end
//
// TZPhotoPreviewCell.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAssetModel;
@interface TZAssetPreviewCell : UICollectionViewCell
@property (nonatomic, strong) TZAssetModel *model;
@property (nonatomic, copy) void (^singleTapGestureBlock)(void);
- (void)configSubviews;
- (void)photoPreviewCollectionViewDidScroll;
@end
@class TZAssetModel,TZProgressView,TZPhotoPreviewView;
@interface TZPhotoPreviewCell : TZAssetPreviewCell
@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress);
@property (nonatomic, strong) TZPhotoPreviewView *previewView;
@property (nonatomic, assign) BOOL allowCrop;
@property (nonatomic, assign) CGRect cropRect;
- (void)recoverSubviews;
@end
@interface TZPhotoPreviewView : UIView
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *imageContainerView;
@property (nonatomic, strong) TZProgressView *progressView;
@property (nonatomic, assign) BOOL allowCrop;
@property (nonatomic, assign) CGRect cropRect;
@property (nonatomic, strong) TZAssetModel *model;
@property (nonatomic, strong) id asset;
@property (nonatomic, copy) void (^singleTapGestureBlock)(void);
@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress);
@property (nonatomic, assign) int32_t imageRequestID;
- (void)recoverSubviews;
@end
@class AVPlayer, AVPlayerLayer;
@interface TZVideoPreviewCell : TZAssetPreviewCell
@property (strong, nonatomic) AVPlayer *player;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
@property (strong, nonatomic) UIButton *playButton;
@property (strong, nonatomic) UIImage *cover;
- (void)pausePlayerAndShowNaviBar;
@end
@interface TZGifPreviewCell : TZAssetPreviewCell
@property (strong, nonatomic) TZPhotoPreviewView *previewView;
@end
//
// TZPhotoPreviewController.h
// TZImagePickerController
//
// Created by 谭真 on 15/12/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TZPhotoPreviewController : UIViewController
@property (nonatomic, strong) NSMutableArray *models; ///< All photo models / 所有图片模型数组
@property (nonatomic, strong) NSMutableArray *photos; ///< All photos / 所有图片数组
@property (nonatomic, assign) NSInteger currentIndex; ///< Index of the photo user click / 用户点击的图片的索引
@property (nonatomic, assign) BOOL isSelectOriginalPhoto; ///< If YES,return original photo / 是否返回原图
@property (nonatomic, assign) BOOL isCropImage;
/// Return the new selected photos / 返回最新的选中图片数组
@property (nonatomic, copy) void (^backButtonClickBlock)(BOOL isSelectOriginalPhoto);
@property (nonatomic, copy) void (^doneButtonClickBlock)(BOOL isSelectOriginalPhoto);
@property (nonatomic, copy) void (^doneButtonClickBlockCropMode)(UIImage *cropedImage,id asset);
@property (nonatomic, copy) void (^doneButtonClickBlockWithPreviewType)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto);
@end
//
// TZProgressView.h
// TZImagePickerController
//
// Created by ttouch on 2016/12/6.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TZProgressView : UIView
@property (nonatomic, assign) double progress;
@end
//
// TZProgressView.m
// TZImagePickerController
//
// Created by ttouch on 2016/12/6.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZProgressView.h"
@interface TZProgressView ()
@property (nonatomic, strong) CAShapeLayer *progressLayer;
@end
@implementation TZProgressView
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor];
_progressLayer = [CAShapeLayer layer];
_progressLayer.fillColor = [[UIColor clearColor] CGColor];
_progressLayer.strokeColor = [[UIColor whiteColor] CGColor];
_progressLayer.opacity = 1;
_progressLayer.lineCap = kCALineCapRound;
_progressLayer.lineWidth = 5;
[_progressLayer setShadowColor:[UIColor blackColor].CGColor];
[_progressLayer setShadowOffset:CGSizeMake(1, 1)];
[_progressLayer setShadowOpacity:0.5];
[_progressLayer setShadowRadius:2];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2);
CGFloat radius = rect.size.width / 2;
CGFloat startA = - M_PI_2;
CGFloat endA = - M_PI_2 + M_PI * 2 * _progress;
_progressLayer.frame = self.bounds;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
_progressLayer.path =[path CGPath];
[_progressLayer removeFromSuperlayer];
[self.layer addSublayer:_progressLayer];
}
- (void)setProgress:(double)progress {
_progress = progress;
[self setNeedsDisplay];
}
@end
//
// TZVideoPlayerController.h
// TZImagePickerController
//
// Created by 谭真 on 16/1/5.
// Copyright © 2016年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TZAssetModel;
@interface TZVideoPlayerController : UIViewController
@property (nonatomic, strong) TZAssetModel *model;
@end
\ No newline at end of file
//
// TZVideoPlayerController.m
// TZImagePickerController
//
// Created by 谭真 on 16/1/5.
// Copyright © 2016年 谭真. All rights reserved.
//
#import "TZVideoPlayerController.h"
#import <MediaPlayer/MediaPlayer.h>
#import "UIView+Layout.h"
#import "TZImageManager.h"
#import "TZAssetModel.h"
#import "TZImagePickerController.h"
#import "TZPhotoPreviewController.h"
@interface TZVideoPlayerController () {
AVPlayer *_player;
AVPlayerLayer *_playerLayer;
UIButton *_playButton;
UIImage *_cover;
UIView *_toolBar;
UIButton *_doneButton;
UIProgressView *_progress;
UIStatusBarStyle _originStatusBarStyle;
}
@property (assign, nonatomic) BOOL needShowStatusBar;
@end
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@implementation TZVideoPlayerController
- (void)viewDidLoad {
[super viewDidLoad];
self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden;
self.view.backgroundColor = [UIColor blackColor];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
self.navigationItem.title = tzImagePickerVc.previewBtnTitleStr;
}
[self configMoviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
[UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
}
- (void)configMoviePlayer {
[[TZImageManager manager] getPhotoWithAsset:_model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
if (!isDegraded && photo) {
self->_cover = photo;
self->_doneButton.enabled = YES;
}
}];
[[TZImageManager manager] getVideoWithAsset:_model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_player = [AVPlayer playerWithPlayerItem:playerItem];
self->_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self->_player];
self->_playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:self->_playerLayer];
[self addProgressObserver];
[self configPlayButton];
[self configBottomToolBar];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self->_player.currentItem];
});
}];
}
/// Show progress,do it next time / 给播放器添加进度更新,下次加上
- (void)addProgressObserver{
AVPlayerItem *playerItem = _player.currentItem;
UIProgressView *progress = _progress;
[_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds([playerItem duration]);
if (current) {
[progress setProgress:(current/total) animated:YES];
}
}];
}
- (void)configPlayButton {
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted];
[_playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_playButton];
}
- (void)configBottomToolBar {
_toolBar = [[UIView alloc] initWithFrame:CGRectZero];
CGFloat rgb = 34 / 255.0;
_toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7];
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
if (!_cover) {
_doneButton.enabled = NO;
}
[_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
[_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal];
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal];
} else {
[_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal];
[_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal];
}
[_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorDisabled forState:UIControlStateDisabled];
[_toolBar addSubview:_doneButton];
[self.view addSubview:_toolBar];
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat statusBarHeight = [TZCommonTools tz_statusBarHeight];
CGFloat statusBarAndNaviBarHeight = statusBarHeight + self.navigationController.navigationBar.tz_height;
_playerLayer.frame = self.view.bounds;
CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44;
_toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight);
_doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44);
_playButton.frame = CGRectMake(0, statusBarAndNaviBarHeight, self.view.tz_width, self.view.tz_height - statusBarAndNaviBarHeight - toolBarHeight);
}
#pragma mark - Click Event
- (void)playButtonClick {
CMTime currentTime = _player.currentItem.currentTime;
CMTime durationTime = _player.currentItem.duration;
if (_player.rate == 0.0f) {
if (currentTime.value == durationTime.value) [_player.currentItem seekToTime:CMTimeMake(0, 1)];
[_player play];
[self.navigationController setNavigationBarHidden:YES];
_toolBar.hidden = YES;
[_playButton setImage:nil forState:UIControlStateNormal];
if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES;
} else {
[self pausePlayerAndShowNaviBar];
}
}
- (void)doneButtonClick {
if (self.navigationController) {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
if (imagePickerVc.autoDismiss) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
} else {
[self callDelegateMethod];
}
} else {
[self dismissViewControllerAnimated:YES completion:^{
[self callDelegateMethod];
}];
}
}
- (void)callDelegateMethod {
TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController;
if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingVideo:sourceAssets:)]) {
[imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingVideo:_cover sourceAssets:_model.asset];
}
if (imagePickerVc.didFinishPickingVideoHandle) {
imagePickerVc.didFinishPickingVideoHandle(_cover,_model.asset);
}
}
#pragma mark - Notification Method
- (void)pausePlayerAndShowNaviBar {
[_player pause];
_toolBar.hidden = NO;
[self.navigationController setNavigationBarHidden:NO];
[_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
if (self.needShowStatusBar && iOS7Later) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma clang diagnostic pop
@end
//
// UIView+Layout.h
//
// Created by 谭真 on 15/2/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TZOscillatoryAnimationToBigger,
TZOscillatoryAnimationToSmaller,
} TZOscillatoryAnimationType;
@interface UIView (Layout)
@property (nonatomic) CGFloat tz_left; ///< Shortcut for frame.origin.x.
@property (nonatomic) CGFloat tz_top; ///< Shortcut for frame.origin.y
@property (nonatomic) CGFloat tz_right; ///< Shortcut for frame.origin.x + frame.size.width
@property (nonatomic) CGFloat tz_bottom; ///< Shortcut for frame.origin.y + frame.size.height
@property (nonatomic) CGFloat tz_width; ///< Shortcut for frame.size.width.
@property (nonatomic) CGFloat tz_height; ///< Shortcut for frame.size.height.
@property (nonatomic) CGFloat tz_centerX; ///< Shortcut for center.x
@property (nonatomic) CGFloat tz_centerY; ///< Shortcut for center.y
@property (nonatomic) CGPoint tz_origin; ///< Shortcut for frame.origin.
@property (nonatomic) CGSize tz_size; ///< Shortcut for frame.size.
+ (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(TZOscillatoryAnimationType)type;
@end
//
// UIView+Layout.m
//
// Created by 谭真 on 15/2/24.
// Copyright © 2015年 谭真. All rights reserved.
//
#import "UIView+Layout.h"
@implementation UIView (Layout)
- (CGFloat)tz_left {
return self.frame.origin.x;
}
- (void)setTz_left:(CGFloat)x {
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)tz_top {
return self.frame.origin.y;
}
- (void)setTz_top:(CGFloat)y {
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)tz_right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setTz_right:(CGFloat)right {
CGRect frame = self.frame;
frame.origin.x = right - frame.size.width;
self.frame = frame;
}
- (CGFloat)tz_bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setTz_bottom:(CGFloat)bottom {
CGRect frame = self.frame;
frame.origin.y = bottom - frame.size.height;
self.frame = frame;
}
- (CGFloat)tz_width {
return self.frame.size.width;
}
- (void)setTz_width:(CGFloat)width {
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)tz_height {
return self.frame.size.height;
}
- (void)setTz_height:(CGFloat)height {
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)tz_centerX {
return self.center.x;
}
- (void)setTz_centerX:(CGFloat)centerX {
self.center = CGPointMake(centerX, self.center.y);
}
- (CGFloat)tz_centerY {
return self.center.y;
}
- (void)setTz_centerY:(CGFloat)centerY {
self.center = CGPointMake(self.center.x, centerY);
}
- (CGPoint)tz_origin {
return self.frame.origin;
}
- (void)setTz_origin:(CGPoint)origin {
CGRect frame = self.frame;
frame.origin = origin;
self.frame = frame;
}
- (CGSize)tz_size {
return self.frame.size;
}
- (void)setTz_size:(CGSize)size {
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
+ (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(TZOscillatoryAnimationType)type{
NSNumber *animationScale1 = type == TZOscillatoryAnimationToBigger ? @(1.15) : @(0.5);
NSNumber *animationScale2 = type == TZOscillatoryAnimationToBigger ? @(0.92) : @(1.15);
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
[layer setValue:animationScale1 forKeyPath:@"transform.scale"];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
[layer setValue:animationScale2 forKeyPath:@"transform.scale"];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
[layer setValue:@(1.0) forKeyPath:@"transform.scale"];
} completion:nil];
}];
}];
}
@end
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!