DDZFPlayerKit_Private update

This commit is contained in:
DDIsFriend
2023-08-31 16:17:11 +08:00
parent a53ff1b89e
commit efb4113402
155 changed files with 8382 additions and 9399 deletions

View File

@@ -0,0 +1,244 @@
//
// UIScrollView+ZFPlayer.h
// 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 <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/*
* The scroll direction of scrollView.
*/
typedef NS_ENUM(NSUInteger, ZFPlayerScrollDirection) {
ZFPlayerScrollDirectionNone,
ZFPlayerScrollDirectionUp, // Scroll up
ZFPlayerScrollDirectionDown, // Scroll Down
ZFPlayerScrollDirectionLeft, // Scroll left
ZFPlayerScrollDirectionRight // Scroll right
};
/*
* The scrollView direction.
*/
typedef NS_ENUM(NSInteger, ZFPlayerScrollViewDirection) {
ZFPlayerScrollViewDirectionVertical,
ZFPlayerScrollViewDirectionHorizontal
};
/*
* The player container type
*/
typedef NS_ENUM(NSInteger, ZFPlayerContainerType) {
ZFPlayerContainerTypeView,
ZFPlayerContainerTypeCell
};
typedef NS_ENUM(NSInteger , ZFPlayerScrollViewScrollPosition) {
ZFPlayerScrollViewScrollPositionNone,
/// Apply to UITableView and UICollectionViewDirection is vertical scrolling.
ZFPlayerScrollViewScrollPositionTop,
ZFPlayerScrollViewScrollPositionCenteredVertically,
ZFPlayerScrollViewScrollPositionBottom,
/// Only apply to UICollectionViewDirection is horizontal scrolling.
ZFPlayerScrollViewScrollPositionLeft,
ZFPlayerScrollViewScrollPositionCenteredHorizontally,
ZFPlayerScrollViewScrollPositionRight
};
@interface UIScrollView (ZFPlayer)
/// When the ZFPlayerScrollViewDirection is ZFPlayerScrollViewDirectionVertical,the property has value.
@property (nonatomic, readonly) CGFloat zf_lastOffsetY;
/// When the ZFPlayerScrollViewDirection is ZFPlayerScrollViewDirectionHorizontal,the property has value.
@property (nonatomic, readonly) CGFloat zf_lastOffsetX;
/// The scrollView scroll direction, default is ZFPlayerScrollViewDirectionVertical.
@property (nonatomic) ZFPlayerScrollViewDirection zf_scrollViewDirection;
/// The scroll direction of scrollView while scrolling.
/// When the ZFPlayerScrollViewDirection is ZFPlayerScrollViewDirectionVerticalthis value can only be ZFPlayerScrollDirectionUp or ZFPlayerScrollDirectionDown.
/// When the ZFPlayerScrollViewDirection is ZFPlayerScrollViewDirectionHorizontalthis value can only be ZFPlayerScrollDirectionLeft or ZFPlayerScrollDirectionRight.
@property (nonatomic, readonly) ZFPlayerScrollDirection zf_scrollDirection;
/// Get the cell according to indexPath.
- (UIView *)zf_getCellForIndexPath:(NSIndexPath *)indexPath;
/// Get the indexPath for cell.
- (NSIndexPath *)zf_getIndexPathForCell:(UIView *)cell;
/**
Scroll to indexPath with position.
@param indexPath scroll the indexPath.
@param scrollPosition scrollView scroll position.
@param animated animate.
@param completionHandler Scroll completion callback.
*/
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler;
/**
Scroll to indexPath with position.
@param indexPath scroll the indexPath.
@param scrollPosition scrollView scroll position.
@param duration animate duration.
@param completionHandler Scroll completion callback.
*/
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animateDuration:(NSTimeInterval)duration
completionHandler:(void (^ __nullable)(void))completionHandler;
///------------------------------------
/// The following method must be implemented in UIScrollViewDelegate.
///------------------------------------
- (void)zf_scrollViewDidEndDecelerating;
- (void)zf_scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate;
- (void)zf_scrollViewDidScrollToTop;
- (void)zf_scrollViewDidScroll;
- (void)zf_scrollViewWillBeginDragging;
///------------------------------------
/// end
///------------------------------------
@end
@interface UIScrollView (ZFPlayerCannotCalled)
/// The block invoked When the player appearing.
@property (nonatomic, copy, nullable) void(^zf_playerAppearingInScrollView)(NSIndexPath *indexPath, CGFloat playerApperaPercent);
/// The block invoked When the player disappearing.
@property (nonatomic, copy, nullable) void(^zf_playerDisappearingInScrollView)(NSIndexPath *indexPath, CGFloat playerDisapperaPercent);
/// The block invoked When the player will appeared.
@property (nonatomic, copy, nullable) void(^zf_playerWillAppearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player did appeared.
@property (nonatomic, copy, nullable) void(^zf_playerDidAppearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player will disappear.
@property (nonatomic, copy, nullable) void(^zf_playerWillDisappearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player did disappeared.
@property (nonatomic, copy, nullable) void(^zf_playerDidDisappearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player did stop scroll.
@property (nonatomic, copy, nullable) void(^zf_scrollViewDidEndScrollingCallback)(NSIndexPath *indexPath);
/// The block invoked When the player did scroll.
@property (nonatomic, copy, nullable) void(^zf_scrollViewDidScrollCallback)(NSIndexPath *indexPath);
/// The block invoked When the player should play.
@property (nonatomic, copy, nullable) void(^zf_playerShouldPlayInScrollView)(NSIndexPath *indexPath);
/// The current player scroll slides off the screen percent.
/// the property used when the `stopWhileNotVisible` is YES, stop the current playing player.
/// the property used when the `stopWhileNotVisible` is NO, the current playing player add to small container view.
/// 0.0~1.0, defalut is 0.5.
/// 0.0 is the player will disappear.
/// 1.0 is the player did disappear.
@property (nonatomic) CGFloat zf_playerDisapperaPercent;
/// The current player scroll to the screen percent to play the video.
/// 0.0~1.0, defalut is 0.0.
/// 0.0 is the player will appear.
/// 1.0 is the player did appear.
@property (nonatomic) CGFloat zf_playerApperaPercent;
/// The current player controller is disappear, not dealloc
@property (nonatomic) BOOL zf_viewControllerDisappear;
/// Has stopped playing
@property (nonatomic, assign) BOOL zf_stopPlay;
/// The currently playing cell stop playing when the cell has out off the screendefalut is YES.
@property (nonatomic, assign) BOOL zf_stopWhileNotVisible;
/// The indexPath is playing.
@property (nonatomic, nullable) NSIndexPath *zf_playingIndexPath;
/// The indexPath should be play while scrolling.
@property (nonatomic, nullable) NSIndexPath *zf_shouldPlayIndexPath;
/// WWANA networks play automatically,default NO.
@property (nonatomic, getter=zf_isWWANAutoPlay) BOOL zf_WWANAutoPlay;
/// The player should auto player,default is YES.
@property (nonatomic) BOOL zf_shouldAutoPlay;
/// The view tag that the player display in scrollView.
@property (nonatomic) NSInteger zf_containerViewTag;
/// The video contrainerView in normal model.
@property (nonatomic, strong) UIView *zf_containerView;
/// The video contrainerView type.
@property (nonatomic, assign) ZFPlayerContainerType zf_containerType;
/// Filter the cell that should be played when the scroll is stopped (to play when the scroll is stopped).
- (void)zf_filterShouldPlayCellWhileScrolled:(void (^ __nullable)(NSIndexPath *indexPath))handler;
/// Filter the cell that should be played while scrolling (you can use this to filter the highlighted cell).
- (void)zf_filterShouldPlayCellWhileScrolling:(void (^ __nullable)(NSIndexPath *indexPath))handler;
@end
@interface UIScrollView (ZFPlayerDeprecated)
/// The block invoked When the player did stop scroll.
@property (nonatomic, copy, nullable) void(^zf_scrollViewDidStopScrollCallback)(NSIndexPath *indexPath) __attribute__((deprecated("use `ZFPlayerController.zf_scrollViewDidEndScrollingCallback` instead.")));
/// The block invoked When the player should play.
@property (nonatomic, copy, nullable) void(^zf_shouldPlayIndexPathCallback)(NSIndexPath *indexPath) __attribute__((deprecated("use `ZFPlayerController.zf_playerShouldPlayInScrollView` instead.")));
/// Scroll to indexPath position `ZFPlayerScrollViewScrollPositionTop` with animations.
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
completionHandler:(void (^ __nullable)(void))completionHandler __attribute__((deprecated("use `zf_scrollToRowAtIndexPath:atScrollPosition:animated:completionHandler:` instead.")));
/// Scroll to indexPath position `ZFPlayerScrollViewScrollPositionTop` with animations.
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler __attribute__((deprecated("use `zf_scrollToRowAtIndexPath:atScrollPosition:animated:completionHandler:` instead.")));
/// Scroll to indexPath position `ZFPlayerScrollViewScrollPositionTop` with animations.
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
animateWithDuration:(NSTimeInterval)duration
completionHandler:(void (^ __nullable)(void))completionHandler __attribute__((deprecated("use `zf_scrollToRowAtIndexPath:atScrollPosition:animateDuration:completionHandler:` instead.")));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,915 @@
//
// 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 "ZFPlayerConst.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)
#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;
}
/**
Scroll to indexPath with position.
@param indexPath scroll the indexPath.
@param scrollPosition scrollView scroll position.
@param animated animate.
@param completionHandler Scroll completion callback.
*/
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler {
[self zf_scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animateDuration:animated ? 0.4 : 0.0 completionHandler:completionHandler];
}
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animateDuration:(NSTimeInterval)duration
completionHandler:(void (^ __nullable)(void))completionHandler {
BOOL animated = duration > 0.0;
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
UITableViewScrollPosition tableScrollPosition = UITableViewScrollPositionNone;
if (scrollPosition <= ZFPlayerScrollViewScrollPositionBottom) {
tableScrollPosition = (UITableViewScrollPosition)scrollPosition;
}
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:tableScrollPosition animated:animated];
} else if ([self _isCollectionView]) {
UICollectionView *collectionView = (UICollectionView *)self;
if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
UICollectionViewScrollPosition collectionScrollPosition = UICollectionViewScrollPositionNone;
switch (scrollPosition) {
case ZFPlayerScrollViewScrollPositionNone:
collectionScrollPosition = UICollectionViewScrollPositionNone;
break;
case ZFPlayerScrollViewScrollPositionTop:
collectionScrollPosition = UICollectionViewScrollPositionTop;
break;
case ZFPlayerScrollViewScrollPositionCenteredVertically:
collectionScrollPosition = UICollectionViewScrollPositionCenteredVertically;
break;
case ZFPlayerScrollViewScrollPositionBottom:
collectionScrollPosition = UICollectionViewScrollPositionBottom;
break;
default:
break;
}
[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:collectionScrollPosition animated:animated];
} else if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionHorizontal) {
UICollectionViewScrollPosition collectionScrollPosition = UICollectionViewScrollPositionNone;
switch (scrollPosition) {
case ZFPlayerScrollViewScrollPositionNone:
collectionScrollPosition = UICollectionViewScrollPositionNone;
break;
case ZFPlayerScrollViewScrollPositionLeft:
collectionScrollPosition = UICollectionViewScrollPositionLeft;
break;
case ZFPlayerScrollViewScrollPositionCenteredHorizontally:
collectionScrollPosition = UICollectionViewScrollPositionCenteredHorizontally;
break;
case ZFPlayerScrollViewScrollPositionRight:
collectionScrollPosition = UICollectionViewScrollPositionRight;
break;
default:
break;
}
[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:collectionScrollPosition 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 _findCorrectCellWhenScrollViewDirectionVertical:nil];
[self _scrollViewScrollingDirectionVertical];
} else {
[self _findCorrectCellWhenScrollViewDirectionHorizontal:nil];
[self _scrollViewScrollingDirectionHorizontal];
}
}
- (void)zf_scrollViewWillBeginDragging {
[self _scrollViewBeginDragging];
}
#pragma mark - private method
- (void)_scrollViewDidStopScroll {
self.zf_scrollDirection = ZFPlayerScrollDirectionNone;
@zf_weakify(self)
[self zf_filterShouldPlayCellWhileScrolled:^(NSIndexPath * _Nonnull indexPath) {
@zf_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;
if (!self.zf_stopWhileNotVisible) {
/// If you have a cell that is playing, stop the traversal.
if (self.zf_playingIndexPath) {
NSIndexPath *finalIndexPath = self.zf_playingIndexPath;
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(finalIndexPath);
if (handler) handler(finalIndexPath);
self.zf_shouldPlayIndexPath = finalIndexPath;
return;
}
}
NSArray *visiableCells = nil;
NSIndexPath *indexPath = nil;
BOOL isLast = self.contentOffset.y + self.frame.size.height >= self.contentSize.height;
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
visiableCells = [tableView visibleCells];
// First visible cell indexPath
indexPath = tableView.indexPathsForVisibleRows.firstObject;
if ((self.contentOffset.y <= 0 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
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 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
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;
@zf_weakify(self)
[cells enumerateObjectsUsingBlock:^(UIView *cell, NSUInteger idx, BOOL * _Nonnull stop) {
@zf_strongify(self)
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (!playerView || playerView.hidden || playerView.alpha <= 0.01) 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 (!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;
if (!self.zf_stopWhileNotVisible) {
/// If you have a cell that is playing, stop the traversal.
if (self.zf_playingIndexPath) {
NSIndexPath *finalIndexPath = self.zf_playingIndexPath;
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(finalIndexPath);
if (handler) handler(finalIndexPath);
self.zf_shouldPlayIndexPath = finalIndexPath;
return;
}
}
NSArray *visiableCells = nil;
NSIndexPath *indexPath = nil;
BOOL isLast = self.contentOffset.x + self.frame.size.width >= self.contentSize.width;
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
visiableCells = [tableView visibleCells];
// First visible cell indexPath
indexPath = tableView.indexPathsForVisibleRows.firstObject;
if ((self.contentOffset.x <= 0 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
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 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
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;
@zf_weakify(self)
[cells enumerateObjectsUsingBlock:^(UIView *cell, NSUInteger idx, BOOL * _Nonnull stop) {
@zf_strongify(self)
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (!playerView || playerView.hidden || playerView.alpha <= 0.01) 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 (!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 - 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;
@zf_weakify(self)
[self zf_filterShouldPlayCellWhileScrolling:^(NSIndexPath *indexPath) {
@zf_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 {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.boolValue;
self.zf_stopPlay = YES;
return YES;
}
- (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);
}
#pragma mark - method
- (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 {
[self zf_scrollToRowAtIndexPath:indexPath atScrollPosition:ZFPlayerScrollViewScrollPositionTop animateDuration:duration completionHandler:completionHandler];
}
@end
#pragma clang diagnostic pop

View File

@@ -0,0 +1,120 @@
//
// UIViewController+ZFPlayerRotation.m
//
// 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 <UIKit/UIKit.h>
#import <objc/runtime.h>
@implementation UITabBarController (ZFPlayerRotation)
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL selectors[] = {
@selector(selectedIndex)
};
for (NSUInteger 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);
}
}
});
}
- (NSInteger)zf_selectedIndex {
NSInteger index = [self zf_selectedIndex];
if (index > self.viewControllers.count) return 0;
return index;
}
/**
* If the root view of the window is a UINavigationController, you call this Category first, and then UIViewController called.
* All you need to do is revisit the following three methods on a page that supports directions other than portrait.
*/
// Whether automatic screen rotation is supported.
- (BOOL)shouldAutorotate {
return [[self viewControllerRotation] shouldAutorotate];
}
// Which screen directions are supported.
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [[self viewControllerRotation] supportedInterfaceOrientations];
}
// The default screen direction (the current ViewController must be represented by a modal UIViewController (which is not valid with modal navigation) to call this method).
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self viewControllerRotation] preferredInterfaceOrientationForPresentation];
}
// Return ViewController which decide rotationif selected in UITabBarController is UINavigationController,return topViewController
- (UIViewController *)viewControllerRotation {
UIViewController *vc = self.viewControllers[self.selectedIndex];
if ([vc isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)vc;
return nav.topViewController;
} else {
return vc;
}
}
@end
@implementation UINavigationController (ZFPlayerRotation)
/**
* If the root view of the window is a UINavigationController, you call this Category first, and then UIViewController called.
* All you need to do is revisit the following three methods on a page that supports directions other than portrait.
*/
// Whether automatic screen rotation is supported
- (BOOL)shouldAutorotate {
return [self.topViewController shouldAutorotate];
}
// Which screen directions are supported
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
// The default screen direction (the current ViewController must be represented by a modal UIViewController (which is not valid with modal navigation) to call this method).
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return self.topViewController;
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return self.topViewController;
}
@end

View File

@@ -0,0 +1,35 @@
//
// ZFFloatView.h
// 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 <UIKit/UIKit.h>
@interface ZFFloatView : UIView
/// The parent View
@property(nonatomic, weak) UIView *parentView;
/// Safe margins, mainly for those with Navbar and tabbar
@property(nonatomic, assign) UIEdgeInsets safeInsets;
@end

View File

@@ -0,0 +1,85 @@
//
// ZFFloatView.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 "ZFFloatView.h"
@implementation ZFFloatView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initilize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
[self initilize];
}
return self;
}
- (void)initilize {
self.safeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(doMoveAction:)];
[self addGestureRecognizer:panGestureRecognizer];
}
- (void)setParentView:(UIView *)parentView {
_parentView = parentView;
[parentView addSubview:self];
}
#pragma mark - Action
- (void)doMoveAction:(UIPanGestureRecognizer *)recognizer {
/// The position where the gesture is moving in the self.view.
CGPoint translation = [recognizer translationInView:self.parentView];
CGPoint newCenter = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
// Limited screen range:
// Top margin limit.
newCenter.y = MAX(recognizer.view.frame.size.height/2 + self.safeInsets.top, newCenter.y);
// Bottom margin limit.
newCenter.y = MIN(self.parentView.frame.size.height - self.safeInsets.bottom - recognizer.view.frame.size.height/2, newCenter.y);
// Left margin limit.
newCenter.x = MAX(recognizer.view.frame.size.width/2, newCenter.x);
// Right margin limit.
newCenter.x = MIN(self.parentView.frame.size.width - recognizer.view.frame.size.width/2,newCenter.x);
// Set the center point.
recognizer.view.center = newCenter;
// Set the gesture coordinates to 0, otherwise it will add up.
[recognizer setTranslation:CGPointZero inView:self.parentView];
}
@end

View File

@@ -0,0 +1,40 @@
//
// 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 <Foundation/Foundation.h>
@interface ZFKVOController : NSObject
- (instancetype)initWithTarget:(NSObject *)target;
- (void)safelyAddObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
- (void)safelyRemoveObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
- (void)safelyRemoveAllObservers;
@end

View File

@@ -0,0 +1,130 @@
//
// 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 "ZFKVOController.h"
@interface ZFKVOEntry : NSObject
@property (nonatomic, weak) NSObject *observer;
@property (nonatomic, copy) NSString *keyPath;
@end
@implementation ZFKVOEntry
@end
@interface ZFKVOController ()
@property (nonatomic, weak) NSObject *target;
@property (nonatomic, strong) NSMutableArray *observerArray;
@end
@implementation ZFKVOController
- (instancetype)initWithTarget:(NSObject *)target {
self = [super init];
if (self) {
_target = target;
_observerArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)safelyAddObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context {
if (_target == nil) return;
NSInteger indexEntry = [self indexEntryOfObserver:observer forKeyPath:keyPath];
if (indexEntry != NSNotFound) {
// duplicated register
NSLog(@"duplicated observer");
} else {
@try {
[_target addObserver:observer
forKeyPath:keyPath
options:options
context:context];
ZFKVOEntry *entry = [[ZFKVOEntry alloc] init];
entry.observer = observer;
entry.keyPath = keyPath;
[_observerArray addObject:entry];
} @catch (NSException *e) {
NSLog(@"ZFKVO: failed to add observer for %@\n", keyPath);
}
}
}
- (void)safelyRemoveObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath {
if (_target == nil) return;
NSInteger indexEntry = [self indexEntryOfObserver:observer forKeyPath:keyPath];
if (indexEntry == NSNotFound) {
// duplicated register
NSLog(@"duplicated observer");
} else {
[_observerArray removeObjectAtIndex:indexEntry];
@try {
[_target removeObserver:observer
forKeyPath:keyPath];
} @catch (NSException *e) {
NSLog(@"ZFKVO: failed to remove observer for %@\n", keyPath);
}
}
}
- (void)safelyRemoveAllObservers {
if (_target == nil) return;
[_observerArray enumerateObjectsUsingBlock:^(ZFKVOEntry *entry, NSUInteger idx, BOOL *stop) {
if (entry == nil) return;
NSObject *observer = entry.observer;
if (observer == nil) return;
@try {
[_target removeObserver:observer
forKeyPath:entry.keyPath];
} @catch (NSException *e) {
NSLog(@"ZFKVO: failed to remove observer for %@\n", entry.keyPath);
}
}];
[_observerArray removeAllObjects];
}
- (NSInteger)indexEntryOfObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath {
__block NSInteger foundIndex = NSNotFound;
[_observerArray enumerateObjectsUsingBlock:^(ZFKVOEntry *entry, NSUInteger idx, BOOL *stop) {
if (entry.observer == observer &&
[entry.keyPath isEqualToString:keyPath]) {
foundIndex = idx;
*stop = YES;
}
}];
return foundIndex;
}
@end

View File

@@ -0,0 +1,65 @@
//
// ZFFullscreenViewController.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( 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 <UIKit/UIKit.h>
@class ZFLandscapeViewController;
NS_ASSUME_NONNULL_BEGIN
@protocol ZFLandscapeViewControllerDelegate <NSObject>
- (BOOL)ls_shouldAutorotate;
- (void)ls_willRotateToOrientation:(UIInterfaceOrientation)orientation;
- (void)ls_didRotateFromOrientation:(UIInterfaceOrientation)orientation;
- (CGRect)ls_targetRect;
@end
@interface ZFLandscapeViewController : UIViewController
@property (nonatomic, weak) UIView *contentView;
@property (nonatomic, weak) UIView *containerView;
@property (nonatomic, assign) CGRect targetRect;
@property (nonatomic, weak, nullable) id<ZFLandscapeViewControllerDelegate> delegate;
@property (nonatomic, readonly) BOOL isFullscreen;
@property (nonatomic, getter=isRotating) BOOL rotating;
@property (nonatomic, assign) BOOL disableAnimations;
@property (nonatomic, assign) BOOL statusBarHidden;
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle statusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation statusBarAnimation;
@property (nonatomic, copy) void(^rotatingCompleted)(void);
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,140 @@
//
// ZFFullScreenViewController.m
// ZFPlayer
//
// Copyright (c) 2020 ( 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 "ZFLandscapeViewController.h"
@interface ZFLandscapeViewController ()
@property (nonatomic, assign) UIInterfaceOrientation currentOrientation;
@end
@implementation ZFLandscapeViewController
- (instancetype)init {
self = [super init];
if (self) {
_currentOrientation = UIInterfaceOrientationPortrait;
_statusBarStyle = UIStatusBarStyleLightContent;
_statusBarAnimation = UIStatusBarAnimationSlide;
}
return self;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
self.rotating = YES;
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
if (!UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
return;
}
UIInterfaceOrientation newOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
UIInterfaceOrientation oldOrientation = _currentOrientation;
if (UIInterfaceOrientationIsLandscape(newOrientation)) {
if (self.contentView.superview != self.view) {
[self.view addSubview:self.contentView];
}
}
if (oldOrientation == UIInterfaceOrientationPortrait) {
self.contentView.frame = [self.delegate ls_targetRect];
[self.contentView layoutIfNeeded];
}
self.currentOrientation = newOrientation;
[self.delegate ls_willRotateToOrientation:self.currentOrientation];
BOOL isFullscreen = size.width > size.height;
if (self.disableAnimations) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
}
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if (isFullscreen) {
self.contentView.frame = CGRectMake(0, 0, size.width, size.height);
} else {
self.contentView.frame = [self.delegate ls_targetRect];
}
[self.contentView layoutIfNeeded];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if (self.disableAnimations) {
[CATransaction commit];
}
[self.delegate ls_didRotateFromOrientation:self.currentOrientation];
if (!isFullscreen) {
self.contentView.frame = self.containerView.bounds;
[self.contentView layoutIfNeeded];
}
self.disableAnimations = NO;
self.rotating = NO;
}];
}
- (BOOL)isFullscreen {
return UIInterfaceOrientationIsLandscape(_currentOrientation);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate {
return [self.delegate ls_shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (UIInterfaceOrientationIsLandscape(currentOrientation)) {
return UIInterfaceOrientationMaskLandscape;
}
return UIInterfaceOrientationMaskAll;
}
- (BOOL)prefersHomeIndicatorAutoHidden {
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (UIInterfaceOrientationIsLandscape(currentOrientation)) {
return YES;
}
return NO;
}
- (BOOL)prefersStatusBarHidden {
return self.statusBarHidden;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return self.statusBarAnimation;
}
- (void)setRotating:(BOOL)rotating {
_rotating = rotating;
if (!rotating && self.rotatingCompleted) {
self.rotatingCompleted();
}
}
@end

View File

@@ -0,0 +1,36 @@
//
// ZFLandScaprWindow.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( 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 <UIKit/UIKit.h>
#import "ZFLandscapeViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeWindow : UIWindow
@property (nonatomic, strong, readonly) ZFLandscapeViewController *landscapeViewController;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,75 @@
//
// ZFLandScaprWindow.m
// ZFPlayer
//
// Copyright (c) 2020 ( 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 "ZFLandscapeWindow.h"
@implementation ZFLandscapeWindow
@dynamic rootViewController;
- (void)setBackgroundColor:(nullable UIColor *)backgroundColor {}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.windowLevel = UIWindowLevelNormal;
_landscapeViewController = [[ZFLandscapeViewController alloc] init];
self.rootViewController = _landscapeViewController;
if (@available(iOS 13.0, *)) {
if (self.windowScene == nil) {
self.windowScene = UIApplication.sharedApplication.keyWindow.windowScene;
}
}
self.hidden = YES;
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *_Nullable)event {
return YES;
}
- (void)layoutSubviews {
[super layoutSubviews];
static CGRect bounds;
if (!CGRectEqualToRect(bounds, self.bounds)) {
UIView *superview = self;
if (@available(iOS 13.0, *)) {
superview = self.subviews.firstObject;
}
[UIView performWithoutAnimation:^{
for (UIView *view in superview.subviews) {
if (view != self.rootViewController.view && [view isMemberOfClass:UIView.class]) {
view.backgroundColor = UIColor.clearColor;
for (UIView *subview in view.subviews) {
subview.backgroundColor = UIColor.clearColor;
}
}
}
}];
}
bounds = self.bounds;
self.rootViewController.view.frame = bounds;
}
@end

View File

@@ -0,0 +1,170 @@
//
// ZFOrentationObserver.h
// 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 <UIKit/UIKit.h>
#import "ZFPlayerView.h"
NS_ASSUME_NONNULL_BEGIN
/// Full screen mode
typedef NS_ENUM(NSUInteger, ZFFullScreenMode) {
ZFFullScreenModeAutomatic, // Determine full screen mode automatically
ZFFullScreenModeLandscape, // Landscape full screen mode
ZFFullScreenModePortrait // Portrait full screen Model
};
/// Portrait full screen mode.
typedef NS_ENUM(NSUInteger, ZFPortraitFullScreenMode) {
ZFPortraitFullScreenModeScaleToFill, // Full fill
ZFPortraitFullScreenModeScaleAspectFit // contents scaled to fit with fixed aspect. remainder is transparent
};
/// Player view mode
typedef NS_ENUM(NSUInteger, ZFRotateType) {
ZFRotateTypeNormal, // Normal
ZFRotateTypeCell // Cell
};
/**
Rotation of support direction
*/
typedef NS_OPTIONS(NSUInteger, ZFInterfaceOrientationMask) {
ZFInterfaceOrientationMaskPortrait = (1 << 0),
ZFInterfaceOrientationMaskLandscapeLeft = (1 << 1),
ZFInterfaceOrientationMaskLandscapeRight = (1 << 2),
ZFInterfaceOrientationMaskPortraitUpsideDown = (1 << 3),
ZFInterfaceOrientationMaskLandscape = (ZFInterfaceOrientationMaskLandscapeLeft | ZFInterfaceOrientationMaskLandscapeRight),
ZFInterfaceOrientationMaskAll = (ZFInterfaceOrientationMaskPortrait | ZFInterfaceOrientationMaskLandscape | ZFInterfaceOrientationMaskPortraitUpsideDown),
ZFInterfaceOrientationMaskAllButUpsideDown = (ZFInterfaceOrientationMaskPortrait | ZFInterfaceOrientationMaskLandscape),
};
/// This enumeration lists some of the gesture types that the player has by default.
typedef NS_OPTIONS(NSUInteger, ZFDisablePortraitGestureTypes) {
ZFDisablePortraitGestureTypesNone = 0,
ZFDisablePortraitGestureTypesTap = 1 << 0,
ZFDisablePortraitGestureTypesPan = 1 << 1,
ZFDisablePortraitGestureTypesAll = (ZFDisablePortraitGestureTypesTap | ZFDisablePortraitGestureTypesPan)
};
@protocol ZFPortraitOrientationDelegate <NSObject>
- (void)zf_orientationWillChange:(BOOL)isFullScreen;
- (void)zf_orientationDidChanged:(BOOL)isFullScreen;
- (void)zf_interationState:(BOOL)isDragging;
@end
@interface ZFOrientationObserver : NSObject
/// update the rotateView and containerView.
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView;
/// list play
- (void)updateRotateView:(ZFPlayerView *)rotateView
rotateViewAtCell:(UIView *)cell
playerViewTag:(NSInteger)playerViewTag;
/// Container view of a full screen state player.
@property (nonatomic, strong) UIView *fullScreenContainerView;
/// Container view of a small screen state player.
@property (nonatomic, weak) UIView *containerView;
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(ZFOrientationObserver *observer, BOOL isFullScreen);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(ZFOrientationObserver *observer, BOOL isFullScreen);
/// Full screen mode, the default landscape into full screen
@property (nonatomic) ZFFullScreenMode fullScreenMode;
@property (nonatomic, assign) ZFPortraitFullScreenMode portraitFullScreenMode;
/// rotate duration, default is 0.30
@property (nonatomic) NSTimeInterval duration;
/// If the full screen.
@property (nonatomic, readonly, getter=isFullScreen) BOOL fullScreen;
/// Lock screen orientation
@property (nonatomic, getter=isLockedScreen) BOOL lockedScreen;
/// The fullscreen statusbar hidden.
@property (nonatomic, assign) BOOL fullScreenStatusBarHidden;
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle fullScreenStatusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation fullScreenStatusBarAnimation;
@property (nonatomic, assign) CGSize presentationSize;
/// default is ZFDisablePortraitGestureTypesAll.
@property (nonatomic, assign) ZFDisablePortraitGestureTypes disablePortraitGestureTypes;
/// The current orientation of the player.
/// Default is UIInterfaceOrientationPortrait.
@property (nonatomic, readonly) UIInterfaceOrientation currentOrientation;
/// Whether allow the video orientation rotate.
/// default is YES.
@property (nonatomic, assign) BOOL allowOrientationRotation;
/// The support Interface Orientation,default is ZFInterfaceOrientationMaskAllButUpsideDown
@property (nonatomic, assign) ZFInterfaceOrientationMask supportInterfaceOrientation;
/// Add the device orientation observer.
- (void)addDeviceOrientationObserver;
/// Remove the device orientation observer.
- (void)removeDeviceOrientationObserver;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/// FullScreen mode is determined by ZFFullScreenMode.
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
/// FullScreen mode is determined by ZFFullScreenMode.
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,551 @@
// ZFOrentationObserver.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 "ZFOrientationObserver.h"
#import "ZFLandscapeWindow.h"
#import "ZFPortraitViewController.h"
#import "ZFPlayerConst.h"
#import <objc/runtime.h>
@interface UIWindow (CurrentViewController)
/*!
@method currentViewController
@return Returns the topViewController in stack of topMostController.
*/
+ (UIViewController*)zf_currentViewController;
@end
@implementation UIWindow (CurrentViewController)
+ (UIViewController*)zf_currentViewController; {
__block UIWindow *window;
if (@available(iOS 13, *)) {
[[UIApplication sharedApplication].connectedScenes enumerateObjectsUsingBlock:^(UIScene * _Nonnull scene, BOOL * _Nonnull scenesStop) {
if ([scene isKindOfClass: [UIWindowScene class]]) {
UIWindowScene * windowScene = (UIWindowScene *)scene;
[windowScene.windows enumerateObjectsUsingBlock:^(UIWindow * _Nonnull windowTemp, NSUInteger idx, BOOL * _Nonnull windowStop) {
if (windowTemp.isKeyWindow) {
window = windowTemp;
*windowStop = YES;
*scenesStop = YES;
}
}];
}
}];
} else {
window = [[UIApplication sharedApplication].delegate window];
}
UIViewController *topViewController = [window rootViewController];
while (true) {
if (topViewController.presentedViewController) {
topViewController = topViewController.presentedViewController;
} else if ([topViewController isKindOfClass:[UINavigationController class]] && [(UINavigationController *)topViewController topViewController]) {
topViewController = [(UINavigationController *)topViewController topViewController];
} else if ([topViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tab = (UITabBarController *)topViewController;
topViewController = tab.selectedViewController;
} else {
break;
}
}
return topViewController;
}
@end
@interface ZFOrientationObserver () <ZFLandscapeViewControllerDelegate>
@property (nonatomic, weak) ZFPlayerView *view;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@property (nonatomic, strong) UIView *cell;
@property (nonatomic, assign) NSInteger playerViewTag;
@property (nonatomic, assign) ZFRotateType rotateType;
@property (nonatomic, strong) UIWindow *previousKeyWindow;
@property (nonatomic, strong) ZFLandscapeWindow *window;
@property (nonatomic, readonly, getter=isRotating) BOOL rotating;
@property (nonatomic, strong) ZFPortraitViewController *portraitViewController;
/// current device orientation observer is activie.
@property (nonatomic, assign) BOOL activeDeviceObserver;
/// Force Rotaion, default NO.
@property (nonatomic, assign) BOOL forceRotaion;
@end
@implementation ZFOrientationObserver
@synthesize presentationSize = _presentationSize;
- (instancetype)init {
self = [super init];
if (self) {
_duration = 0.30;
_fullScreenMode = ZFFullScreenModeLandscape;
_supportInterfaceOrientation = ZFInterfaceOrientationMaskAllButUpsideDown;
_allowOrientationRotation = YES;
_rotateType = ZFRotateTypeNormal;
_currentOrientation = UIInterfaceOrientationPortrait;
_portraitFullScreenMode = ZFPortraitFullScreenModeScaleToFill;
_disablePortraitGestureTypes = ZFDisablePortraitGestureTypesAll;
}
return self;
}
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView {
self.rotateType = ZFRotateTypeNormal;
self.view = rotateView;
self.containerView = containerView;
}
- (void)updateRotateView:(ZFPlayerView *)rotateView rotateViewAtCell:(UIView *)cell playerViewTag:(NSInteger)playerViewTag {
self.rotateType = ZFRotateTypeCell;
self.view = rotateView;
self.cell = cell;
self.playerViewTag = playerViewTag;
}
- (void)dealloc {
[self removeDeviceOrientationObserver];
}
- (void)addDeviceOrientationObserver {
if (self.allowOrientationRotation) {
self.activeDeviceObserver = YES;
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeviceOrientationChange) name:UIDeviceOrientationDidChangeNotification object:nil];
}
}
- (void)removeDeviceOrientationObserver {
self.activeDeviceObserver = NO;
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)handleDeviceOrientationChange {
if (self.fullScreenMode == ZFFullScreenModePortrait || !self.allowOrientationRotation) return;
if (!UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
return;
}
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
// Determine that if the current direction is the same as the direction you want to rotate, do nothing
if (currentOrientation == _currentOrientation) return;
_currentOrientation = currentOrientation;
if (_currentOrientation == UIInterfaceOrientationPortraitUpsideDown) return;
switch (currentOrientation) {
case UIInterfaceOrientationPortrait: {
if ([self _isSupportedPortrait]) {
[self rotateToOrientation:UIInterfaceOrientationPortrait animated:YES];
}
}
break;
case UIInterfaceOrientationLandscapeLeft: {
if ([self _isSupportedLandscapeLeft]) {
[self rotateToOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];
}
}
break;
case UIInterfaceOrientationLandscapeRight: {
if ([self _isSupportedLandscapeRight]) {
[self rotateToOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
}
}
break;
default: break;
}
}
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation {
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
UIInterfaceOrientation val = orientation;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
#pragma mark - public
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
[self rotateToOrientation:orientation animated:animated completion:nil];
}
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion {
if (self.fullScreenMode == ZFFullScreenModePortrait) return;
_currentOrientation = orientation;
self.forceRotaion = YES;
if (UIInterfaceOrientationIsLandscape(orientation)) {
if (!self.fullScreen) {
UIView *containerView = nil;
if (self.rotateType == ZFRotateTypeCell) {
containerView = [self.cell viewWithTag:self.playerViewTag];
} else {
containerView = self.containerView;
}
CGRect targetRect = [self.view convertRect:self.view.frame toView:containerView.window];
if (!self.window) {
self.window = [ZFLandscapeWindow new];
self.window.landscapeViewController.delegate = self;
if (@available(iOS 9.0, *)) {
[self.window.rootViewController loadViewIfNeeded];
} else {
[self.window.rootViewController view];
}
}
self.window.landscapeViewController.targetRect = targetRect;
self.window.landscapeViewController.contentView = self.view;
self.window.landscapeViewController.containerView = self.containerView;
self.fullScreen = YES;
}
if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
} else {
self.fullScreen = NO;
}
self.window.landscapeViewController.disableAnimations = !animated;
@zf_weakify(self)
self.window.landscapeViewController.rotatingCompleted = ^{
@zf_strongify(self)
self.forceRotaion = NO;
if (completion) completion();
};
[self interfaceOrientation:UIInterfaceOrientationUnknown];
[self interfaceOrientation:orientation];
}
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
[self enterPortraitFullScreen:fullScreen animated:animated completion:nil];
}
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion {
self.fullScreen = fullScreen;
if (fullScreen) {
self.portraitViewController.contentView = self.view;
self.portraitViewController.containerView = self.containerView;
self.portraitViewController.duration = self.duration;
if (self.portraitFullScreenMode == ZFPortraitFullScreenModeScaleAspectFit) {
self.portraitViewController.presentationSize = self.presentationSize;
} else if (self.portraitFullScreenMode == ZFPortraitFullScreenModeScaleToFill) {
self.portraitViewController.presentationSize = CGSizeMake(ZFPlayerScreenWidth, ZFPlayerScreenHeight);
}
self.portraitViewController.fullScreenAnimation = animated;
[[UIWindow zf_currentViewController] presentViewController:self.portraitViewController animated:animated completion:^{
if (completion) completion();
}];
} else {
self.portraitViewController.fullScreenAnimation = animated;
[self.portraitViewController dismissViewControllerAnimated:animated completion:^{
if (completion) completion();
}];
}
}
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
[self enterFullScreen:fullScreen animated:animated];
}
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion {
if (self.fullScreenMode == ZFFullScreenModePortrait) {
[self enterPortraitFullScreen:fullScreen animated:animated completion:completion];
} else {
UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
orientation = fullScreen? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait;
[self rotateToOrientation:orientation animated:animated completion:completion];
}
}
#pragma mark - private
/// is support portrait
- (BOOL)_isSupportedPortrait {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait;
}
/// is support landscapeLeft
- (BOOL)_isSupportedLandscapeLeft {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeLeft;
}
/// is support landscapeRight
- (BOOL)_isSupportedLandscapeRight {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeRight;
}
- (BOOL)_isSupported:(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait;
case UIInterfaceOrientationLandscapeLeft:
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeRight;
default:
return NO;
}
return NO;
}
- (void)_rotationToLandscapeOrientation:(UIInterfaceOrientation)orientation {
if (UIInterfaceOrientationIsLandscape(orientation)) {
UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow;
if (keyWindow != self.window && self.previousKeyWindow != keyWindow) {
self.previousKeyWindow = UIApplication.sharedApplication.keyWindow;
}
if (!self.window.isKeyWindow) {
self.window.hidden = NO;
[self.window makeKeyAndVisible];
}
}
}
- (void)_rotationToPortraitOrientation:(UIInterfaceOrientation)orientation {
if (orientation == UIInterfaceOrientationPortrait && !self.window.hidden) {
UIView *containerView = nil;
if (self.rotateType == ZFRotateTypeCell) {
containerView = [self.cell viewWithTag:self.playerViewTag];
} else {
containerView = self.containerView;
}
UIView *snapshot = [self.view snapshotViewAfterScreenUpdates:NO];
snapshot.frame = containerView.bounds;
[containerView addSubview:snapshot];
[self performSelector:@selector(_contentViewAdd:) onThread:NSThread.mainThread withObject:containerView waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
[self performSelector:@selector(_makeKeyAndVisible:) onThread:NSThread.mainThread withObject:snapshot waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
}
}
- (void)_contentViewAdd:(UIView *)containerView {
[containerView addSubview:self.view];
self.view.frame = containerView.bounds;
[self.view layoutIfNeeded];
}
- (void)_makeKeyAndVisible:(UIView *)snapshot {
[snapshot removeFromSuperview];
UIWindow *previousKeyWindow = self.previousKeyWindow ?: UIApplication.sharedApplication.windows.firstObject;
[previousKeyWindow makeKeyAndVisible];
self.previousKeyWindow = nil;
self.window.hidden = YES;
}
#pragma mark - ZFLandscapeViewControllerDelegate
- (BOOL)ls_shouldAutorotate {
if (self.fullScreenMode == ZFFullScreenModePortrait) {
return NO;
}
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (![self _isSupported:currentOrientation]) {
return NO;
}
if (self.forceRotaion) {
[self _rotationToLandscapeOrientation:currentOrientation];
return YES;
}
if (!self.activeDeviceObserver) {
return NO;
}
[self _rotationToLandscapeOrientation:currentOrientation];
return YES;
}
- (void)ls_willRotateToOrientation:(UIInterfaceOrientation)orientation {
self.fullScreen = UIInterfaceOrientationIsLandscape(orientation);
if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
}
- (void)ls_didRotateFromOrientation:(UIInterfaceOrientation)orientation {
if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
if (!self.isFullScreen) {
[self _rotationToPortraitOrientation:UIInterfaceOrientationPortrait];
}
}
- (CGRect)ls_targetRect {
UIView *containerView = nil;
if (self.rotateType == ZFRotateTypeCell) {
containerView = [self.cell viewWithTag:self.playerViewTag];
} else {
containerView = self.containerView;
}
CGRect targetRect = [containerView convertRect:containerView.bounds toView:containerView.window];
return targetRect;
}
#pragma mark - getter
- (ZFPortraitViewController *)portraitViewController {
if (!_portraitViewController) {
@zf_weakify(self)
_portraitViewController = [[ZFPortraitViewController alloc] init];
if (@available(iOS 9.0, *)) {
[_portraitViewController loadViewIfNeeded];
} else {
[_portraitViewController view];
}
_portraitViewController.orientationWillChange = ^(BOOL isFullScreen) {
@zf_strongify(self)
self.fullScreen = isFullScreen;
if (self.orientationWillChange) self.orientationWillChange(self, isFullScreen);
};
_portraitViewController.orientationDidChanged = ^(BOOL isFullScreen) {
@zf_strongify(self)
self.fullScreen = isFullScreen;
if (self.orientationDidChanged) self.orientationDidChanged(self, isFullScreen);
};
}
return _portraitViewController;
}
#pragma mark - setter
- (void)setLockedScreen:(BOOL)lockedScreen {
_lockedScreen = lockedScreen;
if (lockedScreen) {
[self removeDeviceOrientationObserver];
} else {
[self addDeviceOrientationObserver];
}
}
- (UIView *)fullScreenContainerView {
if (self.fullScreenMode == ZFFullScreenModeLandscape) {
return self.window.landscapeViewController.view;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
return self.portraitViewController.view;
}
return nil;
}
- (void)setFullScreen:(BOOL)fullScreen {
_fullScreen = fullScreen;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
[UIViewController attemptRotationToDeviceOrientation];
}
- (void)setFullScreenStatusBarHidden:(BOOL)fullScreenStatusBarHidden {
_fullScreenStatusBarHidden = fullScreenStatusBarHidden;
if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.statusBarHidden = fullScreenStatusBarHidden;
[self.portraitViewController setNeedsStatusBarAppearanceUpdate];
} else if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.statusBarHidden = fullScreenStatusBarHidden;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
}
}
- (void)setFullScreenStatusBarStyle:(UIStatusBarStyle)fullScreenStatusBarStyle {
_fullScreenStatusBarStyle = fullScreenStatusBarStyle;
if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.statusBarStyle = fullScreenStatusBarStyle;
[self.portraitViewController setNeedsStatusBarAppearanceUpdate];
} else if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.statusBarStyle = fullScreenStatusBarStyle;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
}
}
- (void)setFullScreenStatusBarAnimation:(UIStatusBarAnimation)fullScreenStatusBarAnimation {
_fullScreenStatusBarAnimation = fullScreenStatusBarAnimation;
if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.statusBarAnimation = fullScreenStatusBarAnimation;
[self.portraitViewController setNeedsStatusBarAppearanceUpdate];
} else if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.statusBarAnimation = fullScreenStatusBarAnimation;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
}
}
- (void)setDisablePortraitGestureTypes:(ZFDisablePortraitGestureTypes)disablePortraitGestureTypes {
_disablePortraitGestureTypes = disablePortraitGestureTypes;
self.portraitViewController.disablePortraitGestureTypes = disablePortraitGestureTypes;
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
if (self.fullScreenMode == ZFFullScreenModePortrait && self.portraitFullScreenMode == ZFPortraitFullScreenModeScaleAspectFit) {
self.portraitViewController.presentationSize = presentationSize;
}
}
- (void)setView:(ZFPlayerView *)view {
if (view == _view) {
return;
}
_view = view;
if (self.fullScreenMode == ZFFullScreenModeLandscape && self.window) {
self.window.landscapeViewController.contentView = view;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.contentView = view;
}
}
- (void)setContainerView:(UIView *)containerView {
if (containerView == _containerView) {
return;
}
_containerView = containerView;
if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.containerView = containerView;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.containerView = containerView;
}
}
- (void)setAllowOrientationRotation:(BOOL)allowOrientationRotation {
_allowOrientationRotation = allowOrientationRotation;
if (allowOrientationRotation) {
[self addDeviceOrientationObserver];
} else {
[self removeDeviceOrientationObserver];
}
}
@end

