首次提交
44
Pods/YBImageBrowser/Video/YBIBVideoActionBar.h
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// YBIBVideoActionBar.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class YBIBVideoActionBar;
|
||||
|
||||
@protocol YBIBVideoActionBarDelegate <NSObject>
|
||||
@required
|
||||
|
||||
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPlayButton:(UIButton *)playButton;
|
||||
|
||||
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPauseButton:(UIButton *)pauseButton;
|
||||
|
||||
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar changeValue:(float)value;
|
||||
|
||||
@end
|
||||
|
||||
@interface YBIBVideoActionBar : UIView
|
||||
|
||||
@property (nonatomic, weak) id<YBIBVideoActionBarDelegate> delegate;
|
||||
|
||||
- (void)setMaxValue:(float)value;
|
||||
|
||||
- (void)setCurrentValue:(float)value;
|
||||
|
||||
- (void)pause;
|
||||
|
||||
- (void)play;
|
||||
|
||||
+ (CGFloat)defaultHeight;
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL isTouchInside;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
191
Pods/YBImageBrowser/Video/YBIBVideoActionBar.m
generated
Normal file
@@ -0,0 +1,191 @@
|
||||
//
|
||||
// YBIBVideoActionBar.m
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBVideoActionBar.h"
|
||||
#import "YBIBIconManager.h"
|
||||
|
||||
|
||||
@interface YBVideoBrowseActionSlider : UISlider
|
||||
@end
|
||||
@implementation YBVideoBrowseActionSlider
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setThumbImage:YBIBIconManager.sharedManager.videoDragCircleImage() forState:UIControlStateNormal];
|
||||
self.minimumTrackTintColor = UIColor.whiteColor;
|
||||
self.maximumTrackTintColor = [UIColor.whiteColor colorWithAlphaComponent:0.5];
|
||||
self.layer.shadowColor = UIColor.darkGrayColor.CGColor;
|
||||
self.layer.shadowOffset = CGSizeMake(0, 1);
|
||||
self.layer.shadowOpacity = 1;
|
||||
self.layer.shadowRadius = 4;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (CGRect)trackRectForBounds:(CGRect)bounds {
|
||||
CGRect frame = [super trackRectForBounds:bounds];
|
||||
return CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 2);
|
||||
}
|
||||
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
|
||||
CGRect frame = [super thumbRectForBounds:bounds trackRect:rect value:value];
|
||||
return CGRectMake(frame.origin.x - 10, frame.origin.y - 10, frame.size.width + 20, frame.size.height + 20);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface YBIBVideoActionBar ()
|
||||
@property (nonatomic, strong) UIButton *playButton;
|
||||
@property (nonatomic, strong) UILabel *preTimeLabel;
|
||||
@property (nonatomic, strong) UILabel *sufTimeLabel;
|
||||
@property (nonatomic, strong) YBVideoBrowseActionSlider *slider;
|
||||
@end
|
||||
|
||||
@implementation YBIBVideoActionBar {
|
||||
BOOL _dragging;
|
||||
}
|
||||
|
||||
#pragma mark - life cycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
_dragging = NO;
|
||||
[self addSubview:self.playButton];
|
||||
[self addSubview:self.preTimeLabel];
|
||||
[self addSubview:self.sufTimeLabel];
|
||||
[self addSubview:self.slider];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
CGFloat width = self.bounds.size.width, height = self.bounds.size.height, labelWidth = 55, buttonWidth = 44, labelOffset = 10;
|
||||
CGFloat imageWidth = YBIBIconManager.sharedManager.videoPlayImage().size.width;
|
||||
CGFloat offset = (buttonWidth - imageWidth) * 0.5;
|
||||
|
||||
self.playButton.frame = CGRectMake(10, 0, buttonWidth, height);
|
||||
self.preTimeLabel.frame = CGRectMake(CGRectGetMaxX(self.playButton.frame) + labelOffset - offset, 0, labelWidth, height);
|
||||
self.sufTimeLabel.frame = CGRectMake(width - labelWidth - labelOffset, 0, labelWidth, height);
|
||||
self.slider.frame = CGRectMake(CGRectGetMaxX(self.preTimeLabel.frame), 0, CGRectGetMinX(self.sufTimeLabel.frame) - CGRectGetMaxX(self.preTimeLabel.frame), height);
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
+ (CGFloat)defaultHeight {
|
||||
return 44;
|
||||
}
|
||||
|
||||
- (void)setMaxValue:(float)value {
|
||||
self.slider.maximumValue = value;
|
||||
self.sufTimeLabel.attributedText = [self.class timeformatFromSeconds:value];
|
||||
}
|
||||
|
||||
- (void)setCurrentValue:(float)value {
|
||||
if (!_dragging) {
|
||||
[self.slider setValue:value animated:YES];
|
||||
}
|
||||
self.preTimeLabel.attributedText = [self.class timeformatFromSeconds:value];
|
||||
}
|
||||
|
||||
- (void)pause {
|
||||
self.playButton.selected = NO;
|
||||
}
|
||||
|
||||
- (void)play {
|
||||
_dragging = NO;
|
||||
self.playButton.selected = YES;
|
||||
self.slider.userInteractionEnabled = YES;
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
|
||||
+ (NSAttributedString *)timeformatFromSeconds:(NSInteger)seconds {
|
||||
NSInteger hour = seconds / 3600, min = (seconds % 3600) / 60, sec = seconds % 60;
|
||||
NSString *text = seconds > 3600 ? [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hour, (long)min, (long)sec] : [NSString stringWithFormat:@"%02ld:%02ld", (long)min, (long)sec];
|
||||
|
||||
NSShadow *shadow = [NSShadow new];
|
||||
shadow.shadowBlurRadius = 4;
|
||||
shadow.shadowOffset = CGSizeMake(0, 1);
|
||||
shadow.shadowColor = UIColor.darkGrayColor;
|
||||
NSAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:text attributes:@{NSShadowAttributeName:shadow, NSFontAttributeName:[UIFont boldSystemFontOfSize:11]}];
|
||||
return attr;
|
||||
}
|
||||
|
||||
#pragma mark - touch event
|
||||
|
||||
- (void)clickPlayButton:(UIButton *)button {
|
||||
button.userInteractionEnabled = NO;
|
||||
if (button.selected) {
|
||||
[self.delegate yb_videoActionBar:self clickPauseButton:button];
|
||||
} else {
|
||||
[self.delegate yb_videoActionBar:self clickPlayButton:button];
|
||||
}
|
||||
button.userInteractionEnabled = YES;
|
||||
}
|
||||
|
||||
- (void)respondsToSliderTouchFinished:(UISlider *)slider {
|
||||
[self.delegate yb_videoActionBar:self changeValue:slider.value];
|
||||
}
|
||||
|
||||
- (void)respondsToSliderTouchDown:(UISlider *)slider {
|
||||
_dragging = YES;
|
||||
slider.userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
#pragma mark - getters
|
||||
|
||||
- (UIButton *)playButton {
|
||||
if (!_playButton) {
|
||||
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_playButton setImage:YBIBIconManager.sharedManager.videoPlayImage() forState:UIControlStateNormal];
|
||||
[_playButton setImage:YBIBIconManager.sharedManager.videoPauseImage() forState:UIControlStateSelected];
|
||||
[_playButton addTarget:self action:@selector(clickPlayButton:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_playButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
|
||||
_playButton.layer.shadowOffset = CGSizeMake(0, 1);
|
||||
_playButton.layer.shadowOpacity = 1;
|
||||
_playButton.layer.shadowRadius = 4;
|
||||
}
|
||||
return _playButton;
|
||||
}
|
||||
|
||||
- (UILabel *)preTimeLabel {
|
||||
if (!_preTimeLabel) {
|
||||
_preTimeLabel = [UILabel new];
|
||||
_preTimeLabel.attributedText = [self.class timeformatFromSeconds:0];
|
||||
_preTimeLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_preTimeLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_preTimeLabel.textColor = [UIColor.whiteColor colorWithAlphaComponent:0.9];
|
||||
}
|
||||
return _preTimeLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)sufTimeLabel {
|
||||
if (!_sufTimeLabel) {
|
||||
_sufTimeLabel = [UILabel new];
|
||||
_sufTimeLabel.attributedText = [self.class timeformatFromSeconds:0];
|
||||
_sufTimeLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_sufTimeLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_sufTimeLabel.textColor = [UIColor.whiteColor colorWithAlphaComponent:0.9];
|
||||
}
|
||||
return _sufTimeLabel;
|
||||
}
|
||||
|
||||
- (YBVideoBrowseActionSlider *)slider {
|
||||
if (!_slider) {
|
||||
_slider = [YBVideoBrowseActionSlider new];
|
||||
[_slider addTarget:self action:@selector(respondsToSliderTouchFinished:) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchCancel|UIControlEventTouchUpOutside];
|
||||
[_slider addTarget:self action:@selector(respondsToSliderTouchDown:) forControlEvents:UIControlEventTouchDown];
|
||||
}
|
||||
return _slider;
|
||||
}
|
||||
|
||||
- (BOOL)isTouchInside {
|
||||
return self.slider.isTouchInside;
|
||||
}
|
||||
|
||||
@end
|
||||
20
Pods/YBImageBrowser/Video/YBIBVideoCell+Internal.h
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// YBIBVideoCell+Internal.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/12/23.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBVideoCell.h"
|
||||
#import "YBIBVideoView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface YBIBVideoCell ()
|
||||
|
||||
@property (nonatomic, strong) YBIBVideoView *videoView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
17
Pods/YBImageBrowser/Video/YBIBVideoCell.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// YBIBVideoCell.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/10.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBCellProtocol.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface YBIBVideoCell : UICollectionViewCell <YBIBCellProtocol>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
448
Pods/YBImageBrowser/Video/YBIBVideoCell.m
generated
Normal file
@@ -0,0 +1,448 @@
|
||||
//
|
||||
// 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
|
||||
50
Pods/YBImageBrowser/Video/YBIBVideoData+Internal.h
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// YBIBVideoData+Internal.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBVideoData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class YBIBVideoData;
|
||||
|
||||
@protocol YBIBVideoDataDelegate <NSObject>
|
||||
@required
|
||||
|
||||
- (void)yb_startLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data;
|
||||
|
||||
- (void)yb_finishLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data;
|
||||
|
||||
- (void)yb_startLoadingFirstFrameForData:(YBIBVideoData *)data;
|
||||
|
||||
- (void)yb_finishLoadingFirstFrameForData:(YBIBVideoData *)data;
|
||||
|
||||
- (void)yb_videoData:(YBIBVideoData *)data downloadingWithProgress:(CGFloat)progress;
|
||||
|
||||
- (void)yb_finishDownloadingForData:(YBIBVideoData *)data;
|
||||
|
||||
- (void)yb_videoData:(YBIBVideoData *)data readyForThumbImage:(UIImage *)image;
|
||||
|
||||
- (void)yb_videoData:(YBIBVideoData *)data readyForAVAsset:(AVAsset *)asset;
|
||||
|
||||
- (void)yb_videoIsInvalidForData:(YBIBVideoData *)data;
|
||||
|
||||
@end
|
||||
|
||||
@interface YBIBVideoData ()
|
||||
|
||||
@property (nonatomic, assign, getter=isLoadingAVAssetFromPHAsset) BOOL loadingAVAssetFromPHAsset;
|
||||
|
||||
@property (nonatomic, assign, getter=isLoadingFirstFrame) BOOL loadingFirstFrame;
|
||||
|
||||
@property (nonatomic, assign, getter=isDownloading) BOOL downloading;
|
||||
|
||||
@property (nonatomic, weak) id<YBIBVideoDataDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
64
Pods/YBImageBrowser/Video/YBIBVideoData.h
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// YBIBVideoData.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/10.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Photos/Photos.h>
|
||||
#import "YBIBDataProtocol.h"
|
||||
#import "YBIBInteractionProfile.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class YBIBVideoData;
|
||||
|
||||
/// 单击事件的处理闭包
|
||||
typedef void (^YBIBVideoSingleTouchBlock)(YBIBVideoData *videoData);
|
||||
|
||||
|
||||
/**
|
||||
图片数据类,承担配置数据和处理数据的责任
|
||||
*/
|
||||
@interface YBIBVideoData : NSObject <YBIBDataProtocol>
|
||||
|
||||
/// 视频 URL
|
||||
@property (nonatomic, copy, nullable) NSURL *videoURL;
|
||||
|
||||
/// 相册视频资源
|
||||
@property (nonatomic, strong, nullable) PHAsset *videoPHAsset;
|
||||
|
||||
/// 视频 AVAsset (通常使用 AVURLAsset)
|
||||
@property (nonatomic, strong, nullable) AVAsset *videoAVAsset;
|
||||
|
||||
/// 投影视图,当前数据模型对应外界业务的 UIView (通常为 UIImageView),做转场动效用
|
||||
@property (nonatomic, weak, nullable) __kindof UIView *projectiveView;
|
||||
|
||||
/// 预览图/缩约图,若 projectiveView 存在且是 UIImageView 类型将会自动获取缩约图
|
||||
@property (nonatomic, strong, nullable) UIImage *thumbImage;
|
||||
|
||||
/// 是否允许保存到相册
|
||||
@property (nonatomic, assign) BOOL allowSaveToPhotoAlbum;
|
||||
|
||||
/// 自动播放次数,默认为 0,NSUIntegerMax 表示无限次
|
||||
@property (nonatomic, assign) NSUInteger autoPlayCount;
|
||||
|
||||
/// 重复播放次数,默认为 0,NSUIntegerMax 表示无限次
|
||||
@property (nonatomic, assign) NSUInteger repeatPlayCount;
|
||||
|
||||
/// 预留属性可随意使用
|
||||
@property (nonatomic, strong, nullable) id extraData;
|
||||
|
||||
/// 手势交互动效配置文件
|
||||
@property (nonatomic, strong) YBIBInteractionProfile *interactionProfile;
|
||||
|
||||
/// 单击的处理(视频未播放时),默认是退出图片浏览器
|
||||
@property (nonatomic, copy, nullable) YBIBVideoSingleTouchBlock singleTouchBlock;
|
||||
|
||||
/// 是否要隐藏播放时的叉叉(取消)按钮
|
||||
@property (nonatomic, assign) BOOL shouldHideForkButton;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
296
Pods/YBImageBrowser/Video/YBIBVideoData.m
generated
Normal file
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// YBIBVideoData.m
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/10.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBVideoData.h"
|
||||
#import "YBIBVideoCell.h"
|
||||
#import "YBIBVideoData+Internal.h"
|
||||
#import "YBIBUtilities.h"
|
||||
#import "YBIBPhotoAlbumManager.h"
|
||||
#import "YBIBCopywriter.h"
|
||||
|
||||
extern CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay);
|
||||
|
||||
@interface YBIBVideoData () <NSURLSessionDelegate>
|
||||
@end
|
||||
|
||||
@implementation YBIBVideoData {
|
||||
NSURLSessionDownloadTask *_downloadTask;
|
||||
}
|
||||
|
||||
#pragma mark - life cycle
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self initValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initValue {
|
||||
_loadingFirstFrame = NO;
|
||||
_loadingAVAssetFromPHAsset = NO;
|
||||
_downloading = NO;
|
||||
_interactionProfile = [YBIBInteractionProfile new];
|
||||
_repeatPlayCount = 0;
|
||||
_autoPlayCount = 0;
|
||||
_shouldHideForkButton = NO;
|
||||
_allowSaveToPhotoAlbum = YES;
|
||||
}
|
||||
|
||||
#pragma mark - load data
|
||||
|
||||
- (void)loadData {
|
||||
// Always load 'thumbImage'.
|
||||
[self loadThumbImage];
|
||||
|
||||
if (self.videoAVAsset) {
|
||||
[self.delegate yb_videoData:self readyForAVAsset:self.videoAVAsset];
|
||||
} else if (self.videoPHAsset) {
|
||||
[self loadAVAssetFromPHAsset];
|
||||
} else {
|
||||
[self.delegate yb_videoIsInvalidForData:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadAVAssetFromPHAsset {
|
||||
if (!self.videoPHAsset) return;
|
||||
if (self.isLoadingAVAssetFromPHAsset) {
|
||||
self.loadingAVAssetFromPHAsset = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
self.loadingAVAssetFromPHAsset = YES;
|
||||
[YBIBPhotoAlbumManager getAVAssetWithPHAsset:self.videoPHAsset completion:^(AVAsset * _Nullable asset) {
|
||||
YBIB_DISPATCH_ASYNC_MAIN(^{
|
||||
self.loadingAVAssetFromPHAsset = NO;
|
||||
|
||||
self.videoAVAsset = asset;
|
||||
[self.delegate yb_videoData:self readyForAVAsset:self.videoAVAsset];
|
||||
[self loadThumbImage];
|
||||
})
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)loadThumbImage {
|
||||
if (self.thumbImage) {
|
||||
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
|
||||
} else if (self.projectiveView && [self.projectiveView isKindOfClass:UIImageView.self] && ((UIImageView *)self.projectiveView).image) {
|
||||
self.thumbImage = ((UIImageView *)self.projectiveView).image;
|
||||
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
|
||||
} else {
|
||||
[self loadThumbImage_firstFrame];
|
||||
}
|
||||
}
|
||||
- (void)loadThumbImage_firstFrame {
|
||||
if (!self.videoAVAsset) return;
|
||||
if (self.isLoadingFirstFrame) {
|
||||
self.loadingFirstFrame = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
self.loadingFirstFrame = YES;
|
||||
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
|
||||
CGSize maximumSize = containerSize;
|
||||
|
||||
__weak typeof(self) wSelf = self;
|
||||
YBIB_DISPATCH_ASYNC(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.videoAVAsset];
|
||||
generator.appliesPreferredTrackTransform = YES;
|
||||
generator.maximumSize = maximumSize;
|
||||
NSError *error = nil;
|
||||
CGImageRef cgImage = [generator copyCGImageAtTime:CMTimeMake(0, 1) actualTime:NULL error:&error];
|
||||
CGImageRef decodedImage = YYCGImageCreateDecodedCopy(cgImage, YES);
|
||||
UIImage *resultImage = [UIImage imageWithCGImage:decodedImage];
|
||||
if (cgImage) CGImageRelease(cgImage);
|
||||
if (decodedImage) CGImageRelease(decodedImage);
|
||||
|
||||
YBIB_DISPATCH_ASYNC_MAIN(^{
|
||||
__strong typeof(wSelf) self = wSelf;
|
||||
if (!self) return;
|
||||
self.loadingFirstFrame = NO;
|
||||
if (!error && resultImage) {
|
||||
self.thumbImage = resultImage;
|
||||
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#pragma mark - <YBIBDataProtocol>
|
||||
|
||||
@synthesize yb_currentOrientation = _yb_currentOrientation;
|
||||
@synthesize yb_containerView = _yb_containerView;
|
||||
@synthesize yb_containerSize = _yb_containerSize;
|
||||
@synthesize yb_isHideTransitioning = _yb_isHideTransitioning;
|
||||
@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
|
||||
|
||||
- (nonnull Class)yb_classOfCell {
|
||||
return YBIBVideoCell.self;
|
||||
}
|
||||
|
||||
- (UIView *)yb_projectiveView {
|
||||
return self.projectiveView;
|
||||
}
|
||||
|
||||
- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
|
||||
if (containerSize.width <= 0 || containerSize.height <= 0 || imageSize.width <= 0 || imageSize.height <= 0) return CGRectZero;
|
||||
CGFloat x = 0, y = 0, width = 0, height = 0;
|
||||
if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height) {
|
||||
width = containerSize.width;
|
||||
height = containerSize.width * (imageSize.height / imageSize.width);
|
||||
x = 0;
|
||||
y = (containerSize.height - height) / 2.0;
|
||||
} else {
|
||||
height = containerSize.height;
|
||||
width = containerSize.height * (imageSize.width / imageSize.height);
|
||||
x = (containerSize.width - width) / 2.0;
|
||||
y = 0;
|
||||
}
|
||||
return CGRectMake(x, y, width, height);
|
||||
}
|
||||
|
||||
- (void)yb_preload {
|
||||
if (!self.delegate) {
|
||||
[self loadData];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)yb_allowSaveToPhotoAlbum {
|
||||
return self.allowSaveToPhotoAlbum;
|
||||
}
|
||||
|
||||
- (void)yb_saveToPhotoAlbum {
|
||||
void(^unableToSave)(void) = ^(){
|
||||
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].unableToSave];
|
||||
};
|
||||
|
||||
if (self.videoAVAsset && [self.videoAVAsset isKindOfClass:AVURLAsset.class]) {
|
||||
AVURLAsset *asset = (AVURLAsset *)self.videoAVAsset;
|
||||
NSURL *URL = asset.URL;
|
||||
if ([URL.scheme isEqualToString:@"file"]) {
|
||||
NSString *path = URL.path;
|
||||
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) {
|
||||
UISaveVideoAtPathToSavedPhotosAlbum(path, self, @selector(UISaveVideoAtPathToSavedPhotosAlbum_videoPath:didFinishSavingWithError:contextInfo:), nil);
|
||||
} else {
|
||||
unableToSave();
|
||||
}
|
||||
} else if ([URL.scheme containsString:@"http"]) {
|
||||
[self downloadWithURL:URL];
|
||||
} else {
|
||||
unableToSave();
|
||||
}
|
||||
} else {
|
||||
unableToSave();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
|
||||
- (void)UISaveVideoAtPathToSavedPhotosAlbum_videoPath:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
|
||||
if (error) {
|
||||
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
|
||||
} else {
|
||||
[self.yb_auxiliaryViewHandler() yb_showCorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumSuccess];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)downloadWithURL:(NSURL *)URL {
|
||||
if (self.isDownloading) {
|
||||
self.downloading = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
self.downloading = YES;
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
|
||||
_downloadTask = [session downloadTaskWithURL:URL];
|
||||
[_downloadTask resume];
|
||||
}
|
||||
|
||||
#pragma mark - <NSURLSessionDelegate>
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didWriteData:(int64_t)bytesWritten
|
||||
totalBytesWritten:(int64_t)totalBytesWritten
|
||||
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
CGFloat progress = totalBytesWritten / (double)totalBytesExpectedToWrite;
|
||||
if (progress < 0) progress = 0;
|
||||
if (progress > 1) progress = 1;
|
||||
[self.delegate yb_videoData:self downloadingWithProgress:progress];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(nullable NSError *)error {
|
||||
if (error) {
|
||||
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].downloadFailed];
|
||||
}
|
||||
self.downloading = NO;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didFinishDownloadingToURL:(NSURL *)location {
|
||||
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
|
||||
NSString *file = [cache stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
|
||||
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:file] error:nil];
|
||||
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(file)) {
|
||||
UISaveVideoAtPathToSavedPhotosAlbum(file, self, @selector(UISaveVideoAtPathToSavedPhotosAlbum_videoPath:didFinishSavingWithError:contextInfo:), nil);
|
||||
} else {
|
||||
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
|
||||
}
|
||||
self.downloading = NO;
|
||||
}
|
||||
|
||||
#pragma mark - getters & setters
|
||||
|
||||
- (void)setVideoURL:(NSURL *)videoURL{
|
||||
_videoURL = [videoURL isKindOfClass:NSString.class] ? [NSURL URLWithString:(NSString *)videoURL] : videoURL;
|
||||
self.videoAVAsset = [AVURLAsset URLAssetWithURL:_videoURL options:nil];
|
||||
}
|
||||
|
||||
- (void)setDownloading:(BOOL)downloading {
|
||||
_downloading = downloading;
|
||||
if (downloading) {
|
||||
[self.delegate yb_videoData:self downloadingWithProgress:0];
|
||||
} else {
|
||||
[self.delegate yb_finishDownloadingForData:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLoadingAVAssetFromPHAsset:(BOOL)loadingAVAssetFromPHAsset {
|
||||
_loadingAVAssetFromPHAsset = loadingAVAssetFromPHAsset;
|
||||
if (loadingAVAssetFromPHAsset) {
|
||||
[self.delegate yb_startLoadingAVAssetFromPHAssetForData:self];
|
||||
} else {
|
||||
[self.delegate yb_finishLoadingAVAssetFromPHAssetForData:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLoadingFirstFrame:(BOOL)loadingFirstFrame {
|
||||
_loadingFirstFrame = loadingFirstFrame;
|
||||
if (loadingFirstFrame) {
|
||||
[self.delegate yb_startLoadingFirstFrameForData:self];
|
||||
} else {
|
||||
[self.delegate yb_finishLoadingFirstFrameForData:self];
|
||||
}
|
||||
}
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
- (void)setDelegate:(id<YBIBVideoDataDelegate>)delegate {
|
||||
_delegate = delegate;
|
||||
if (delegate) {
|
||||
[self loadData];
|
||||
}
|
||||
}
|
||||
- (id<YBIBVideoDataDelegate>)delegate {
|
||||
// Stop sending data to the '_delegate' if it is transiting.
|
||||
return self.yb_isHideTransitioning() ? nil : _delegate;
|
||||
}
|
||||
|
||||
@end
|
||||
21
Pods/YBImageBrowser/Video/YBIBVideoTopBar.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// YBIBVideoTopBar.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface YBIBVideoTopBar : UIView
|
||||
|
||||
@property (nonatomic, strong, readonly) UIButton *cancelButton;
|
||||
|
||||
+ (CGFloat)defaultHeight;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
54
Pods/YBImageBrowser/Video/YBIBVideoTopBar.m
generated
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// YBIBVideoTopBar.m
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBVideoTopBar.h"
|
||||
#import "YBIBIconManager.h"
|
||||
|
||||
@interface YBIBVideoTopBar ()
|
||||
@property (nonatomic, strong) UIButton *cancelButton;
|
||||
@end
|
||||
|
||||
@implementation YBIBVideoTopBar
|
||||
|
||||
#pragma mark - life cycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self addSubview:self.cancelButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
CGFloat buttonWidth = 54;
|
||||
self.cancelButton.frame = CGRectMake(0, 0, buttonWidth, self.bounds.size.height);
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
+ (CGFloat)defaultHeight {
|
||||
return 50;
|
||||
}
|
||||
|
||||
#pragma mark - getter
|
||||
|
||||
- (UIButton *)cancelButton {
|
||||
if (!_cancelButton) {
|
||||
_cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_cancelButton setImage:YBIBIconManager.sharedManager.videoCancelImage() forState:UIControlStateNormal];
|
||||
_cancelButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
|
||||
_cancelButton.layer.shadowOffset = CGSizeMake(0, 1);
|
||||
_cancelButton.layer.shadowOpacity = 1;
|
||||
_cancelButton.layer.shadowRadius = 4;
|
||||
}
|
||||
return _cancelButton;
|
||||
}
|
||||
|
||||
@end
|
||||
76
Pods/YBImageBrowser/Video/YBIBVideoView.h
generated
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// YBIBVideoView.h
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import "YBIBVideoActionBar.h"
|
||||
#import "YBIBVideoTopBar.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class YBIBVideoView;
|
||||
|
||||
@protocol YBIBVideoViewDelegate <NSObject>
|
||||
@required
|
||||
|
||||
- (BOOL)yb_isFreezingForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_preparePlayForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_startPlayForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_finishPlayForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_didPlayToEndTimeForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_playFailedForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_respondsToTapGestureForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_cancelledForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (CGSize)yb_containerSizeForVideoView:(YBIBVideoView *)view;
|
||||
|
||||
- (void)yb_autoPlayCountChanged:(NSUInteger)count;
|
||||
|
||||
@end
|
||||
|
||||
@interface YBIBVideoView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIImageView *thumbImageView;
|
||||
|
||||
@property (nonatomic, weak) id<YBIBVideoViewDelegate> delegate;
|
||||
|
||||
- (void)updateLayoutWithExpectOrientation:(UIDeviceOrientation)orientation containerSize:(CGSize)containerSize;
|
||||
|
||||
@property (nonatomic, strong, nullable) AVAsset *asset;
|
||||
|
||||
@property (nonatomic, assign, readonly, getter=isPlaying) BOOL playing;
|
||||
|
||||
@property (nonatomic, assign, readonly, getter=isPlayFailed) BOOL playFailed;
|
||||
|
||||
@property (nonatomic, assign, readonly, getter=isPreparingPlay) BOOL preparingPlay;
|
||||
|
||||
@property (nonatomic, strong, readonly) UITapGestureRecognizer *tapGesture;
|
||||
|
||||
- (void)reset;
|
||||
|
||||
- (void)hideToolBar:(BOOL)hide;
|
||||
|
||||
- (void)hidePlayButton;
|
||||
|
||||
- (void)preparPlay;
|
||||
|
||||
@property (nonatomic, assign) BOOL needAutoPlay;
|
||||
|
||||
@property (nonatomic, assign) NSUInteger autoPlayCount;
|
||||
|
||||
@property (nonatomic, strong, readonly) YBIBVideoTopBar *topBar;
|
||||
@property (nonatomic, strong, readonly) YBIBVideoActionBar *actionBar;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
394
Pods/YBImageBrowser/Video/YBIBVideoView.m
generated
Normal file
@@ -0,0 +1,394 @@
|
||||
//
|
||||
// YBIBVideoView.m
|
||||
// YBImageBrowserDemo
|
||||
//
|
||||
// Created by 波儿菜 on 2019/7/11.
|
||||
// Copyright © 2019 杨波. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "YBIBVideoView.h"
|
||||
#import "YBIBVideoActionBar.h"
|
||||
#import "YBIBVideoTopBar.h"
|
||||
#import "YBIBUtilities.h"
|
||||
#import "YBIBIconManager.h"
|
||||
|
||||
@interface YBIBVideoView () <YBIBVideoActionBarDelegate>
|
||||
@property (nonatomic, strong) YBIBVideoTopBar *topBar;
|
||||
@property (nonatomic, strong) YBIBVideoActionBar *actionBar;
|
||||
@property (nonatomic, strong) UIButton *playButton;
|
||||
@end
|
||||
|
||||
@implementation YBIBVideoView {
|
||||
AVPlayer *_player;
|
||||
AVPlayerItem *_playerItem;
|
||||
AVPlayerLayer *_playerLayer;
|
||||
BOOL _active;
|
||||
}
|
||||
|
||||
#pragma mark - life cycle
|
||||
|
||||
- (void)dealloc {
|
||||
[self removeObserverForSystem];
|
||||
[self reset];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initValue];
|
||||
self.backgroundColor = UIColor.clearColor;
|
||||
|
||||
[self addSubview:self.thumbImageView];
|
||||
[self addSubview:self.topBar];
|
||||
[self addSubview:self.actionBar];
|
||||
[self addSubview:self.playButton];
|
||||
[self addObserverForSystem];
|
||||
|
||||
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapGesture:)];
|
||||
[self addGestureRecognizer:_tapGesture];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initValue {
|
||||
_playing = NO;
|
||||
_active = YES;
|
||||
_needAutoPlay = NO;
|
||||
_autoPlayCount = 0;
|
||||
_playFailed = NO;
|
||||
_preparingPlay = NO;
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
- (void)updateLayoutWithExpectOrientation:(UIDeviceOrientation)orientation containerSize:(CGSize)containerSize {
|
||||
UIEdgeInsets padding = YBIBPaddingByBrowserOrientation(orientation);
|
||||
CGFloat width = containerSize.width - padding.left - padding.right, height = containerSize.height;
|
||||
self.topBar.frame = CGRectMake(padding.left, padding.top, width, [YBIBVideoTopBar defaultHeight]);
|
||||
self.actionBar.frame = CGRectMake(padding.left, height - [YBIBVideoActionBar defaultHeight] - padding.bottom - 10, width, [YBIBVideoActionBar defaultHeight]);
|
||||
self.playButton.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
|
||||
_playerLayer.frame = (CGRect){CGPointZero, containerSize};
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
[self removeObserverForPlayer];
|
||||
|
||||
// If set '_playerLayer.player = nil' or '_player = nil', can not cancel observeing of 'addPeriodicTimeObserverForInterval'.
|
||||
[_player pause];
|
||||
_playerItem = nil;
|
||||
[_playerLayer removeFromSuperlayer];
|
||||
_playerLayer = nil;
|
||||
|
||||
[self finishPlay];
|
||||
}
|
||||
|
||||
- (void)hideToolBar:(BOOL)hide {
|
||||
if (hide) {
|
||||
self.actionBar.hidden = YES;
|
||||
self.topBar.hidden = YES;
|
||||
} else if (self.isPlaying) {
|
||||
self.actionBar.hidden = NO;
|
||||
self.topBar.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hidePlayButton {
|
||||
self.playButton.hidden = YES;
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
|
||||
- (void)videoJumpWithScale:(float)scale {
|
||||
CMTime startTime = CMTimeMakeWithSeconds(scale, _player.currentTime.timescale);
|
||||
AVPlayer *tmpPlayer = _player;
|
||||
|
||||
if (CMTIME_IS_INDEFINITE(startTime) || CMTIME_IS_INVALID(startTime)) return;
|
||||
[_player seekToTime:startTime toleranceBefore:CMTimeMake(1, 1000) toleranceAfter:CMTimeMake(1, 1000) completionHandler:^(BOOL finished) {
|
||||
if (finished && tmpPlayer == self->_player) {
|
||||
[self startPlay];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)preparPlay {
|
||||
_preparingPlay = YES;
|
||||
_playFailed = NO;
|
||||
|
||||
self.playButton.hidden = YES;
|
||||
|
||||
[self.delegate yb_preparePlayForVideoView:self];
|
||||
|
||||
if (!_playerLayer) {
|
||||
_playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
|
||||
_player = [AVPlayer playerWithPlayerItem:_playerItem];
|
||||
|
||||
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
|
||||
_playerLayer.frame = (CGRect){CGPointZero, [self.delegate yb_containerSizeForVideoView:self]};
|
||||
[self.layer insertSublayer:_playerLayer above:self.thumbImageView.layer];
|
||||
|
||||
[self addObserverForPlayer];
|
||||
} else {
|
||||
[self videoJumpWithScale:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startPlay {
|
||||
if (_player) {
|
||||
_playing = YES;
|
||||
|
||||
[_player play];
|
||||
[self.actionBar play];
|
||||
|
||||
self.topBar.hidden = NO;
|
||||
self.actionBar.hidden = NO;
|
||||
|
||||
[self.delegate yb_startPlayForVideoView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)finishPlay {
|
||||
self.playButton.hidden = NO;
|
||||
[self.actionBar setCurrentValue:0];
|
||||
self.actionBar.hidden = YES;
|
||||
self.topBar.hidden = YES;
|
||||
|
||||
_playing = NO;
|
||||
|
||||
[self.delegate yb_finishPlayForVideoView:self];
|
||||
}
|
||||
|
||||
- (void)playerPause {
|
||||
if (_player) {
|
||||
[_player pause];
|
||||
[self.actionBar pause];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)autoPlay {
|
||||
if (self.autoPlayCount == NSUIntegerMax) {
|
||||
[self preparPlay];
|
||||
} else if (self.autoPlayCount > 0) {
|
||||
--self.autoPlayCount;
|
||||
[self.delegate yb_autoPlayCountChanged:self.autoPlayCount];
|
||||
[self preparPlay];
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - <YBIBVideoActionBarDelegate>
|
||||
|
||||
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPlayButton:(UIButton *)playButton {
|
||||
[self startPlay];
|
||||
}
|
||||
|
||||
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPauseButton:(UIButton *)pauseButton {
|
||||
[self playerPause];
|
||||
}
|
||||
|
||||
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar changeValue:(float)value {
|
||||
[self videoJumpWithScale:value];
|
||||
}
|
||||
|
||||
#pragma mark - observe
|
||||
|
||||
- (void)addObserverForPlayer {
|
||||
[_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
|
||||
__weak typeof(self) wSelf = self;
|
||||
[_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
|
||||
__strong typeof(wSelf) self = wSelf;
|
||||
if (!self) return;
|
||||
float currentTime = time.value / time.timescale;
|
||||
[self.actionBar setCurrentValue:currentTime];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
|
||||
}
|
||||
|
||||
- (void)removeObserverForPlayer {
|
||||
[_playerItem removeObserver:self forKeyPath:@"status"];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
|
||||
if (![self.delegate yb_isFreezingForVideoView:self]) {
|
||||
if (object == _playerItem) {
|
||||
if ([keyPath isEqualToString:@"status"]) {
|
||||
[self playerItemStatusChanged];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didPlayToEndTime:(NSNotification *)noti {
|
||||
if (noti.object == _playerItem) {
|
||||
[self finishPlay];
|
||||
[self.delegate yb_didPlayToEndTimeForVideoView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)playerItemStatusChanged {
|
||||
if (!_active) return;
|
||||
|
||||
_preparingPlay = NO;
|
||||
|
||||
switch (_playerItem.status) {
|
||||
case AVPlayerItemStatusReadyToPlay: {
|
||||
// Delay to update UI.
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self startPlay];
|
||||
double max = CMTimeGetSeconds(self->_playerItem.duration);
|
||||
[self.actionBar setMaxValue:(isnan(max) || isinf(max)) ? 0 : max];
|
||||
});
|
||||
}
|
||||
break;
|
||||
case AVPlayerItemStatusUnknown: {
|
||||
_playFailed = YES;
|
||||
[self.delegate yb_playFailedForVideoView:self];
|
||||
[self reset];
|
||||
}
|
||||
break;
|
||||
case AVPlayerItemStatusFailed: {
|
||||
_playFailed = YES;
|
||||
[self.delegate yb_playFailedForVideoView:self];
|
||||
[self reset];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObserverForSystem {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)addObserverForSystem {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarFrame) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||
[[AVAudioSession sharedInstance] setActive:YES error:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
_active = NO;
|
||||
[self playerPause];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
_active = YES;
|
||||
}
|
||||
|
||||
- (void)didChangeStatusBarFrame {
|
||||
if ([UIApplication sharedApplication].statusBarFrame.size.height > YBIBStatusbarHeight()) {
|
||||
[self playerPause];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification {
|
||||
YBIB_DISPATCH_ASYNC_MAIN(^{
|
||||
NSDictionary *interuptionDict = notification.userInfo;
|
||||
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
|
||||
switch (routeChangeReason) {
|
||||
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
|
||||
[self playerPause];
|
||||
break;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#pragma mark - event
|
||||
|
||||
- (void)respondsToTapGesture:(UITapGestureRecognizer *)tap {
|
||||
if (self.isPlaying) {
|
||||
self.actionBar.hidden = !self.actionBar.isHidden;
|
||||
self.topBar.hidden = !self.topBar.isHidden;
|
||||
} else {
|
||||
[self.delegate yb_respondsToTapGestureForVideoView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clickCancelButton:(UIButton *)button {
|
||||
[self.delegate yb_cancelledForVideoView:self];
|
||||
}
|
||||
|
||||
- (void)clickPlayButton:(UIButton *)button {
|
||||
[self preparPlay];
|
||||
}
|
||||
|
||||
#pragma mark - getters & setters
|
||||
|
||||
- (void)setNeedAutoPlay:(BOOL)needAutoPlay {
|
||||
if (needAutoPlay && _asset && !self.isPlaying) {
|
||||
[self autoPlay];
|
||||
} else {
|
||||
_needAutoPlay = needAutoPlay;
|
||||
}
|
||||
}
|
||||
|
||||
@synthesize asset = _asset;
|
||||
- (void)setAsset:(AVAsset *)asset {
|
||||
_asset = asset;
|
||||
if (!asset) return;
|
||||
if (self.needAutoPlay) {
|
||||
if (![self autoPlay]) {
|
||||
self.playButton.hidden = NO;
|
||||
}
|
||||
self.needAutoPlay = NO;
|
||||
} else {
|
||||
self.playButton.hidden = NO;
|
||||
}
|
||||
}
|
||||
- (AVAsset *)asset {
|
||||
if ([_asset isKindOfClass:AVURLAsset.class]) {
|
||||
_asset = [AVURLAsset assetWithURL:((AVURLAsset *)_asset).URL];
|
||||
}
|
||||
return _asset;
|
||||
}
|
||||
|
||||
- (YBIBVideoTopBar *)topBar {
|
||||
if (!_topBar) {
|
||||
_topBar = [YBIBVideoTopBar new];
|
||||
[_topBar.cancelButton addTarget:self action:@selector(clickCancelButton:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_topBar.hidden = YES;
|
||||
}
|
||||
return _topBar;
|
||||
}
|
||||
|
||||
- (YBIBVideoActionBar *)actionBar {
|
||||
if (!_actionBar) {
|
||||
_actionBar = [YBIBVideoActionBar new];
|
||||
_actionBar.delegate = self;
|
||||
_actionBar.hidden = YES;
|
||||
}
|
||||
return _actionBar;
|
||||
}
|
||||
|
||||
- (UIButton *)playButton {
|
||||
if (!_playButton) {
|
||||
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_playButton.bounds = CGRectMake(0, 0, 100, 100);
|
||||
[_playButton setImage:YBIBIconManager.sharedManager.videoBigPlayImage() forState:UIControlStateNormal];
|
||||
[_playButton addTarget:self action:@selector(clickPlayButton:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_playButton.hidden = YES;
|
||||
_playButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
|
||||
_playButton.layer.shadowOffset = CGSizeMake(0, 1);
|
||||
_playButton.layer.shadowOpacity = 1;
|
||||
_playButton.layer.shadowRadius = 4;
|
||||
}
|
||||
return _playButton;
|
||||
}
|
||||
|
||||
- (UIImageView *)thumbImageView {
|
||||
if (!_thumbImageView) {
|
||||
_thumbImageView = [UIImageView new];
|
||||
_thumbImageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
_thumbImageView.layer.masksToBounds = YES;
|
||||
}
|
||||
return _thumbImageView;
|
||||
}
|
||||
|
||||
@end
|
||||
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_bigPlay@2x.png
generated
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_bigPlay@3x.png
generated
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_cancel@2x.png
generated
Normal file
|
After Width: | Height: | Size: 850 B |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_cancel@3x.png
generated
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_circlePoint@2x.png
generated
Normal file
|
After Width: | Height: | Size: 799 B |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_circlePoint@3x.png
generated
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_pause@2x.png
generated
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_pause@3x.png
generated
Normal file
|
After Width: | Height: | Size: 961 B |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_play@2x.png
generated
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_play@3x.png
generated
Normal file
|
After Width: | Height: | Size: 1.6 KiB |