123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- //
- // TZPhotoPreviewCell.m
- // TZImagePickerController
- //
- // Created by 谭真 on 15/12/24.
- // Copyright © 2015年 谭真. All rights reserved.
- //
- #import "TZPhotoPreviewCell.h"
- #import "TZAssetModel.h"
- #import "UIView+Layout.h"
- #import "TZImageManager.h"
- #import "TZProgressView.h"
- #import "TZImageCropManager.h"
- #import <MediaPlayer/MediaPlayer.h>
- #import "TZImagePickerController.h"
- @implementation TZAssetPreviewCell
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- self.backgroundColor = [UIColor blackColor];
- [self configSubviews];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(photoPreviewCollectionViewDidScroll) name:@"photoPreviewCollectionViewDidScroll" object:nil];
- }
- return self;
- }
- - (void)configSubviews {
-
- }
- #pragma mark - Notification
- - (void)photoPreviewCollectionViewDidScroll {
-
- }
- - (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- @end
- @implementation TZPhotoPreviewCell
- - (void)configSubviews {
- self.previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero];
- __weak typeof(self) weakSelf = self;
- [self.previewView setSingleTapGestureBlock:^{
- __strong typeof(weakSelf) strongSelf = weakSelf;
- if (strongSelf.singleTapGestureBlock) {
- strongSelf.singleTapGestureBlock();
- }
- }];
- [self.previewView setImageProgressUpdateBlock:^(double progress) {
- __strong typeof(weakSelf) strongSelf = weakSelf;
- if (strongSelf.imageProgressUpdateBlock) {
- strongSelf.imageProgressUpdateBlock(progress);
- }
- }];
- [self addSubview:self.previewView];
- }
- - (void)setModel:(TZAssetModel *)model {
- [super setModel:model];
- _previewView.asset = model.asset;
- }
- - (void)recoverSubviews {
- [_previewView recoverSubviews];
- }
- - (void)setAllowCrop:(BOOL)allowCrop {
- _allowCrop = allowCrop;
- _previewView.allowCrop = allowCrop;
- }
- - (void)setScaleAspectFillCrop:(BOOL)scaleAspectFillCrop {
- _scaleAspectFillCrop = scaleAspectFillCrop;
- _previewView.scaleAspectFillCrop = scaleAspectFillCrop;
- }
- - (void)setCropRect:(CGRect)cropRect {
- _cropRect = cropRect;
- _previewView.cropRect = cropRect;
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
- self.previewView.frame = self.bounds;
- }
- @end
- @interface TZPhotoPreviewView ()<UIScrollViewDelegate>
- @property (assign, nonatomic) BOOL isRequestingGIF;
- @end
- @implementation TZPhotoPreviewView
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- _scrollView = [[UIScrollView alloc] init];
- _scrollView.bouncesZoom = YES;
- _scrollView.maximumZoomScale = 2.5;
- _scrollView.minimumZoomScale = 1.0;
- _scrollView.multipleTouchEnabled = YES;
- _scrollView.delegate = self;
- _scrollView.scrollsToTop = NO;
- _scrollView.showsHorizontalScrollIndicator = NO;
- _scrollView.showsVerticalScrollIndicator = YES;
- _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- _scrollView.delaysContentTouches = NO;
- _scrollView.canCancelContentTouches = YES;
- _scrollView.alwaysBounceVertical = NO;
- if (@available(iOS 11, *)) {
- _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
- }
- [self addSubview:_scrollView];
-
- _imageContainerView = [[UIView alloc] init];
- _imageContainerView.clipsToBounds = YES;
- _imageContainerView.contentMode = UIViewContentModeScaleAspectFill;
- [_scrollView addSubview:_imageContainerView];
-
- _imageView = [[UIImageView alloc] init];
- _imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500];
- _imageView.contentMode = UIViewContentModeScaleAspectFill;
- _imageView.clipsToBounds = YES;
- [_imageContainerView addSubview:_imageView];
-
- UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
- [self addGestureRecognizer:tap1];
- UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
- tap2.numberOfTapsRequired = 2;
- [tap1 requireGestureRecognizerToFail:tap2];
- [self addGestureRecognizer:tap2];
-
- [self configProgressView];
- }
- return self;
- }
- - (void)configProgressView {
- _progressView = [[TZProgressView alloc] init];
- _progressView.hidden = YES;
- [self addSubview:_progressView];
- }
- - (void)setModel:(TZAssetModel *)model {
- _model = model;
- self.isRequestingGIF = NO;
- [_scrollView setZoomScale:1.0 animated:NO];
- if (model.type == TZAssetModelMediaTypePhotoGif) {
- // 先显示缩略图
- [[TZImageManager manager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
- self.imageView.image = photo;
- [self resizeSubviews];
- if (self.isRequestingGIF) {
- return;
- }
- // 再显示gif动图
- self.isRequestingGIF = YES;
- [[TZImageManager manager] getOriginalPhotoDataWithAsset:model.asset progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
- progress = progress > 0.02 ? progress : 0.02;
- dispatch_async(dispatch_get_main_queue(), ^{
- self.progressView.progress = progress;
- if (progress >= 1) {
- self.progressView.hidden = YES;
- } else {
- self.progressView.hidden = NO;
- }
- });
- #ifdef DEBUG
- NSLog(@"[TZImagePickerController] getOriginalPhotoDataWithAsset:%f error:%@", progress, error);
- #endif
- } completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) {
- if (!isDegraded) {
- self.isRequestingGIF = NO;
- self.progressView.hidden = YES;
- if ([TZImagePickerConfig sharedInstance].gifImagePlayBlock) {
- [TZImagePickerConfig sharedInstance].gifImagePlayBlock(self, self.imageView, data, info);
- } else {
- self.imageView.image = [UIImage sd_tz_animatedGIFWithData:data];
- }
- [self resizeSubviews];
- }
- }];
- } progressHandler:nil networkAccessAllowed:NO];
- } else {
- self.asset = model.asset;
- }
- }
- - (void)setAsset:(PHAsset *)asset {
- if (_asset && self.imageRequestID) {
- [[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID];
- }
-
- _asset = asset;
- self.imageRequestID = [[TZImageManager manager] getPhotoWithAsset:asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
- if (![asset isEqual:self->_asset]) return;
- self.imageView.image = photo;
- [self resizeSubviews];
- if (self.imageView.tz_height && self.allowCrop) {
- CGFloat scale = MAX(self.cropRect.size.width / self.imageView.tz_width, self.cropRect.size.height / self.imageView.tz_height);
- if (self.scaleAspectFillCrop && scale > 1) { // 如果设置图片缩放裁剪并且图片需要缩放
- CGFloat multiple = self.scrollView.maximumZoomScale / self.scrollView.minimumZoomScale;
- self.scrollView.minimumZoomScale = scale;
- self.scrollView.maximumZoomScale = scale * MAX(multiple, 2);
- [self.scrollView setZoomScale:scale animated:YES];
- }
- }
- self->_progressView.hidden = YES;
- if (self.imageProgressUpdateBlock) {
- self.imageProgressUpdateBlock(1);
- }
- if (!isDegraded) {
- self.imageRequestID = 0;
- }
- } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
- if (![asset isEqual:self->_asset]) return;
- self->_progressView.hidden = NO;
- [self bringSubviewToFront:self->_progressView];
- progress = progress > 0.02 ? progress : 0.02;
- self->_progressView.progress = progress;
- if (self.imageProgressUpdateBlock && progress < 1) {
- self.imageProgressUpdateBlock(progress);
- }
-
- if (progress >= 1) {
- self->_progressView.hidden = YES;
- self.imageRequestID = 0;
- }
- } networkAccessAllowed:YES];
-
- [self configMaximumZoomScale];
- }
- - (void)recoverSubviews {
- [_scrollView setZoomScale:_scrollView.minimumZoomScale animated:NO];
- [self resizeSubviews];
- }
- - (void)resizeSubviews {
- _imageContainerView.tz_origin = CGPointZero;
- _imageContainerView.tz_width = self.scrollView.tz_width;
-
- UIImage *image = _imageView.image;
- if (image.size.height / image.size.width > self.tz_height / self.scrollView.tz_width) {
- _imageContainerView.tz_height = floor(image.size.height / (image.size.width / self.scrollView.tz_width));
- } else {
- CGFloat height = image.size.height / image.size.width * self.scrollView.tz_width;
- if (height < 1 || isnan(height)) height = self.tz_height;
- height = floor(height);
- _imageContainerView.tz_height = height;
- _imageContainerView.tz_centerY = self.tz_height / 2;
- }
- if (_imageContainerView.tz_height > self.tz_height && _imageContainerView.tz_height - self.tz_height <= 1) {
- _imageContainerView.tz_height = self.tz_height;
- }
- CGFloat contentSizeH = MAX(_imageContainerView.tz_height, self.tz_height);
- _scrollView.contentSize = CGSizeMake(self.scrollView.tz_width, contentSizeH);
- [_scrollView scrollRectToVisible:self.bounds animated:NO];
- _scrollView.alwaysBounceVertical = _imageContainerView.tz_height <= self.tz_height ? NO : YES;
- _imageView.frame = _imageContainerView.bounds;
-
- [self refreshScrollViewContentSize];
- }
- - (void)configMaximumZoomScale {
- _scrollView.maximumZoomScale = _allowCrop ? 4.0 : 2.5;
-
- if ([self.asset isKindOfClass:[PHAsset class]]) {
- PHAsset *phAsset = (PHAsset *)self.asset;
- CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight;
- // 优化超宽图片的显示
- if (aspectRatio > 1.5) {
- self.scrollView.maximumZoomScale *= aspectRatio / 1.5;
- }
- }
- }
- - (void)refreshScrollViewContentSize {
- if (_allowCrop) {
- // 1.7.2 如果允许裁剪,需要让图片的任意部分都能在裁剪框内,于是对_scrollView做了如下处理:
- // 1.让contentSize增大(裁剪框右下角的图片部分)
- CGFloat contentWidthAdd = self.scrollView.tz_width - CGRectGetMaxX(_cropRect);
- CGFloat contentHeightAdd = (MIN(_imageContainerView.tz_height, self.tz_height) - self.cropRect.size.height) / 2;
- CGFloat newSizeW = self.scrollView.contentSize.width + contentWidthAdd;
- CGFloat newSizeH = MAX(self.scrollView.contentSize.height, self.tz_height) + contentHeightAdd;
- _scrollView.contentSize = CGSizeMake(newSizeW, newSizeH);
- _scrollView.alwaysBounceVertical = YES;
- // 2.让scrollView新增滑动区域(裁剪框左上角的图片部分)
- if (contentHeightAdd > 0 || contentWidthAdd > 0) {
- _scrollView.contentInset = UIEdgeInsetsMake(contentHeightAdd, _cropRect.origin.x, 0, 0);
- } else {
- _scrollView.contentInset = UIEdgeInsetsZero;
- }
- }
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
- _scrollView.frame = CGRectMake(10, 0, self.tz_width - 20, self.tz_height);
- static CGFloat progressWH = 40;
- CGFloat progressX = (self.tz_width - progressWH) / 2;
- CGFloat progressY = (self.tz_height - progressWH) / 2;
- _progressView.frame = CGRectMake(progressX, progressY, progressWH, progressWH);
-
- [self recoverSubviews];
- }
- #pragma mark - UITapGestureRecognizer Event
- - (void)doubleTap:(UITapGestureRecognizer *)tap {
- if (_scrollView.zoomScale > _scrollView.minimumZoomScale) {
- _scrollView.contentInset = UIEdgeInsetsZero;
- [_scrollView setZoomScale:_scrollView.minimumZoomScale animated:YES];
- } else {
- CGPoint touchPoint = [tap locationInView:self.imageView];
- CGFloat newZoomScale = _scrollView.maximumZoomScale;
- CGFloat xsize = self.frame.size.width / newZoomScale;
- CGFloat ysize = self.frame.size.height / newZoomScale;
- [_scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
- }
- }
- - (void)singleTap:(UITapGestureRecognizer *)tap {
- if (self.singleTapGestureBlock) {
- self.singleTapGestureBlock();
- }
- }
- #pragma mark - UIScrollViewDelegate
- - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
- return _imageContainerView;
- }
- - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
- scrollView.contentInset = UIEdgeInsetsZero;
- }
- - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
- [self refreshImageContainerViewCenter];
- }
- - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
- [self refreshScrollViewContentSize];
- }
- #pragma mark - Private
- - (void)refreshImageContainerViewCenter {
- CGFloat offsetX = (_scrollView.tz_width > _scrollView.contentSize.width) ? ((_scrollView.tz_width - _scrollView.contentSize.width) * 0.5) : 0.0;
- CGFloat offsetY = (_scrollView.tz_height > _scrollView.contentSize.height) ? ((_scrollView.tz_height - _scrollView.contentSize.height) * 0.5) : 0.0;
- self.imageContainerView.center = CGPointMake(_scrollView.contentSize.width * 0.5 + offsetX, _scrollView.contentSize.height * 0.5 + offsetY);
- }
- @end
- @implementation TZVideoPreviewCell
- - (void)configSubviews {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil];
- }
- - (void)configPlayButton {
- if (_playButton) {
- [_playButton removeFromSuperview];
- }
- _playButton = [UIButton buttonWithType:UIButtonTypeCustom];
- [_playButton setImage:[UIImage tz_imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
- [_playButton setImage:[UIImage tz_imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted];
- [_playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside];
- [self addSubview:_playButton];
- }
- - (void)setModel:(TZAssetModel *)model {
- [super setModel:model];
- [self configMoviePlayer];
- }
- - (void)setVideoURL:(NSURL *)videoURL {
- _videoURL = videoURL;
- [self configMoviePlayer];
- }
- - (void)configMoviePlayer {
- if (_player) {
- [_playerLayer removeFromSuperlayer];
- _playerLayer = nil;
- [_player pause];
- _player = nil;
- }
-
- if (self.model && self.model.asset) {
- [[TZImageManager manager] getPhotoWithAsset:self.model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
- self.cover = photo;
- }];
- [[TZImageManager manager] getVideoWithAsset:self.model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [self configPlayerWithItem:playerItem];
- });
- }];
- } else {
- AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:self.videoURL];
- [self configPlayerWithItem:playerItem];
- }
- }
- - (void)configPlayerWithItem:(AVPlayerItem *)playerItem {
- self.player = [AVPlayer playerWithPlayerItem:playerItem];
- self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
- self.playerLayer.backgroundColor = [UIColor blackColor].CGColor;
- self.playerLayer.frame = self.bounds;
- [self.layer addSublayer:self.playerLayer];
- [self configPlayButton];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
- _playerLayer.frame = self.bounds;
- _playButton.frame = CGRectMake(0, 64, self.tz_width, self.tz_height - 64 - 44);
- }
- - (void)photoPreviewCollectionViewDidScroll {
- if (_player && _player.rate != 0.0) {
- [self pausePlayerAndShowNaviBar];
- }
- }
- #pragma mark - Notification
- - (void)appWillResignActiveNotification {
- if (_player && _player.rate != 0.0) {
- [self pausePlayerAndShowNaviBar];
- }
- }
- #pragma mark - Click Event
- - (void)playButtonClick {
- CMTime currentTime = _player.currentItem.currentTime;
- CMTime durationTime = _player.currentItem.duration;
- if (_player.rate == 0.0f) {
- if (currentTime.value == durationTime.value) [_player.currentItem seekToTime:CMTimeMake(0, 1)];
- [_player play];
- [_playButton setImage:nil forState:UIControlStateNormal];
- [UIApplication sharedApplication].statusBarHidden = YES;
- if (self.singleTapGestureBlock) {
- self.singleTapGestureBlock();
- }
- } else {
- [self pausePlayerAndShowNaviBar];
- }
- }
- - (void)pausePlayerAndShowNaviBar {
- [_player pause];
- [_playButton setImage:[UIImage tz_imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
- if (self.singleTapGestureBlock) {
- self.singleTapGestureBlock();
- }
- }
- @end
- @implementation TZGifPreviewCell
- - (void)configSubviews {
- [self configPreviewView];
- }
- - (void)configPreviewView {
- _previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero];
- __weak typeof(self) weakSelf = self;
- [_previewView setSingleTapGestureBlock:^{
- __strong typeof(weakSelf) strongSelf = weakSelf;
- [strongSelf signleTapAction];
- }];
- [self addSubview:_previewView];
- }
- - (void)setModel:(TZAssetModel *)model {
- [super setModel:model];
- _previewView.model = self.model;
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
- _previewView.frame = self.bounds;
- }
- #pragma mark - Click Event
- - (void)signleTapAction {
- if (self.singleTapGestureBlock) {
- self.singleTapGestureBlock();
- }
- }
- @end
|