View File

@@ -0,0 +1,47 @@
//
// ZFPersentInteractiveTransition.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( 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 <UIKit/UIKit.h>
#import "ZFOrientationObserver.h"
@interface ZFPersentInteractiveTransition : UIPercentDrivenInteractiveTransition
@property (nonatomic, weak) id<ZFPortraitOrientationDelegate> delagate;
@property (nonatomic, assign) BOOL interation;
/// default is ZFDisablePortraitGestureTypesNone.
@property (nonatomic, assign) ZFDisablePortraitGestureTypes disablePortraitGestureTypes;
@property (nonatomic, assign) BOOL fullScreenAnimation;
@property (nonatomic, assign) CGRect contentFullScreenRect;
@property (nonatomic, weak) UIViewController *viewController;
- (void)updateContentView:(UIView *)contenView
containerView:(UIView *)containerView;
@end

View File

@@ -0,0 +1,296 @@
//
// ZFPersentInteractiveTransition.m
// ZFPlayer
//
// Copyright (c) 2020 ( 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 "ZFPersentInteractiveTransition.h"
@interface ZFPersentInteractiveTransition () <UIGestureRecognizerDelegate>
@property (nonatomic, weak) id<UIViewControllerContextTransitioning> transitionContext;
@property (nonatomic, strong) UIView *bgView;
@property (nonatomic, assign) CGPoint transitionImgViewCenter;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, assign) CGFloat scrollViewZoomScale;
@property (nonatomic, assign) CGSize scrollViewContentSize;
@property (nonatomic, assign) CGPoint scrollViewContentOffset;
@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;
@property (nonatomic, assign) BOOL isDragging;
@end
@implementation ZFPersentInteractiveTransition
- (void)updateContentView:(UIView *)contenView
containerView:(UIView *)containerView {
self.contentView = contenView;
self.containerView = containerView;
}
- (void)removeGestureToView:(UIView *)view {
[view removeGestureRecognizer:self.tapGesture];
[view removeGestureRecognizer:self.panGesture];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([otherGestureRecognizer.view isKindOfClass:[UICollectionView class]]) {
return NO;
}
if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (UIScrollView *)otherGestureRecognizer.view;
if (scrollView.contentOffset.y <= 0 && !scrollView.zooming) {
return YES;
}
}
if ([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
return NO;
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
ZFDisablePortraitGestureTypes type = ZFDisablePortraitGestureTypesNone;
if (gestureRecognizer == self.tapGesture) type = ZFDisablePortraitGestureTypesTap;
else if (gestureRecognizer == self.panGesture) type = ZFDisablePortraitGestureTypesPan;
else return NO;
switch (type) {
case ZFDisablePortraitGestureTypesTap: {
if (self.disablePortraitGestureTypes & ZFDisablePortraitGestureTypesTap) {
return NO;
}
}
break;
case ZFDisablePortraitGestureTypesPan: {
if (self.disablePortraitGestureTypes & ZFDisablePortraitGestureTypesPan) {
return NO;
}
}
break;
default:
break;
}
return YES;
}
- (void)tapGestureAction {
[self.viewController dismissViewControllerAnimated:self.fullScreenAnimation completion:nil];
}
- (void)gestureRecognizeDidUpdate:(UIPanGestureRecognizer *)gestureRecognizer {
CGFloat scale = 0;
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
scale = translation.y / ((gestureRecognizer.view.frame.size.height - 50) / 2);
if (scale > 1.f) {
scale = 1.f;
}
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan: {
if (scale < 0) return;
self.interation = YES;
[self.viewController dismissViewControllerAnimated:self.fullScreenAnimation completion:nil];
}
break;
case UIGestureRecognizerStateChanged: {
if (self.interation) {
if (scale < 0.f) {
scale = 0.f;
}
CGFloat imageViewScale = 1 - scale * 0.5;
if (imageViewScale < 0.4) {
imageViewScale = 0.4;
}
self.contentView.center = CGPointMake(self.transitionImgViewCenter.x + translation.x, self.transitionImgViewCenter.y + translation.y);
self.contentView.transform = CGAffineTransformMakeScale(imageViewScale, imageViewScale);
[self updateInterPercent:imageViewScale];
[self updateInteractiveTransition:scale];
}
}
break;
case UIGestureRecognizerStateEnded: {
if (self.interation) {
if (scale < 0.f) {
scale = 0.f;
}
self.interation = NO;
if (scale < 0.15f) {
[self cancelInteractiveTransition];
[self interPercentCancel];
} else {
[self finishInteractiveTransition];
[self interPercentFinish];
}
}
}
break;
default: {
if (self.interation) {
self.interation = NO;
[self cancelInteractiveTransition];
[self interPercentCancel];
}
}
break;
}
}
- (void)beginInterPercent {
id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if ([toVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)toVC;
toVC = nav.viewControllers.lastObject;
} else if ([toVC isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBar = (UITabBarController *)toVC;
if ([tabBar.selectedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)tabBar.selectedViewController;
toVC = nav.viewControllers.lastObject;
} else {
toVC = tabBar.selectedViewController;
}
}
UIView *containerView = [transitionContext containerView];
CGRect tempImageViewFrame = [fromVC.view convertRect:self.contentView.frame toView:toVC.view];
self.bgView = [[UIView alloc] initWithFrame:containerView.bounds];
self.contentView.frame = tempImageViewFrame;
self.transitionImgViewCenter = self.contentView.center;
[containerView addSubview:self.bgView];
[containerView addSubview:self.contentView];
[containerView addSubview:fromVC.view];
self.bgView.backgroundColor = [UIColor blackColor];
fromVC.view.backgroundColor = [UIColor clearColor];
}
- (void)updateInterPercent:(CGFloat)scale {
UIViewController *fromVC = [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
fromVC.view.alpha = scale;
self.bgView.alpha = scale;
}
- (void)interPercentCancel {
id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[UIView animateWithDuration:0.2f animations:^{
fromVC.view.alpha = 1;
self.contentView.transform = CGAffineTransformIdentity;
self.contentView.center = self.transitionImgViewCenter;
self.bgView.alpha = 1;
} completion:^(BOOL finished) {
fromVC.view.backgroundColor = [UIColor blackColor];
self.contentView.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
self.contentView.frame = self.contentFullScreenRect;
if (self.scrollViewContentOffset.y < 0) {
self.scrollViewContentOffset = CGPointMake(self.scrollViewContentOffset.x, 0);
}
[self.viewController.view addSubview:self.contentView];
[self.bgView removeFromSuperview];
self.bgView = nil;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)interPercentFinish {
id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if ([toVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)toVC;
toVC = nav.viewControllers.lastObject;
} else if ([toVC isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBar = (UITabBarController *)toVC;
if ([tabBar.selectedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)tabBar.selectedViewController;
toVC = nav.viewControllers.lastObject;
} else {
toVC = tabBar.selectedViewController;
}
}
CGRect tempImageViewFrame = self.contentView.frame;
self.contentView.layer.anchorPoint = CGPointMake(0.5, 0.5);
self.contentView.transform = CGAffineTransformIdentity;
self.contentView.frame = tempImageViewFrame;
CGRect toRect = [self.containerView convertRect:self.containerView.bounds toView:self.containerView.window];
[self.delagate zf_orientationWillChange:NO];
[UIView animateWithDuration:0.3f animations:^{
self.contentView.frame = toRect;
fromVC.view.alpha = 0;
self.bgView.alpha = 0;
toVC.navigationController.navigationBar.alpha = 1;
[self.contentView layoutIfNeeded];
} completion:^(BOOL finished) {
[self.containerView addSubview:self.contentView];
self.contentView.frame = self.containerView.bounds;
[self.contentView layoutIfNeeded];
[self.bgView removeFromSuperview];
fromVC.view.backgroundColor = [UIColor blackColor];
[self.delagate zf_orientationDidChanged:NO];
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
self.transitionContext = transitionContext;
[self beginInterPercent];
}
- (void)setInteration:(BOOL)interation {
_interation = interation;
if ([self.delagate respondsToSelector:@selector(zf_interationState:)]) {
[self.delagate zf_interationState:interation];
}
}
- (void)setViewController:(UIViewController *)viewController {
_viewController = viewController;
[self removeGestureToView:viewController.view];
[viewController.view addGestureRecognizer:self.panGesture];
[viewController.view addGestureRecognizer:self.tapGesture];
}
#pragma mark - getter
- (UIPanGestureRecognizer *)panGesture {
if (!_panGesture) {
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognizeDidUpdate:)];
_panGesture.delegate = self;
}
return _panGesture;
}
- (UITapGestureRecognizer *)tapGesture {
if (!_tapGesture) {
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureAction)];
_tapGesture.delegate = self;
}
return _tapGesture;
}
@end

View File

@@ -0,0 +1,38 @@
//
// ZFPlayer.h
// 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 <Foundation/Foundation.h>
//! Project version number for ZFPlayer.
FOUNDATION_EXPORT double ZFPlayerVersionNumber;
//! Project version string for ZFPlayer.
FOUNDATION_EXPORT const unsigned char ZFPlayerVersionString[];
#import "ZFPlayerController.h"
#import "ZFPlayerMediaControl.h"
#import "ZFKVOController.h"
#import "UIScrollView+ZFPlayer.h"
#import "ZFPlayerLogManager.h"
#import "ZFPlayerConst.h"

View File

@@ -0,0 +1,130 @@
//
// ZFPlayerConst.h
// 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.
typedef NS_ENUM(NSUInteger, ZFPlayerPlaybackState) {
ZFPlayerPlayStateUnknown,
ZFPlayerPlayStatePlaying,
ZFPlayerPlayStatePaused,
ZFPlayerPlayStatePlayFailed,
ZFPlayerPlayStatePlayStopped
};
typedef NS_OPTIONS(NSUInteger, ZFPlayerLoadState) {
ZFPlayerLoadStateUnknown = 0,
ZFPlayerLoadStatePrepare = 1 << 0,
ZFPlayerLoadStatePlayable = 1 << 1,
ZFPlayerLoadStatePlaythroughOK = 1 << 2, // Playback will be automatically started.
ZFPlayerLoadStateStalled = 1 << 3, // Playback will be automatically paused in this state, if started.
};
typedef NS_ENUM(NSInteger, ZFPlayerScalingMode) {
ZFPlayerScalingModeNone, // No scaling.
ZFPlayerScalingModeAspectFit, // Uniform scale until one dimension fits.
ZFPlayerScalingModeAspectFill, // Uniform scale until the movie fills the visible bounds. One dimension may have clipped contents.
ZFPlayerScalingModeFill // Non-uniform scale. Both render dimensions will exactly match the visible bounds.
};
/**
Synthsize a weak or strong reference.
Example:
@zf_weakify(self)
[self doSomething^{
@zf_strongify(self)
if (!self) return;
...
}];
*/
#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define zf_weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define zf_weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define zf_weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define zf_weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif
#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define zf_strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define zf_strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define zf_strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define zf_strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif
// Screen width
#define ZFPlayerScreenWidth [[UIScreen mainScreen] bounds].size.width
// Screen height
#define ZFPlayerScreenHeight [[UIScreen mainScreen] bounds].size.height
// deprecated
#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif
#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif

View File

@@ -0,0 +1,608 @@
//
// ZFPlayerController.h
// 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 <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "ZFPlayerMediaPlayback.h"
#import "ZFOrientationObserver.h"
#import "ZFPlayerMediaControl.h"
#import "ZFPlayerGestureControl.h"
#import "ZFPlayerNotification.h"
#import "ZFFloatView.h"
#import "UIScrollView+ZFPlayer.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFPlayerController : NSObject
/// The video contrainerView in normal model.
@property (nonatomic, strong) UIView *containerView;
/// The currentPlayerManager must conform `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, strong) id<ZFPlayerMediaPlayback> currentPlayerManager;
/// The custom controlView must conform `ZFPlayerMediaControl` protocol.
@property (nonatomic, strong) UIView<ZFPlayerMediaControl> *controlView;
/// The notification manager class.
@property (nonatomic, strong, readonly) ZFPlayerNotification *notification;
/// The container view type.
@property (nonatomic, assign, readonly) ZFPlayerContainerType containerType;
/// The player's small container view.
@property (nonatomic, strong, readonly) ZFFloatView *smallFloatView;
/// Whether the small window is displayed.
@property (nonatomic, assign, readonly) BOOL isSmallFloatViewShow;
/*!
@method playerWithPlayerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video frames must set the contrainerView.
@result An instance of ZFPlayerController.
*/
+ (instancetype)playerWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
/*!
@method initWithPlayerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video frames must set the contrainerView.
@result An instance of ZFPlayerController.
*/
- (instancetype)initWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
/*!
@method playerWithScrollView:playerManager:containerViewTag:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UITableView` or `UICollectionView`.
@param scrollView is `tableView` or `collectionView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerViewTag to see the video at scrollView must set the contrainerViewTag.
@result An instance of ZFPlayerController.
*/
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag;
/*!
@method initWithScrollView:playerManager:containerViewTag:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UITableView` or `UICollectionView`.
@param scrollView is `tableView` or `collectionView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerViewTag to see the video at scrollView must set the contrainerViewTag.
@result An instance of ZFPlayerController.
*/
- (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag;
/*!
@method playerWithScrollView:playerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UIScrollView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video at the scrollView.
@result An instance of ZFPlayerController.
*/
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
/*!
@method initWithScrollView:playerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UIScrollView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video at the scrollView.
@result An instance of ZFPlayerController.
*/
- (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
@end
@interface ZFPlayerController (ZFPlayerTimeControl)
/// The player current play time.
@property (nonatomic, readonly) NSTimeInterval currentTime;
/// The player total time.
@property (nonatomic, readonly) NSTimeInterval totalTime;
/// The player buffer time.
@property (nonatomic, readonly) NSTimeInterval bufferTime;
/// The player progress, 0...1
@property (nonatomic, readonly) float progress;
/// The player bufferProgress, 0...1
@property (nonatomic, readonly) float bufferProgress;
/**
Use this method to seek to a specified time for the current player and to be notified when the seek operation is complete.
@param time seek time.
@param completionHandler completion handler.
*/
- (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
@end
@interface ZFPlayerController (ZFPlayerPlaybackControl)
/// Resume playback record.default is NO.
/// Memory storage playback records.
@property (nonatomic, assign) BOOL resumePlayRecord;
/// 0...1.0
/// Only affects audio volume for the device instance and not for the player.
/// You can change device volume or player volume as needed,change the player volume you can conform the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic) float volume;
/// The device muted.
/// Only affects audio muting for the device instance and not for the player.
/// You can change device mute or player mute as needed,change the player mute you can conform the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, getter=isMuted) BOOL muted;
// 0...1.0, where 1.0 is maximum brightness. Only supported by main screen.
@property (nonatomic) float brightness;
/// The play asset URL.
@property (nonatomic) NSURL *assetURL;
/// If tableView or collectionView has only one section , use `assetURLs`.
/// If tableView or collectionView has more sections , use `sectionAssetURLs`.
/// Set this you can use `playTheNext` `playThePrevious` `playTheIndex:` method.
@property (nonatomic, copy, nullable) NSArray <NSURL *>*assetURLs;
/// The currently playing index,limited to one-dimensional arrays.
@property (nonatomic) NSInteger currentPlayIndex;
/// is the last asset URL in `assetURLs`.
@property (nonatomic, readonly) BOOL isLastAssetURL;
/// is the first asset URL in `assetURLs`.
@property (nonatomic, readonly) BOOL isFirstAssetURL;
/// If Yes, player will be called pause method When Received `UIApplicationWillResignActiveNotification` notification.
/// default is YES.
@property (nonatomic) BOOL pauseWhenAppResignActive;
/// When the player is playing, it is paused by some event,not by user click to pause.
/// For example, when the player is playing, application goes into the background or pushed to another viewController
@property (nonatomic, getter=isPauseByEvent) BOOL pauseByEvent;
/// The current player controller is disappear, not dealloc
@property (nonatomic, getter=isViewControllerDisappear) BOOL viewControllerDisappear;
/// You can custom the AVAudioSession,
/// default is NO.
@property (nonatomic, assign) BOOL customAudioSession;
/// The block invoked when the player is Prepare to play.
@property (nonatomic, copy, nullable) void(^playerPrepareToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player is Ready to play.
@property (nonatomic, copy, nullable) void(^playerReadyToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player play progress changed.
@property (nonatomic, copy, nullable) void(^playerPlayTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval currentTime, NSTimeInterval duration);
/// The block invoked when the player play buffer changed.
@property (nonatomic, copy, nullable) void(^playerBufferTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval bufferTime);
/// The block invoked when the player playback state changed.
@property (nonatomic, copy, nullable) void(^playerPlayStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerPlaybackState playState);
/// The block invoked when the player load state changed.
@property (nonatomic, copy, nullable) void(^playerLoadStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerLoadState loadState);
/// The block invoked when the player play failed.
@property (nonatomic, copy, nullable) void(^playerPlayFailed)(id<ZFPlayerMediaPlayback> asset, id error);
/// The block invoked when the player play end.
@property (nonatomic, copy, nullable) void(^playerDidToEnd)(id<ZFPlayerMediaPlayback> asset);
// The block invoked when video size changed.
@property (nonatomic, copy, nullable) void(^presentationSizeChanged)(id<ZFPlayerMediaPlayback> asset, CGSize size);
/**
Play the next url ,while the `assetURLs` is not NULL.
*/
- (void)playTheNext;
/**
Play the previous url ,while the `assetURLs` is not NULL.
*/
- (void)playThePrevious;
/**
Play the index of url ,while the `assetURLs` is not NULL.
@param index play the index.
*/
- (void)playTheIndex:(NSInteger)index;
/**
Player stop and playerView remove from super view,remove other notification.
*/
- (void)stop;
/*!
@method replaceCurrentPlayerManager:
@abstract Replaces the player's current playeranager with the specified player item.
@param manager must conform `ZFPlayerMediaPlayback` protocol
@discussion The playerManager that will become the player's current playeranager.
*/
- (void)replaceCurrentPlayerManager:(id<ZFPlayerMediaPlayback>)manager;
/**
Add video to cell.
*/
- (void)addPlayerViewToCell;
/**
Add video to container view.
*/
- (void)addPlayerViewToContainerView:(UIView *)containerView;
/**
Add to small float view.
*/
- (void)addPlayerViewToSmallFloatView;
/**
Stop the current playing video and remove the playerView.
*/
- (void)stopCurrentPlayingView;
/**
stop the current playing video on cell.
*/
- (void)stopCurrentPlayingCell;
@end
@interface ZFPlayerController (ZFPlayerOrientationRotation)
@property (nonatomic, readonly) ZFOrientationObserver *orientationObserver;
/// Whether automatic screen rotation is supported.
/// The value is NO.
/// This property is used for the return value of UIViewController `shouldAutorotate` method.
@property (nonatomic, readonly) BOOL shouldAutorotate;
/// Whether allow the video orientation rotate.
/// default is YES.
@property (nonatomic) BOOL allowOrentitaionRotation;
/// When ZFFullScreenMode is ZFFullScreenModeLandscape the orientation is LandscapeLeft or LandscapeRight, this value is YES.
/// When ZFFullScreenMode is ZFFullScreenModePortrait, while the player fullSceen this value is YES.
@property (nonatomic, readonly) BOOL isFullScreen;
/// when call the `stop` method, exit the fullScreen model, default is YES.
@property (nonatomic, assign) BOOL exitFullScreenWhenStop;
/// Lock the screen orientation.
@property (nonatomic, getter=isLockedScreen) BOOL lockedScreen;
/// The current orientation of the player.
/// Default is UIInterfaceOrientationPortrait.
@property (nonatomic, readonly) UIInterfaceOrientation currentOrientation;
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(ZFPlayerController *player, BOOL isFullScreen);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(ZFPlayerController *player, BOOL isFullScreen);
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle fullScreenStatusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation fullScreenStatusBarAnimation;
/// The fullscreen statusbar hidden.
@property (nonatomic, getter=isStatusBarHidden) BOOL statusBarHidden;
/**
Add the device orientation observer.
*/
- (void)addDeviceOrientationObserver;
/**
Remove the device orientation observer.
*/
- (void)removeDeviceOrientationObserver;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
@param orientation is UIInterfaceOrientation.
@param animated is animated.
*/
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
@param orientation is UIInterfaceOrientation.
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
@param fullScreen is fullscreen.
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
@param fullScreen is fullscreen.
@param animated is animated.
*/
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
/**
FullScreen mode is determined by ZFFullScreenMode.
@param fullScreen is fullscreen.
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/**
FullScreen mode is determined by ZFFullScreenMode.
@param fullScreen is fullscreen.
@param animated is animated.
*/
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
@end
@interface ZFPlayerController (ZFPlayerViewGesture)
/// An instance of ZFPlayerGestureControl.
@property (nonatomic, readonly) ZFPlayerGestureControl *gestureControl;
/// The gesture types that the player not support.
@property (nonatomic, assign) ZFPlayerDisableGestureTypes disableGestureTypes;
/// The pan gesture moving direction that the player not support.
@property (nonatomic) ZFPlayerDisablePanMovingDirection disablePanMovingDirection;
@end
@interface ZFPlayerController (ZFPlayerScrollView)
/// The scroll view is `tableView` or `collectionView`.
@property (nonatomic, readonly, nullable) UIScrollView *scrollView;
/// The scrollView player should auto player, default is YES.
@property (nonatomic) BOOL shouldAutoPlay;
/// WWAN network auto play, only support in scrollView mode when the `shouldAutoPlay` is YES, default is NO.
@property (nonatomic, getter=isWWANAutoPlay) BOOL WWANAutoPlay;
/// The indexPath is playing.
@property (nonatomic, readonly, nullable) NSIndexPath *playingIndexPath;
/// The indexPath should be play while scrolling.
@property (nonatomic, readonly, nullable) NSIndexPath *shouldPlayIndexPath;
/// The view tag that the player display in scrollView.
@property (nonatomic, readonly) NSInteger containerViewTag;
/// The current playing cell stop playing when the cell has out off the screendefalut is YES.
@property (nonatomic) BOOL stopWhileNotVisible;
/**
The current player scroll slides off the screen percent.
the property used when the `stopWhileNotVisible` is YES, stop the current playing player.
the property used when the `stopWhileNotVisible` is NO, the current playing player add to small container view.
The range is 0.0~1.0, defalut is 0.5.
0.0 is the player will disappear.
1.0 is the player did disappear.
*/
@property (nonatomic) CGFloat playerDisapperaPercent;
/**
The current player scroll to the screen percent to play the video.
The range is 0.0~1.0, defalut is 0.0.
0.0 is the player will appear.
1.0 is the player did appear.
*/
@property (nonatomic) CGFloat playerApperaPercent;
/// If tableView or collectionView has more sections, use `sectionAssetURLs`.
@property (nonatomic, copy, nullable) NSArray <NSArray <NSURL *>*>*sectionAssetURLs;
/// The block invoked When the player appearing.
@property (nonatomic, copy, nullable) void(^zf_playerAppearingInScrollView)(NSIndexPath *indexPath, CGFloat playerApperaPercent);
/// The block invoked When the player disappearing.
@property (nonatomic, copy, nullable) void(^zf_playerDisappearingInScrollView)(NSIndexPath *indexPath, CGFloat playerDisapperaPercent);
/// The block invoked When the player will appeared.
@property (nonatomic, copy, nullable) void(^zf_playerWillAppearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player did appeared.
@property (nonatomic, copy, nullable) void(^zf_playerDidAppearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player will disappear.
@property (nonatomic, copy, nullable) void(^zf_playerWillDisappearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player did disappeared.
@property (nonatomic, copy, nullable) void(^zf_playerDidDisappearInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player should play.
@property (nonatomic, copy, nullable) void(^zf_playerShouldPlayInScrollView)(NSIndexPath *indexPath);
/// The block invoked When the player did stop scroll.
@property (nonatomic, copy, nullable) void(^zf_scrollViewDidEndScrollingCallback)(NSIndexPath *indexPath);
/// Filter the cell that should be played when the scroll is stopped (to play when the scroll is stopped).
- (void)zf_filterShouldPlayCellWhileScrolled:(void (^ __nullable)(NSIndexPath *indexPath))handler;
/// Filter the cell that should be played while scrolling (you can use this to filter the highlighted cell).
- (void)zf_filterShouldPlayCellWhileScrolling:(void (^ __nullable)(NSIndexPath *indexPath))handler;
/**
Play the indexPath of url without scroll postion, while the `assetURLs` or `sectionAssetURLs` is not NULL.
@param indexPath Play the indexPath of url.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath;
/**
Play the indexPath of url, while the `assetURLs` or `sectionAssetURLs` is not NULL.
@param indexPath Play the indexPath of url.
@param scrollPosition scroll position.
@param animated scroll animation.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath
scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated;
/**
Play the indexPath of url with scroll postion, while the `assetURLs` or `sectionAssetURLs` is not NULL.
@param indexPath Play the indexPath of url.
@param scrollPosition scroll position.
@param animated scroll animation.
@param completionHandler Scroll completion callback.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath
scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler;
/**
Play the indexPath of url with scroll postion.
@param indexPath Play the indexPath of url
@param assetURL The player URL.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath assetURL:(NSURL *)assetURL;
/**
Play the indexPath of url with scroll postion.
@param indexPath Play the indexPath of url
@param assetURL The player URL.
@param scrollPosition scroll position.
@param animated scroll animation.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath
assetURL:(NSURL *)assetURL
scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated;
/**
Play the indexPath of url with scroll postion.
@param indexPath Play the indexPath of url
@param assetURL The player URL.
@param scrollPosition scroll position.
@param animated scroll animation.
@param completionHandler Scroll completion callback.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath
assetURL:(NSURL *)assetURL
scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler;
@end
@interface ZFPlayerController (ZFPlayerDeprecated)
/**
Add the playerView to cell.
*/
- (void)updateScrollViewPlayerToCell __attribute__((deprecated("use `addPlayerViewToCell:` instead.")));
/**
Add the playerView to containerView.
@param containerView The playerView containerView.
*/
- (void)updateNoramlPlayerWithContainerView:(UIView *)containerView __attribute__((deprecated("use `addPlayerViewToContainerView:` instead.")));
/**
Play the indexPath of url ,while the `assetURLs` or `sectionAssetURLs` is not NULL.
@param indexPath Play the indexPath of url
@param scrollToTop Scroll the current cell to top with animations.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath scrollToTop:(BOOL)scrollToTop __attribute__((deprecated("use `playTheIndexPath:scrollPosition:animated:` instead.")));
/**
Play the indexPath of url with scroll postion.
@param indexPath Play the indexPath of url
@param assetURL The player URL.
@param scrollToTop Scroll the current cell to top with animations.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath assetURL:(NSURL *)assetURL scrollToTop:(BOOL)scrollToTop __attribute__((deprecated("use `playTheIndexPath:assetURL:scrollPosition:animated:` instead.")));
/**
Play the indexPath of url ,while the `assetURLs` or `sectionAssetURLs` is not NULL.
@param indexPath Play the indexPath of url
@param scrollToTop scroll the current cell to top with animations.
@param completionHandler Scroll completion callback.
*/
- (void)playTheIndexPath:(NSIndexPath *)indexPath scrollToTop:(BOOL)scrollToTop completionHandler:(void (^ __nullable)(void))completionHandler __attribute__((deprecated("use `playTheIndexPath:scrollPosition:animated:completionHandler:` instead.")));
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
@param orientation UIInterfaceOrientation
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)enterLandscapeFullScreen:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion __attribute__((deprecated("use `rotateToOrientation:animated:completion:` instead.")));
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
@param orientation UIInterfaceOrientation
@param animated is animated.
*/
- (void)enterLandscapeFullScreen:(UIInterfaceOrientation)orientation animated:(BOOL)animated __attribute__((deprecated("use `rotateToOrientation:animated:` instead.")));
/**
Add to the keyWindow.
*/
- (void)addPlayerViewToKeyWindow __attribute__((deprecated("use `addPlayerViewToSmallFloatView` instead.")));;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,137 @@
//
// ZFPlayerGestureControl.h
// 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 <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, ZFPlayerGestureType) {
ZFPlayerGestureTypeUnknown,
ZFPlayerGestureTypeSingleTap,
ZFPlayerGestureTypeDoubleTap,
ZFPlayerGestureTypePan,
ZFPlayerGestureTypePinch
};
typedef NS_ENUM(NSUInteger, ZFPanDirection) {
ZFPanDirectionUnknown,
ZFPanDirectionV,
ZFPanDirectionH,
};
typedef NS_ENUM(NSUInteger, ZFPanLocation) {
ZFPanLocationUnknown,
ZFPanLocationLeft,
ZFPanLocationRight,
};
typedef NS_ENUM(NSUInteger, ZFPanMovingDirection) {
ZFPanMovingDirectionUnkown,
ZFPanMovingDirectionTop,
ZFPanMovingDirectionLeft,
ZFPanMovingDirectionBottom,
ZFPanMovingDirectionRight,
};
/// This enumeration lists some of the gesture types that the player has by default.
typedef NS_OPTIONS(NSUInteger, ZFPlayerDisableGestureTypes) {
ZFPlayerDisableGestureTypesNone = 0,
ZFPlayerDisableGestureTypesSingleTap = 1 << 0,
ZFPlayerDisableGestureTypesDoubleTap = 1 << 1,
ZFPlayerDisableGestureTypesPan = 1 << 2,
ZFPlayerDisableGestureTypesPinch = 1 << 3,
ZFPlayerDisableGestureTypesAll = (ZFPlayerDisableGestureTypesSingleTap | ZFPlayerDisableGestureTypesDoubleTap | ZFPlayerDisableGestureTypesPan | ZFPlayerDisableGestureTypesPinch)
};
/// This enumeration lists some of the pan gesture moving direction that the player not support.
typedef NS_OPTIONS(NSUInteger, ZFPlayerDisablePanMovingDirection) {
ZFPlayerDisablePanMovingDirectionNone = 0, /// Not disable pan moving direction.
ZFPlayerDisablePanMovingDirectionVertical = 1 << 0, /// Disable pan moving vertical direction.
ZFPlayerDisablePanMovingDirectionHorizontal = 1 << 1, /// Disable pan moving horizontal direction.
ZFPlayerDisablePanMovingDirectionAll = (ZFPlayerDisablePanMovingDirectionVertical | ZFPlayerDisablePanMovingDirectionHorizontal) /// Disable pan moving all direction.
};
@interface ZFPlayerGestureControl : NSObject
/// Gesture condition callback.
@property (nonatomic, copy, nullable) BOOL(^triggerCondition)(ZFPlayerGestureControl *control, ZFPlayerGestureType type, UIGestureRecognizer *gesture, UITouch *touch);
/// Single tap gesture callback.
@property (nonatomic, copy, nullable) void(^singleTapped)(ZFPlayerGestureControl *control);
/// Double tap gesture callback.
@property (nonatomic, copy, nullable) void(^doubleTapped)(ZFPlayerGestureControl *control);
/// Begin pan gesture callback.
@property (nonatomic, copy, nullable) void(^beganPan)(ZFPlayerGestureControl *control, ZFPanDirection direction, ZFPanLocation location);
/// Pan gesture changing callback.
@property (nonatomic, copy, nullable) void(^changedPan)(ZFPlayerGestureControl *control, ZFPanDirection direction, ZFPanLocation location, CGPoint velocity);
/// End the Pan gesture callback.
@property (nonatomic, copy, nullable) void(^endedPan)(ZFPlayerGestureControl *control, ZFPanDirection direction, ZFPanLocation location);
/// Pinch gesture callback.
@property (nonatomic, copy, nullable) void(^pinched)(ZFPlayerGestureControl *control, float scale);
/// The single tap gesture.
@property (nonatomic, strong, readonly) UITapGestureRecognizer *singleTap;
/// The double tap gesture.
@property (nonatomic, strong, readonly) UITapGestureRecognizer *doubleTap;
/// The pan tap gesture.
@property (nonatomic, strong, readonly) UIPanGestureRecognizer *panGR;
/// The pinch tap gesture.
@property (nonatomic, strong, readonly) UIPinchGestureRecognizer *pinchGR;
/// The pan gesture direction.
@property (nonatomic, readonly) ZFPanDirection panDirection;
/// The pan location.
@property (nonatomic, readonly) ZFPanLocation panLocation;
/// The moving drection.
@property (nonatomic, readonly) ZFPanMovingDirection panMovingDirection;
/// The gesture types that the player not support.
@property (nonatomic) ZFPlayerDisableGestureTypes disableTypes;
/// The pan gesture moving direction that the player not support.
@property (nonatomic) ZFPlayerDisablePanMovingDirection disablePanMovingDirection;
/**
Add all gestures(singleTap、doubleTap、panGR、pinchGR) to the view.
*/
- (void)addGestureToView:(UIView *)view;
/**
Remove all gestures(singleTap、doubleTap、panGR、pinchGR) form the view.
*/
- (void)removeGestureToView:(UIView *)view;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,261 @@
//
// ZFPlayerGestureControl.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 "ZFPlayerGestureControl.h"
@interface ZFPlayerGestureControl ()<UIGestureRecognizerDelegate>
@property (nonatomic, strong) UITapGestureRecognizer *singleTap;
@property (nonatomic, strong) UITapGestureRecognizer *doubleTap;
@property (nonatomic, strong) UIPanGestureRecognizer *panGR;
@property (nonatomic, strong) UIPinchGestureRecognizer *pinchGR;
@property (nonatomic) ZFPanDirection panDirection;
@property (nonatomic) ZFPanLocation panLocation;
@property (nonatomic) ZFPanMovingDirection panMovingDirection;
@property (nonatomic, weak) UIView *targetView;
@end
@implementation ZFPlayerGestureControl
- (void)addGestureToView:(UIView *)view {
self.targetView = view;
self.targetView.multipleTouchEnabled = YES;
[self.singleTap requireGestureRecognizerToFail:self.doubleTap];
[self.singleTap requireGestureRecognizerToFail:self.panGR];
[self.targetView addGestureRecognizer:self.singleTap];
[self.targetView addGestureRecognizer:self.doubleTap];
[self.targetView addGestureRecognizer:self.panGR];
[self.targetView addGestureRecognizer:self.pinchGR];
}
- (void)removeGestureToView:(UIView *)view {
[view removeGestureRecognizer:self.singleTap];
[view removeGestureRecognizer:self.doubleTap];
[view removeGestureRecognizer:self.panGR];
[view removeGestureRecognizer:self.pinchGR];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.panGR) {
CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:self.targetView];
CGFloat x = fabs(translation.x);
CGFloat y = fabs(translation.y);
if (x < y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionVertical) { /// up and down moving direction.
return NO;
} else if (x > y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionHorizontal) { /// left and right moving direction.
return NO;
}
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
ZFPlayerGestureType type = ZFPlayerGestureTypeUnknown;
if (gestureRecognizer == self.singleTap) type = ZFPlayerGestureTypeSingleTap;
else if (gestureRecognizer == self.doubleTap) type = ZFPlayerGestureTypeDoubleTap;
else if (gestureRecognizer == self.panGR) type = ZFPlayerGestureTypePan;
else if (gestureRecognizer == self.pinchGR) type = ZFPlayerGestureTypePinch;
CGPoint locationPoint = [touch locationInView:touch.view];
if (locationPoint.x > _targetView.bounds.size.width / 2) {
self.panLocation = ZFPanLocationRight;
} else {
self.panLocation = ZFPanLocationLeft;
}
switch (type) {
case ZFPlayerGestureTypeUnknown: break;
case ZFPlayerGestureTypePan: {
if (self.disableTypes & ZFPlayerDisableGestureTypesPan) {
return NO;
}
}
break;
case ZFPlayerGestureTypePinch: {
if (self.disableTypes & ZFPlayerDisableGestureTypesPinch) {
return NO;
}
}
break;
case ZFPlayerGestureTypeDoubleTap: {
if (self.disableTypes & ZFPlayerDisableGestureTypesDoubleTap) {
return NO;
}
}
break;
case ZFPlayerGestureTypeSingleTap: {
if (self.disableTypes & ZFPlayerDisableGestureTypesSingleTap) {
return NO;
}
}
break;
}
if (self.triggerCondition) return self.triggerCondition(self, type, gestureRecognizer, touch);
return YES;
}
// Whether to support multi-trigger, return YES, you can trigger a method with multiple gestures, return NO is mutually exclusive
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (otherGestureRecognizer != self.singleTap &&
otherGestureRecognizer != self.doubleTap &&
otherGestureRecognizer != self.panGR &&
otherGestureRecognizer != self.pinchGR) return NO;
if (gestureRecognizer == self.panGR) {
CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:self.targetView];
CGFloat x = fabs(translation.x);
CGFloat y = fabs(translation.y);
if (x < y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionVertical) {
return YES;
} else if (x > y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionHorizontal) {
return YES;
}
}
if (gestureRecognizer.numberOfTouches >= 2) {
return NO;
}
return YES;
}
- (UITapGestureRecognizer *)singleTap {
if (!_singleTap){
_singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
_singleTap.delegate = self;
_singleTap.delaysTouchesBegan = YES;
_singleTap.delaysTouchesEnded = YES;
_singleTap.numberOfTouchesRequired = 1;
_singleTap.numberOfTapsRequired = 1;
}
return _singleTap;
}
- (UITapGestureRecognizer *)doubleTap {
if (!_doubleTap) {
_doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
_doubleTap.delegate = self;
_doubleTap.delaysTouchesBegan = YES;
_singleTap.delaysTouchesEnded = YES;
_doubleTap.numberOfTouchesRequired = 1;
_doubleTap.numberOfTapsRequired = 2;
}
return _doubleTap;
}
- (UIPanGestureRecognizer *)panGR {
if (!_panGR) {
_panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
_panGR.delegate = self;
_panGR.delaysTouchesBegan = YES;
_panGR.delaysTouchesEnded = YES;
_panGR.maximumNumberOfTouches = 1;
_panGR.cancelsTouchesInView = YES;
}
return _panGR;
}
- (UIPinchGestureRecognizer *)pinchGR {
if (!_pinchGR) {
_pinchGR = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
_pinchGR.delegate = self;
_pinchGR.delaysTouchesBegan = YES;
}
return _pinchGR;
}
- (void)handleSingleTap:(UITapGestureRecognizer *)tap {
if (self.singleTapped) self.singleTapped(self);
}
- (void)handleDoubleTap:(UITapGestureRecognizer *)tap {
if (self.doubleTapped) self.doubleTapped(self);
}
- (void)handlePan:(UIPanGestureRecognizer *)pan {
CGPoint translate = [pan translationInView:pan.view];
CGPoint velocity = [pan velocityInView:pan.view];
switch (pan.state) {
case UIGestureRecognizerStateBegan: {
self.panMovingDirection = ZFPanMovingDirectionUnkown;
CGFloat x = fabs(velocity.x);
CGFloat y = fabs(velocity.y);
if (x > y) {
self.panDirection = ZFPanDirectionH;
} else if (x < y) {
self.panDirection = ZFPanDirectionV;
} else {
self.panDirection = ZFPanDirectionUnknown;
}
if (self.beganPan) self.beganPan(self, self.panDirection, self.panLocation);
}
break;
case UIGestureRecognizerStateChanged: {
switch (_panDirection) {
case ZFPanDirectionH: {
if (translate.x > 0) {
self.panMovingDirection = ZFPanMovingDirectionRight;
} else {
self.panMovingDirection = ZFPanMovingDirectionLeft;
}
}
break;
case ZFPanDirectionV: {
if (translate.y > 0) {
self.panMovingDirection = ZFPanMovingDirectionBottom;
} else {
self.panMovingDirection = ZFPanMovingDirectionTop;
}
}
break;
case ZFPanDirectionUnknown:
break;
}
if (self.changedPan) self.changedPan(self, self.panDirection, self.panLocation, velocity);
}
break;
case UIGestureRecognizerStateFailed:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded: {
if (self.endedPan) self.endedPan(self, self.panDirection, self.panLocation);
}
break;
default:
break;
}
[pan setTranslation:CGPointZero inView:pan.view];
}
- (void)handlePinch:(UIPinchGestureRecognizer *)pinch {
switch (pinch.state) {
case UIGestureRecognizerStateEnded: {
if (self.pinched) self.pinched(self, pinch.scale);
}
break;
default:
break;
}
}
@end

View File

@@ -0,0 +1,44 @@
//
// ZFPlayerLogManager.h
// 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.
#define ZFPlayerLog(format,...) [ZFPlayerLogManager logWithFunction:__FUNCTION__ lineNumber:__LINE__ formatString:[NSString stringWithFormat:format, ##__VA_ARGS__]]
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ZFPlayerLogManager : NSObject
// Set the log output status.
+ (void)setLogEnable:(BOOL)enable;
// Gets the log output status.
+ (BOOL)getLogEnable;
/// Get ZFPlayer version.
+ (NSString *)version;
// Log output method.
+ (void)logWithFunction:(const char *)function lineNumber:(int)lineNumber formatString:(NSString *)formatString;
@end

View File

@@ -0,0 +1,49 @@
//
// ZFPlayerLogManager.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 "ZFPlayerLogManager.h"
static BOOL kLogEnable = NO;
@implementation ZFPlayerLogManager
+ (void)setLogEnable:(BOOL)enable {
kLogEnable = enable;
}
+ (BOOL)getLogEnable {
return kLogEnable;
}
+ (NSString *)version {
return @"4.0.2";
}
+ (void)logWithFunction:(const char *)function lineNumber:(int)lineNumber formatString:(NSString *)formatString {
if ([self getLogEnable]) {
NSLog(@"%s[%d]%@", function, lineNumber, formatString);
}
}
@end

View File

@@ -0,0 +1,210 @@
//
// ZFPlayerMediaControl.h
// 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 <Foundation/Foundation.h>
#import "ZFPlayerMediaPlayback.h"
#import "ZFOrientationObserver.h"
#import "ZFPlayerGestureControl.h"
#import "ZFReachabilityManager.h"
@class ZFPlayerController;
NS_ASSUME_NONNULL_BEGIN
@protocol ZFPlayerMediaControl <NSObject>
@required
/// Current playerController
@property (nonatomic, weak) ZFPlayerController *player;
@optional
#pragma mark - Playback state
/// When the player prepare to play the video.
- (void)videoPlayer:(ZFPlayerController *)videoPlayer prepareToPlay:(NSURL *)assetURL;
/// When th player playback state changed.
- (void)videoPlayer:(ZFPlayerController *)videoPlayer playStateChanged:(ZFPlayerPlaybackState)state;
/// When th player loading state changed.
- (void)videoPlayer:(ZFPlayerController *)videoPlayer loadStateChanged:(ZFPlayerLoadState)state;
#pragma mark - progress
/**
When the playback changed.
@param videoPlayer the player.
@param currentTime the current play time.
@param totalTime the video total time.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer
currentTime:(NSTimeInterval)currentTime
totalTime:(NSTimeInterval)totalTime;
/**
When buffer progress changed.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer
bufferTime:(NSTimeInterval)bufferTime;
/**
When you are dragging to change the video progress.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer
draggingTime:(NSTimeInterval)seekTime
totalTime:(NSTimeInterval)totalTime;
/**
When play end.
*/
- (void)videoPlayerPlayEnd:(ZFPlayerController *)videoPlayer;
/**
When play failed.
*/
- (void)videoPlayerPlayFailed:(ZFPlayerController *)videoPlayer error:(id)error;
#pragma mark - lock screen
/**
When set `videoPlayer.lockedScreen`.
*/
- (void)lockedVideoPlayer:(ZFPlayerController *)videoPlayer lockedScreen:(BOOL)locked;
#pragma mark - Screen rotation
/**
When the fullScreen maode will changed.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationWillChange:(ZFOrientationObserver *)observer;
/**
When the fullScreen maode did changed.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationDidChanged:(ZFOrientationObserver *)observer;
#pragma mark - The network changed
/**
When the network changed
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer reachabilityChanged:(ZFReachabilityStatus)status;
#pragma mark - The video size changed
/**
When the video size changed
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size;
#pragma mark - Gesture
/**
When the gesture condition
*/
- (BOOL)gestureTriggerCondition:(ZFPlayerGestureControl *)gestureControl
gestureType:(ZFPlayerGestureType)gestureType
gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
touch:(UITouch *)touch;
/**
When the gesture single tapped
*/
- (void)gestureSingleTapped:(ZFPlayerGestureControl *)gestureControl;
/**
When the gesture double tapped
*/
- (void)gestureDoubleTapped:(ZFPlayerGestureControl *)gestureControl;
/**
When the gesture begin panGesture
*/
- (void)gestureBeganPan:(ZFPlayerGestureControl *)gestureControl
panDirection:(ZFPanDirection)direction
panLocation:(ZFPanLocation)location;
/**
When the gesture paning
*/
- (void)gestureChangedPan:(ZFPlayerGestureControl *)gestureControl
panDirection:(ZFPanDirection)direction
panLocation:(ZFPanLocation)location
withVelocity:(CGPoint)velocity;
/**
When the end panGesture
*/
- (void)gestureEndedPan:(ZFPlayerGestureControl *)gestureControl
panDirection:(ZFPanDirection)direction
panLocation:(ZFPanLocation)location;
/**
When the pinchGesture changed
*/
- (void)gesturePinched:(ZFPlayerGestureControl *)gestureControl
scale:(float)scale;
#pragma mark - scrollview
/**
When the player will appear in scrollView.
*/
- (void)playerWillAppearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player did appear in scrollView.
*/
- (void)playerDidAppearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player will disappear in scrollView.
*/
- (void)playerWillDisappearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player did disappear in scrollView.
*/
- (void)playerDidDisappearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player appearing in scrollView.
*/
- (void)playerAppearingInScrollView:(ZFPlayerController *)videoPlayer playerApperaPercent:(CGFloat)playerApperaPercent;
/**
When the player disappearing in scrollView.
*/
- (void)playerDisappearingInScrollView:(ZFPlayerController *)videoPlayer playerDisapperaPercent:(CGFloat)playerDisapperaPercent;
/**
When the small float view show.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer floatViewShow:(BOOL)show;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,160 @@
//
// ZFMediaPlayback.h
// 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 <Foundation/Foundation.h>
#import "ZFPlayerView.h"
#import "ZFPlayerConst.h"
NS_ASSUME_NONNULL_BEGIN
@protocol ZFPlayerMediaPlayback <NSObject>
@required
/// The view must inherited `ZFPlayerView`,this view deals with some gesture conflicts.
@property (nonatomic) ZFPlayerView *view;
/// The player volume.
/// Only affects audio volume for the player instance and not for the device.
/// You can change device volume or player volume as needed,change the player volume you can follow the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic) float volume;
/// The player muted.
/// indicates whether or not audio output of the player is muted. Only affects audio muting for the player instance and not for the device.
/// You can change device volume or player muted as needed,change the player muted you can follow the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, getter=isMuted) BOOL muted;
/// Playback speed,0.5...2
@property (nonatomic) float rate;
/// The player current play time.
@property (nonatomic, readonly) NSTimeInterval currentTime;
/// The player total time.
@property (nonatomic, readonly) NSTimeInterval totalTime;
/// The player buffer time.
@property (nonatomic, readonly) NSTimeInterval bufferTime;
/// The player seek time.
@property (nonatomic) NSTimeInterval seekTime;
/// The player play state,playing or not playing.
@property (nonatomic, readonly) BOOL isPlaying;
/// Determines how the content scales to fit the view. Defaults to ZFPlayerScalingModeNone.
@property (nonatomic) ZFPlayerScalingMode scalingMode;
/**
@abstract Check whether video preparation is complete.
@discussion isPreparedToPlay processing logic
* If isPreparedToPlay is true, you can call [ZFPlayerMediaPlayback play] API start playing;
* If isPreparedToPlay to false, direct call [ZFPlayerMediaPlayback play], in the play the internal automatic call [ZFPlayerMediaPlayback prepareToPlay] API.
* Returns true if prepared for playback.
*/
@property (nonatomic, readonly) BOOL isPreparedToPlay;
/// The player should auto player, default is YES.
@property (nonatomic) BOOL shouldAutoPlay;
/// The play asset URL.
@property (nonatomic) NSURL *assetURL;
/// The video size.
@property (nonatomic) CGSize presentationSize;
/// The playback state.
@property (nonatomic, readonly) ZFPlayerPlaybackState playState;
/// The player load state.
@property (nonatomic, readonly) ZFPlayerLoadState loadState;
///------------------------------------
/// If you don't appoint the controlView, you can called the following blocks.
/// If you appoint the controlView, The following block cannot be called outside, only for `ZFPlayerController` calls.
///------------------------------------
/// The block invoked when the player is Prepare to play.
@property (nonatomic, copy, nullable) void(^playerPrepareToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player is Ready to play.
@property (nonatomic, copy, nullable) void(^playerReadyToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player play progress changed.
@property (nonatomic, copy, nullable) void(^playerPlayTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval currentTime, NSTimeInterval duration);
/// The block invoked when the player play buffer changed.
@property (nonatomic, copy, nullable) void(^playerBufferTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval bufferTime);
/// The block invoked when the player playback state changed.
@property (nonatomic, copy, nullable) void(^playerPlayStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerPlaybackState playState);
/// The block invoked when the player load state changed.
@property (nonatomic, copy, nullable) void(^playerLoadStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerLoadState loadState);
/// The block invoked when the player play failed.
@property (nonatomic, copy, nullable) void(^playerPlayFailed)(id<ZFPlayerMediaPlayback> asset, id error);
/// The block invoked when the player play end.
@property (nonatomic, copy, nullable) void(^playerDidToEnd)(id<ZFPlayerMediaPlayback> asset);
// The block invoked when video size changed.
@property (nonatomic, copy, nullable) void(^presentationSizeChanged)(id<ZFPlayerMediaPlayback> asset, CGSize size);
///------------------------------------
/// end
///------------------------------------
/// Prepares the current queue for playback, interrupting any active (non-mixible) audio sessions.
- (void)prepareToPlay;
/// Reload player.
- (void)reloadPlayer;
/// Play playback.
- (void)play;
/// Pauses playback.
- (void)pause;
/// Replay playback.
- (void)replay;
/// Stop playback.
- (void)stop;
/// Use this method to seek to a specified time for the current player and to be notified when the seek operation is complete.
- (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
@optional
/// Video UIImage at the current time.
- (UIImage *)thumbnailImageAtCurrentTime;
/// Video UIImage at the current time.
- (void)thumbnailImageAtCurrentTime:(void(^)(UIImage *))handler;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,59 @@
//
// ZFPlayerNotification.h
// 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 <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, ZFPlayerBackgroundState) {
ZFPlayerBackgroundStateForeground, // Enter the foreground from the background.
ZFPlayerBackgroundStateBackground, // From the foreground to the background.
};
@interface ZFPlayerNotification : NSObject
@property (nonatomic, readonly) ZFPlayerBackgroundState backgroundState;
@property (nonatomic, copy, nullable) void(^willResignActive)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^didBecomeActive)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^newDeviceAvailable)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^oldDeviceUnavailable)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^categoryChange)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^volumeChanged)(float volume);
@property (nonatomic, copy, nullable) void(^audioInterruptionCallback)(AVAudioSessionInterruptionType interruptionType);
- (void)addNotification;
- (void)removeNotification;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,113 @@
//
// ZFPlayerNotification.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 "ZFPlayerNotification.h"
@interface ZFPlayerNotification ()
@property (nonatomic, assign) ZFPlayerBackgroundState backgroundState;
@end
@implementation ZFPlayerNotification
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioSessionRouteChangeNotification:)
name:AVAudioSessionRouteChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActiveNotification)
name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActiveNotification)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(volumeDidChangeNotification:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioSessionInterruptionNotification:)
name:AVAudioSessionInterruptionNotification
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
}
- (void)dealloc {
[self removeNotification];
}
- (void)audioSessionRouteChangeNotification:(NSNotification*)notification {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
if (self.newDeviceAvailable) self.newDeviceAvailable(self);
}
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
if (self.oldDeviceUnavailable) self.oldDeviceUnavailable(self);
}
break;
case AVAudioSessionRouteChangeReasonCategoryChange: {
if (self.categoryChange) self.categoryChange(self);
}
break;
}
});
}
- (void)volumeDidChangeNotification:(NSNotification *)notification {
float volume = [[[notification userInfo] objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
if (self.volumeChanged) self.volumeChanged(volume);
}
- (void)applicationWillResignActiveNotification {
self.backgroundState = ZFPlayerBackgroundStateBackground;
if (_willResignActive) _willResignActive(self);
}
- (void)applicationDidBecomeActiveNotification {
self.backgroundState = ZFPlayerBackgroundStateForeground;
if (_didBecomeActive) _didBecomeActive(self);
}
- (void)audioSessionInterruptionNotification:(NSNotification *)notification {
NSDictionary *interuptionDict = notification.userInfo;
AVAudioSessionInterruptionType interruptionType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
if (self.audioInterruptionCallback) self.audioInterruptionCallback(interruptionType);
}
@end

View File

@@ -0,0 +1,42 @@
//
// ZFPlayerView.h
// 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 <UIKit/UIKit.h>
#import "ZFPlayerConst.h"
@interface ZFPlayerView : UIView
/// player content view.
@property (nonatomic, strong) UIView *playerView;
/// Determines how the content scales to fit the view.
@property (nonatomic, assign) ZFPlayerScalingMode scalingMode;
/// The video size.
@property (nonatomic, assign) CGSize presentationSize;
/// The cover for playerView.
@property (nonatomic, strong, readonly) UIImageView *coverImageView;
@end

View File

@@ -0,0 +1,125 @@
//
// ZFPlayerView.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 "ZFPlayerView.h"
#import "ZFPlayerConst.h"
@implementation ZFPlayerView
@synthesize presentationSize = _presentationSize;
@synthesize coverImageView = _coverImageView;
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor blackColor];
[self addSubview:self.coverImageView];
}
return self;
}
- (void)setPlayerView:(UIView *)playerView {
if (_playerView) {
[_playerView removeFromSuperview];
self.presentationSize = CGSizeZero;
}
_playerView = playerView;
if (playerView != nil) {
[self addSubview:playerView];
}
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.frame.size.width;
CGFloat min_view_h = self.frame.size.height;
CGSize playerViewSize = CGSizeZero;
CGFloat videoWidth = self.presentationSize.width;
CGFloat videoHeight = self.presentationSize.height;
if (videoHeight == 0) return;
CGFloat screenScale = min_view_w/min_view_h;
CGFloat videoScale = videoWidth/videoHeight;
if (screenScale > videoScale) {
CGFloat height = min_view_h;
CGFloat width = height * videoScale;
playerViewSize = CGSizeMake(width, height);
} else {
CGFloat width = min_view_w;
CGFloat height = width / videoScale;
playerViewSize = CGSizeMake(width, height);
}
if (self.scalingMode == ZFPlayerScalingModeNone || self.scalingMode == ZFPlayerScalingModeAspectFit) {
min_w = playerViewSize.width;
min_h = playerViewSize.height;
min_x = (min_view_w - min_w) / 2.0;
min_y = (min_view_h - min_h) / 2.0;
self.playerView.frame = CGRectMake(min_x, min_y, min_w, min_h);
} else if (self.scalingMode == ZFPlayerScalingModeAspectFill || self.scalingMode == ZFPlayerScalingModeFill) {
self.playerView.frame = self.bounds;
}
self.coverImageView.frame = self.playerView.frame;
}
- (CGSize)presentationSize {
if (CGSizeEqualToSize(_presentationSize, CGSizeZero)) {
_presentationSize = self.frame.size;
}
return _presentationSize;
}
- (UIImageView *)coverImageView {
if (!_coverImageView) {
_coverImageView = [[UIImageView alloc] init];
_coverImageView.userInteractionEnabled = YES;
_coverImageView.clipsToBounds = YES;
_coverImageView.contentMode = UIViewContentModeScaleAspectFit;
}
return _coverImageView;
}
- (void)setScalingMode:(ZFPlayerScalingMode)scalingMode {
_scalingMode = scalingMode;
if (scalingMode == ZFPlayerScalingModeNone || scalingMode == ZFPlayerScalingModeAspectFit) {
self.coverImageView.contentMode = UIViewContentModeScaleAspectFit;
} else if (scalingMode == ZFPlayerScalingModeAspectFill) {
self.coverImageView.contentMode = UIViewContentModeScaleAspectFill;
} else if (scalingMode == ZFPlayerScalingModeFill) {
self.coverImageView.contentMode = UIViewContentModeScaleToFill;
}
[self layoutIfNeeded];
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
if (CGSizeEqualToSize(CGSizeZero, presentationSize)) return;
[self setNeedsLayout];
[self layoutIfNeeded];
}
@end

