123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 |
- //
- // UIScrollView+ZFPlayer.m
- // ZFPlayer
- //
- // Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "UIScrollView+ZFPlayer.h"
- #import <objc/runtime.h>
- #import "ZFReachabilityManager.h"
- #import "ZFPlayer.h"
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored"-Wdeprecated-declarations"
- @interface UIScrollView ()
- @property (nonatomic) CGFloat zf_lastOffsetY;
- @property (nonatomic) CGFloat zf_lastOffsetX;
- @property (nonatomic) ZFPlayerScrollDirection zf_scrollDirection;
- @end
- @implementation UIScrollView (ZFPlayer)
- + (void)load {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- SEL selectors[] = {
- @selector(setContentOffset:)
- };
-
- for (NSInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) {
- SEL originalSelector = selectors[index];
- SEL swizzledSelector = NSSelectorFromString([@"zf_" stringByAppendingString:NSStringFromSelector(originalSelector)]);
- Method originalMethod = class_getInstanceMethod(self, originalSelector);
- Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
- if (class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
- class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
- } else {
- method_exchangeImplementations(originalMethod, swizzledMethod);
- }
- }
- });
- }
- - (void)zf_setContentOffset:(CGPoint)contentOffset {
- if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
- [self _findCorrectCellWhenScrollViewDirectionVertical:nil];
- } else {
- [self _findCorrectCellWhenScrollViewDirectionHorizontal:nil];
- }
- [self zf_setContentOffset:contentOffset];
- }
- #pragma mark - private method
- - (void)_scrollViewDidStopScroll {
- @weakify(self)
- [self zf_filterShouldPlayCellWhileScrolled:^(NSIndexPath * _Nonnull indexPath) {
- @strongify(self)
- if (self.zf_scrollViewDidStopScrollCallback) self.zf_scrollViewDidStopScrollCallback(indexPath);
- if (self.zf_scrollViewDidEndScrollingCallback) self.zf_scrollViewDidEndScrollingCallback(indexPath);
- }];
- }
- - (void)_scrollViewBeginDragging {
- if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
- self.zf_lastOffsetY = self.contentOffset.y;
- } else {
- self.zf_lastOffsetX = self.contentOffset.x;
- }
- }
- /**
- The percentage of scrolling processed in vertical scrolling.
- */
- - (void)_scrollViewScrollingDirectionVertical {
- CGFloat offsetY = self.contentOffset.y;
- self.zf_scrollDirection = (offsetY - self.zf_lastOffsetY > 0) ? ZFPlayerScrollDirectionUp : ZFPlayerScrollDirectionDown;
- self.zf_lastOffsetY = offsetY;
- if (self.zf_stopPlay) return;
-
- UIView *playerView;
- if (self.zf_containerType == ZFPlayerContainerTypeCell) {
- // Avoid being paused the first time you play it.
- if (self.contentOffset.y < 0) return;
- if (!self.zf_playingIndexPath) return;
-
- UIView *cell = [self zf_getCellForIndexPath:self.zf_playingIndexPath];
- if (!cell) {
- if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
- return;
- }
- playerView = [cell viewWithTag:self.zf_containerViewTag];
- } else if (self.zf_containerType == ZFPlayerContainerTypeView) {
- if (!self.zf_containerView) return;
- playerView = self.zf_containerView;
- }
-
- CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
- CGRect rect = [self convertRect:rect1 toView:self.superview];
- /// playerView top to scrollView top space.
- CGFloat topSpacing = CGRectGetMinY(rect) - CGRectGetMinY(self.frame) - CGRectGetMinY(playerView.frame);
- /// playerView bottom to scrollView bottom space.
- CGFloat bottomSpacing = CGRectGetMaxY(self.frame) - CGRectGetMaxY(rect) + CGRectGetMinY(playerView.frame);
- /// The height of the content area.
- CGFloat contentInsetHeight = CGRectGetMaxY(self.frame) - CGRectGetMinY(self.frame);
-
- CGFloat playerDisapperaPercent = 0;
- CGFloat playerApperaPercent = 0;
-
- if (self.zf_scrollDirection == ZFPlayerScrollDirectionUp) { /// Scroll up
- /// Player is disappearing.
- if (topSpacing <= 0 && CGRectGetHeight(rect) != 0) {
- playerDisapperaPercent = -topSpacing/CGRectGetHeight(rect);
- if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
- if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
- }
-
- /// Top area
- if (topSpacing <= 0 && topSpacing > -CGRectGetHeight(rect)/2) {
- /// When the player will disappear.
- if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
- } else if (topSpacing <= -CGRectGetHeight(rect)) {
- /// When the player did disappeared.
- if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
- } else if (topSpacing > 0 && topSpacing <= contentInsetHeight) {
- /// Player is appearing.
- if (CGRectGetHeight(rect) != 0) {
- playerApperaPercent = -(topSpacing-contentInsetHeight)/CGRectGetHeight(rect);
- if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
- if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
- }
- /// In visable area
- if (topSpacing <= contentInsetHeight && topSpacing > contentInsetHeight-CGRectGetHeight(rect)/2) {
- /// When the player will appear.
- if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
- } else {
- /// When the player did appeared.
- if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
- }
- }
-
- } else if (self.zf_scrollDirection == ZFPlayerScrollDirectionDown) { /// Scroll Down
- /// Player is disappearing.
- if (bottomSpacing <= 0 && CGRectGetHeight(rect) != 0) {
- playerDisapperaPercent = -bottomSpacing/CGRectGetHeight(rect);
- if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
- if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
- }
-
- /// Bottom area
- if (bottomSpacing <= 0 && bottomSpacing > -CGRectGetHeight(rect)/2) {
- /// When the player will disappear.
- if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
- } else if (bottomSpacing <= -CGRectGetHeight(rect)) {
- /// When the player did disappeared.
- if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
- } else if (bottomSpacing > 0 && bottomSpacing <= contentInsetHeight) {
- /// Player is appearing.
- if (CGRectGetHeight(rect) != 0) {
- playerApperaPercent = -(bottomSpacing-contentInsetHeight)/CGRectGetHeight(rect);
- if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
- if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
- }
- /// In visable area
- if (bottomSpacing <= contentInsetHeight && bottomSpacing > contentInsetHeight-CGRectGetHeight(rect)/2) {
- /// When the player will appear.
- if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
- } else {
- /// When the player did appeared.
- if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
- }
- }
- }
- }
- /**
- The percentage of scrolling processed in horizontal scrolling.
- */
- - (void)_scrollViewScrollingDirectionHorizontal {
- CGFloat offsetX = self.contentOffset.x;
- self.zf_scrollDirection = (offsetX - self.zf_lastOffsetX > 0) ? ZFPlayerScrollDirectionLeft : ZFPlayerScrollDirectionRight;
- self.zf_lastOffsetX = offsetX;
- if (self.zf_stopPlay) return;
-
- UIView *playerView;
- if (self.zf_containerType == ZFPlayerContainerTypeCell) {
- // Avoid being paused the first time you play it.
- if (self.contentOffset.x < 0) return;
- if (!self.zf_playingIndexPath) return;
-
- UIView *cell = [self zf_getCellForIndexPath:self.zf_playingIndexPath];
- if (!cell) {
- if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
- return;
- }
- playerView = [cell viewWithTag:self.zf_containerViewTag];
- } else if (self.zf_containerType == ZFPlayerContainerTypeView) {
- if (!self.zf_containerView) return;
- playerView = self.zf_containerView;
- }
-
- CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
- CGRect rect = [self convertRect:rect1 toView:self.superview];
- /// playerView left to scrollView left space.
- CGFloat leftSpacing = CGRectGetMinX(rect) - CGRectGetMinX(self.frame) - CGRectGetMinX(playerView.frame);
- /// playerView bottom to scrollView right space.
- CGFloat rightSpacing = CGRectGetMaxX(self.frame) - CGRectGetMaxX(rect) + CGRectGetMinX(playerView.frame);
- /// The height of the content area.
- CGFloat contentInsetWidth = CGRectGetMaxX(self.frame) - CGRectGetMinX(self.frame);
-
- CGFloat playerDisapperaPercent = 0;
- CGFloat playerApperaPercent = 0;
-
- if (self.zf_scrollDirection == ZFPlayerScrollDirectionLeft) { /// Scroll left
- /// Player is disappearing.
- if (leftSpacing <= 0 && CGRectGetWidth(rect) != 0) {
- playerDisapperaPercent = -leftSpacing/CGRectGetWidth(rect);
- if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
- if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
- }
-
- /// Top area
- if (leftSpacing <= 0 && leftSpacing > -CGRectGetWidth(rect)/2) {
- /// When the player will disappear.
- if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
- } else if (leftSpacing <= -CGRectGetWidth(rect)) {
- /// When the player did disappeared.
- if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
- } else if (leftSpacing > 0 && leftSpacing <= contentInsetWidth) {
- /// Player is appearing.
- if (CGRectGetWidth(rect) != 0) {
- playerApperaPercent = -(leftSpacing-contentInsetWidth)/CGRectGetWidth(rect);
- if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
- if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
- }
- /// In visable area
- if (leftSpacing <= contentInsetWidth && leftSpacing > contentInsetWidth-CGRectGetWidth(rect)/2) {
- /// When the player will appear.
- if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
- } else {
- /// When the player did appeared.
- if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
- }
- }
-
- } else if (self.zf_scrollDirection == ZFPlayerScrollDirectionRight) { /// Scroll right
- /// Player is disappearing.
- if (rightSpacing <= 0 && CGRectGetWidth(rect) != 0) {
- playerDisapperaPercent = -rightSpacing/CGRectGetWidth(rect);
- if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
- if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
- }
-
- /// Bottom area
- if (rightSpacing <= 0 && rightSpacing > -CGRectGetWidth(rect)/2) {
- /// When the player will disappear.
- if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
- } else if (rightSpacing <= -CGRectGetWidth(rect)) {
- /// When the player did disappeared.
- if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
- } else if (rightSpacing > 0 && rightSpacing <= contentInsetWidth) {
- /// Player is appearing.
- if (CGRectGetWidth(rect) != 0) {
- playerApperaPercent = -(rightSpacing-contentInsetWidth)/CGRectGetWidth(rect);
- if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
- if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
- }
- /// In visable area
- if (rightSpacing <= contentInsetWidth && rightSpacing > contentInsetWidth-CGRectGetWidth(rect)/2) {
- /// When the player will appear.
- if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
- } else {
- /// When the player did appeared.
- if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
- }
- }
- }
- }
- /**
- Find the playing cell while the scrollDirection is vertical.
- */
- - (void)_findCorrectCellWhenScrollViewDirectionVertical:(void (^ __nullable)(NSIndexPath *indexPath))handler {
- if (!self.zf_shouldAutoPlay) return;
- if (self.zf_containerType == ZFPlayerContainerTypeView) return;
- NSArray *visiableCells = nil;
- NSIndexPath *indexPath = nil;
- if ([self _isTableView]) {
- UITableView *tableView = (UITableView *)self;
- visiableCells = [tableView visibleCells];
- // First visible cell indexPath
- indexPath = tableView.indexPathsForVisibleRows.firstObject;
- if (self.contentOffset.y <= 0 && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
-
- // Last visible cell indexPath
- indexPath = tableView.indexPathsForVisibleRows.lastObject;
- if (self.contentOffset.y + self.frame.size.height >= self.contentSize.height && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
- } else if ([self _isCollectionView]) {
- UICollectionView *collectionView = (UICollectionView *)self;
- visiableCells = [collectionView visibleCells];
- NSArray *sortedIndexPaths = [collectionView.indexPathsForVisibleItems sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
- return [obj1 compare:obj2];
- }];
-
- visiableCells = [visiableCells sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
- NSIndexPath *path1 = (NSIndexPath *)[collectionView indexPathForCell:obj1];
- NSIndexPath *path2 = (NSIndexPath *)[collectionView indexPathForCell:obj2];
- return [path1 compare:path2];
- }];
-
- // First visible cell indexPath
- indexPath = sortedIndexPaths.firstObject;
- if (self.contentOffset.y <= 0 && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
-
- // Last visible cell indexPath
- indexPath = sortedIndexPaths.lastObject;
- if (self.contentOffset.y + self.frame.size.height >= self.contentSize.height && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
- }
-
- NSArray *cells = nil;
- if (self.zf_scrollDirection == ZFPlayerScrollDirectionUp) {
- cells = visiableCells;
- } else {
- cells = [visiableCells reverseObjectEnumerator].allObjects;
- }
-
- /// Mid line.
- CGFloat scrollViewMidY = CGRectGetHeight(self.frame)/2;
- /// The final playing indexPath.
- __block NSIndexPath *finalIndexPath = nil;
- /// The final distance from the center line.
- __block CGFloat finalSpace = 0;
- @weakify(self)
- [cells enumerateObjectsUsingBlock:^(UIView *cell, NSUInteger idx, BOOL * _Nonnull stop) {
- @strongify(self)
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (!playerView) return;
- CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
- CGRect rect = [self convertRect:rect1 toView:self.superview];
- /// playerView top to scrollView top space.
- CGFloat topSpacing = CGRectGetMinY(rect) - CGRectGetMinY(self.frame) - CGRectGetMinY(playerView.frame);
- /// playerView bottom to scrollView bottom space.
- CGFloat bottomSpacing = CGRectGetMaxY(self.frame) - CGRectGetMaxY(rect) + CGRectGetMinY(playerView.frame);
- CGFloat centerSpacing = ABS(scrollViewMidY - CGRectGetMidY(rect));
- NSIndexPath *indexPath = [self zf_getIndexPathForCell:cell];
-
- /// Play when the video playback section is visible.
- if ((topSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetHeight(rect)) && (bottomSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetHeight(rect))) {
- /// If you have a cell that is playing, stop the traversal.
- if (self.zf_playingIndexPath) {
- indexPath = self.zf_playingIndexPath;
- finalIndexPath = indexPath;
- *stop = YES;
- return;
- }
- if (!finalIndexPath || centerSpacing < finalSpace) {
- finalIndexPath = indexPath;
- finalSpace = centerSpacing;
- }
- }
- }];
- /// if find the playing indexPath.
- if (finalIndexPath) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(finalIndexPath);
- }
- self.zf_shouldPlayIndexPath = finalIndexPath;
- }
- /**
- Find the playing cell while the scrollDirection is horizontal.
- */
- - (void)_findCorrectCellWhenScrollViewDirectionHorizontal:(void (^ __nullable)(NSIndexPath *indexPath))handler {
- if (!self.zf_shouldAutoPlay) return;
- if (self.zf_containerType == ZFPlayerContainerTypeView) return;
-
- NSArray *visiableCells = nil;
- NSIndexPath *indexPath = nil;
- if ([self _isTableView]) {
- UITableView *tableView = (UITableView *)self;
- visiableCells = [tableView visibleCells];
- // First visible cell indexPath
- indexPath = tableView.indexPathsForVisibleRows.firstObject;
- if (self.contentOffset.x <= 0 && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
-
- // Last visible cell indexPath
- indexPath = tableView.indexPathsForVisibleRows.lastObject;
- if (self.contentOffset.x + self.frame.size.width >= self.contentSize.width && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
- } else if ([self _isCollectionView]) {
- UICollectionView *collectionView = (UICollectionView *)self;
- visiableCells = [collectionView visibleCells];
- NSArray *sortedIndexPaths = [collectionView.indexPathsForVisibleItems sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
- return [obj1 compare:obj2];
- }];
-
- visiableCells = [visiableCells sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
- NSIndexPath *path1 = (NSIndexPath *)[collectionView indexPathForCell:obj1];
- NSIndexPath *path2 = (NSIndexPath *)[collectionView indexPathForCell:obj2];
- return [path1 compare:path2];
- }];
-
- // First visible cell indexPath
- indexPath = sortedIndexPaths.firstObject;
- if (self.contentOffset.x <= 0 && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
-
- // Last visible cell indexPath
- indexPath = sortedIndexPaths.lastObject;
- if (self.contentOffset.x + self.frame.size.width >= self.contentSize.width && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
- UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (playerView) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(indexPath);
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- }
- }
-
- NSArray *cells = nil;
- if (self.zf_scrollDirection == ZFPlayerScrollDirectionUp) {
- cells = visiableCells;
- } else {
- cells = [visiableCells reverseObjectEnumerator].allObjects;
- }
-
- /// Mid line.
- CGFloat scrollViewMidX = CGRectGetWidth(self.frame)/2;
- /// The final playing indexPath.
- __block NSIndexPath *finalIndexPath = nil;
- /// The final distance from the center line.
- __block CGFloat finalSpace = 0;
- @weakify(self)
- [cells enumerateObjectsUsingBlock:^(UIView *cell, NSUInteger idx, BOOL * _Nonnull stop) {
- @strongify(self)
- UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
- if (!playerView) return;
- CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
- CGRect rect = [self convertRect:rect1 toView:self.superview];
- /// playerView left to scrollView top space.
- CGFloat leftSpacing = CGRectGetMinX(rect) - CGRectGetMinX(self.frame) - CGRectGetMinX(playerView.frame);
- /// playerView right to scrollView top space.
- CGFloat rightSpacing = CGRectGetMaxX(self.frame) - CGRectGetMaxX(rect) + CGRectGetMinX(playerView.frame);
- CGFloat centerSpacing = ABS(scrollViewMidX - CGRectGetMidX(rect));
- NSIndexPath *indexPath = [self zf_getIndexPathForCell:cell];
-
- /// Play when the video playback section is visible.
- if ((leftSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetWidth(rect)) && (rightSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetWidth(rect))) {
- /// If you have a cell that is playing, stop the traversal.
- if (self.zf_playingIndexPath) {
- indexPath = self.zf_playingIndexPath;
- finalIndexPath = indexPath;
- *stop = YES;
- return;
- }
- if (!finalIndexPath || centerSpacing < finalSpace) {
- finalIndexPath = indexPath;
- finalSpace = centerSpacing;
- }
- }
- }];
- /// if find the playing indexPath.
- if (finalIndexPath) {
- if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
- if (handler) handler(finalIndexPath);
- self.zf_shouldPlayIndexPath = finalIndexPath;
- }
- }
- - (BOOL)_isTableView {
- return [self isKindOfClass:[UITableView class]];
- }
- - (BOOL)_isCollectionView {
- return [self isKindOfClass:[UICollectionView class]];
- }
- #pragma mark - public method
- - (UIView *)zf_getCellForIndexPath:(NSIndexPath *)indexPath {
- if ([self _isTableView]) {
- UITableView *tableView = (UITableView *)self;
- UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- return cell;
- } else if ([self _isCollectionView]) {
- UICollectionView *collectionView = (UICollectionView *)self;
- UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
- return cell;
- }
- return nil;
- }
- - (NSIndexPath *)zf_getIndexPathForCell:(UIView *)cell {
- if ([self _isTableView]) {
- UITableView *tableView = (UITableView *)self;
- NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)cell];
- return indexPath;
- } else if ([self _isCollectionView]) {
- UICollectionView *collectionView = (UICollectionView *)self;
- NSIndexPath *indexPath = [collectionView indexPathForCell:(UICollectionViewCell *)cell];
- return indexPath;
- }
- return nil;
- }
- - (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^ __nullable)(void))completionHandler {
- [self zf_scrollToRowAtIndexPath:indexPath animated:YES completionHandler:completionHandler];
- }
- - (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated completionHandler:(void (^ __nullable)(void))completionHandler {
- [self zf_scrollToRowAtIndexPath:indexPath animateWithDuration:animated ? 0.4 : 0.0 completionHandler:completionHandler];
- }
- /// Scroll to indexPath with animations duration.
- - (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath animateWithDuration:(NSTimeInterval)duration completionHandler:(void (^ __nullable)(void))completionHandler {
- BOOL animated = duration > 0.0;
- if ([self _isTableView]) {
- UITableView *tableView = (UITableView *)self;
- [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:animated];
- } else if ([self _isCollectionView]) {
- UICollectionView *collectionView = (UICollectionView *)self;
- [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionTop animated:animated];
- }
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- if (completionHandler) completionHandler();
- });
- }
- - (void)zf_scrollViewDidEndDecelerating {
- BOOL scrollToScrollStop = !self.tracking && !self.dragging && !self.decelerating;
- if (scrollToScrollStop) {
- [self _scrollViewDidStopScroll];
- }
- }
- - (void)zf_scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate {
- if (!decelerate) {
- BOOL dragToDragStop = self.tracking && !self.dragging && !self.decelerating;
- if (dragToDragStop) {
- [self _scrollViewDidStopScroll];
- }
- }
- }
- - (void)zf_scrollViewDidScrollToTop {
- [self _scrollViewDidStopScroll];
- }
- - (void)zf_scrollViewDidScroll {
- if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
- [self _scrollViewScrollingDirectionVertical];
- } else {
- [self _scrollViewScrollingDirectionHorizontal];
- }
- }
- - (void)zf_scrollViewWillBeginDragging {
- [self _scrollViewBeginDragging];
- }
- #pragma mark - getter
- - (ZFPlayerScrollDirection)zf_scrollDirection {
- return [objc_getAssociatedObject(self, _cmd) integerValue];
- }
- - (ZFPlayerScrollViewDirection)zf_scrollViewDirection {
- return [objc_getAssociatedObject(self, _cmd) integerValue];
- }
- - (CGFloat)zf_lastOffsetY {
- return [objc_getAssociatedObject(self, _cmd) floatValue];
- }
- - (CGFloat)zf_lastOffsetX {
- return [objc_getAssociatedObject(self, _cmd) floatValue];
- }
- #pragma mark - setter
- - (void)setZf_scrollDirection:(ZFPlayerScrollDirection)zf_scrollDirection {
- objc_setAssociatedObject(self, @selector(zf_scrollDirection), @(zf_scrollDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_scrollViewDirection:(ZFPlayerScrollViewDirection)zf_scrollViewDirection {
- objc_setAssociatedObject(self, @selector(zf_scrollViewDirection), @(zf_scrollViewDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_lastOffsetY:(CGFloat)zf_lastOffsetY {
- objc_setAssociatedObject(self, @selector(zf_lastOffsetY), @(zf_lastOffsetY), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_lastOffsetX:(CGFloat)zf_lastOffsetX {
- objc_setAssociatedObject(self, @selector(zf_lastOffsetX), @(zf_lastOffsetX), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- @end
- @implementation UIScrollView (ZFPlayerCannotCalled)
- - (void)zf_filterShouldPlayCellWhileScrolling:(void (^ __nullable)(NSIndexPath *indexPath))handler {
- if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
- [self _findCorrectCellWhenScrollViewDirectionVertical:handler];
- } else {
- [self _findCorrectCellWhenScrollViewDirectionHorizontal:handler];
- }
- }
- - (void)zf_filterShouldPlayCellWhileScrolled:(void (^ __nullable)(NSIndexPath *indexPath))handler {
- if (!self.zf_shouldAutoPlay) return;
- @weakify(self)
- [self zf_filterShouldPlayCellWhileScrolling:^(NSIndexPath *indexPath) {
- @strongify(self)
- /// 如果当前控制器已经消失,直接return
- if (self.zf_viewControllerDisappear) return;
- if ([ZFReachabilityManager sharedManager].isReachableViaWWAN && !self.zf_WWANAutoPlay) {
- /// 移动网络
- self.zf_shouldPlayIndexPath = indexPath;
- return;
- }
- if (handler) handler(indexPath);
- self.zf_playingIndexPath = indexPath;
- }];
- }
- #pragma mark - getter
- - (void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerDisappearingInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerAppearingInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_playerDidAppearInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_playerWillDisappearInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_playerWillAppearInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_playerDidDisappearInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidEndScrollingCallback {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidScrollCallback {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_playerShouldPlayInScrollView {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (CGFloat)zf_playerApperaPercent {
- return [objc_getAssociatedObject(self, _cmd) floatValue];
- }
- - (CGFloat)zf_playerDisapperaPercent {
- return [objc_getAssociatedObject(self, _cmd) floatValue];
- }
- - (BOOL)zf_viewControllerDisappear {
- return [objc_getAssociatedObject(self, _cmd) boolValue];
- }
- - (BOOL)zf_stopPlay {
- return [objc_getAssociatedObject(self, _cmd) boolValue];
- }
- - (BOOL)zf_stopWhileNotVisible {
- return [objc_getAssociatedObject(self, _cmd) boolValue];
- }
- - (NSIndexPath *)zf_playingIndexPath {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (NSIndexPath *)zf_shouldPlayIndexPath {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (NSInteger)zf_containerViewTag {
- return [objc_getAssociatedObject(self, _cmd) integerValue];
- }
- - (BOOL)zf_isWWANAutoPlay {
- return [objc_getAssociatedObject(self, _cmd) boolValue];
- }
- - (BOOL)zf_shouldAutoPlay {
- NSNumber *number = objc_getAssociatedObject(self, _cmd);
- if (number) return number.boolValue;
- self.zf_shouldAutoPlay = YES;
- return YES;
- }
- - (ZFPlayerContainerType)zf_containerType {
- return [objc_getAssociatedObject(self, _cmd) integerValue];
- }
- - (UIView *)zf_containerView {
- return objc_getAssociatedObject(self, _cmd);
- }
- #pragma mark - setter
- - (void)setZf_playerDisappearingInScrollView:(void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerDisappearingInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerDisappearingInScrollView), zf_playerDisappearingInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerAppearingInScrollView:(void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerAppearingInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerAppearingInScrollView), zf_playerAppearingInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerDidAppearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerDidAppearInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerDidAppearInScrollView), zf_playerDidAppearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerWillDisappearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerWillDisappearInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerWillDisappearInScrollView), zf_playerWillDisappearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerWillAppearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerWillAppearInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerWillAppearInScrollView), zf_playerWillAppearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerDidDisappearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerDidDisappearInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerDidDisappearInScrollView), zf_playerDidDisappearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_scrollViewDidEndScrollingCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidEndScrollingCallback {
- objc_setAssociatedObject(self, @selector(zf_scrollViewDidEndScrollingCallback), zf_scrollViewDidEndScrollingCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_scrollViewDidScrollCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidScrollCallback {
- objc_setAssociatedObject(self, @selector(zf_scrollViewDidScrollCallback), zf_scrollViewDidScrollCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerShouldPlayInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerShouldPlayInScrollView {
- objc_setAssociatedObject(self, @selector(zf_playerShouldPlayInScrollView), zf_playerShouldPlayInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerApperaPercent:(CGFloat)zf_playerApperaPercent {
- objc_setAssociatedObject(self, @selector(zf_playerApperaPercent), @(zf_playerApperaPercent), OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_playerDisapperaPercent:(CGFloat)zf_playerDisapperaPercent {
- objc_setAssociatedObject(self, @selector(zf_playerDisapperaPercent), @(zf_playerDisapperaPercent), OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_viewControllerDisappear:(BOOL)zf_viewControllerDisappear {
- objc_setAssociatedObject(self, @selector(zf_viewControllerDisappear), @(zf_viewControllerDisappear), OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_stopPlay:(BOOL)zf_stopPlay {
- objc_setAssociatedObject(self, @selector(zf_stopPlay), @(zf_stopPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_stopWhileNotVisible:(BOOL)zf_stopWhileNotVisible {
- objc_setAssociatedObject(self, @selector(zf_stopWhileNotVisible), @(zf_stopWhileNotVisible), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_playingIndexPath:(NSIndexPath *)zf_playingIndexPath {
- objc_setAssociatedObject(self, @selector(zf_playingIndexPath), zf_playingIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- if (zf_playingIndexPath && [zf_playingIndexPath compare:self.zf_shouldPlayIndexPath] != NSOrderedSame) {
- self.zf_shouldPlayIndexPath = zf_playingIndexPath;
- }
- }
- - (void)setZf_shouldPlayIndexPath:(NSIndexPath *)zf_shouldPlayIndexPath {
- if (self.zf_playerShouldPlayInScrollView) self.zf_playerShouldPlayInScrollView(zf_shouldPlayIndexPath);
- if (self.zf_shouldPlayIndexPathCallback) self.zf_shouldPlayIndexPathCallback(zf_shouldPlayIndexPath);
- objc_setAssociatedObject(self, @selector(zf_shouldPlayIndexPath), zf_shouldPlayIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_containerViewTag:(NSInteger)zf_containerViewTag {
- objc_setAssociatedObject(self, @selector(zf_containerViewTag), @(zf_containerViewTag), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_containerType:(ZFPlayerContainerType)zf_containerType {
- objc_setAssociatedObject(self, @selector(zf_containerType), @(zf_containerType), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_containerView:(UIView *)zf_containerView {
- objc_setAssociatedObject(self, @selector(zf_containerView), zf_containerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_shouldAutoPlay:(BOOL)zf_shouldAutoPlay {
- objc_setAssociatedObject(self, @selector(zf_shouldAutoPlay), @(zf_shouldAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)setZf_WWANAutoPlay:(BOOL)zf_WWANAutoPlay {
- objc_setAssociatedObject(self, @selector(zf_isWWANAutoPlay), @(zf_WWANAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- @end
- @implementation UIScrollView (ZFPlayerDeprecated)
- #pragma mark - getter
- - (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidStopScrollCallback {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void (^)(NSIndexPath * _Nonnull))zf_shouldPlayIndexPathCallback {
- return objc_getAssociatedObject(self, _cmd);
- }
- #pragma mark - setter
- - (void)setZf_scrollViewDidStopScrollCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidStopScrollCallback {
- objc_setAssociatedObject(self, @selector(zf_scrollViewDidStopScrollCallback), zf_scrollViewDidStopScrollCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (void)setZf_shouldPlayIndexPathCallback:(void (^)(NSIndexPath * _Nonnull))zf_shouldPlayIndexPathCallback {
- objc_setAssociatedObject(self, @selector(zf_shouldPlayIndexPathCallback), zf_shouldPlayIndexPathCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- @end
- #pragma clang diagnostic pop
|