From c95cd90bd16536369b1669b4c350cb591aeaba3a Mon Sep 17 00:00:00 2001 From: changsanjiang Date: Mon, 18 Jul 2022 11:19:36 +0800 Subject: [PATCH] Release 3.4.2 --- SJBaseVideoPlayer.podspec | 2 +- .../Implements/SJPlayerAutoplayConfig.h | 19 +- .../Implements/SJPlayerAutoplayConfig.m | 2 - .../Common/Implements/SJRotationManager_4.m | 2 +- .../UIKit/UIView+SJBaseVideoPlayerExtended.m | 3 +- SJBaseVideoPlayer/SJBaseVideoPlayer.m | 2 +- .../UIScrollView+ListViewAutoplaySJAdd.h | 19 +- .../UIScrollView+ListViewAutoplaySJAdd.m | 409 ++++++++---------- 8 files changed, 182 insertions(+), 276 deletions(-) diff --git a/SJBaseVideoPlayer.podspec b/SJBaseVideoPlayer.podspec index cc946c74..7ced3ff9 100644 --- a/SJBaseVideoPlayer.podspec +++ b/SJBaseVideoPlayer.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SJBaseVideoPlayer' - s.version = '3.7.1' + s.version = '3.7.2' s.summary = 'video player.' s.description = 'https://github.com/changsanjiang/SJBaseVideoPlayer/blob/master/README.md' s.homepage = 'https://github.com/changsanjiang/SJBaseVideoPlayer' diff --git a/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.h b/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.h index 76355f2e..2aef4834 100755 --- a/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.h +++ b/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.h @@ -10,17 +10,6 @@ NS_ASSUME_NONNULL_BEGIN @protocol SJPlayerAutoplayDelegate; -typedef NS_ENUM(NSUInteger, SJAutoplayScrollAnimationType) { - SJAutoplayScrollAnimationTypeNone, - SJAutoplayScrollAnimationTypeTop, - SJAutoplayScrollAnimationTypeMiddle, -}; - -typedef NS_ENUM(NSUInteger, SJAutoplayPosition) { - SJAutoplayPositionTop, - SJAutoplayPositionMiddle, -}; - @interface SJPlayerAutoplayConfig : NSObject + (instancetype)configWithPlayerSuperviewSelector:(nullable SEL)playerSuperviewSelector autoplayDelegate:(id)delegate; @@ -28,11 +17,9 @@ typedef NS_ENUM(NSUInteger, SJAutoplayPosition) { @property (nonatomic, weak, nullable, readonly) id autoplayDelegate; -/// 滚动的动画类型 -/// default is .Middle; -@property (nonatomic) SJAutoplayScrollAnimationType animationType; -/// default is .Middle; -@property (nonatomic) SJAutoplayPosition autoplayPosition; +/// 滑动方向默认为 垂直方向, 当 UICollectionView 水平滑动时, 记得设置此属性; +@property (nonatomic) UICollectionViewScrollDirection scrollDirection; + /// 可播区域的insets @property (nonatomic) UIEdgeInsets playableAreaInsets; @end diff --git a/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.m b/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.m index a1a0a2c9..93757d03 100755 --- a/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.m +++ b/SJBaseVideoPlayer/Common/Implements/SJPlayerAutoplayConfig.m @@ -17,8 +17,6 @@ + (instancetype)configWithPlayerSuperviewSelector:(nullable SEL)playerSuperviewS SJPlayerAutoplayConfig *config = [[self alloc] init]; config->_autoplayDelegate = delegate; - config->_animationType = SJAutoplayScrollAnimationTypeMiddle; - config->_autoplayPosition = SJAutoplayPositionMiddle; config->_playerSuperviewSelector = playerSuperviewSelector; return config; } diff --git a/SJBaseVideoPlayer/Common/Implements/SJRotationManager_4.m b/SJBaseVideoPlayer/Common/Implements/SJRotationManager_4.m index 571bf3c3..07dee72c 100644 --- a/SJBaseVideoPlayer/Common/Implements/SJRotationManager_4.m +++ b/SJBaseVideoPlayer/Common/Implements/SJRotationManager_4.m @@ -745,7 +745,7 @@ - (void)setNeedsUpdateOfSupportedInterfaceOrientations { [self.window.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations]; #else [(id)UIApplication.sharedApplication.keyWindow.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations]; - [(id)self.window setNeedsUpdateOfSupportedInterfaceOrientations]; + [(id)self.window.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations]; #endif } diff --git a/SJBaseVideoPlayer/Common/UIKit/UIView+SJBaseVideoPlayerExtended.m b/SJBaseVideoPlayer/Common/UIKit/UIView+SJBaseVideoPlayerExtended.m index 64b751f1..c78e2217 100644 --- a/SJBaseVideoPlayer/Common/UIKit/UIView+SJBaseVideoPlayerExtended.m +++ b/SJBaseVideoPlayer/Common/UIKit/UIView+SJBaseVideoPlayerExtended.m @@ -24,9 +24,8 @@ - (CGRect)intersectionWithView:(UIView *)view insets:(UIEdgeInsets)insets { if ( view == nil || view.window == nil || self.window == nil ) return CGRectZero; CGRect rect1 = [view convertRect:view.bounds toView:self.window]; CGRect rect2 = [self convertRect:self.bounds toView:self.window]; - rect1 = UIEdgeInsetsInsetRect(rect1, insets); rect2 = UIEdgeInsetsInsetRect(rect2, insets); - + CGRect intersection = CGRectIntersection(rect1, rect2); return (CGRectIsEmpty(intersection) || CGRectIsNull(intersection)) ? CGRectZero : intersection; } diff --git a/SJBaseVideoPlayer/SJBaseVideoPlayer.m b/SJBaseVideoPlayer/SJBaseVideoPlayer.m index 8739f35a..3fa337fc 100755 --- a/SJBaseVideoPlayer/SJBaseVideoPlayer.m +++ b/SJBaseVideoPlayer/SJBaseVideoPlayer.m @@ -169,7 +169,7 @@ + (instancetype)player { } + (NSString *)version { - return @"v3.7.1"; + return @"v3.7.2"; } - (void)setVideoGravity:(SJVideoGravity)videoGravity { diff --git a/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.h b/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.h index 5322b931..0583a1cb 100755 --- a/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.h +++ b/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.h @@ -19,11 +19,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL sj_enabledAutoplay; -/// -/// 指定位置播放 -/// -- (void)sj_playAssetAtIndexPath:(NSIndexPath *)indexPath scrollAnimated:(BOOL)animated; - /// /// enable autoplay /// 开启 @@ -33,12 +28,7 @@ NS_ASSUME_NONNULL_BEGIN /// /// 关闭 /// -- (void)sj_disenableAutoplay; - -/// -/// 播放下一个(在已显示的cell中查找) -/// -- (void)sj_playNextVisibleAsset; +- (void)sj_disableAutoplay; /// /// 移除当前播放视图 @@ -49,13 +39,8 @@ NS_ASSUME_NONNULL_BEGIN /// Developers don't need to care, this category is automatically maintained by the SJBaseVideoPlayer. /// 开发者无需关心, 此分类由播放器自动维护 -@interface UIScrollView (SJAutoplayPrivate) +@interface UIScrollView (SJAutoplayPlayerAssigns) @property (nonatomic, strong, nullable, readonly) NSIndexPath *sj_currentPlayingIndexPath; - (void)setSj_currentPlayingIndexPath:(nullable NSIndexPath *)sj_currentPlayingIndexPath; @end - - -@interface UIScrollView (SJAutoplayDeprecated) -- (void)sj_needPlayNextAsset __deprecated_msg("use `sj_playNextVisibleAsset`"); -@end NS_ASSUME_NONNULL_END diff --git a/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.m b/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.m index 62afbce2..03b371aa 100755 --- a/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.m +++ b/SJBaseVideoPlayer/UIScrollView+ListViewAutoplaySJAdd.m @@ -24,10 +24,21 @@ #import "SJRunLoopTaskQueue.h" #endif -NS_ASSUME_NONNULL_BEGIN +@interface UIScrollView (SJAutoplayInternal) +@property (nonatomic, strong, nullable) SJPlayerAutoplayConfig *sj_autoplayConfig; +@property (nonatomic, readonly) BOOL sj_isScrolledToTop; +@property (nonatomic, readonly) BOOL sj_isScrolledToLeft; +@property (nonatomic, readonly) BOOL sj_isScrolledToBottom; +@property (nonatomic, readonly) BOOL sj_isScrolledToRight; +- (BOOL)sj_isAutoplayTargetViewAppearedForConfiguration:(SJPlayerAutoplayConfig *)config atIndexPath:(NSIndexPath *)indexPath; +- (nullable UIView *)sj_autoplayTargetViewForConfiguration:(SJPlayerAutoplayConfig *)config atIndexPath:(NSIndexPath *)indexPath; +- (CGFloat)sj_autoplayGuidelineForConfiguration:(SJPlayerAutoplayConfig *)config; +- (UIEdgeInsets)sj_autoplayPlayableAreaInsetsForConfiguration:(SJPlayerAutoplayConfig *)config; +- (NSArray *_Nullable)sj_sortedVisibleIndexPaths; +@end + + static NSString *const kQueue = @"SJBaseVideoPlayerAutoplayTaskQueue"; -static void sj_exeAnima(__kindof UIScrollView *scrollView, NSIndexPath *indexPath, SJAutoplayScrollAnimationType animationType); -static void sj_playAsset(UIScrollView *scrollView, NSIndexPath *indexPath, BOOL animated); static SJRunLoopTaskQueue * sj_queue(void) { static SJRunLoopTaskQueue *queue = nil; @@ -38,19 +49,7 @@ return queue; } -@implementation UIScrollView (SJAutoplayPrivate) -- (void)setSj_currentPlayingIndexPath:(nullable NSIndexPath *)sj_currentPlayingIndexPath { - objc_setAssociatedObject(self, @selector(sj_currentPlayingIndexPath), sj_currentPlayingIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (nullable NSIndexPath *)sj_currentPlayingIndexPath { - return objc_getAssociatedObject(self, _cmd); -} -@end - static void sj_playNextAssetAfterEndScroll(__kindof UIScrollView *scrollView); -static void sj_playNextVisibleAsset(__kindof UIScrollView *scrollView); - static void sj_scrollViewContentOffsetDidChange(UIScrollView *scrollView, void(^contentOffsetDidChangeExeBlock)(void)); static void sj_removeContentOffsetObserver(UIScrollView *scrollView); @@ -62,62 +61,6 @@ - (BOOL)sj_enabledAutoplay { return [objc_getAssociatedObject(self, _cmd) boolValue]; } -- (void)sj_playAssetAtIndexPath:(NSIndexPath *)indexPath scrollAnimated:(BOOL)animated { - if ( self.window == nil ) - return; - - [self sj_removeCurrentPlayerView]; - - sj_queue().empty(); - if ( [self isKindOfClass:UITableView.class] ) { - UITableView *tableView = (id)self; - UITableViewScrollPosition position = [self sj_autoplayConfig].animationType != SJAutoplayScrollAnimationTypeTop ? UITableViewScrollPositionMiddle : UITableViewScrollPositionTop; - if (@available(iOS 11.0, *)) { - [tableView performBatchUpdates:^{ - [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationNone]; - } completion:^(BOOL finished) { - if ( [tableView numberOfRowsInSection:indexPath.section] > indexPath.row ) { - [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:position animated:animated]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - sj_queue().empty(); - [[self sj_autoplayConfig].autoplayDelegate sj_playerNeedPlayNewAssetAtIndexPath:indexPath]; - }); - } - }]; - } else { - [UIView animateWithDuration:animated ? 0.2 : 0 animations:^{ - [tableView beginUpdates]; - [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationNone]; - [tableView endUpdates]; - } completion:^(BOOL finished) { - if ( [tableView numberOfRowsInSection:indexPath.section] > indexPath.row ) { - [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:position animated:animated]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - sj_queue().empty(); - [[self sj_autoplayConfig].autoplayDelegate sj_playerNeedPlayNewAssetAtIndexPath:indexPath]; - }); - } - }]; - } - } - else if ( [self isKindOfClass:UICollectionView.class] ) { - UICollectionView *collectionView = (id)self; - UICollectionViewScrollPosition position = [self sj_autoplayConfig].animationType != SJAutoplayScrollAnimationTypeTop ? UICollectionViewScrollPositionCenteredVertically : UICollectionViewScrollPositionTop; - [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:position animated:animated]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - sj_queue().empty(); - [[self sj_autoplayConfig].autoplayDelegate sj_playerNeedPlayNewAssetAtIndexPath:indexPath]; - }); - } -} - -- (void)setSj_autoplayConfig:(nullable SJPlayerAutoplayConfig *)sj_autoplayConfig { - objc_setAssociatedObject(self, @selector(sj_autoplayConfig), sj_autoplayConfig, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} -- (nullable SJPlayerAutoplayConfig *)sj_autoplayConfig { - return objc_getAssociatedObject(self, _cmd); -} - - (void)sj_enableAutoplayWithConfig:(SJPlayerAutoplayConfig *)autoplayConfig { self.sj_enabledAutoplay = YES; self.sj_autoplayConfig = autoplayConfig; @@ -148,19 +91,13 @@ - (void)sj_enableAutoplayWithConfig:(SJPlayerAutoplayConfig *)autoplayConfig { }); } -- (void)sj_disenableAutoplay { +- (void)sj_disableAutoplay { self.sj_enabledAutoplay = NO; self.sj_autoplayConfig = nil; self.sj_currentPlayingIndexPath = nil; sj_removeContentOffsetObserver(self); } - -- (void)sj_playNextVisibleAsset { - dispatch_async(dispatch_get_main_queue(), ^{ - sj_playNextVisibleAsset(self); - }); -} - + - (void)sj_removeCurrentPlayerView { self.sj_currentPlayingIndexPath = nil; [[self viewWithTag:SJPlayerViewTag] removeFromSuperview]; @@ -173,17 +110,6 @@ - (void)sj_playNextAssetAfterEndScroll { }); } -- (NSArray *_Nullable)sj_visibleIndexPaths { - NSArray *_Nullable visibleIndexPaths = nil; - if ( [self isKindOfClass:[UITableView class]] ) - visibleIndexPaths = [(UITableView *)self indexPathsForVisibleRows]; - else if ( [self isKindOfClass:[UICollectionView class]] ) - visibleIndexPaths = [[(UICollectionView *)self indexPathsForVisibleItems] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return [obj1 compare:obj2]; - }]; - return visibleIndexPaths; -} - - (void)setSj_hasDelayedEndScrollTask:(BOOL)sj_hasDelayedEndScrollTask { objc_setAssociatedObject(self, @selector(sj_hasDelayedEndScrollTask), @(sj_hasDelayedEndScrollTask), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @@ -231,179 +157,190 @@ static void sj_removeContentOffsetObserver(UIScrollView *scrollView) { #pragma mark - -static void sj_playNextAssetAfterEndScroll(__kindof __kindof UIScrollView *scrollView) { - NSArray *_Nullable visibleIndexPaths = [scrollView sj_visibleIndexPaths]; - if ( visibleIndexPaths.count < 1 ) +static void sj_playNextAssetAfterEndScroll(__kindof __kindof UIScrollView *self) { + NSArray *_Nullable sortedVisibleIndexPaths = [self sj_sortedVisibleIndexPaths]; + if ( sortedVisibleIndexPaths.count < 1 ) return; - - SJPlayerAutoplayConfig *config = [scrollView sj_autoplayConfig]; - NSIndexPath *_Nullable current = [scrollView sj_currentPlayingIndexPath]; - if ( config.playerSuperviewSelector != NULL && [scrollView isViewAppearedForSelector:config.playerSuperviewSelector insets:config.playableAreaInsets atIndexPath:current] ) + + SJPlayerAutoplayConfig *config = [self sj_autoplayConfig]; + NSIndexPath *_Nullable current = [self sj_currentPlayingIndexPath]; + + UICollectionViewScrollDirection scrollDirection = config.scrollDirection; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + if ( !self.sj_isScrolledToTop && !self.sj_isScrolledToBottom && + [self sj_isAutoplayTargetViewAppearedForConfiguration:config atIndexPath:current] ) + return; + break; + case UICollectionViewScrollDirectionHorizontal: + if ( !self.sj_isScrolledToLeft && !self.sj_isScrolledToRight && + [self sj_isAutoplayTargetViewAppearedForConfiguration:config atIndexPath:current] ) + return; + break; + } + + NSIndexPath *_Nullable next = nil; + // 参考线 + // 距离参考线最近的视频将会被播放 + CGFloat guideline = [self sj_autoplayGuidelineForConfiguration:config]; + + if ( guideline < 0 ) return; + + NSInteger count = sortedVisibleIndexPaths.count; + CGFloat subs = CGFLOAT_MAX; + for ( NSInteger i = 0 ; i < count ; ++ i ) { + NSIndexPath *indexPath = sortedVisibleIndexPaths[i]; + UIView *_Nullable target = [self sj_autoplayTargetViewForConfiguration:config atIndexPath:indexPath]; + if ( !target ) continue; + UIEdgeInsets playableAreaInsets = [self sj_autoplayPlayableAreaInsetsForConfiguration:config]; + CGRect intersection = [self intersectionWithView:target insets:playableAreaInsets]; + CGFloat result = CGFLOAT_MAX; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + if ( intersection.size.height != 0 ) result = floor(ABS(guideline - CGRectGetMidY(intersection))); + break; + case UICollectionViewScrollDirectionHorizontal: + if ( intersection.size.width != 0 ) result = floor(ABS(guideline - CGRectGetMidX(intersection))); + break; + } + + if ( result < subs ) { + subs = result; + next = indexPath; + } + } + + if ( next != nil && ![next isEqual:current] ) [config.autoplayDelegate sj_playerNeedPlayNewAssetAtIndexPath:next]; +} + + +@implementation UIScrollView (SJAutoplayInternal) + +- (BOOL)sj_isScrolledToTop { + return floor(self.contentOffset.y) == 0; +} + +- (BOOL)sj_isScrolledToLeft { + return floor(self.contentOffset.x) == 0; +} + +- (BOOL)sj_isScrolledToRight { + return floor(self.contentOffset.x + self.bounds.size.width) == floor(self.contentSize.width); +} + +- (BOOL)sj_isScrolledToBottom { + return floor(self.contentOffset.y + self.bounds.size.height) == floor(self.contentSize.height); +} + +- (BOOL)sj_isAutoplayTargetViewAppearedForConfiguration:(SJPlayerAutoplayConfig *)config atIndexPath:(NSIndexPath *)indexPath { + SEL playerSuperviewSelector = config.playerSuperviewSelector; + if ( playerSuperviewSelector != NULL ) + return [self isViewAppearedForSelector:playerSuperviewSelector insets:config.playableAreaInsets atIndexPath:indexPath]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" NSInteger superviewTag = config.playerSuperviewTag; #pragma clang diagnostic pop - if ( superviewTag != 0 && [scrollView isViewAppearedWithTag:superviewTag insets:config.playableAreaInsets atIndexPath:current] ) - return; - if ( [scrollView isViewAppearedWithProtocol:@protocol(SJPlayModelPlayerSuperview) tag:0 insets:config.playableAreaInsets atIndexPath:current] ) - return; + if ( superviewTag != 0 ) + return [self isViewAppearedWithTag:superviewTag insets:config.playableAreaInsets atIndexPath:indexPath]; - NSIndexPath *_Nullable next = nil; - switch ( config.autoplayPosition ) { - case SJAutoplayPositionTop: { - for ( NSIndexPath *indexPath in visibleIndexPaths ) { - UIView *_Nullable target = nil; - if ( config.playerSuperviewSelector != NULL ) { - target = [scrollView viewForSelector:config.playerSuperviewSelector atIndexPath:indexPath]; - } - else { - target = superviewTag != 0 ? - [scrollView viewWithTag:superviewTag atIndexPath:indexPath] : - [scrollView viewWithProtocol:@protocol(SJPlayModelPlayerSuperview) tag:0 atIndexPath:indexPath]; - } - if ( !target ) continue; - CGRect intersection = [scrollView intersectionWithView:target insets:config.playableAreaInsets]; - if ( floor(intersection.size.height) >= floor(target.bounds.size.height) ) { - next = indexPath; - break; - } + return [self isViewAppearedWithProtocol:@protocol(SJPlayModelPlayerSuperview) tag:0 insets:config.playableAreaInsets atIndexPath:indexPath]; +} + +- (nullable UIView *)sj_autoplayTargetViewForConfiguration:(SJPlayerAutoplayConfig *)config atIndexPath:(NSIndexPath *)indexPath { + SEL playerSuperviewSelector = config.playerSuperviewSelector; + if ( playerSuperviewSelector != NULL ) { + return [self viewForSelector:playerSuperviewSelector atIndexPath:indexPath]; + } + else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSInteger superviewTag = config.playerSuperviewTag; +#pragma clang diagnostic pop + return superviewTag != 0 ? + [self viewWithTag:superviewTag atIndexPath:indexPath] : + [self viewWithProtocol:@protocol(SJPlayModelPlayerSuperview) tag:0 atIndexPath:indexPath]; + } +} + +- (CGFloat)sj_autoplayGuidelineForConfiguration:(SJPlayerAutoplayConfig *)config { + CGFloat guideline = 0; + switch ( config.scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + if ( self.sj_isScrolledToTop ) { + // nothing + } + else if ( self.sj_isScrolledToBottom ) { + guideline = self.contentSize.height; + } + else { + if (@available(iOS 11.0, *)) + guideline = floor((CGRectGetHeight(self.bounds) - self.adjustedContentInset.top) * 0.5); + else + guideline = floor((CGRectGetHeight(self.bounds) - self.contentInset.top) * 0.5); } } break; - case SJAutoplayPositionMiddle: { - // 中线, 距离中心最近的那个玩意儿 - CGFloat mid = 0; - { - CGFloat offset = 0; + case UICollectionViewScrollDirectionHorizontal: { + if ( self.sj_isScrolledToLeft ) { + // nothing + } + else if ( self.sj_isScrolledToBottom ) { + guideline = self.contentSize.width; + } + else { if (@available(iOS 11.0, *)) - mid = floor((CGRectGetHeight(scrollView.bounds) - scrollView.adjustedContentInset.top) * 0.5 + offset); + guideline = floor((CGRectGetWidth(self.bounds) - self.adjustedContentInset.left) * 0.5); else - mid = floor((CGRectGetHeight(scrollView.bounds) - scrollView.contentInset.top) * 0.5 + offset); - - if ( mid < 1 ) - return; - } - - NSInteger count = visibleIndexPaths.count; - CGFloat sub = CGFLOAT_MAX; - for ( NSInteger i = 0 ; i < count ; ++ i ) { - NSIndexPath *indexPath = visibleIndexPaths[i]; - UIView *_Nullable target = nil; - if ( config.playerSuperviewSelector != NULL ) { - target = [scrollView viewForSelector:config.playerSuperviewSelector atIndexPath:indexPath]; - } - else { - target = superviewTag != 0 ? - [scrollView viewWithTag:superviewTag atIndexPath:indexPath] : - [scrollView viewWithProtocol:@protocol(SJPlayModelPlayerSuperview) tag:0 atIndexPath:indexPath]; - } - if ( !target ) continue; - CGRect intersection = [scrollView intersectionWithView:target insets:config.playableAreaInsets]; - CGFloat result = floor(ABS(mid - CGRectGetMidY(intersection))); - if ( result < sub ) { - sub = result; - next = indexPath; - } + guideline = floor((CGRectGetWidth(self.bounds) - self.contentInset.left) * 0.5); } } break; } - - if ( next ) - sj_playAsset(scrollView, next, NO); + return guideline; } -/// 执行动画 -static void sj_exeAnima(__kindof UIScrollView *scrollView, NSIndexPath *indexPath, SJAutoplayScrollAnimationType animationType) { - switch ( animationType ) { - case SJAutoplayScrollAnimationTypeNone: break; - case SJAutoplayScrollAnimationTypeTop: { - @try{ - if ( [scrollView isKindOfClass:[UITableView class]] ) { - [UIView animateWithDuration:0.6 animations:^{ - [(UITableView *)scrollView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO]; - }]; - } - else if ( [scrollView isKindOfClass:[UICollectionView class]] ) { - [(UICollectionView *)scrollView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionTop animated:YES]; - } - }@catch(NSException *__unused ex) {} - } +- (UIEdgeInsets)sj_autoplayPlayableAreaInsetsForConfiguration:(SJPlayerAutoplayConfig *)config { + UIEdgeInsets insets = config.playableAreaInsets; + switch ( config.scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + if ( self.sj_isScrolledToTop ) insets.top = 0; + else if ( self.sj_isScrolledToBottom ) insets.bottom = 0; break; - case SJAutoplayScrollAnimationTypeMiddle: { - @try{ - if ( [scrollView isKindOfClass:[UITableView class]] ) { - [UIView animateWithDuration:0.6 animations:^{ - [(UITableView *)scrollView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO]; - }]; - } - else if ( [scrollView isKindOfClass:[UICollectionView class]] ) { - [(UICollectionView *)scrollView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; - } - }@catch(NSException *__unused ex) {} - } + case UICollectionViewScrollDirectionHorizontal: + if ( self.sj_isScrolledToLeft ) insets.left = 0; + else if ( self.sj_isScrolledToRight ) insets.right = 0; break; } + return insets; } -static void sj_playNextVisibleAsset(__kindof UIScrollView *scrollView) { - NSArray *_Nullable visibleIndexPaths = [scrollView sj_visibleIndexPaths]; - if ( visibleIndexPaths.count < 1 ) - return; - - NSArray *_Nullable remain = nil; - NSIndexPath *_Nullable current = [scrollView sj_currentPlayingIndexPath]; - { - NSInteger idx = NSNotFound; - if ( !current || (idx = [visibleIndexPaths indexOfObject:current]) == NSNotFound ) { - remain = visibleIndexPaths; - } - else if ( ++idx < visibleIndexPaths.count ) { - remain = [visibleIndexPaths subarrayWithRange:NSMakeRange(idx, visibleIndexPaths.count - idx)]; - } - - if ( remain.count < 1 ) - return; - } - - SJPlayerAutoplayConfig *config = [scrollView sj_autoplayConfig]; - NSIndexPath *_Nullable next = nil; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSInteger superviewTag = config.playerSuperviewTag; -#pragma clang diagnostic pop - for ( NSIndexPath *indexPath in remain ) { - UIView *_Nullable target = nil; - if ( config.playerSuperviewSelector != NULL ) { - target = [scrollView viewForSelector:config.playerSuperviewSelector atIndexPath:indexPath]; - } - else { - target = superviewTag != 0 ? - [scrollView viewWithTag:superviewTag atIndexPath:indexPath] : - [scrollView viewWithProtocol:@protocol(SJPlayModelPlayerSuperview) tag:0 atIndexPath:indexPath]; - } - if ( !target ) continue; - CGRect intersection = [scrollView intersectionWithView:target insets:config.playableAreaInsets]; - if ( floor(intersection.size.height) >= floor(target.bounds.size.height) ) { - next = indexPath; - break; - } - } - - if ( next ) - sj_playAsset(scrollView, next, current != nil); +- (void)setSj_autoplayConfig:(nullable SJPlayerAutoplayConfig *)sj_autoplayConfig { + objc_setAssociatedObject(self, @selector(sj_autoplayConfig), sj_autoplayConfig, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} +- (nullable SJPlayerAutoplayConfig *)sj_autoplayConfig { + return objc_getAssociatedObject(self, _cmd); } -static void sj_playAsset(UIScrollView *scrollView, NSIndexPath *indexPath, BOOL animated) { - [[scrollView sj_autoplayConfig].autoplayDelegate sj_playerNeedPlayNewAssetAtIndexPath:indexPath]; - if ( animated ) sj_exeAnima(scrollView, indexPath, [scrollView sj_autoplayConfig].animationType); +- (NSArray *_Nullable)sj_sortedVisibleIndexPaths { + NSArray *_Nullable visibleIndexPaths = nil; + if ( [self isKindOfClass:[UITableView class]] ) + visibleIndexPaths = [(UITableView *)self indexPathsForVisibleRows]; + else if ( [self isKindOfClass:[UICollectionView class]] ) + visibleIndexPaths = [[(UICollectionView *)self indexPathsForVisibleItems] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { + return [obj1 compare:obj2]; + }]; + return visibleIndexPaths; } +@end +@implementation UIScrollView (SJAutoplayPlayerAssigns) +- (void)setSj_currentPlayingIndexPath:(nullable NSIndexPath *)sj_currentPlayingIndexPath { + objc_setAssociatedObject(self, @selector(sj_currentPlayingIndexPath), sj_currentPlayingIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} -@implementation UIScrollView (SJAutoplayDeprecated) -- (void)sj_needPlayNextAsset __deprecated_msg("use `sj_playNextVisibleAsset`") { - [self sj_playNextVisibleAsset]; +- (nullable NSIndexPath *)sj_currentPlayingIndexPath { + return objc_getAssociatedObject(self, _cmd); } + @end -NS_ASSUME_NONNULL_END