View File

@@ -0,0 +1,60 @@
//
// ZFPortraitViewController.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( 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 <UIKit/UIKit.h>
#import "ZFOrientationObserver.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFPortraitViewController : UIViewController
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(BOOL isFullScreen);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(BOOL isFullScreen);
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, assign) BOOL statusBarHidden;
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle statusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation statusBarAnimation;
/// default is ZFDisablePortraitGestureTypesNone.
@property (nonatomic, assign) ZFDisablePortraitGestureTypes disablePortraitGestureTypes;
@property (nonatomic, assign) CGSize presentationSize;
@property (nonatomic, assign) BOOL fullScreenAnimation;
@property (nonatomic, assign) NSTimeInterval duration;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,231 @@
//
// ZFPortraitViewController.m
// ZFPlayer
//
// Copyright (c) 2020 ( 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 "ZFPortraitViewController.h"
#import "ZFPersentInteractiveTransition.h"
#import "ZFPresentTransition.h"
@interface ZFPortraitViewController ()<UIViewControllerTransitioningDelegate,ZFPortraitOrientationDelegate>
@property (nonatomic, strong) ZFPresentTransition *transition;
@property (nonatomic, strong) ZFPersentInteractiveTransition *interactiveTransition;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@end
@implementation ZFPortraitViewController
- (instancetype)init {
self = [super init];
if (self) {
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
self.modalPresentationCapturesStatusBarAppearance = YES;
_statusBarStyle = UIStatusBarStyleLightContent;
_statusBarAnimation = UIStatusBarAnimationSlide;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (!self.fullScreenAnimation) {
if (self.orientationWillChange) {
self.orientationWillChange(YES);
}
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (!self.fullScreenAnimation) {
self.view.alpha = 1;
[self.view addSubview:self.contentView];
self.contentView.frame = [self contentFullScreenRect];
if (self.orientationDidChanged) {
self.orientationDidChanged(YES);
}
}
self.fullScreen = YES;
[self.interactiveTransition updateContentView:self.contentView
containerView:self.containerView];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (!self.fullScreenAnimation) {
if (self.orientationWillChange) {
self.orientationWillChange(NO);
}
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.fullScreen = NO;
if (!self.fullScreenAnimation) {
[self.containerView addSubview:self.contentView];
self.contentView.frame = self.containerView.bounds;
if (self.orientationDidChanged) {
self.orientationDidChanged(NO);
}
}
}
#pragma mark - transition delegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
[self.transition transitionWithTransitionType:ZFPresentTransitionTypePresent contentView:self.contentView containerView:self.containerView];
return self.transition;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
[self.transition transitionWithTransitionType:ZFPresentTransitionTypeDismiss contentView:self.contentView containerView:self.containerView];
return self.transition;
}
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
return self.interactiveTransition.interation ? self.interactiveTransition : nil;
}
- (BOOL)shouldAutorotate {
return NO;
}
- (BOOL)prefersStatusBarHidden {
return self.statusBarHidden;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return self.statusBarAnimation;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#pragma mark - ZFPortraitOrientationDelegate
- (void)zf_orientationWillChange:(BOOL)isFullScreen {
if (self.orientationWillChange) {
self.orientationWillChange(isFullScreen);
}
}
- (void)zf_orientationDidChanged:(BOOL)isFullScreen {
if (self.orientationDidChanged) {
self.orientationDidChanged(isFullScreen);
}
}
- (void)zf_interationState:(BOOL)isDragging {
self.transition.interation = isDragging;
}
#pragma mark - getter
- (ZFPresentTransition *)transition {
if (!_transition) {
_transition = [[ZFPresentTransition alloc] init];
_transition.contentFullScreenRect = [self contentFullScreenRect];
_transition.delagate = self;
}
return _transition;
}
- (ZFPersentInteractiveTransition *)interactiveTransition {
if (!_interactiveTransition) {
_interactiveTransition = [[ZFPersentInteractiveTransition alloc] init];
_interactiveTransition.contentFullScreenRect = [self contentFullScreenRect];
_interactiveTransition.viewController = self;
_interactiveTransition.delagate = self;
}
return _interactiveTransition;;
}
- (void)setDisablePortraitGestureTypes:(ZFDisablePortraitGestureTypes)disablePortraitGestureTypes {
_disablePortraitGestureTypes = disablePortraitGestureTypes;
self.interactiveTransition.disablePortraitGestureTypes = disablePortraitGestureTypes;
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
self.transition.contentFullScreenRect = [self contentFullScreenRect];
self.interactiveTransition.contentFullScreenRect = [self contentFullScreenRect];
if (!self.fullScreenAnimation && self.isFullScreen) {
self.contentView.frame = [self contentFullScreenRect];
}
}
- (void)setFullScreen:(BOOL)fullScreen {
_fullScreen = fullScreen;
self.transition.fullScreen = fullScreen;
}
- (void)setFullScreenAnimation:(BOOL)fullScreenAnimation {
_fullScreenAnimation = fullScreenAnimation;
self.interactiveTransition.fullScreenAnimation = fullScreenAnimation;
}
- (void)setDuration:(NSTimeInterval)duration {
_duration = duration;
self.transition.duration = duration;
}
- (CGRect)contentFullScreenRect {
CGFloat videoWidth = self.presentationSize.width;
CGFloat videoHeight = self.presentationSize.height;
if (videoHeight == 0) {
return CGRectZero;
}
CGSize fullScreenScaleSize = CGSizeZero;
CGFloat screenScale = ZFPlayerScreenWidth/ZFPlayerScreenHeight;
CGFloat videoScale = videoWidth/videoHeight;
if (screenScale > videoScale) {
CGFloat height = ZFPlayerScreenHeight;
CGFloat width = height * videoScale;
fullScreenScaleSize = CGSizeMake(width, height);
} else {
CGFloat width = ZFPlayerScreenWidth;
CGFloat height = (CGFloat)(width / videoScale);
fullScreenScaleSize = CGSizeMake(width, height);
}
videoWidth = fullScreenScaleSize.width;
videoHeight = fullScreenScaleSize.height;
CGRect rect = CGRectMake((ZFPlayerScreenWidth - videoWidth) / 2.0, (ZFPlayerScreenHeight - videoHeight) / 2.0, videoWidth, videoHeight);
return rect;
}
@end

View File

@@ -0,0 +1,49 @@
//
// ZFPresentTransition.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( 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 <UIKit/UIKit.h>
#import "ZFOrientationObserver.h"
typedef NS_ENUM(NSUInteger, ZFPresentTransitionType) {
ZFPresentTransitionTypePresent,
ZFPresentTransitionTypeDismiss,
};
@interface ZFPresentTransition : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic, weak) id<ZFPortraitOrientationDelegate> delagate;
@property (nonatomic, assign) CGRect contentFullScreenRect;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@property (nonatomic, assign) BOOL interation;
@property (nonatomic, assign) NSTimeInterval duration;
- (void)transitionWithTransitionType:(ZFPresentTransitionType)type
contentView:(UIView *)contentView
containerView:(UIView *)containerView;
@end

View File

@@ -0,0 +1,156 @@
//
// ZFPresentTransition.m
// ZFPlayer
//
// Copyright (c) 2020 ( 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 "ZFPresentTransition.h"
#import "ZFPlayerConst.h"
@interface ZFPresentTransition ()
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, assign) ZFPresentTransitionType type;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, assign, getter=isTransiting) BOOL transiting;
@end
@implementation ZFPresentTransition
- (void)transitionWithTransitionType:(ZFPresentTransitionType)type
contentView:(UIView *)contentView
containerView:(UIView *)containerView {
self.type = type;
self.contentView = contentView;
self.containerView = containerView;
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return self.duration == 0 ? 0.25f : self.duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
switch (self.type) {
case ZFPresentTransitionTypePresent: {
[self presentAnimation:transitionContext];
}
break;
case ZFPresentTransitionTypeDismiss: {
[self dismissAnimation:transitionContext];
}
break;
}
}
- (void)presentAnimation:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if ([fromVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)fromVC;
fromVC = nav.viewControllers.lastObject;
} else if ([fromVC isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBar = (UITabBarController *)fromVC;
if ([tabBar.selectedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)tabBar.selectedViewController;
fromVC = nav.viewControllers.lastObject;
} else {
fromVC = tabBar.selectedViewController;
}
}
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView addSubview:self.contentView];
CGRect originRect = [self.containerView convertRect:self.contentView.frame toView:toVC.view];
self.contentView.frame = originRect;
UIColor *tempColor = toVC.view.backgroundColor;
toVC.view.backgroundColor = [tempColor colorWithAlphaComponent:0];
toVC.view.alpha = 1;
[self.delagate zf_orientationWillChange:YES];
CGRect toRect = self.contentFullScreenRect;
self.transiting = YES;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
self.contentView.frame = toRect;
[self.contentView layoutIfNeeded];
toVC.view.backgroundColor = [tempColor colorWithAlphaComponent:1.f];
} completion:^(BOOL finished) {
self.transiting = NO;
[toVC.view addSubview:self.contentView];
[transitionContext completeTransition:YES];
[self.delagate zf_orientationDidChanged:YES];
if (!CGRectEqualToRect(toRect, self.contentFullScreenRect)) {
self.contentView.frame = self.contentFullScreenRect;
[self.contentView layoutIfNeeded];
}
}];
}
- (void)dismissAnimation:(id<UIViewControllerContextTransitioning>)transitionContext {
UIView *containerView = [transitionContext containerView];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if ([toVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)toVC;
toVC = nav.viewControllers.lastObject;
} else if ([toVC isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBar = (UITabBarController *)toVC;
if ([tabBar.selectedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)tabBar.selectedViewController;
toVC = nav.viewControllers.lastObject;
} else {
toVC = tabBar.selectedViewController;
}
}
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
fromVC.view.frame = containerView.bounds;
[containerView addSubview:fromVC.view];
[containerView addSubview:self.contentView];
CGRect originRect = [fromVC.view convertRect:self.contentView.frame toView:toVC.view];
self.contentView.frame = originRect;
CGRect toRect = [self.containerView convertRect:self.containerView.bounds toView:toVC.view];
[fromVC.view convertRect:self.contentView.bounds toView:self.containerView.window];
[self.delagate zf_orientationWillChange:NO];
self.transiting = YES;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromVC.view.alpha = 0;
self.contentView.frame = toRect;
[self.contentView layoutIfNeeded];
} completion:^(BOOL finished) {
[self.containerView addSubview:self.contentView];
self.contentView.frame = self.containerView.bounds;
[transitionContext completeTransition:YES];
[self.delagate zf_orientationDidChanged:NO];
self.transiting = NO;
}];
}
- (void)setContentFullScreenRect:(CGRect)contentFullScreenRect {
_contentFullScreenRect = contentFullScreenRect;
if (!self.transiting && self.isFullScreen && !self.interation) {
self.contentView.frame = contentFullScreenRect;
}
}
@end

