jsdw_ios/Pods/YBImageBrowser/YBImageBrowser/YBImageBrowser.m

539 lines
19 KiB
Objective-C

//
// YBImageBrowser.m
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/5.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBImageBrowser.h"
#import "YBIBUtilities.h"
#import "YBIBCellProtocol.h"
#import "YBIBDataMediator.h"
#import "YBIBScreenRotationHandler.h"
#import "NSObject+YBImageBrowser.h"
#import "YBImageBrowser+Internal.h"
#if __has_include("YBIBDefaultWebImageMediator.h")
#import "YBIBDefaultWebImageMediator.h"
#endif
@interface YBImageBrowser () <UICollectionViewDelegate, UICollectionViewDataSource>
@property (nonatomic, strong) YBIBCollectionView *collectionView;
@property (nonatomic, strong) YBIBDataMediator *dataMediator;
@property (nonatomic, strong) YBIBScreenRotationHandler *rotationHandler;
@end
@implementation YBImageBrowser {
BOOL _originStatusBarHidden;
}
#pragma mark - life cycle
- (void)dealloc {
self.hiddenProjectiveView = nil;
[self showStatusBar];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = UIColor.blackColor;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToLongPress:)];
[self addGestureRecognizer:longPress];
[self initValue];
}
return self;
}
- (void)initValue {
_transitioning = _showTransitioning = _hideTransitioning = NO;
_defaultAnimatedTransition = _animatedTransition = [YBIBAnimatedTransition new];
_toolViewHandlers = @[[YBIBToolViewHandler new]];
_defaultToolViewHandler = _toolViewHandlers[0];
_auxiliaryViewHandler = [YBIBAuxiliaryViewHandler new];
_shouldHideStatusBar = YES;
_autoHideProjectiveView = YES;
#if __has_include("YBIBDefaultWebImageMediator.h")
_webImageMediator = [YBIBDefaultWebImageMediator new];
#endif
}
#pragma mark - private
- (void)build {
[self addSubview:self.collectionView];
self.collectionView.frame = self.bounds;
self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:self.containerView];
self.containerView.frame = self.bounds;
self.containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self buildToolView];
[self layoutIfNeeded];
[self collectionViewScrollToPage:self.currentPage];
[self.rotationHandler startObserveDeviceOrientation];
}
- (void)buildToolView {
for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
[self implementGetBaseInfoProtocol:handler];
[self implementOperateBrowserProtocol:handler];
__weak typeof(self) wSelf = self;
if ([handler respondsToSelector:@selector(setYb_currentData:)]) {
[handler setYb_currentData:^id<YBIBDataProtocol>{
__strong typeof(wSelf) self = wSelf;
if (!self) return nil;
return self.currentData;
}];
}
[handler yb_containerViewIsReadied];
[handler yb_hide:NO];
}
}
- (void)rebuild {
self.hiddenProjectiveView = nil;
[self showStatusBar];
[self.containerView removeFromSuperview];
_containerView = nil;
[self.collectionView removeFromSuperview];
_collectionView = nil;
[self.dataMediator clear];
[self.rotationHandler clear];
}
- (void)collectionViewScrollToPage:(NSInteger)page {
[self.collectionView scrollToPage:page];
[self pageNumberChanged];
}
- (void)pageNumberChanged {
id<YBIBDataProtocol> data = self.currentData;
UIView *projectiveView = nil;
if ([data respondsToSelector:@selector(yb_projectiveView)]) {
projectiveView = [data yb_projectiveView];
}
self.hiddenProjectiveView = projectiveView;
if (self.delegate && [self.delegate respondsToSelector:@selector(yb_imageBrowser:pageChanged:data:)]) {
[self.delegate yb_imageBrowser:self pageChanged:self.currentPage data:data];
}
for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
if ([handler respondsToSelector:@selector(yb_pageChanged)]) {
[handler yb_pageChanged];
}
}
NSArray *visibleCells = self.collectionView.visibleCells;
for (UICollectionViewCell<YBIBCellProtocol> *cell in visibleCells) {
if ([cell respondsToSelector:@selector(yb_pageChanged)]) {
[cell yb_pageChanged];
}
}
}
- (void)showStatusBar {
if (self.shouldHideStatusBar) {
[UIApplication sharedApplication].statusBarHidden = _originStatusBarHidden;
}
}
- (void)hideStatusBar {
if (self.shouldHideStatusBar) {
[UIApplication sharedApplication].statusBarHidden = YES;
}
}
#pragma mark - public
- (void)show {
[self showToView:[UIApplication sharedApplication].keyWindow];
}
- (void)showToView:(UIView *)view {
[self showToView:view containerSize:view.bounds.size];
}
- (void)showToView:(UIView *)view containerSize:(CGSize)containerSize {
[self.rotationHandler startObserveStatusBarOrientation];
[view addSubview:self];
self.frame = view.bounds;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_originStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden;
[self.rotationHandler configContainerSize:containerSize];
[self.dataMediator preloadWithPage:self.currentPage];
__kindof UIView *startView;
UIImage *startImage;
CGRect endFrame = CGRectZero;
id<YBIBDataProtocol> data = [self.dataMediator dataForCellAtIndex:self.currentPage];
if ([data respondsToSelector:@selector(yb_projectiveView)]) {
startView = data.yb_projectiveView;
self.hiddenProjectiveView = startView;
if ([startView isKindOfClass:UIImageView.class]) {
startImage = ((UIImageView *)startView).image;
} else {
startImage = YBIBSnapshotView(startView);
}
}
if ([data respondsToSelector:@selector(yb_imageViewFrameWithContainerSize:imageSize:orientation:)]) {
endFrame = [data yb_imageViewFrameWithContainerSize:self.bounds.size imageSize:startImage.size orientation:self.rotationHandler.currentOrientation];
}
[self setTransitioning:YES isShow:YES];
[self.animatedTransition yb_showTransitioningWithContainer:self startView:startView startImage:startImage endFrame:endFrame orientation:self.rotationHandler.currentOrientation completion:^{
[self hideStatusBar];
[self build];
[self setTransitioning:NO isShow:YES];
}];
}
- (void)hide {
__kindof UIView *startView;
__kindof UIView *endView;
UICollectionViewCell<YBIBCellProtocol> *cell = (UICollectionViewCell<YBIBCellProtocol> *)self.collectionView.centerCell;
if ([cell respondsToSelector:@selector(yb_foregroundView)]) {
startView = cell.yb_foregroundView;
}
if ([cell.yb_cellData respondsToSelector:@selector(yb_projectiveView)]) {
endView = cell.yb_cellData.yb_projectiveView;
}
for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
[handler yb_hide:YES];
}
[self showStatusBar];
[self setTransitioning:YES isShow:NO];
[self.animatedTransition yb_hideTransitioningWithContainer:self startView:startView endView:endView orientation:self.rotationHandler.currentOrientation completion:^{
[self rebuild];
[self removeFromSuperview];
[self setTransitioning:NO isShow:NO];
}];
}
- (void)reloadData {
[self.dataMediator clear];
NSInteger page = self.currentPage;
[self.collectionView reloadData];
self.currentPage = page;
}
- (id<YBIBDataProtocol>)currentData {
return [self.dataMediator dataForCellAtIndex:self.currentPage];
}
#pragma mark - internal
- (void)setHiddenProjectiveView:(NSObject *)hiddenProjectiveView {
if (_hiddenProjectiveView && [_hiddenProjectiveView respondsToSelector:@selector(setAlpha:)]) {
CGFloat originAlpha = _hiddenProjectiveView.ybib_originAlpha;
if (originAlpha != 1) {
[_hiddenProjectiveView setValue:@(1) forKey:@"alpha"];
[UIView animateWithDuration:0.2 animations:^{
[self->_hiddenProjectiveView setValue:@(originAlpha) forKey:@"alpha"];
}];
} else {
[_hiddenProjectiveView setValue:@(originAlpha) forKey:@"alpha"];
}
}
_hiddenProjectiveView = hiddenProjectiveView;
if (!self.autoHideProjectiveView) return;
if (hiddenProjectiveView && [hiddenProjectiveView respondsToSelector:@selector(setAlpha:)]) {
hiddenProjectiveView.ybib_originAlpha = ((NSNumber *)[hiddenProjectiveView valueForKey:@"alpha"]).floatValue;
[hiddenProjectiveView setValue:@(0) forKey:@"alpha"];
}
}
- (void)implementOperateBrowserProtocol:(id<YBIBOperateBrowserProtocol>)obj {
__weak typeof(self) wSelf = self;
if ([obj respondsToSelector:@selector(setYb_hideBrowser:)]) {
[obj setYb_hideBrowser:^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
[self hide];
}];
}
if ([obj respondsToSelector:@selector(setYb_hideStatusBar:)]) {
[obj setYb_hideStatusBar:^(BOOL hide) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
hide ? [self hideStatusBar] : [self showStatusBar];
}];
}
if ([obj respondsToSelector:@selector(setYb_hideToolViews:)]) {
[obj setYb_hideToolViews:^(BOOL hide) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
[handler yb_hide:hide];
}
}];
}
}
- (void)implementGetBaseInfoProtocol:(id<YBIBGetBaseInfoProtocol>)obj {
__weak typeof(self) wSelf = self;
if ([obj respondsToSelector:@selector(setYb_currentOrientation:)]) {
[obj setYb_currentOrientation:^UIDeviceOrientation{
__strong typeof(wSelf) self = wSelf;
if (!self) return UIDeviceOrientationPortrait;
return self.rotationHandler.currentOrientation;
}];
}
if ([obj respondsToSelector:@selector(setYb_containerSize:)]) {
[obj setYb_containerSize:^CGSize(UIDeviceOrientation orientation) {
__strong typeof(wSelf) self = wSelf;
if (!self) return CGSizeZero;
return [self.rotationHandler containerSizeWithOrientation:orientation];
}];
}
if ([obj respondsToSelector:@selector(setYb_auxiliaryViewHandler:)]) {
[obj setYb_auxiliaryViewHandler:^id<YBIBAuxiliaryViewHandler>{
__strong typeof(wSelf) self = wSelf;
if (!self) return nil;
return self.auxiliaryViewHandler;
}];
}
if ([obj respondsToSelector:@selector(setYb_webImageMediator:)]) {
[obj setYb_webImageMediator:^id<YBIBWebImageMediator> {
__strong typeof(wSelf) self = wSelf;
if (!self) return nil;
NSAssert(self.webImageMediator, @"'webImageMediator' should not be nil.");
return self.webImageMediator;
}];
}
if ([obj respondsToSelector:@selector(setYb_currentPage:)]) {
[obj setYb_currentPage:^NSInteger{
__strong typeof(wSelf) self = wSelf;
if (!self) return 0;
return self.currentPage;
}];
}
if ([obj respondsToSelector:@selector(setYb_totalPage:)]) {
[obj setYb_totalPage:^NSInteger{
__strong typeof(wSelf) self = wSelf;
if (!self) return 0;
return [self.dataMediator numberOfCells];
}];
}
if ([obj respondsToSelector:@selector(setYb_backView:)]) {
obj.yb_backView = self;
}
if ([obj respondsToSelector:@selector(setYb_containerView:)]) {
obj.yb_containerView = self.containerView;
}
if ([obj respondsToSelector:@selector(setYb_collectionView:)]) {
[obj setYb_collectionView:^__kindof UICollectionView *{
__strong typeof(wSelf) self = wSelf;
if (!self) return nil;
return self.collectionView;
}];
}
if ([obj respondsToSelector:@selector(setYb_cellIsInCenter:)]) {
[obj setYb_cellIsInCenter:^BOOL{
__strong typeof(wSelf) self = wSelf;
CGFloat pageF = self.collectionView.contentOffset.x / self.collectionView.bounds.size.width;
// '0.001' is admissible error.
return ABS(pageF - (NSInteger)pageF) <= 0.001;
}];
}
if ([obj respondsToSelector:@selector(setYb_isTransitioning:)]) {
[obj setYb_isTransitioning:^BOOL{
__strong typeof(wSelf) self = wSelf;
if (!self) return NO;
return self.isTransitioning;
}];
}
if ([obj respondsToSelector:@selector(setYb_isShowTransitioning:)]) {
[obj setYb_isShowTransitioning:^BOOL{
__strong typeof(wSelf) self = wSelf;
if (!self) return NO;
return self.isShowTransitioning;
}];
}
if ([obj respondsToSelector:@selector(setYb_isHideTransitioning:)]) {
[obj setYb_isHideTransitioning:^BOOL{
__strong typeof(wSelf) self = wSelf;
if (!self) return NO;
return self.isHideTransitioning;
}];
}
if ([obj respondsToSelector:@selector(setYb_isRotating:)]) {
[obj setYb_isRotating:^BOOL{
__strong typeof(wSelf) self = wSelf;
if (!self) return NO;
return self.rotationHandler.isRotating;
}];
}
}
#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.dataMediator numberOfCells];
}
- (UICollectionViewCell *)collectionView:(YBIBCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
id<YBIBDataProtocol> data = [self.dataMediator dataForCellAtIndex:indexPath.row];
UICollectionViewCell<YBIBCellProtocol> *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[collectionView reuseIdentifierForCellClass:data.yb_classOfCell] forIndexPath:indexPath];
[self implementGetBaseInfoProtocol:cell];
[self implementOperateBrowserProtocol:cell];
if ([cell respondsToSelector:@selector(setYb_selfPage:)]) {
[cell setYb_selfPage:^NSInteger{
return indexPath.row;
}];
}
cell.yb_cellData = data;
if ([cell respondsToSelector:@selector(yb_pageChanged)]) {
[cell yb_pageChanged];
}
[self.dataMediator preloadWithPage:indexPath.row];
return cell;
}
#pragma mark - <UICollectionViewDelegate>
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat pageF = scrollView.contentOffset.x / scrollView.bounds.size.width;
NSInteger page = (NSInteger)(pageF + 0.5);
for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
if ([handler respondsToSelector:@selector(yb_offsetXChanged:)]) {
[handler yb_offsetXChanged:pageF];
}
}
if (!scrollView.isDecelerating && !scrollView.isDragging) {
// Return if not scrolled by finger.
return;
}
if (page < 0 || page > [self.dataMediator numberOfCells] - 1) return;
if (self.rotationHandler.isRotating) return;
if (page != _currentPage) {
_currentPage = page;
[self pageNumberChanged];
}
}
#pragma mark - event
- (void)respondsToLongPress:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
if ([self.delegate respondsToSelector:@selector(yb_imageBrowser:respondsToLongPressWithData:)]) {
[self.delegate yb_imageBrowser:self respondsToLongPressWithData:[self currentData]];
} else {
for (id<YBIBToolViewHandler> handler in self.toolViewHandlers) {
if ([handler respondsToSelector:@selector(yb_respondsToLongPress)]) {
[handler yb_respondsToLongPress];
}
}
}
}
}
#pragma mark - getters & setters
- (YBIBContainerView *)containerView {
if (!_containerView) {
_containerView = [YBIBContainerView new];
_containerView.backgroundColor = UIColor.clearColor;
_containerView.layer.masksToBounds = YES;
}
return _containerView;
}
- (YBIBCollectionView *)collectionView {
if (!_collectionView) {
_collectionView = [YBIBCollectionView new];
_collectionView.delegate = self;
_collectionView.dataSource = self;
}
return _collectionView;
}
- (void)setCurrentPage:(NSInteger)currentPage {
NSInteger maxPage = self.dataMediator.numberOfCells - 1;
if (currentPage > maxPage) {
currentPage = maxPage;
}
_currentPage = currentPage;
if (self.collectionView.superview) {
[self collectionViewScrollToPage:currentPage];
}
}
- (void)setDistanceBetweenPages:(CGFloat)distanceBetweenPages {
self.collectionView.layout.distanceBetweenPages = distanceBetweenPages;
}
- (CGFloat)distanceBetweenPages {
return self.collectionView.layout.distanceBetweenPages;
}
- (void)setTransitioning:(BOOL)transitioning isShow:(BOOL)isShow {
_transitioning = transitioning;
_showTransitioning = transitioning && isShow;
_hideTransitioning = transitioning && !isShow;
// Make 'self.userInteractionEnabled' always 'YES' to block external interaction.
self.containerView.userInteractionEnabled = !transitioning;
self.collectionView.userInteractionEnabled = !transitioning;
if (transitioning) {
if ([self.delegate respondsToSelector:@selector(yb_imageBrowser:beginTransitioningWithIsShow:)]) {
[self.delegate yb_imageBrowser:self beginTransitioningWithIsShow:isShow];
}
} else {
if ([self.delegate respondsToSelector:@selector(yb_imageBrowser:endTransitioningWithIsShow:)]) {
[self.delegate yb_imageBrowser:self endTransitioningWithIsShow:isShow];
}
}
}
- (YBIBDataMediator *)dataMediator {
if (!_dataMediator) {
_dataMediator = [[YBIBDataMediator alloc] initWithBrowser:self];
_dataMediator.dataCacheCountLimit = YBIBLowMemory() ? 9 : 27;
_dataMediator.preloadCount = YBIBLowMemory() ? 0 : 2;
}
return _dataMediator;
}
- (void)setPreloadCount:(NSUInteger)preloadCount {
self.dataMediator.preloadCount = preloadCount;
}
- (NSUInteger)preloadCount {
return self.dataMediator.preloadCount;
}
- (YBIBScreenRotationHandler *)rotationHandler {
if (!_rotationHandler) {
_rotationHandler = [[YBIBScreenRotationHandler alloc] initWithBrowser:self];
}
return _rotationHandler;
}
- (void)setSupportedOrientations:(UIInterfaceOrientationMask)supportedOrientations {
self.rotationHandler.supportedOrientations = supportedOrientations;
}
- (UIInterfaceOrientationMask)supportedOrientations {
return self.rotationHandler.supportedOrientations;
}
- (UIDeviceOrientation)currentOrientation {
return self.rotationHandler.currentOrientation;
}
@end