123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- //
- // YBIBVideoCell.m
- // YBImageBrowserDemo
- //
- // Created by 波儿菜 on 2019/7/10.
- // Copyright © 2019 杨波. All rights reserved.
- //
- #import "YBIBVideoCell.h"
- #import "YBIBVideoData.h"
- #import "YBIBVideoData+Internal.h"
- #import "YBIBCopywriter.h"
- #import "YBIBIconManager.h"
- #import <objc/runtime.h>
- #import "YBIBVideoCell+Internal.h"
- @interface NSObject (YBIBVideoPlayingRecord)
- - (void)ybib_videoPlayingAdd:(NSObject *)obj;
- - (void)ybib_videoPlayingRemove:(NSObject *)obj;
- - (BOOL)ybib_noVideoPlaying;
- @end
- @implementation NSObject (YBIBVideoPlayingRecord)
- - (NSMutableSet *)ybib_videoPlayingSet {
- static void *kRecordKey = &kRecordKey;
- NSMutableSet *set = objc_getAssociatedObject(self, kRecordKey);
- if (!set) {
- set = [NSMutableSet set];
- objc_setAssociatedObject(self, kRecordKey, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- return set;
- }
- - (void)ybib_videoPlayingAdd:(NSObject *)obj {
- [[self ybib_videoPlayingSet] addObject:[NSString stringWithFormat:@"%p", obj]];
- }
- - (void)ybib_videoPlayingRemove:(NSObject *)obj {
- [[self ybib_videoPlayingSet] removeObject:[NSString stringWithFormat:@"%p", obj]];
- }
- - (BOOL)ybib_noVideoPlaying {
- return [self ybib_videoPlayingSet].count == 0;
- }
- @end
- @interface YBIBVideoCell () <YBIBVideoDataDelegate, YBIBVideoViewDelegate, UIGestureRecognizerDelegate>
- @end
- @implementation YBIBVideoCell {
- CGPoint _interactStartPoint;
- BOOL _interacting;
- }
- #pragma mark - life cycle
- - (void)dealloc {
- [self.yb_backView ybib_videoPlayingRemove:self];
- }
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- [self initValue];
- [self.contentView addSubview:self.videoView];
- [self addGesture];
- }
- return self;
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
- self.videoView.frame = self.bounds;
- }
- - (void)initValue {
- _interactStartPoint = CGPointZero;
- _interacting = NO;
- }
- - (void)prepareForReuse {
- ((YBIBVideoData *)self.yb_cellData).delegate = nil;
- self.videoView.thumbImageView.image = nil;
- [self hideAuxiliaryView];
- [self.videoView reset];
- self.videoView.asset = nil;
- [super prepareForReuse];
- }
- #pragma mark - private
- - (void)hideAuxiliaryView {
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- [self.yb_auxiliaryViewHandler() yb_hideToastWithContainer:self];
- }
- - (void)updateImageLayoutWithOrientation:(UIDeviceOrientation)orientation previousImageSize:(CGSize)previousImageSize {
- YBIBVideoData *data = self.yb_cellData;
- UIImage *image = self.videoView.thumbImageView.image;
- CGSize imageSize = image.size;
-
- CGRect imageViewFrame = [data yb_imageViewFrameWithContainerSize:self.yb_containerSize(orientation) imageSize:imageSize orientation:orientation];
-
- CGFloat scale;
- if (previousImageSize.width > 0 && previousImageSize.height > 0) {
- scale = imageSize.width / imageSize.height - previousImageSize.width / previousImageSize.height;
- } else {
- scale = 0;
- }
- // '0.001' is admissible error.
- if (ABS(scale) <= 0.001) {
- self.videoView.thumbImageView.frame = imageViewFrame;
- } else {
- [UIView animateWithDuration:0.25 animations:^{
- self.videoView.thumbImageView.frame = imageViewFrame;
- }];
- }
- }
- - (void)hideBrowser {
- ((YBIBVideoData *)self.yb_cellData).delegate = nil;
- self.videoView.thumbImageView.hidden = NO;
- self.videoView.autoPlayCount = 0;
- [self.videoView reset];
- [self.videoView hideToolBar:YES];
- [self.videoView hidePlayButton];
- self.yb_hideBrowser();
- _interacting = NO;
- }
- - (void)hideToolViews:(BOOL)hide {
- if (hide) {
- self.yb_hideToolViews(YES);
- } else {
- if ([self.yb_backView ybib_noVideoPlaying]) {
- self.yb_hideToolViews(NO);
- }
- }
- }
- #pragma mark - <YBIBCellProtocol>
- @synthesize yb_currentOrientation = _yb_currentOrientation;
- @synthesize yb_containerSize = _yb_containerSize;
- @synthesize yb_backView = _yb_backView;
- @synthesize yb_collectionView = _yb_collectionView;
- @synthesize yb_isTransitioning = _yb_isTransitioning;
- @synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
- @synthesize yb_hideStatusBar = _yb_hideStatusBar;
- @synthesize yb_hideBrowser = _yb_hideBrowser;
- @synthesize yb_hideToolViews = _yb_hideToolViews;
- @synthesize yb_cellData = _yb_cellData;
- @synthesize yb_currentPage = _yb_currentPage;
- @synthesize yb_selfPage = _yb_selfPage;
- @synthesize yb_cellIsInCenter = _yb_cellIsInCenter;
- @synthesize yb_isRotating = _yb_isRotating;
- - (void)setYb_cellData:(id<YBIBDataProtocol>)yb_cellData {
- _yb_cellData = yb_cellData;
- YBIBVideoData *data = (YBIBVideoData *)yb_cellData;
- data.delegate = self;
-
- UIDeviceOrientation orientation = self.yb_currentOrientation();
- CGSize containerSize = self.yb_containerSize(orientation);
- [self.videoView updateLayoutWithExpectOrientation:orientation containerSize:containerSize];
- self.videoView.autoPlayCount = data.autoPlayCount;
- self.videoView.topBar.cancelButton.hidden = data.shouldHideForkButton;
- }
- - (void)yb_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation {
- if (_interacting) [self restoreGestureInteractionWithDuration:0];
- }
- - (void)yb_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation {
- [self updateImageLayoutWithOrientation:orientation previousImageSize:self.videoView.thumbImageView.image.size];
- CGSize containerSize = self.yb_containerSize(orientation);
- [self.videoView updateLayoutWithExpectOrientation:orientation containerSize:containerSize];
- }
- - (UIView *)yb_foregroundView {
- return self.videoView.thumbImageView;
- }
- - (void)yb_pageChanged {
- if (self.yb_currentPage() != self.yb_selfPage()) {
- [self.videoView reset];
- [self hideToolViews:NO];
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- if (_interacting) [self restoreGestureInteractionWithDuration:0];
- self.videoView.needAutoPlay = NO;
- } else {
- self.videoView.needAutoPlay = YES;
- }
- }
- #pragma mark - <YBIBVideoDataDelegate>
- - (void)yb_startLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data {}
- - (void)yb_finishLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data {}
- - (void)yb_startLoadingFirstFrameForData:(YBIBVideoData *)data {
- if (!self.videoView.thumbImageView.image) {
- [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
- }
- }
- - (void)yb_finishLoadingFirstFrameForData:(YBIBVideoData *)data {
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- }
- - (void)yb_videoData:(YBIBVideoData *)data downloadingWithProgress:(CGFloat)progress {
- [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self progress:progress];
- }
- - (void)yb_finishDownloadingForData:(YBIBVideoData *)data {
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- }
- - (void)yb_videoData:(YBIBVideoData *)data readyForAVAsset:(AVAsset *)asset {
- self.videoView.asset = asset;
- }
- - (void)yb_videoData:(YBIBVideoData *)data readyForThumbImage:(UIImage *)image {
- if (!self.videoView.isPlaying) {
- self.videoView.thumbImageView.hidden = NO;
- }
-
- if (!self.videoView.thumbImageView.image) {
- CGSize previousSize = self.videoView.thumbImageView.image.size;
- self.videoView.thumbImageView.image = image;
- [self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:previousSize];
- }
- }
- - (void)yb_videoIsInvalidForData:(YBIBVideoData *)data {
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- NSString *imageIsInvalid = [YBIBCopywriter sharedCopywriter].videoIsInvalid;
- if (self.videoView.thumbImageView.image) {
- [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:imageIsInvalid];
- } else {
- [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self text:imageIsInvalid];
- }
- }
- #pragma mark - <YBIBVideoViewDelegate>
- - (BOOL)yb_isFreezingForVideoView:(YBIBVideoView *)view {
- return self.yb_isTransitioning();
- }
- - (void)yb_preparePlayForVideoView:(YBIBVideoView *)view {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- if (!view.isPlaying && !view.isPlayFailed && self.yb_selfPage() == self.yb_currentPage()) {
- [self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
- }
- });
- }
- - (void)yb_startPlayForVideoView:(YBIBVideoView *)view {
- self.videoView.thumbImageView.hidden = YES;
- [self.yb_backView ybib_videoPlayingAdd:self];
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- [self hideToolViews:YES];
- }
- - (void)yb_didPlayToEndTimeForVideoView:(YBIBVideoView *)view {
- YBIBVideoData *data = (YBIBVideoData *)self.yb_cellData;
- if (data.repeatPlayCount == NSUIntegerMax) {
- [view preparPlay];
- } else if (data.repeatPlayCount > 0) {
- --data.repeatPlayCount;
- [view preparPlay];
- } else {
- [self hideToolViews:NO];
- }
- }
- - (void)yb_finishPlayForVideoView:(YBIBVideoView *)view {
- [self.yb_backView ybib_videoPlayingRemove:self];
- [self hideToolViews:NO];
- }
- - (void)yb_playFailedForVideoView:(YBIBVideoView *)view {
- [self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
- [self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:YBIBCopywriter.sharedCopywriter.videoError];
- }
- - (void)yb_respondsToTapGestureForVideoView:(YBIBVideoView *)view {
- if (self.yb_isRotating()) return;
-
- YBIBVideoData *data = self.yb_cellData;
- if (data.singleTouchBlock) {
- data.singleTouchBlock(data);
- } else {
- [self hideBrowser];
- }
- }
- - (void)yb_cancelledForVideoView:(YBIBVideoView *)view {
- if (self.yb_isRotating()) return;
-
- [self hideBrowser];
- }
- - (CGSize)yb_containerSizeForVideoView:(YBIBVideoView *)view {
- return self.yb_containerSize(self.yb_currentOrientation());
- }
- - (void)yb_autoPlayCountChanged:(NSUInteger)count {
- YBIBVideoData *data = (YBIBVideoData *)self.yb_cellData;
- data.autoPlayCount = count;
- }
- #pragma mark - <UIGestureRecognizerDelegate>
- - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
- return YES;
- }
- #pragma mark - gesture
- - (void)addGesture {
- UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToPanGesture:)];
- panGesture.cancelsTouchesInView = NO;
- panGesture.delegate = self;
- [self.videoView.tapGesture requireGestureRecognizerToFail:panGesture];
- [self.videoView addGestureRecognizer:panGesture];
- }
- - (void)respondsToPanGesture:(UIPanGestureRecognizer *)pan {
- if (self.yb_isRotating()) return;
- if ((!self.videoView.thumbImageView.image && !self.videoView.isPlaying)) return;
-
- YBIBInteractionProfile *profile = ((YBIBVideoData *)self.yb_cellData).interactionProfile;
- if (profile.disable) return;
-
- CGPoint point = [pan locationInView:self];
- CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
-
- if (pan.state == UIGestureRecognizerStateBegan) {
- _interactStartPoint = point;
- } else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateRecognized || pan.state == UIGestureRecognizerStateFailed) {
-
- // End
- if (_interacting) {
- CGPoint velocity = [pan velocityInView:self.videoView];
-
- BOOL velocityArrive = ABS(velocity.y) > profile.dismissVelocityY;
- BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > containerSize.height * profile.dismissScale;
-
- BOOL shouldDismiss = distanceArrive || velocityArrive;
- if (shouldDismiss) {
- [self hideBrowser];
- } else {
- [self restoreGestureInteractionWithDuration:profile.restoreDuration];
- }
- }
-
- } else if (pan.state == UIGestureRecognizerStateChanged) {
-
- if (_interacting) {
-
- // Change
- self.videoView.center = point;
- CGFloat scale = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 1.2);
- if (scale > 1) scale = 1;
- if (scale < 0.35) scale = 0.35;
- self.videoView.transform = CGAffineTransformMakeScale(scale, scale);
-
- CGFloat alpha = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 0.7);
- if (alpha > 1) alpha = 1;
- if (alpha < 0) alpha = 0;
- self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:alpha];
-
- } else {
-
- // Start
- if (CGPointEqualToPoint(_interactStartPoint, CGPointZero) || self.yb_currentPage() != self.yb_selfPage() || !self.yb_cellIsInCenter() || self.videoView.actionBar.isTouchInside) return;
-
- CGPoint velocityPoint = [pan velocityInView:self.videoView];
- CGFloat triggerDistance = profile.triggerDistance;
-
- BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > triggerDistance && (ABS(point.x - _interactStartPoint.x) < triggerDistance && ABS(velocityPoint.x) < 500);
-
- BOOL shouldStart = distanceArrive;
- if (!shouldStart) return;
-
- [self.videoView hideToolBar:YES];
-
- _interactStartPoint = point;
-
- CGRect startFrame = self.videoView.bounds;
- CGFloat anchorX = (point.x - startFrame.origin.x) / startFrame.size.width,
- anchorY = (point.y - startFrame.origin.y) / startFrame.size.height;
- self.videoView.layer.anchorPoint = CGPointMake(anchorX, anchorY);
- self.videoView.userInteractionEnabled = NO;
- self.videoView.center = point;
-
- [self hideToolViews:YES];
- self.yb_hideStatusBar(NO);
- self.yb_collectionView().scrollEnabled = NO;
-
- _interacting = YES;
- }
- }
- }
- - (void)restoreGestureInteractionWithDuration:(NSTimeInterval)duration {
- [self.videoView hideToolBar:NO];
-
- CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
-
- void (^animations)(void) = ^{
- self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:1];
-
- CGPoint anchorPoint = self.videoView.layer.anchorPoint;
- self.videoView.center = CGPointMake(containerSize.width * anchorPoint.x, containerSize.height * anchorPoint.y);
- self.videoView.transform = CGAffineTransformIdentity;
- };
- void (^completion)(BOOL finished) = ^(BOOL finished){
- self.videoView.layer.anchorPoint = CGPointMake(0.5, 0.5);
- self.videoView.center = CGPointMake(containerSize.width * 0.5, containerSize.height * 0.5);
- self.videoView.userInteractionEnabled = YES;
-
- self.yb_hideStatusBar(YES);
- self.yb_collectionView().scrollEnabled = YES;
- if (!self.videoView.isPlaying) [self hideToolViews:NO];;
-
- self->_interactStartPoint = CGPointZero;
- self->_interacting = NO;
- };
- if (duration <= 0) {
- animations();
- completion(NO);
- } else {
- [UIView animateWithDuration:duration animations:animations completion:completion];
- }
- }
- #pragma mark - getters & setters
- - (YBIBVideoView *)videoView {
- if (!_videoView) {
- _videoView = [YBIBVideoView new];
- _videoView.delegate = self;
- }
- return _videoView;
- }
- @end
|