View File

@@ -0,0 +1,167 @@
//
// ZFReachabilityManager.h
// 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 <Foundation/Foundation.h>
#if !TARGET_OS_WATCH
#import <SystemConfiguration/SystemConfiguration.h>
typedef NS_ENUM(NSInteger, ZFReachabilityStatus) {
ZFReachabilityStatusUnknown = -1,
ZFReachabilityStatusNotReachable = 0,
ZFReachabilityStatusReachableViaWiFi = 1,
ZFReachabilityStatusReachableVia2G = 2,
ZFReachabilityStatusReachableVia3G = 3,
ZFReachabilityStatusReachableVia4G = 4,
};
NS_ASSUME_NONNULL_BEGIN
/**
`ZFReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability.
See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ )
@warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined.
*/
@interface ZFReachabilityManager : NSObject
/**
The current network reachability status.
*/
@property (readonly, nonatomic, assign) ZFReachabilityStatus networkReachabilityStatus;
/**
Whether or not the network is currently reachable.
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
Whether or not the network is currently reachable via WWAN.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
Whether or not the network is currently reachable via WiFi.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
///---------------------
/// @name Initialization
///---------------------
/**
Returns the shared network reachability manager.
*/
+ (instancetype)sharedManager;
/**
Creates and returns a network reachability manager with the default socket address.
@return An initialized network reachability manager, actively monitoring the default socket address.
*/
+ (instancetype)manager;
/**
Creates and returns a network reachability manager for the specified domain.
@param domain The domain used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified domain.
*/
+ (instancetype)managerForDomain:(NSString *)domain;
/**
Creates and returns a network reachability manager for the socket address.
@param address The socket address (`sockaddr_in6`) used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified socket address.
*/
+ (instancetype)managerForAddress:(const void *)address;
/**
Initializes an instance of a network reachability manager from the specified reachability object.
@param reachability The reachability object to monitor.
@return An initialized network reachability manager, actively monitoring the specified reachability.
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
///--------------------------------------------------
/// @name Starting & Stopping Reachability Monitoring
///--------------------------------------------------
/**
Starts monitoring for changes in network reachability status.
*/
- (void)startMonitoring;
/**
Stops monitoring for changes in network reachability status.
*/
- (void)stopMonitoring;
///-------------------------------------------------
/// @name Getting Localized Reachability Description
///-------------------------------------------------
/**
Returns a localized string representation of the current network reachability status.
*/
- (NSString *)localizedNetworkReachabilityStatusString;
///---------------------------------------------------
/// @name Setting Network Reachability Change Callback
///---------------------------------------------------
/**
Sets a callback to be executed when the network availability of the `baseURL` host changes.
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(ZFReachabilityStatus status))block;
@end
///--------------------
/// @name Notifications
///--------------------
FOUNDATION_EXPORT NSString * const ZFReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const ZFReachabilityNotificationStatusItem;
///--------------------
/// @name Functions
///--------------------
/**
Returns a localized string representation of an `ZFReachabilityStatus` value.
*/
FOUNDATION_EXPORT NSString * ZFStringFromNetworkReachabilityStatus(ZFReachabilityStatus status);
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,269 @@
//
// ZFReachabilityManager.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 "ZFReachabilityManager.h"
#if !TARGET_OS_WATCH
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
NSString * const ZFReachabilityDidChangeNotification = @"com.ZFPlayer.reachability.change";
NSString * const ZFReachabilityNotificationStatusItem = @"ZFNetworkingReachabilityNotificationStatusItem";
typedef void (^ZFReachabilityStatusBlock)(ZFReachabilityStatus status);
NSString * ZFStringFromNetworkReachabilityStatus(ZFReachabilityStatus status) {
switch (status) {
case ZFReachabilityStatusNotReachable:
return NSLocalizedStringFromTable(@"Not Reachable", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableViaWiFi:
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableVia2G:
return NSLocalizedStringFromTable(@"Reachable via 2G", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableVia3G:
return NSLocalizedStringFromTable(@"Reachable via 3G", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableVia4G:
return NSLocalizedStringFromTable(@"Reachable via 4G", @"ZFPlayer", nil);
case ZFReachabilityStatusUnknown:
default:
return NSLocalizedStringFromTable(@"Unknown", @"ZFPlayer", nil);
}
}
static ZFReachabilityStatus ZFReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
ZFReachabilityStatus status = ZFReachabilityStatusUnknown;
if (isNetworkReachable == NO) {
status = ZFReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc] init];
NSString *currentRadioAccessTechnology = info.currentRadioAccessTechnology;
if (currentRadioAccessTechnology) {
if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
status = ZFReachabilityStatusReachableVia4G;
} else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
status = ZFReachabilityStatusReachableVia2G;
} else {
status = ZFReachabilityStatusReachableVia3G;
}
}
}
#endif
else {
status = ZFReachabilityStatusReachableViaWiFi;
}
return status;
}
/**
* Queue a status change notification for the main thread.
*
* This is done to ensure that the notifications are received in the same order
* as they are sent. If notifications are sent directly, it is possible that
* a queued notification (for an earlier status condition) is processed after
* the later update, resulting in the listener being left in the wrong state.
*/
static void ZFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, ZFReachabilityStatusBlock block) {
ZFReachabilityStatus status = ZFReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
if (block) block(status);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSMutableDictionary *userInfo = @{}.mutableCopy;
userInfo[ZFReachabilityNotificationStatusItem] = @(status);
[notificationCenter postNotificationName:ZFReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
ZFPostReachabilityStatusChange(flags, (__bridge ZFReachabilityStatusBlock)info);
}
static const void * ZFReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
static void ZFReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
@interface ZFReachabilityManager ()
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
@property (readwrite, nonatomic, assign) ZFReachabilityStatus networkReachabilityStatus;
@property (readwrite, nonatomic, copy) ZFReachabilityStatusBlock networkReachabilityStatusBlock;
@end
@implementation ZFReachabilityManager
+ (instancetype)sharedManager {
static ZFReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)manager {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = ZFReachabilityStatusUnknown;
return self;
}
- (instancetype)init NS_UNAVAILABLE
{
return nil;
}
- (void)dealloc {
[self stopMonitoring];
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
}
}
#pragma mark -
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return (self.networkReachabilityStatus == ZFReachabilityStatusReachableVia2G ||self.networkReachabilityStatus == ZFReachabilityStatusReachableVia3G || self.networkReachabilityStatus == ZFReachabilityStatusReachableVia4G);
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == ZFReachabilityStatusReachableViaWiFi;
}
#pragma mark -
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
ZFReachabilityStatusBlock callback = ^(ZFReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, ZFReachabilityRetainCallback, ZFReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
ZFPostReachabilityStatusChange(flags, callback);
}
});
}
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
#pragma mark -
- (NSString *)localizedNetworkReachabilityStatusString {
return ZFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}
#pragma mark -
- (void)setReachabilityStatusChangeBlock:(void (^)(ZFReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
#pragma mark - NSKeyValueObserving
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
return [NSSet setWithObject:@"networkReachabilityStatus"];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
@end
#endif