This commit is contained in:
DDIsFriend
2023-08-18 17:28:57 +08:00
commit f0e8a1709d
4282 changed files with 192396 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
//
// ZFAVPlayerManager.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 <AVFoundation/AVFoundation.h>
#if __has_include(<ZFPlayer/ZFPlayerMediaPlayback.h>)
#import <ZFPlayer/ZFPlayerMediaPlayback.h>
#else
#import "ZFPlayerMediaPlayback.h"
#endif
@interface ZFAVPlayerManager : NSObject <ZFPlayerMediaPlayback>
@property (nonatomic, strong, readonly) AVURLAsset *asset;
@property (nonatomic, strong, readonly) AVPlayerItem *playerItem;
@property (nonatomic, strong, readonly) AVPlayer *player;
@property (nonatomic, assign) NSTimeInterval timeRefreshInterval;
/// 视频请求头
@property (nonatomic, strong) NSDictionary *requestHeader;
@property (nonatomic, strong, readonly) AVPlayerLayer *avPlayerLayer;
@end

View File

@@ -0,0 +1,527 @@
//
// ZFAVPlayerManager.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 "ZFAVPlayerManager.h"
#import <UIKit/UIKit.h>
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFKVOController.h>
#import <ZFPlayer/ZFPlayerConst.h>
#import <ZFPlayer/ZFReachabilityManager.h>
#else
#import "ZFKVOController.h"
#import "ZFPlayerConst.h"
#import "ZFReachabilityManager.h"
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
/*!
* Refresh interval for timed observations of AVPlayer
*/
static NSString *const kStatus = @"status";
static NSString *const kLoadedTimeRanges = @"loadedTimeRanges";
static NSString *const kPlaybackBufferEmpty = @"playbackBufferEmpty";
static NSString *const kPlaybackLikelyToKeepUp = @"playbackLikelyToKeepUp";
static NSString *const kPresentationSize = @"presentationSize";
@interface ZFPlayerPresentView : UIView
@property (nonatomic, strong) AVPlayer *player;
/// default is AVLayerVideoGravityResizeAspect.
@property (nonatomic, strong) AVLayerVideoGravity videoGravity;
@end
@implementation ZFPlayerPresentView
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayerLayer *)avLayer {
return (AVPlayerLayer *)self.layer;
}
- (void)setPlayer:(AVPlayer *)player {
if (player == _player) return;
self.avLayer.player = player;
}
- (void)setVideoGravity:(AVLayerVideoGravity)videoGravity {
if (videoGravity == self.videoGravity) return;
[self avLayer].videoGravity = videoGravity;
}
- (AVLayerVideoGravity)videoGravity {
return [self avLayer].videoGravity;
}
@end
@interface ZFAVPlayerManager () {
id _timeObserver;
id _itemEndObserver;
ZFKVOController *_playerItemKVO;
}
@property (nonatomic, strong) AVPlayerLayer *playerLayer;
@property (nonatomic, assign) BOOL isBuffering;
@property (nonatomic, assign) BOOL isReadyToPlay;
@property (nonatomic, strong) AVAssetImageGenerator *imageGenerator;
@end
@implementation ZFAVPlayerManager
@synthesize view = _view;
@synthesize currentTime = _currentTime;
@synthesize totalTime = _totalTime;
@synthesize playerPlayTimeChanged = _playerPlayTimeChanged;
@synthesize playerBufferTimeChanged = _playerBufferTimeChanged;
@synthesize playerDidToEnd = _playerDidToEnd;
@synthesize bufferTime = _bufferTime;
@synthesize playState = _playState;
@synthesize loadState = _loadState;
@synthesize assetURL = _assetURL;
@synthesize playerPrepareToPlay = _playerPrepareToPlay;
@synthesize playerReadyToPlay = _playerReadyToPlay;
@synthesize playerPlayStateChanged = _playerPlayStateChanged;
@synthesize playerLoadStateChanged = _playerLoadStateChanged;
@synthesize seekTime = _seekTime;
@synthesize muted = _muted;
@synthesize volume = _volume;
@synthesize presentationSize = _presentationSize;
@synthesize isPlaying = _isPlaying;
@synthesize rate = _rate;
@synthesize isPreparedToPlay = _isPreparedToPlay;
@synthesize shouldAutoPlay = _shouldAutoPlay;
@synthesize scalingMode = _scalingMode;
@synthesize playerPlayFailed = _playerPlayFailed;
@synthesize presentationSizeChanged = _presentationSizeChanged;
- (instancetype)init {
self = [super init];
if (self) {
_scalingMode = ZFPlayerScalingModeAspectFit;
_shouldAutoPlay = YES;
}
return self;
}
- (void)prepareToPlay {
if (!_assetURL) return;
_isPreparedToPlay = YES;
[self initializePlayer];
if (self.shouldAutoPlay) {
[self play];
}
self.loadState = ZFPlayerLoadStatePrepare;
if (self.playerPrepareToPlay) self.playerPrepareToPlay(self, self.assetURL);
}
- (void)reloadPlayer {
self.seekTime = self.currentTime;
[self prepareToPlay];
}
- (void)play {
if (!_isPreparedToPlay) {
[self prepareToPlay];
} else {
[self.player play];
self.player.rate = self.rate;
self->_isPlaying = YES;
self.playState = ZFPlayerPlayStatePlaying;
}
}
- (void)pause {
[self.player pause];
self->_isPlaying = NO;
self.playState = ZFPlayerPlayStatePaused;
[_playerItem cancelPendingSeeks];
[_asset cancelLoading];
}
- (void)stop {
[_playerItemKVO safelyRemoveAllObservers];
self.loadState = ZFPlayerLoadStateUnknown;
self.playState = ZFPlayerPlayStatePlayStopped;
if (self.player.rate != 0) [self.player pause];
[_playerItem cancelPendingSeeks];
[_asset cancelLoading];
[self.player removeTimeObserver:_timeObserver];
[self.player replaceCurrentItemWithPlayerItem:nil];
self.presentationSize = CGSizeZero;
_timeObserver = nil;
[[NSNotificationCenter defaultCenter] removeObserver:_itemEndObserver name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
_itemEndObserver = nil;
_isPlaying = NO;
_player = nil;
_assetURL = nil;
_playerItem = nil;
_isPreparedToPlay = NO;
self->_currentTime = 0;
self->_totalTime = 0;
self->_bufferTime = 0;
self.isReadyToPlay = NO;
}
- (void)replay {
@zf_weakify(self)
[self seekToTime:0 completionHandler:^(BOOL finished) {
@zf_strongify(self)
if (finished) {
[self play];
}
}];
}
- (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
if (self.totalTime > 0) {
[_player.currentItem cancelPendingSeeks];
int32_t timeScale = _player.currentItem.asset.duration.timescale;
CMTime seekTime = CMTimeMakeWithSeconds(time, timeScale);
[_player seekToTime:seekTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:completionHandler];
} else {
self.seekTime = time;
}
}
- (UIImage *)thumbnailImageAtCurrentTime {
CMTime expectedTime = self.playerItem.currentTime;
CGImageRef cgImage = NULL;
self.imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
self.imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
cgImage = [self.imageGenerator copyCGImageAtTime:expectedTime actualTime:NULL error:NULL];
if (!cgImage) {
self.imageGenerator.requestedTimeToleranceBefore = kCMTimePositiveInfinity;
self.imageGenerator.requestedTimeToleranceAfter = kCMTimePositiveInfinity;
cgImage = [self.imageGenerator copyCGImageAtTime:expectedTime actualTime:NULL error:NULL];
}
UIImage *image = [UIImage imageWithCGImage:cgImage];
return image;
}
- (void)thumbnailImageAtCurrentTime:(void(^)(UIImage *))handler {
CMTime expectedTime = self.playerItem.currentTime;
[self.imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:expectedTime]] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
if (handler) {
UIImage *finalImage = [UIImage imageWithCGImage:image];
handler(finalImage);
}
}];
}
#pragma mark - private method
/// Calculate buffer progress
- (NSTimeInterval)availableDuration {
NSArray *timeRangeArray = _playerItem.loadedTimeRanges;
CMTime currentTime = [_player currentTime];
BOOL foundRange = NO;
CMTimeRange aTimeRange = {0};
if (timeRangeArray.count) {
aTimeRange = [[timeRangeArray objectAtIndex:0] CMTimeRangeValue];
if (CMTimeRangeContainsTime(aTimeRange, currentTime)) {
foundRange = YES;
}
}
if (foundRange) {
CMTime maxTime = CMTimeRangeGetEnd(aTimeRange);
NSTimeInterval playableDuration = CMTimeGetSeconds(maxTime);
if (playableDuration > 0) {
return playableDuration;
}
}
return 0;
}
- (void)initializePlayer {
_asset = [AVURLAsset URLAssetWithURL:self.assetURL options:self.requestHeader];
_playerItem = [AVPlayerItem playerItemWithAsset:_asset];
_player = [AVPlayer playerWithPlayerItem:_playerItem];
_imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:_asset];
[self enableAudioTracks:YES inPlayerItem:_playerItem];
ZFPlayerPresentView *presentView = [[ZFPlayerPresentView alloc] init];
presentView.player = _player;
self.view.playerView = presentView;
self.scalingMode = _scalingMode;
if (@available(iOS 9.0, *)) {
_playerItem.canUseNetworkResourcesForLiveStreamingWhilePaused = NO;
}
if (@available(iOS 10.0, *)) {
_playerItem.preferredForwardBufferDuration = 5;
/// AVPlayer
_player.automaticallyWaitsToMinimizeStalling = NO;
}
[self itemObserving];
}
/// Playback speed switching method
- (void)enableAudioTracks:(BOOL)enable inPlayerItem:(AVPlayerItem*)playerItem {
for (AVPlayerItemTrack *track in playerItem.tracks){
if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo]) {
track.enabled = enable;
}
}
}
/**
*
*/
- (void)bufferingSomeSecond {
// playbackBufferEmptybufferingOneSecondbufferingSomeSecond
if (self.isBuffering || self.playState == ZFPlayerPlayStatePlayStopped) return;
///
if ([ZFReachabilityManager sharedManager].networkReachabilityStatus == ZFReachabilityStatusNotReachable) return;
self.isBuffering = YES;
//
[self pause];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
if (!self.isPlaying && self.loadState == ZFPlayerLoadStateStalled) {
self.isBuffering = NO;
return;
}
[self play];
// play
self.isBuffering = NO;
if (!self.playerItem.isPlaybackLikelyToKeepUp) [self bufferingSomeSecond];
});
}
- (void)itemObserving {
[_playerItemKVO safelyRemoveAllObservers];
_playerItemKVO = [[ZFKVOController alloc] initWithTarget:_playerItem];
[_playerItemKVO safelyAddObserver:self
forKeyPath:kStatus
options:NSKeyValueObservingOptionNew
context:nil];
[_playerItemKVO safelyAddObserver:self
forKeyPath:kPlaybackBufferEmpty
options:NSKeyValueObservingOptionNew
context:nil];
[_playerItemKVO safelyAddObserver:self
forKeyPath:kPlaybackLikelyToKeepUp
options:NSKeyValueObservingOptionNew
context:nil];
[_playerItemKVO safelyAddObserver:self
forKeyPath:kLoadedTimeRanges
options:NSKeyValueObservingOptionNew
context:nil];
[_playerItemKVO safelyAddObserver:self
forKeyPath:kPresentationSize
options:NSKeyValueObservingOptionNew
context:nil];
CMTime interval = CMTimeMakeWithSeconds(self.timeRefreshInterval > 0 ? self.timeRefreshInterval : 0.1, NSEC_PER_SEC);
@zf_weakify(self)
_timeObserver = [self.player addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
@zf_strongify(self)
if (!self) return;
NSArray *loadedRanges = self.playerItem.seekableTimeRanges;
if (self.isPlaying && self.loadState == ZFPlayerLoadStateStalled) self.player.rate = self.rate;
if (loadedRanges.count > 0) {
if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
}
}];
_itemEndObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
@zf_strongify(self)
if (!self) return;
self.playState = ZFPlayerPlayStatePlayStopped;
if (self.playerDidToEnd) self.playerDidToEnd(self);
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
dispatch_async(dispatch_get_main_queue(), ^{
if ([keyPath isEqualToString:kStatus]) {
if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
if (!self.isReadyToPlay) {
self.isReadyToPlay = YES;
self.loadState = ZFPlayerLoadStatePlaythroughOK;
if (self.playerReadyToPlay) self.playerReadyToPlay(self, self.assetURL);
}
if (self.seekTime) {
if (self.shouldAutoPlay) [self pause];
@zf_weakify(self)
[self seekToTime:self.seekTime completionHandler:^(BOOL finished) {
@zf_strongify(self)
if (finished) {
if (self.shouldAutoPlay) [self play];
}
}];
self.seekTime = 0;
} else {
if (self.shouldAutoPlay && self.isPlaying) [self play];
}
self.player.muted = self.muted;
NSArray *loadedRanges = self.playerItem.seekableTimeRanges;
if (loadedRanges.count > 0) {
if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
}
} else if (self.player.currentItem.status == AVPlayerItemStatusFailed) {
self.playState = ZFPlayerPlayStatePlayFailed;
self->_isPlaying = NO;
NSError *error = self.player.currentItem.error;
if (self.playerPlayFailed) self.playerPlayFailed(self, error);
}
} else if ([keyPath isEqualToString:kPlaybackBufferEmpty]) {
// When the buffer is empty
if (self.playerItem.playbackBufferEmpty) {
self.loadState = ZFPlayerLoadStateStalled;
[self bufferingSomeSecond];
}
} else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUp]) {
// When the buffer is good
if (self.playerItem.playbackLikelyToKeepUp) {
self.loadState = ZFPlayerLoadStatePlayable;
if (self.isPlaying) [self.player play];
}
} else if ([keyPath isEqualToString:kLoadedTimeRanges]) {
NSTimeInterval bufferTime = [self availableDuration];
self->_bufferTime = bufferTime;
if (self.playerBufferTimeChanged) self.playerBufferTimeChanged(self, bufferTime);
} else if ([keyPath isEqualToString:kPresentationSize]) {
self.presentationSize = self.playerItem.presentationSize;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
});
}
#pragma mark - getter
- (ZFPlayerView *)view {
if (!_view) {
ZFPlayerView *view = [[ZFPlayerView alloc] init];
_view = view;
}
return _view;
}
- (AVPlayerLayer *)avPlayerLayer {
ZFPlayerPresentView *view = (ZFPlayerPresentView *)self.view.playerView;
return [view avLayer];
}
- (float)rate {
return _rate == 0 ?1:_rate;
}
- (NSTimeInterval)totalTime {
NSTimeInterval sec = CMTimeGetSeconds(self.player.currentItem.duration);
if (isnan(sec)) {
return 0;
}
return sec;
}
- (NSTimeInterval)currentTime {
NSTimeInterval sec = CMTimeGetSeconds(self.playerItem.currentTime);
if (isnan(sec) || sec < 0) {
return 0;
}
return sec;
}
#pragma mark - setter
- (void)setPlayState:(ZFPlayerPlaybackState)playState {
_playState = playState;
if (self.playerPlayStateChanged) self.playerPlayStateChanged(self, playState);
}
- (void)setLoadState:(ZFPlayerLoadState)loadState {
_loadState = loadState;
if (self.playerLoadStateChanged) self.playerLoadStateChanged(self, loadState);
}
- (void)setAssetURL:(NSURL *)assetURL {
if (self.player) [self stop];
_assetURL = assetURL;
[self prepareToPlay];
}
- (void)setRate:(float)rate {
_rate = rate;
if (self.player && fabsf(_player.rate) > 0.00001f) {
self.player.rate = rate;
}
}
- (void)setMuted:(BOOL)muted {
_muted = muted;
self.player.muted = muted;
}
- (void)setScalingMode:(ZFPlayerScalingMode)scalingMode {
_scalingMode = scalingMode;
ZFPlayerPresentView *presentView = (ZFPlayerPresentView *)self.view.playerView;
self.view.scalingMode = scalingMode;
switch (scalingMode) {
case ZFPlayerScalingModeNone:
presentView.videoGravity = AVLayerVideoGravityResizeAspect;
break;
case ZFPlayerScalingModeAspectFit:
presentView.videoGravity = AVLayerVideoGravityResizeAspect;
break;
case ZFPlayerScalingModeAspectFill:
presentView.videoGravity = AVLayerVideoGravityResizeAspectFill;
break;
case ZFPlayerScalingModeFill:
presentView.videoGravity = AVLayerVideoGravityResize;
break;
default:
break;
}
}
- (void)setVolume:(float)volume {
_volume = MIN(MAX(0, volume), 1);
self.player.volume = _volume;
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
self.view.presentationSize = presentationSize;
if (self.presentationSizeChanged) {
self.presentationSizeChanged(self, self.presentationSize);
}
}
@end
#pragma clang diagnostic pop

View File

@@ -0,0 +1,127 @@
//
// UIImageView+ZFCache.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>
typedef void (^ZFDownLoadDataCallBack)(NSData *data, NSError *error);
typedef void (^ZFDownloadProgressBlock)(unsigned long long total, unsigned long long current);
@interface ZFImageDownloader : NSObject<NSURLSessionDownloadDelegate>
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionDownloadTask *task;
@property (nonatomic, assign) unsigned long long totalLength;
@property (nonatomic, assign) unsigned long long currentLength;
@property (nonatomic, copy) ZFDownloadProgressBlock progressBlock;
@property (nonatomic, copy) ZFDownLoadDataCallBack callbackOnFinished;
- (void)startDownloadImageWithUrl:(NSString *)url
progress:(ZFDownloadProgressBlock)progress
finished:(ZFDownLoadDataCallBack)finished;
@end
typedef void (^ZFImageBlock)(UIImage *image);
@interface UIImageView (ZFCache)
/**
* Get/Set the callback block when download the image finished.
*
* The image object from network or from disk.
*/
@property (nonatomic, copy) ZFImageBlock completion;
/**
* Image downloader
*/
@property (nonatomic, strong) ZFImageDownloader *imageDownloader;
/**
* Specify the URL to download images fails, the number of retries, the default is 2
*/
@property (nonatomic, assign) NSUInteger attemptToReloadTimesForFailedURL;
/**
* Will automatically download to cutting for UIImageView size of image.The default value is NO.
* If set to YES, then the download after a successful store only after cutting the image
*/
@property (nonatomic, assign) BOOL shouldAutoClipImageToViewSize;
/**
* Set the imageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImageName The image name to be set initially, until the image request finishes.
*/
- (void)setImageWithURLString:(NSString *)url placeholderImageName:(NSString *)placeholderImageName;
/**
* Set the imageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImage The image to be set initially, until the image request finishes.
*/
- (void)setImageWithURLString:(NSString *)url placeholder:(UIImage *)placeholderImage;
/**
* Set the imageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImage The image to be set initially, until the image request finishes.
* @param completion A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrieved from the local cache or from the network.
* The fourth parameter is the original image url.
*/
- (void)setImageWithURLString:(NSString *)url
placeholder:(UIImage *)placeholderImage
completion:(void (^)(UIImage *image))completion;
/**
* Set the imageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImageName The image name to be set initially, until the image request finishes.
* @param completion A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrieved from the local cache or from the network.
* The fourth parameter is the original image url.
*/
- (void)setImageWithURLString:(NSString *)url
placeholderImageName:(NSString *)placeholderImageName
completion:(void (^)(UIImage *image))completion;
@end

View File

@@ -0,0 +1,411 @@
//
// UIImageView+ZFCache.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 "UIImageView+ZFCache.h"
#import <objc/runtime.h>
#import <CommonCrypto/CommonDigest.h>
@implementation ZFImageDownloader
- (void)startDownloadImageWithUrl:(NSString *)url
progress:(ZFDownloadProgressBlock)progress
finished:(ZFDownLoadDataCallBack)finished {
self.progressBlock = progress;
self.callbackOnFinished = finished;
if ([NSURL URLWithString:url] == nil) {
if (finished) { finished(nil, nil); }
return;
}
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:60];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
self.session = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:queue];
NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request];
[task resume];
self.task = task;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSData *data = [NSData dataWithContentsOfURL:location];
if (self.progressBlock) {
self.progressBlock(self.totalLength, self.currentLength);
}
if (self.callbackOnFinished) {
self.callbackOnFinished(data, nil);
//
self.callbackOnFinished = nil;
}
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
self.currentLength = totalBytesWritten;
self.totalLength = totalBytesExpectedToWrite;
if (self.progressBlock) {
self.progressBlock(self.totalLength, self.currentLength);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if ([error code] != NSURLErrorCancelled) {
if (self.callbackOnFinished) {
self.callbackOnFinished(nil, error);
}
self.callbackOnFinished = nil;
}
}
@end
@interface NSString (md5)
+ (NSString *)cachedFileNameForKey:(NSString *)key;
+ (NSString *)zf_cachePath;
+ (NSString *)zf_keyForRequest:(NSURLRequest *)request;
@end
@implementation NSString (md5)
+ (NSString *)zf_keyForRequest:(NSURLRequest *)request {
return request.URL.absoluteString;
}
+ (NSString *)zf_cachePath {
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [NSString stringWithFormat:@"%@/%@/%@",cachePath,@"default",@"com.hackemist.SDWebImageCache.default"];
return directoryPath;
}
+ (NSString *)cachedFileNameForKey:(NSString *)key {
const char *str = [key UTF8String];
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];
return filename;
}
@end
@interface UIApplication (ZFCacheImage)
@property (nonatomic, strong, readonly) NSMutableDictionary *zf_cacheFaileTimes;
- (UIImage *)zf_cacheImageForRequest:(NSURLRequest *)request;
- (void)zf_cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request;
- (void)zf_cacheFailRequest:(NSURLRequest *)request;
- (NSUInteger)zf_failTimesForRequest:(NSURLRequest *)request;
@end
@implementation UIApplication (ZFCacheImage)
- (NSMutableDictionary *)zf_cacheFaileTimes {
NSMutableDictionary *dict = objc_getAssociatedObject(self, _cmd);
if (!dict) {
dict = [[NSMutableDictionary alloc] init];
}
return dict;
}
- (void)setZf_cacheFaileTimes:(NSMutableDictionary *)zf_cacheFaileTimes {
objc_setAssociatedObject(self, @selector(zf_cacheFaileTimes), zf_cacheFaileTimes, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)zf_clearCache {
[self.zf_cacheFaileTimes removeAllObjects];
self.zf_cacheFaileTimes = nil;
}
- (void)zf_clearDiskCaches {
NSString *directoryPath = [NSString zf_cachePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
dispatch_queue_t ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
dispatch_async(ioQueue, ^{
[[NSFileManager defaultManager] removeItemAtPath:directoryPath error:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:nil];
});
}
[self zf_clearCache];
}
- (UIImage *)zf_cacheImageForRequest:(NSURLRequest *)request {
if (request) {
NSString *directoryPath = [NSString zf_cachePath];
NSString *path = [NSString stringWithFormat:@"%@/%@", directoryPath, [NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
return [UIImage imageWithContentsOfFile:path];
}
return nil;
}
- (NSUInteger)zf_failTimesForRequest:(NSURLRequest *)request {
NSNumber *faileTimes = [self.zf_cacheFaileTimes objectForKey:[NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
if (faileTimes && [faileTimes respondsToSelector:@selector(integerValue)]) {
return faileTimes.integerValue;
}
return 0;
}
- (void)zf_cacheFailRequest:(NSURLRequest *)request {
NSNumber *faileTimes = [self.zf_cacheFaileTimes objectForKey:[NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
NSUInteger times = 0;
if (faileTimes && [faileTimes respondsToSelector:@selector(integerValue)]) {
times = [faileTimes integerValue];
}
times++;
[self.zf_cacheFaileTimes setObject:@(times) forKey:[NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
}
- (void)zf_cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request {
if (!image || !request) { return; }
NSString *directoryPath = [NSString zf_cachePath];
if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) { return; }
}
NSString *path = [NSString stringWithFormat:@"%@/%@", directoryPath, [NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
NSData *data = UIImagePNGRepresentation(image);
if (data) {
[[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
}
}
@end
@implementation UIImageView (ZFCache)
#pragma mark - getter
- (ZFImageBlock)completion {
return objc_getAssociatedObject(self, _cmd);
}
- (ZFImageDownloader *)imageDownloader {
return objc_getAssociatedObject(self, _cmd);
}
- (NSUInteger)attemptToReloadTimesForFailedURL {
NSUInteger count = [objc_getAssociatedObject(self, _cmd) integerValue];
if (count == 0) { count = 2; }
return count;
}
- (BOOL)shouldAutoClipImageToViewSize {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
#pragma mark - setter
- (void)setCompletion:(ZFImageBlock)completion {
objc_setAssociatedObject(self, @selector(completion), completion, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setImageDownloader:(ZFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(imageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setAttemptToReloadTimesForFailedURL:(NSUInteger)attemptToReloadTimesForFailedURL {
objc_setAssociatedObject(self, @selector(attemptToReloadTimesForFailedURL), @(attemptToReloadTimesForFailedURL), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setShouldAutoClipImageToViewSize:(BOOL)shouldAutoClipImageToViewSize {
objc_setAssociatedObject(self, @selector(shouldAutoClipImageToViewSize), @(shouldAutoClipImageToViewSize), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - public method
- (void)setImageWithURLString:(NSString *)url
placeholderImageName:(NSString *)placeholderImageName {
return [self setImageWithURLString:url placeholderImageName:placeholderImageName completion:nil];
}
- (void)setImageWithURLString:(NSString *)url placeholder:(UIImage *)placeholderImage {
return [self setImageWithURLString:url placeholder:placeholderImage completion:nil];
}
- (void)setImageWithURLString:(NSString *)url
placeholderImageName:(NSString *)placeholderImage
completion:(void (^)(UIImage *image))completion {
NSString *path = [[NSBundle mainBundle] pathForResource:placeholderImage ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path];
if (image == nil) { image = [UIImage imageNamed:placeholderImage]; }
[self setImageWithURLString:url placeholder:image completion:completion];
}
- (void)setImageWithURLString:(NSString *)url
placeholder:(UIImage *)placeholderImageName
completion:(void (^)(UIImage *image))completion {
[self.layer removeAllAnimations];
self.completion = completion;
if (url == nil || [url isKindOfClass:[NSNull class]] || (![url hasPrefix:@"http://"] && ![url hasPrefix:@"https://"])) {
[self setImage:placeholderImageName isFromCache:YES];
if (completion) {
self.completion(self.image);
}
return;
}
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
[self downloadWithReqeust:request holder:placeholderImageName];
}
#pragma mark - private method
- (void)downloadWithReqeust:(NSURLRequest *)theRequest holder:(UIImage *)holder {
UIImage *cachedImage = [[UIApplication sharedApplication] zf_cacheImageForRequest:theRequest];
if (cachedImage) {
[self setImage:cachedImage isFromCache:YES];
if (self.completion) {
self.completion(cachedImage);
}
return;
}
[self setImage:holder isFromCache:YES];
if ([[UIApplication sharedApplication] zf_failTimesForRequest:theRequest] >= self.attemptToReloadTimesForFailedURL) {
return;
}
[self cancelRequest];
self.imageDownloader = nil;
__weak __typeof(self) weakSelf = self;
self.imageDownloader = [[ZFImageDownloader alloc] init];
[self.imageDownloader startDownloadImageWithUrl:theRequest.URL.absoluteString progress:nil finished:^(NSData *data, NSError *error) {
// success
if (data != nil && error == nil) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIImage *image = [UIImage imageWithData:data];
UIImage *finalImage = image;
if (image) {
if (weakSelf.shouldAutoClipImageToViewSize) {
// cutting
if (fabs(weakSelf.frame.size.width - image.size.width) != 0
&& fabs(weakSelf.frame.size.height - image.size.height) != 0) {
finalImage = [self clipImage:image toSize:weakSelf.frame.size isScaleToMax:YES];
}
}
[[UIApplication sharedApplication] zf_cacheImage:finalImage forRequest:theRequest];
} else {
[[UIApplication sharedApplication] zf_cacheFailRequest:theRequest];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (finalImage) {
[weakSelf setImage:finalImage isFromCache:NO];
if (weakSelf.completion) {
weakSelf.completion(weakSelf.image);
}
} else {// error data
if (weakSelf.completion) {
weakSelf.completion(weakSelf.image);
}
}
});
});
} else { // error
[[UIApplication sharedApplication] zf_cacheFailRequest:theRequest];
if (weakSelf.completion) {
weakSelf.completion(weakSelf.image);
}
}
}];
}
- (void)setImage:(UIImage *)image isFromCache:(BOOL)isFromCache {
self.image = image;
if (!isFromCache) {
CATransition *animation = [CATransition animation];
[animation setDuration:0.6f];
[animation setType:kCATransitionFade];
animation.removedOnCompletion = YES;
[self.layer addAnimation:animation forKey:@"transition"];
}
}
- (void)cancelRequest {
[self.imageDownloader.task cancel];
}
- (UIImage *)clipImage:(UIImage *)image toSize:(CGSize)size isScaleToMax:(BOOL)isScaleToMax {
CGFloat scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(size, NO, scale);
CGSize aspectFitSize = CGSizeZero;
if (image.size.width != 0 && image.size.height != 0) {
CGFloat rateWidth = size.width / image.size.width;
CGFloat rateHeight = size.height / image.size.height;
CGFloat rate = isScaleToMax ? MAX(rateHeight, rateWidth) : MIN(rateHeight, rateWidth);
aspectFitSize = CGSizeMake(image.size.width * rate, image.size.height * rate);
}
[image drawInRect:CGRectMake(0, 0, aspectFitSize.width, aspectFitSize.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
@end

View File

@@ -0,0 +1,45 @@
//
// UIView+ZFFrame.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 UIView (ZFFrame)
@property (nonatomic) CGFloat zf_x;
@property (nonatomic) CGFloat zf_y;
@property (nonatomic) CGFloat zf_width;
@property (nonatomic) CGFloat zf_height;
@property (nonatomic) CGFloat zf_top;
@property (nonatomic) CGFloat zf_bottom;
@property (nonatomic) CGFloat zf_left;
@property (nonatomic) CGFloat zf_right;
@property (nonatomic) CGFloat zf_centerX;
@property (nonatomic) CGFloat zf_centerY;
@property (nonatomic) CGPoint zf_origin;
@property (nonatomic) CGSize zf_size;
@end

View File

@@ -0,0 +1,149 @@
//
// UIView+ZFFrame.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 "UIView+ZFFrame.h"
@implementation UIView (ZFFrame)
- (CGFloat)zf_x {
return self.frame.origin.x;
}
- (void)setZf_x:(CGFloat)zf_x {
CGRect newFrame = self.frame;
newFrame.origin.x = zf_x;
self.frame = newFrame;
}
- (CGFloat)zf_y {
return self.frame.origin.y;
}
- (void)setZf_y:(CGFloat)zf_y {
CGRect newFrame = self.frame;
newFrame.origin.y = zf_y;
self.frame = newFrame;
}
- (CGFloat)zf_width {
return CGRectGetWidth(self.bounds);
}
- (void)setZf_width:(CGFloat)zf_width {
CGRect newFrame = self.frame;
newFrame.size.width = zf_width;
self.frame = newFrame;
}
- (CGFloat)zf_height {
return CGRectGetHeight(self.bounds);
}
- (void)setZf_height:(CGFloat)zf_height {
CGRect newFrame = self.frame;
newFrame.size.height = zf_height;
self.frame = newFrame;
}
- (CGFloat)zf_top {
return self.frame.origin.y;
}
- (void)setZf_top:(CGFloat)zf_top {
CGRect newFrame = self.frame;
newFrame.origin.y = zf_top;
self.frame = newFrame;
}
- (CGFloat)zf_bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setZf_bottom:(CGFloat)zf_bottom {
CGRect newFrame = self.frame;
newFrame.origin.y = zf_bottom - self.frame.size.height;
self.frame = newFrame;
}
- (CGFloat)zf_left {
return self.frame.origin.x;
}
- (void)setZf_left:(CGFloat)zf_left {
CGRect newFrame = self.frame;
newFrame.origin.x = zf_left;
self.frame = newFrame;
}
- (CGFloat)zf_right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setZf_right:(CGFloat)zf_right {
CGRect newFrame = self.frame;
newFrame.origin.x = zf_right - self.frame.size.width;
self.frame = newFrame;
}
- (CGFloat)zf_centerX {
return self.center.x;
}
- (void)setZf_centerX:(CGFloat)zf_centerX {
CGPoint newCenter = self.center;
newCenter.x = zf_centerX;
self.center = newCenter;
}
- (CGFloat)zf_centerY {
return self.center.y;
}
- (void)setZf_centerY:(CGFloat)zf_centerY {
CGPoint newCenter = self.center;
newCenter.y = zf_centerY;
self.center = newCenter;
}
- (CGPoint)zf_origin {
return self.frame.origin;
}
- (void)setZf_origin:(CGPoint)zf_origin {
CGRect newFrame = self.frame;
newFrame.origin = zf_origin;
self.frame = newFrame;
}
- (CGSize)zf_size {
return self.frame.size;
}
- (void)setZf_size:(CGSize)zf_size {
CGRect newFrame = self.frame;
newFrame.size = zf_size;
self.frame = newFrame;
}
@end

View File

@@ -0,0 +1,126 @@
//
// ZFLandScapeControlView.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 "ZFSliderView.h"
#if __has_include(<ZFPlayer/ZFPlayerController.h>)
#import <ZFPlayer/ZFPlayerController.h>
#else
#import "ZFPlayerController.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandScapeControlView : UIView
/// 顶部工具栏
@property (nonatomic, strong, readonly) UIView *topToolView;
/// 返回按钮
@property (nonatomic, strong, readonly) UIButton *backBtn;
/// 标题
@property (nonatomic, strong, readonly) UILabel *titleLabel;
/// 底部工具栏
@property (nonatomic, strong, readonly) UIView *bottomToolView;
/// 播放或暂停按钮
@property (nonatomic, strong, readonly) UIButton *playOrPauseBtn;
/// 播放的当前时间
@property (nonatomic, strong, readonly) UILabel *currentTimeLabel;
/// 滑杆
@property (nonatomic, strong, readonly) ZFSliderView *slider;
/// 视频总时间
@property (nonatomic, strong, readonly) UILabel *totalTimeLabel;
/// 锁定屏幕按钮
@property (nonatomic, strong, readonly) UIButton *lockBtn;
/// 横屏时候是否显示自定义状态栏(iOS13+),默认 NO.
@property (nonatomic, assign) BOOL showCustomStatusBar;
/// 播放器
@property (nonatomic, weak) ZFPlayerController *player;
/// slider滑动中
@property (nonatomic, copy, nullable) void(^sliderValueChanging)(CGFloat value,BOOL forward);
/// slider滑动结束
@property (nonatomic, copy, nullable) void(^sliderValueChanged)(CGFloat value);
/// 返回按钮点击回调
@property (nonatomic, copy) void(^backBtnClickCallback)(void);
/// 如果是暂停状态seek完是否播放默认YES
@property (nonatomic, assign) BOOL seekToPlay;
/// 全屏模式
@property (nonatomic, assign) ZFFullScreenMode fullScreenMode;
/// 重置控制层
- (void)resetControlView;
/// 显示控制层
- (void)showControlView;
/// 隐藏控制层
- (void)hideControlView;
/// 设置播放时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime;
/// 设置缓冲时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime;
/// 是否响应该手势
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch;
/// 视频尺寸改变
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size;
/// 视频view已经旋转
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationWillChange:(ZFOrientationObserver *)observer;
/// 标题和全屏模式
- (void)showTitle:(NSString *_Nullable)title fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/// 根据当前播放状态取反
- (void)playOrPause;
/// 播放按钮状态
- (void)playBtnSelectedState:(BOOL)selected;
/// 调节播放进度slider和当前时间更新
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString;
/// 滑杆结束滑动
- (void)sliderChangeEnded;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,501 @@
//
// ZFLandScapeControlView.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 "ZFLandScapeControlView.h"
#import "UIView+ZFFrame.h"
#import "ZFUtilities.h"
#import "ZFPlayerStatusBar.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayerConst.h"
#endif
@interface ZFLandScapeControlView () <ZFSliderViewDelegate>
@property (nonatomic, strong) ZFPlayerStatusBar *statusBarView;
///
@property (nonatomic, strong) UIView *topToolView;
///
@property (nonatomic, strong) UIButton *backBtn;
///
@property (nonatomic, strong) UILabel *titleLabel;
///
@property (nonatomic, strong) UIView *bottomToolView;
///
@property (nonatomic, strong) UIButton *playOrPauseBtn;
///
@property (nonatomic, strong) UILabel *currentTimeLabel;
///
@property (nonatomic, strong) ZFSliderView *slider;
///
@property (nonatomic, strong) UILabel *totalTimeLabel;
///
@property (nonatomic, strong) UIButton *lockBtn;
@property (nonatomic, assign) BOOL isShow;
@end
@implementation ZFLandScapeControlView
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.topToolView];
[self.topToolView addSubview:self.statusBarView];
[self.topToolView addSubview:self.backBtn];
[self.topToolView addSubview:self.titleLabel];
[self addSubview:self.bottomToolView];
[self.bottomToolView addSubview:self.playOrPauseBtn];
[self.bottomToolView addSubview:self.currentTimeLabel];
[self.bottomToolView addSubview:self.slider];
[self.bottomToolView addSubview:self.totalTimeLabel];
[self addSubview:self.lockBtn];
//
[self makeSubViewsAction];
[self resetControlView];
/// statusBarFrame changed
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(layoutControllerViews) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
}
return self;
}
- (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.bounds.size.width;
CGFloat min_view_h = self.bounds.size.height;
CGFloat min_margin = 9;
min_x = 0;
min_y = 0;
min_w = min_view_w;
min_h = iPhoneX ? 110 : 80;
self.topToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = min_view_w;
min_h = 20;
self.statusBarView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 44: 15;
if (@available(iOS 13.0, *)) {
if (self.showCustomStatusBar) {
min_y = self.statusBarView.zf_bottom;
} else {
min_y = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) ? 10 : (iPhoneX ? 40 : 20);
}
} else {
min_y = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 10: (iPhoneX ? 40 : 20);
}
min_w = 40;
min_h = 40;
self.backBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = self.backBtn.zf_right + 5;
min_y = 0;
min_w = min_view_w - min_x - 15 ;
min_h = 30;
self.titleLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.titleLabel.zf_centerY = self.backBtn.zf_centerY;
min_h = iPhoneX ? 100 : 73;
min_x = 0;
min_y = min_view_h - min_h;
min_w = min_view_w;
self.bottomToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 44: 15;
min_y = 32;
min_w = 30;
min_h = 30;
self.playOrPauseBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = self.playOrPauseBtn.zf_right + 4;
min_y = 0;
min_w = 62;
min_h = 30;
self.currentTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.currentTimeLabel.zf_centerY = self.playOrPauseBtn.zf_centerY;
min_w = 62;
min_x = self.bottomToolView.zf_width - min_w - ((iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 44: min_margin);
min_y = 0;
min_h = 30;
self.totalTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.totalTimeLabel.zf_centerY = self.playOrPauseBtn.zf_centerY;
min_x = self.currentTimeLabel.zf_right + 4;
min_y = 0;
min_w = self.totalTimeLabel.zf_left - min_x - 4;
min_h = 30;
self.slider.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.slider.zf_centerY = self.playOrPauseBtn.zf_centerY;
min_x = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 50: 18;
min_y = 0;
min_w = 40;
min_h = 40;
self.lockBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.lockBtn.zf_centerY = self.zf_centerY;
if (!self.isShow) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.lockBtn.zf_left = iPhoneX ? -82: -47;
} else {
self.lockBtn.zf_left = iPhoneX ? 50: 18;
if (self.player.isLockedScreen) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
} else {
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
}
}
}
- (void)makeSubViewsAction {
[self.backBtn addTarget:self action:@selector(backBtnClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.playOrPauseBtn addTarget:self action:@selector(playPauseButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.lockBtn addTarget:self action:@selector(lockButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - action
- (void)layoutControllerViews {
[self layoutIfNeeded];
[self setNeedsLayout];
}
- (void)backBtnClickAction:(UIButton *)sender {
self.lockBtn.selected = NO;
self.player.lockedScreen = NO;
self.lockBtn.selected = NO;
if (self.player.orientationObserver.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait) {
[self.player enterFullScreen:NO animated:YES];
}
if (self.backBtnClickCallback) {
self.backBtnClickCallback();
}
}
- (void)playPauseButtonClickAction:(UIButton *)sender {
[self playOrPause];
}
///
- (void)playOrPause {
self.playOrPauseBtn.selected = !self.playOrPauseBtn.isSelected;
self.playOrPauseBtn.isSelected? [self.player.currentPlayerManager play]: [self.player.currentPlayerManager pause];
}
- (void)playBtnSelectedState:(BOOL)selected {
self.playOrPauseBtn.selected = selected;
}
- (void)lockButtonClickAction:(UIButton *)sender {
sender.selected = !sender.selected;
self.player.lockedScreen = sender.selected;
}
#pragma mark - ZFSliderViewDelegate
- (void)sliderTouchBegan:(float)value {
self.slider.isdragging = YES;
}
- (void)sliderTouchEnded:(float)value {
if (self.player.totalTime > 0) {
self.slider.isdragging = YES;
if (self.sliderValueChanging) self.sliderValueChanging(value, self.slider.isForward);
@zf_weakify(self)
[self.player seekToTime:self.player.totalTime*value completionHandler:^(BOOL finished) {
@zf_strongify(self)
self.slider.isdragging = NO;
if (finished) {
if (self.sliderValueChanged) self.sliderValueChanged(value);
if (self.seekToPlay) {
[self.player.currentPlayerManager play];
}
}
}];
} else {
self.slider.isdragging = NO;
self.slider.value = 0;
}
}
- (void)sliderValueChanged:(float)value {
if (self.player.totalTime == 0) {
self.slider.value = 0;
return;
}
self.slider.isdragging = YES;
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
if (self.sliderValueChanging) self.sliderValueChanging(value,self.slider.isForward);
}
- (void)sliderTapped:(float)value {
[self sliderTouchEnded:value];
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
}
#pragma mark - public method
/// ControlView
- (void)resetControlView {
self.slider.value = 0;
self.slider.bufferValue = 0;
self.currentTimeLabel.text = @"00:00";
self.totalTimeLabel.text = @"00:00";
self.backgroundColor = [UIColor clearColor];
self.playOrPauseBtn.selected = YES;
self.titleLabel.text = @"";
self.topToolView.alpha = 1;
self.bottomToolView.alpha = 1;
self.isShow = NO;
self.lockBtn.selected = self.player.isLockedScreen;
}
- (void)showControlView {
self.lockBtn.alpha = 1;
self.isShow = YES;
if (self.player.isLockedScreen) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
} else {
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
}
self.lockBtn.zf_left = iPhoneX ? 50: 18;
self.player.statusBarHidden = NO;
if (self.player.isLockedScreen) {
self.topToolView.alpha = 0;
self.bottomToolView.alpha = 0;
} else {
self.topToolView.alpha = 1;
self.bottomToolView.alpha = 1;
}
}
- (void)hideControlView {
self.isShow = NO;
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.lockBtn.zf_left = iPhoneX ? -82: -47;
self.player.statusBarHidden = YES;
self.topToolView.alpha = 0;
self.bottomToolView.alpha = 0;
self.lockBtn.alpha = 0;
}
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch {
CGRect sliderRect = [self.bottomToolView convertRect:self.slider.frame toView:self];
if (CGRectContainsPoint(sliderRect, point)) {
return NO;
}
if (self.player.isLockedScreen && type != ZFPlayerGestureTypeSingleTap) { // tap
return NO;
}
return YES;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size {
self.lockBtn.hidden = self.player.orientationObserver.fullScreenMode == ZFFullScreenModePortrait;
}
/// view
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationWillChange:(ZFOrientationObserver *)observer {
if (self.showCustomStatusBar) {
if (self.hidden) {
[self.statusBarView destoryTimer];
} else {
[self.statusBarView startTimer];
}
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
if (!self.slider.isdragging) {
NSString *currentTimeString = [ZFUtilities convertTimeSecond:currentTime];
self.currentTimeLabel.text = currentTimeString;
NSString *totalTimeString = [ZFUtilities convertTimeSecond:totalTime];
self.totalTimeLabel.text = totalTimeString;
self.slider.value = videoPlayer.progress;
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime {
self.slider.bufferValue = videoPlayer.bufferProgress;
}
- (void)showTitle:(NSString *)title fullScreenMode:(ZFFullScreenMode)fullScreenMode {
self.titleLabel.text = title;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
self.lockBtn.hidden = fullScreenMode == ZFFullScreenModePortrait;
}
/// slider
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString {
self.slider.value = value;
self.currentTimeLabel.text = timeString;
self.slider.isdragging = YES;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
}
///
- (void)sliderChangeEnded {
self.slider.isdragging = NO;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformIdentity;
}];
}
#pragma mark - setter
- (void)setFullScreenMode:(ZFFullScreenMode)fullScreenMode {
_fullScreenMode = fullScreenMode;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
self.lockBtn.hidden = fullScreenMode == ZFFullScreenModePortrait;
}
- (void)setShowCustomStatusBar:(BOOL)showCustomStatusBar {
_showCustomStatusBar = showCustomStatusBar;
self.statusBarView.hidden = !showCustomStatusBar;
}
#pragma mark - getter
- (ZFPlayerStatusBar *)statusBarView {
if (!_statusBarView) {
_statusBarView = [[ZFPlayerStatusBar alloc] init];
_statusBarView.hidden = YES;
}
return _statusBarView;
}
- (UIView *)topToolView {
if (!_topToolView) {
_topToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_top_shadow");
_topToolView.layer.contents = (id)image.CGImage;
}
return _topToolView;
}
- (UIButton *)backBtn {
if (!_backBtn) {
_backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_backBtn setImage:ZFPlayer_Image(@"ZFPlayer_back_full") forState:UIControlStateNormal];
}
return _backBtn;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.font = [UIFont systemFontOfSize:15.0];
}
return _titleLabel;
}
- (UIView *)bottomToolView {
if (!_bottomToolView) {
_bottomToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_bottom_shadow");
_bottomToolView.layer.contents = (id)image.CGImage;
}
return _bottomToolView;
}
- (UIButton *)playOrPauseBtn {
if (!_playOrPauseBtn) {
_playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"ZFPlayer_play") forState:UIControlStateNormal];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"ZFPlayer_pause") forState:UIControlStateSelected];
}
return _playOrPauseBtn;
}
- (UILabel *)currentTimeLabel {
if (!_currentTimeLabel) {
_currentTimeLabel = [[UILabel alloc] init];
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_currentTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _currentTimeLabel;
}
- (ZFSliderView *)slider {
if (!_slider) {
_slider = [[ZFSliderView alloc] init];
_slider.delegate = self;
_slider.maximumTrackTintColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8];
_slider.bufferTrackTintColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
_slider.minimumTrackTintColor = [UIColor whiteColor];
[_slider setThumbImage:ZFPlayer_Image(@"ZFPlayer_slider") forState:UIControlStateNormal];
_slider.sliderHeight = 2;
}
return _slider;
}
- (UILabel *)totalTimeLabel {
if (!_totalTimeLabel) {
_totalTimeLabel = [[UILabel alloc] init];
_totalTimeLabel.textColor = [UIColor whiteColor];
_totalTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_totalTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _totalTimeLabel;
}
- (UIButton *)lockBtn {
if (!_lockBtn) {
_lockBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_lockBtn setImage:ZFPlayer_Image(@"ZFPlayer_unlock-nor") forState:UIControlStateNormal];
[_lockBtn setImage:ZFPlayer_Image(@"ZFPlayer_lock-nor") forState:UIControlStateSelected];
}
return _lockBtn;
}
@end

View File

@@ -0,0 +1,66 @@
//
// ZFLoadingView.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, ZFLoadingType) {
ZFLoadingTypeKeep,
ZFLoadingTypeFadeOut,
};
@interface ZFLoadingView : UIView
/// default is ZFLoadingTypeKeep.
@property (nonatomic, assign) ZFLoadingType animType;
/// default is whiteColor.
@property (nonatomic, strong, null_resettable) UIColor *lineColor;
/// Sets the line width of the spinner's circle.
@property (nonatomic) CGFloat lineWidth;
/// Sets whether the view is hidden when not animating.
@property (nonatomic) BOOL hidesWhenStopped;
/// Property indicating the duration of the animation, default is 1.5s.
@property (nonatomic, readwrite) NSTimeInterval duration;
/// anima state
@property (nonatomic, assign, readonly, getter=isAnimating) BOOL animating;
/**
* Starts animation of the spinner.
*/
- (void)startAnimating;
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,183 @@
//
// ZFLoadingView.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 "ZFLoadingView.h"
@interface ZFLoadingView ()
@property (nonatomic, strong, readonly) CAShapeLayer *shapeLayer;
@property (nonatomic, assign, getter=isAnimating) BOOL animating;
@property (nonatomic, assign) BOOL strokeShow;
@end
@implementation ZFLoadingView
@synthesize lineColor = _lineColor;
@synthesize shapeLayer = _shapeLayer;
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self initialize];
}
- (void)initialize {
[self.layer addSublayer:self.shapeLayer];
self.duration = 1;
self.lineWidth = 1;
self.lineColor = [UIColor whiteColor];
self.userInteractionEnabled = NO;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat width = MIN(self.bounds.size.width, self.bounds.size.height);
CGFloat height = width;
self.shapeLayer.frame = CGRectMake(0, 0, width, height);
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat radius = MIN(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2) - self.shapeLayer.lineWidth / 2;
CGFloat startAngle = (CGFloat)(0);
CGFloat endAngle = (CGFloat)(2*M_PI);
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
self.shapeLayer.path = path.CGPath;
}
- (void)startAnimating {
if (self.animating) return;
self.animating = YES;
if (self.animType == ZFLoadingTypeFadeOut) [self fadeOutShow];
CABasicAnimation *rotationAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnim.toValue = [NSNumber numberWithFloat:2 * M_PI];
rotationAnim.duration = self.duration;
rotationAnim.repeatCount = CGFLOAT_MAX;
rotationAnim.removedOnCompletion = NO;
[self.shapeLayer addAnimation:rotationAnim forKey:@"rotation"];
if (self.hidesWhenStopped) {
self.hidden = NO;
}
}
- (void)stopAnimating {
if (!self.animating) return;
self.animating = NO;
[self.shapeLayer removeAllAnimations];
if (self.hidesWhenStopped) {
self.hidden = YES;
}
}
- (void)fadeOutShow {
CABasicAnimation *headAnimation = [CABasicAnimation animation];
headAnimation.keyPath = @"strokeStart";
headAnimation.duration = self.duration / 1.5f;
headAnimation.fromValue = @(0.f);
headAnimation.toValue = @(0.25f);
CABasicAnimation *tailAnimation = [CABasicAnimation animation];
tailAnimation.keyPath = @"strokeEnd";
tailAnimation.duration = self.duration / 1.5f;
tailAnimation.fromValue = @(0.f);
tailAnimation.toValue = @(1.f);
CABasicAnimation *endHeadAnimation = [CABasicAnimation animation];
endHeadAnimation.keyPath = @"strokeStart";
endHeadAnimation.beginTime = self.duration / 1.5f;
endHeadAnimation.duration = self.duration / 3.0f;
endHeadAnimation.fromValue = @(0.25f);
endHeadAnimation.toValue = @(1.f);
CABasicAnimation *endTailAnimation = [CABasicAnimation animation];
endTailAnimation.keyPath = @"strokeEnd";
endTailAnimation.beginTime = self.duration / 1.5f;
endTailAnimation.duration = self.duration / 3.0f;
endTailAnimation.fromValue = @(1.f);
endTailAnimation.toValue = @(1.f);
CAAnimationGroup *animations = [CAAnimationGroup animation];
[animations setDuration:self.duration];
[animations setAnimations:@[headAnimation, tailAnimation, endHeadAnimation, endTailAnimation]];
animations.repeatCount = INFINITY;
animations.removedOnCompletion = NO;
[self.shapeLayer addAnimation:animations forKey:@"strokeAnim"];
if (self.hidesWhenStopped) {
self.hidden = NO;
}
}
#pragma mark - setter and getter
- (CAShapeLayer *)shapeLayer {
if (!_shapeLayer) {
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.strokeColor = self.lineColor.CGColor;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.strokeStart = 0.1;
_shapeLayer.strokeEnd = 1;
_shapeLayer.lineCap = @"round";
_shapeLayer.anchorPoint = CGPointMake(0.5, 0.5);
}
return _shapeLayer;
}
- (UIColor *)lineColor {
if (!_lineColor) {
return [UIColor whiteColor];
}
return _lineColor;
}
- (void)setLineWidth:(CGFloat)lineWidth {
_lineWidth = lineWidth;
self.shapeLayer.lineWidth = lineWidth;
}
- (void)setLineColor:(UIColor *)lineColor {
if (!lineColor) return;
_lineColor = lineColor;
self.shapeLayer.strokeColor = lineColor.CGColor;
}
- (void)setHidesWhenStopped:(BOOL)hidesWhenStopped {
_hidesWhenStopped = hidesWhenStopped;
self.hidden = !self.isAnimating && hidesWhenStopped;
}
@end

View File

@@ -0,0 +1,39 @@
//
// ZFNetworkSpeedMonitor.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>
extern NSString *const ZFDownloadNetworkSpeedNotificationKey;
extern NSString *const ZFUploadNetworkSpeedNotificationKey;
extern NSString *const ZFNetworkSpeedNotificationKey;
@interface ZFNetworkSpeedMonitor : NSObject
@property (nonatomic, copy, readonly) NSString *downloadNetworkSpeed;
@property (nonatomic, copy, readonly) NSString *uploadNetworkSpeed;
- (void)startNetworkSpeedMonitor;
- (void)stopNetworkSpeedMonitor;
@end

View File

@@ -0,0 +1,167 @@
//
// ZFNetworkSpeedMonitor.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 "ZFNetworkSpeedMonitor.h"
#if __has_include(<ZFPlayer/ZFPlayerLogManager.h>)
#import <ZFPlayer/ZFPlayerLogManager.h>
#else
#import "ZFPlayerLogManager.h"
#endif
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <net/if_dl.h>
NSString *const ZFDownloadNetworkSpeedNotificationKey = @"ZFDownloadNetworkSpeedNotificationKey";
NSString *const ZFUploadNetworkSpeedNotificationKey = @"ZFUploadNetworkSpeedNotificationKey";
NSString *const ZFNetworkSpeedNotificationKey = @"ZFNetworkSpeedNotificationKey";
@interface ZFNetworkSpeedMonitor () {
//
uint32_t _iBytes;
uint32_t _oBytes;
uint32_t _allFlow;
// wifi
uint32_t _wifiIBytes;
uint32_t _wifiOBytes;
uint32_t _wifiFlow;
// 3G
uint32_t _wwanIBytes;
uint32_t _wwanOBytes;
uint32_t _wwanFlow;
}
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation ZFNetworkSpeedMonitor
- (instancetype)init {
if (self = [super init]) {
_iBytes = _oBytes = _allFlow = _wifiIBytes = _wifiOBytes = _wifiFlow = _wwanIBytes = _wwanOBytes = _wwanFlow = 0;
}
return self;
}
//
- (void)startNetworkSpeedMonitor {
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(checkNetworkSpeed) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
[_timer fire];
}
}
//
- (void)stopNetworkSpeedMonitor {
if ([_timer isValid]) {
[_timer invalidate];
_timer = nil;
}
}
- (NSString *)stringWithbytes:(int)bytes {
if (bytes < 1024) { // B
return [NSString stringWithFormat:@"%dB", bytes];
} else if (bytes >= 1024 && bytes < 1024 * 1024) { // KB
return [NSString stringWithFormat:@"%.0fKB", (double)bytes / 1024];
} else if (bytes >= 1024 * 1024 && bytes < 1024 * 1024 * 1024) { // MB
return [NSString stringWithFormat:@"%.1fMB", (double)bytes / (1024 * 1024)];
} else { // GB
return [NSString stringWithFormat:@"%.1fGB", (double)bytes / (1024 * 1024 * 1024)];
}
}
- (void)checkNetworkSpeed {
struct ifaddrs *ifa_list = 0, *ifa;
if (getifaddrs(&ifa_list) == -1) return;
uint32_t iBytes = 0;
uint32_t oBytes = 0;
uint32_t allFlow = 0;
uint32_t wifiIBytes = 0;
uint32_t wifiOBytes = 0;
uint32_t wifiFlow = 0;
uint32_t wwanIBytes = 0;
uint32_t wwanOBytes = 0;
uint32_t wwanFlow = 0;
for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
if (AF_LINK != ifa->ifa_addr->sa_family) continue;
if (!(ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_RUNNING)) continue;
if (ifa->ifa_data == 0) continue;
// network
if (strncmp(ifa->ifa_name, "lo", 2)) {
struct if_data* if_data = (struct if_data*)ifa->ifa_data;
iBytes += if_data->ifi_ibytes;
oBytes += if_data->ifi_obytes;
allFlow = iBytes + oBytes;
}
//wifi
if (!strcmp(ifa->ifa_name, "en0")) {
struct if_data* if_data = (struct if_data*)ifa->ifa_data;
wifiIBytes += if_data->ifi_ibytes;
wifiOBytes += if_data->ifi_obytes;
wifiFlow = wifiIBytes + wifiOBytes;
}
//3G or gprs
if (!strcmp(ifa->ifa_name, "pdp_ip0")) {
struct if_data* if_data = (struct if_data*)ifa->ifa_data;
wwanIBytes += if_data->ifi_ibytes;
wwanOBytes += if_data->ifi_obytes;
wwanFlow = wwanIBytes + wwanOBytes;
}
}
freeifaddrs(ifa_list);
if (_iBytes != 0) {
_downloadNetworkSpeed = [[self stringWithbytes:iBytes - _iBytes] stringByAppendingString:@"/s"];
NSMutableDictionary *userInfo = @{}.mutableCopy;
userInfo[ZFNetworkSpeedNotificationKey] = _downloadNetworkSpeed;
[[NSNotificationCenter defaultCenter] postNotificationName:ZFDownloadNetworkSpeedNotificationKey object:nil userInfo:userInfo];
ZFPlayerLog(@"downloadNetworkSpeed : %@",_downloadNetworkSpeed);
}
_iBytes = iBytes;
if (_oBytes != 0) {
_uploadNetworkSpeed = [[self stringWithbytes:oBytes - _oBytes] stringByAppendingString:@"/s"];
NSMutableDictionary *userInfo = @{}.mutableCopy;
userInfo[ZFNetworkSpeedNotificationKey] = _uploadNetworkSpeed;
[[NSNotificationCenter defaultCenter] postNotificationName:ZFUploadNetworkSpeedNotificationKey object:nil userInfo:userInfo];
ZFPlayerLog(@"uploadNetworkSpeed :%@",_uploadNetworkSpeed);
}
_oBytes = oBytes;
}
@end

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,154 @@
//
// ZFPlayerControlView.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 "ZFPortraitControlView.h"
#import "ZFLandScapeControlView.h"
#import "ZFSpeedLoadingView.h"
#import "ZFSmallFloatControlView.h"
#if __has_include(<ZFPlayer/ZFPlayerMediaControl.h>)
#import <ZFPlayer/ZFPlayerMediaControl.h>
#else
#import "ZFPlayerMediaControl.h"
#endif
@interface ZFPlayerControlView : UIView <ZFPlayerMediaControl>
/// 竖屏控制层的View
@property (nonatomic, strong, readonly) ZFPortraitControlView *portraitControlView;
/// 横屏控制层的View
@property (nonatomic, strong, readonly) ZFLandScapeControlView *landScapeControlView;
/// 加载loading
@property (nonatomic, strong, readonly) ZFSpeedLoadingView *activity;
/// 快进快退View
@property (nonatomic, strong, readonly) UIView *fastView;
/// 快进快退进度progress
@property (nonatomic, strong, readonly) ZFSliderView *fastProgressView;
/// 快进快退时间
@property (nonatomic, strong, readonly) UILabel *fastTimeLabel;
/// 快进快退ImageView
@property (nonatomic, strong, readonly) UIImageView *fastImageView;
/// 加载失败按钮
@property (nonatomic, strong, readonly) UIButton *failBtn;
/// 底部播放进度
@property (nonatomic, strong, readonly) ZFSliderView *bottomPgrogress;
/// 封面图
@property (nonatomic, strong, readonly) UIImageView *coverImageView;
/// 高斯模糊的背景图
@property (nonatomic, strong, readonly) UIImageView *bgImgView;
/// 高斯模糊视图
@property (nonatomic, strong, readonly) UIView *effectView;
/// 小窗口控制层
@property (nonatomic, strong, readonly) ZFSmallFloatControlView *floatControlView;
/// 快进视图是否显示动画默认NO.
@property (nonatomic, assign) BOOL fastViewAnimated;
/// 视频之外区域是否高斯模糊显示默认YES.
@property (nonatomic, assign) BOOL effectViewShow;
/// 如果是暂停状态seek完是否播放默认YES
@property (nonatomic, assign) BOOL seekToPlay;
/// 返回按钮点击回调
@property (nonatomic, copy) void(^backBtnClickCallback)(void);
/// 控制层显示或者隐藏
@property (nonatomic, readonly) BOOL controlViewAppeared;
/// 控制层显示或者隐藏的回调
@property (nonatomic, copy) void(^controlViewAppearedCallback)(BOOL appeared);
/// 控制层自动隐藏的时间默认2.5秒
@property (nonatomic, assign) NSTimeInterval autoHiddenTimeInterval;
/// 控制层显示、隐藏动画的时长默认0.25秒
@property (nonatomic, assign) NSTimeInterval autoFadeTimeInterval;
/// 横向滑动控制播放进度时是否显示控制层,默认 YES.
@property (nonatomic, assign) BOOL horizontalPanShowControlView;
/// prepare时候是否显示控制层,默认 NO.
@property (nonatomic, assign) BOOL prepareShowControlView;
/// prepare时候是否显示loading,默认 NO.
@property (nonatomic, assign) BOOL prepareShowLoading;
/// 是否自定义禁止pan手势默认 NO.
@property (nonatomic, assign) BOOL customDisablePanMovingDirection;
/// 横屏时候是否显示自定义状态栏(iOS13+),默认 NO.
@property (nonatomic, assign) BOOL showCustomStatusBar;
/// 全屏模式
@property (nonatomic, assign) ZFFullScreenMode fullScreenMode;
/**
设置标题、封面、全屏模式
@param title 视频的标题
@param coverUrl 视频的封面,占位图默认是灰色的
@param fullScreenMode 全屏模式
*/
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/**
设置标题、封面、默认占位图、全屏模式
@param title 视频的标题
@param coverUrl 视频的封面
@param placeholder 指定封面的placeholder
@param fullScreenMode 全屏模式
*/
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl placeholderImage:(UIImage *)placeholder fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/**
设置标题、UIImage封面、全屏模式
@param title 视频的标题
@param image 视频的封面UIImage
@param fullScreenMode 全屏模式
*/
- (void)showTitle:(NSString *)title coverImage:(UIImage *)image fullScreenMode:(ZFFullScreenMode)fullScreenMode;
//- (void)showFullScreen
/**
重置控制层
*/
- (void)resetControlView;
@end

View File

@@ -0,0 +1,831 @@
//
// ZFPlayerControlView.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 "ZFPlayerControlView.h"
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
#import "UIView+ZFFrame.h"
#import "ZFSliderView.h"
#import "ZFUtilities.h"
#import "UIImageView+ZFCache.h"
#import <MediaPlayer/MediaPlayer.h>
#import "ZFVolumeBrightnessView.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayerConst.h"
#endif
@interface ZFPlayerControlView () <ZFSliderViewDelegate>
/// View
@property (nonatomic, strong) ZFPortraitControlView *portraitControlView;
/// View
@property (nonatomic, strong) ZFLandScapeControlView *landScapeControlView;
/// loading
@property (nonatomic, strong) ZFSpeedLoadingView *activity;
/// 退View
@property (nonatomic, strong) UIView *fastView;
/// 退progress
@property (nonatomic, strong) ZFSliderView *fastProgressView;
/// 退
@property (nonatomic, strong) UILabel *fastTimeLabel;
/// 退ImageView
@property (nonatomic, strong) UIImageView *fastImageView;
///
@property (nonatomic, strong) UIButton *failBtn;
///
@property (nonatomic, strong) ZFSliderView *bottomPgrogress;
///
@property (nonatomic, assign, getter=isShowing) BOOL showing;
///
@property (nonatomic, assign, getter=isPlayEnd) BOOL playeEnd;
@property (nonatomic, assign) BOOL controlViewAppeared;
@property (nonatomic, assign) NSTimeInterval sumTime;
@property (nonatomic, strong) dispatch_block_t afterBlock;
@property (nonatomic, strong) ZFSmallFloatControlView *floatControlView;
@property (nonatomic, strong) ZFVolumeBrightnessView *volumeBrightnessView;
@property (nonatomic, strong) UIImageView *bgImgView;
@property (nonatomic, strong) UIView *effectView;
@end
@implementation ZFPlayerControlView
@synthesize player = _player;
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addAllSubViews];
self.landScapeControlView.hidden = YES;
self.floatControlView.hidden = YES;
self.seekToPlay = YES;
self.effectViewShow = YES;
self.horizontalPanShowControlView = YES;
self.autoFadeTimeInterval = 0.25;
self.autoHiddenTimeInterval = 2.5;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
}
return self;
}
- (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.zf_width;
CGFloat min_view_h = self.zf_height;
self.portraitControlView.frame = self.bounds;
self.landScapeControlView.frame = self.bounds;
self.floatControlView.frame = self.bounds;
self.coverImageView.frame = self.bounds;
self.bgImgView.frame = self.bounds;
self.effectView.frame = self.bounds;
min_w = 80;
min_h = 80;
self.activity.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.activity.zf_centerX = self.zf_centerX;
self.activity.zf_centerY = self.zf_centerY + 10;
min_w = 150;
min_h = 30;
self.failBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.failBtn.center = self.center;
min_w = 140;
min_h = 80;
self.fastView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.fastView.center = self.center;
min_w = 32;
min_x = (self.fastView.zf_width - min_w) / 2;
min_y = 5;
min_h = 32;
self.fastImageView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = self.fastImageView.zf_bottom + 2;
min_w = self.fastView.zf_width;
min_h = 20;
self.fastTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 12;
min_y = self.fastTimeLabel.zf_bottom + 5;
min_w = self.fastView.zf_width - 2 * min_x;
min_h = 10;
self.fastProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = min_view_h - 1;
min_w = min_view_w;
min_h = 1;
self.bottomPgrogress.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = iPhoneX ? 54 : 30;
min_w = 170;
min_h = 35;
self.volumeBrightnessView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.volumeBrightnessView.zf_centerX = self.zf_centerX;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
[self cancelAutoFadeOutControlView];
}
///
- (void)addAllSubViews {
[self addSubview:self.portraitControlView];
[self addSubview:self.landScapeControlView];
[self addSubview:self.floatControlView];
[self addSubview:self.activity];
[self addSubview:self.failBtn];
[self addSubview:self.fastView];
[self.fastView addSubview:self.fastImageView];
[self.fastView addSubview:self.fastTimeLabel];
[self.fastView addSubview:self.fastProgressView];
[self addSubview:self.bottomPgrogress];
[self addSubview:self.volumeBrightnessView];
}
- (void)autoFadeOutControlView {
self.controlViewAppeared = YES;
[self cancelAutoFadeOutControlView];
@zf_weakify(self)
self.afterBlock = dispatch_block_create(0, ^{
@zf_strongify(self)
[self hideControlViewWithAnimated:YES];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.autoHiddenTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(),self.afterBlock);
}
/// controlView
- (void)cancelAutoFadeOutControlView {
if (self.afterBlock) {
dispatch_block_cancel(self.afterBlock);
self.afterBlock = nil;
}
}
///
- (void)hideControlViewWithAnimated:(BOOL)animated {
self.controlViewAppeared = NO;
if (self.controlViewAppearedCallback) {
self.controlViewAppearedCallback(NO);
}
[UIView animateWithDuration:animated ? self.autoFadeTimeInterval : 0 animations:^{
if (self.player.isFullScreen) {
[self.landScapeControlView hideControlView];
} else {
if (!self.player.isSmallFloatViewShow) {
[self.portraitControlView hideControlView];
}
}
} completion:^(BOOL finished) {
self.bottomPgrogress.hidden = NO;
}];
}
///
- (void)showControlViewWithAnimated:(BOOL)animated {
self.controlViewAppeared = YES;
if (self.controlViewAppearedCallback) {
self.controlViewAppearedCallback(YES);
}
[self autoFadeOutControlView];
[UIView animateWithDuration:animated ? self.autoFadeTimeInterval : 0 animations:^{
if (self.player.isFullScreen) {
[self.landScapeControlView showControlView];
} else {
if (!self.player.isSmallFloatViewShow) {
[self.portraitControlView showControlView];
}
}
} completion:^(BOOL finished) {
self.bottomPgrogress.hidden = YES;
}];
}
///
- (void)volumeChanged:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
NSString *reasonstr = userInfo[@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"];
if ([reasonstr isEqualToString:@"ExplicitVolumeChange"]) {
float volume = [ userInfo[@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
if (self.player.isFullScreen) {
[self.volumeBrightnessView updateProgress:volume withVolumeBrightnessType:ZFVolumeBrightnessTypeVolume];
} else {
[self.volumeBrightnessView addSystemVolumeView];
}
}
}
#pragma mark - Public Method
///
- (void)resetControlView {
[self.portraitControlView resetControlView];
[self.landScapeControlView resetControlView];
[self cancelAutoFadeOutControlView];
self.bottomPgrogress.value = 0;
self.bottomPgrogress.bufferValue = 0;
self.floatControlView.hidden = YES;
self.failBtn.hidden = YES;
self.volumeBrightnessView.hidden = YES;
self.portraitControlView.hidden = self.player.isFullScreen;
self.landScapeControlView.hidden = !self.player.isFullScreen;
if (self.controlViewAppeared) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
///
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl fullScreenMode:(ZFFullScreenMode)fullScreenMode {
UIImage *placeholder = [ZFUtilities imageWithColor:[UIColor colorWithRed:220/255.0 green:220/255.0 blue:220/255.0 alpha:1] size:self.bgImgView.bounds.size];
[self showTitle:title coverURLString:coverUrl placeholderImage:placeholder fullScreenMode:fullScreenMode];
}
///
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl placeholderImage:(UIImage *)placeholder fullScreenMode:(ZFFullScreenMode)fullScreenMode {
[self resetControlView];
[self layoutIfNeeded];
[self setNeedsDisplay];
[self.portraitControlView showTitle:title fullScreenMode:fullScreenMode];
[self.landScapeControlView showTitle:title fullScreenMode:fullScreenMode];
/// coverImageView
[self.player.currentPlayerManager.view.coverImageView setImageWithURLString:coverUrl placeholder:placeholder];
[self.bgImgView setImageWithURLString:coverUrl placeholder:placeholder];
if (self.prepareShowControlView) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
/// UIImage
- (void)showTitle:(NSString *)title coverImage:(UIImage *)image fullScreenMode:(ZFFullScreenMode)fullScreenMode {
[self resetControlView];
[self layoutIfNeeded];
[self setNeedsDisplay];
[self.portraitControlView showTitle:title fullScreenMode:fullScreenMode];
[self.landScapeControlView showTitle:title fullScreenMode:fullScreenMode];
self.coverImageView.image = image;
self.bgImgView.image = image;
if (self.prepareShowControlView) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
#pragma mark - ZFPlayerControlViewDelegate
/// NO
- (BOOL)gestureTriggerCondition:(ZFPlayerGestureControl *)gestureControl gestureType:(ZFPlayerGestureType)gestureType gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer touch:(nonnull UITouch *)touch {
CGPoint point = [touch locationInView:self];
if (self.player.isSmallFloatViewShow && !self.player.isFullScreen && gestureType != ZFPlayerGestureTypeSingleTap) {
return NO;
}
if (self.player.isFullScreen) {
if (!self.customDisablePanMovingDirection) {
///
self.player.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionNone;
}
return [self.landScapeControlView shouldResponseGestureWithPoint:point withGestureType:gestureType touch:touch];
} else {
if (!self.customDisablePanMovingDirection) {
if (self.player.scrollView) { ///
self.player.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionVertical;
} else { ///
self.player.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionNone;
}
}
return [self.portraitControlView shouldResponseGestureWithPoint:point withGestureType:gestureType touch:touch];
}
}
///
- (void)gestureSingleTapped:(ZFPlayerGestureControl *)gestureControl {
if (!self.player) return;
if (self.player.isSmallFloatViewShow && !self.player.isFullScreen) {
[self.player enterFullScreen:YES animated:YES];
} else {
if (self.controlViewAppeared) {
[self hideControlViewWithAnimated:YES];
} else {
[self showControlViewWithAnimated:YES];
}
}
}
///
- (void)gestureDoubleTapped:(ZFPlayerGestureControl *)gestureControl {
if (self.player.isFullScreen) {
[self.landScapeControlView playOrPause];
} else {
[self.portraitControlView playOrPause];
}
}
///
- (void)gestureBeganPan:(ZFPlayerGestureControl *)gestureControl panDirection:(ZFPanDirection)direction panLocation:(ZFPanLocation)location {
if (direction == ZFPanDirectionH) {
self.sumTime = self.player.currentTime;
}
}
///
- (void)gestureChangedPan:(ZFPlayerGestureControl *)gestureControl panDirection:(ZFPanDirection)direction panLocation:(ZFPanLocation)location withVelocity:(CGPoint)velocity {
if (direction == ZFPanDirectionH) {
//
self.sumTime += velocity.x / 200;
// sumTime
NSTimeInterval totalMovieDuration = self.player.totalTime;
if (totalMovieDuration == 0) return;
if (self.sumTime > totalMovieDuration) self.sumTime = totalMovieDuration;
if (self.sumTime < 0) self.sumTime = 0;
BOOL style = NO;
if (velocity.x > 0) style = YES;
if (velocity.x < 0) style = NO;
if (velocity.x == 0) return;
[self sliderValueChangingValue:self.sumTime/totalMovieDuration isForward:style];
} else if (direction == ZFPanDirectionV) {
if (location == ZFPanLocationLeft) { ///
self.player.brightness -= (velocity.y) / 10000;
[self.volumeBrightnessView updateProgress:self.player.brightness withVolumeBrightnessType:ZFVolumeBrightnessTypeumeBrightness];
} else if (location == ZFPanLocationRight) { ///
self.player.volume -= (velocity.y) / 10000;
if (self.player.isFullScreen) {
[self.volumeBrightnessView updateProgress:self.player.volume withVolumeBrightnessType:ZFVolumeBrightnessTypeVolume];
}
}
}
}
///
- (void)gestureEndedPan:(ZFPlayerGestureControl *)gestureControl panDirection:(ZFPanDirection)direction panLocation:(ZFPanLocation)location {
@zf_weakify(self)
if (direction == ZFPanDirectionH && self.sumTime >= 0 && self.player.totalTime > 0) {
[self.player seekToTime:self.sumTime completionHandler:^(BOOL finished) {
if (finished) {
@zf_strongify(self)
///
[self.portraitControlView sliderChangeEnded];
[self.landScapeControlView sliderChangeEnded];
self.bottomPgrogress.isdragging = NO;
if (self.controlViewAppeared) {
[self autoFadeOutControlView];
}
}
}];
if (self.seekToPlay) {
[self.player.currentPlayerManager play];
}
self.sumTime = 0;
}
}
///
- (void)gesturePinched:(ZFPlayerGestureControl *)gestureControl scale:(float)scale {
if (scale > 1) {
self.player.currentPlayerManager.scalingMode = ZFPlayerScalingModeAspectFill;
} else {
self.player.currentPlayerManager.scalingMode = ZFPlayerScalingModeAspectFit;
}
}
///
- (void)videoPlayer:(ZFPlayerController *)videoPlayer prepareToPlay:(NSURL *)assetURL {
[self hideControlViewWithAnimated:NO];
}
///
- (void)videoPlayer:(ZFPlayerController *)videoPlayer playStateChanged:(ZFPlayerPlaybackState)state {
if (state == ZFPlayerPlayStatePlaying) {
[self.portraitControlView playBtnSelectedState:YES];
[self.landScapeControlView playBtnSelectedState:YES];
self.failBtn.hidden = YES;
/// loading
if (videoPlayer.currentPlayerManager.loadState == ZFPlayerLoadStateStalled && !self.prepareShowLoading) {
[self.activity startAnimating];
} else if ((videoPlayer.currentPlayerManager.loadState == ZFPlayerLoadStateStalled || videoPlayer.currentPlayerManager.loadState == ZFPlayerLoadStatePrepare) && self.prepareShowLoading) {
[self.activity startAnimating];
}
} else if (state == ZFPlayerPlayStatePaused) {
[self.portraitControlView playBtnSelectedState:NO];
[self.landScapeControlView playBtnSelectedState:NO];
/// loading
[self.activity stopAnimating];
self.failBtn.hidden = YES;
} else if (state == ZFPlayerPlayStatePlayFailed) {
self.failBtn.hidden = NO;
[self.activity stopAnimating];
}
}
///
- (void)videoPlayer:(ZFPlayerController *)videoPlayer loadStateChanged:(ZFPlayerLoadState)state {
if (state == ZFPlayerLoadStatePrepare) {
self.coverImageView.hidden = NO;
[self.portraitControlView playBtnSelectedState:videoPlayer.currentPlayerManager.shouldAutoPlay];
[self.landScapeControlView playBtnSelectedState:videoPlayer.currentPlayerManager.shouldAutoPlay];
} else if (state == ZFPlayerLoadStatePlaythroughOK || state == ZFPlayerLoadStatePlayable) {
self.coverImageView.hidden = YES;
if (self.effectViewShow) {
self.effectView.hidden = NO;
} else {
self.effectView.hidden = YES;
self.player.currentPlayerManager.view.backgroundColor = [UIColor blackColor];
}
}
if (state == ZFPlayerLoadStateStalled && videoPlayer.currentPlayerManager.isPlaying && !self.prepareShowLoading) {
[self.activity startAnimating];
} else if ((state == ZFPlayerLoadStateStalled || state == ZFPlayerLoadStatePrepare) && videoPlayer.currentPlayerManager.isPlaying && self.prepareShowLoading) {
[self.activity startAnimating];
} else {
[self.activity stopAnimating];
}
}
///
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
[self.portraitControlView videoPlayer:videoPlayer currentTime:currentTime totalTime:totalTime];
[self.landScapeControlView videoPlayer:videoPlayer currentTime:currentTime totalTime:totalTime];
if (!self.bottomPgrogress.isdragging) {
self.bottomPgrogress.value = videoPlayer.progress;
}
}
///
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime {
[self.portraitControlView videoPlayer:videoPlayer bufferTime:bufferTime];
[self.landScapeControlView videoPlayer:videoPlayer bufferTime:bufferTime];
self.bottomPgrogress.bufferValue = videoPlayer.bufferProgress;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size {
[self.landScapeControlView videoPlayer:videoPlayer presentationSizeChanged:size];
}
/// view
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationWillChange:(ZFOrientationObserver *)observer {
self.portraitControlView.hidden = observer.isFullScreen;
self.landScapeControlView.hidden = !observer.isFullScreen;
if (videoPlayer.isSmallFloatViewShow) {
self.floatControlView.hidden = observer.isFullScreen;
self.portraitControlView.hidden = YES;
if (observer.isFullScreen) {
self.controlViewAppeared = NO;
[self cancelAutoFadeOutControlView];
}
}
if (self.controlViewAppeared) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
if (observer.isFullScreen) {
[self.volumeBrightnessView removeSystemVolumeView];
} else {
[self.volumeBrightnessView addSystemVolumeView];
}
[self.landScapeControlView videoPlayer:videoPlayer orientationWillChange:observer];
}
///
- (void)lockedVideoPlayer:(ZFPlayerController *)videoPlayer lockedScreen:(BOOL)locked {
[self showControlViewWithAnimated:YES];
}
/// view
- (void)playerDidAppearInScrollView:(ZFPlayerController *)videoPlayer {
if (!self.player.stopWhileNotVisible && !videoPlayer.isFullScreen) {
self.floatControlView.hidden = YES;
self.portraitControlView.hidden = NO;
}
}
/// view
- (void)playerDidDisappearInScrollView:(ZFPlayerController *)videoPlayer {
if (!self.player.stopWhileNotVisible && !videoPlayer.isFullScreen) {
self.floatControlView.hidden = NO;
self.portraitControlView.hidden = YES;
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer floatViewShow:(BOOL)show {
self.floatControlView.hidden = !show;
self.portraitControlView.hidden = show;
}
#pragma mark - Private Method
- (void)sliderValueChangingValue:(CGFloat)value isForward:(BOOL)forward {
if (self.horizontalPanShowControlView) {
///
[self showControlViewWithAnimated:NO];
[self cancelAutoFadeOutControlView];
}
self.fastProgressView.value = value;
self.fastView.hidden = NO;
self.fastView.alpha = 1;
if (forward) {
self.fastImageView.image = ZFPlayer_Image(@"ZFPlayer_fast_forward");
} else {
self.fastImageView.image = ZFPlayer_Image(@"ZFPlayer_fast_backward");
}
NSString *draggedTime = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
NSString *totalTime = [ZFUtilities convertTimeSecond:self.player.totalTime];
self.fastTimeLabel.text = [NSString stringWithFormat:@"%@ / %@",draggedTime,totalTime];
///
[self.portraitControlView sliderValueChanged:value currentTimeString:draggedTime];
[self.landScapeControlView sliderValueChanged:value currentTimeString:draggedTime];
self.bottomPgrogress.isdragging = YES;
self.bottomPgrogress.value = value;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideFastView) object:nil];
[self performSelector:@selector(hideFastView) withObject:nil afterDelay:0.1];
if (self.fastViewAnimated) {
[UIView animateWithDuration:0.4 animations:^{
self.fastView.transform = CGAffineTransformMakeTranslation(forward?8:-8, 0);
}];
}
}
///
- (void)hideFastView {
[UIView animateWithDuration:0.4 animations:^{
self.fastView.transform = CGAffineTransformIdentity;
self.fastView.alpha = 0;
} completion:^(BOOL finished) {
self.fastView.hidden = YES;
}];
}
///
- (void)failBtnClick:(UIButton *)sender {
[self.player.currentPlayerManager reloadPlayer];
}
#pragma mark - setter
- (void)setPlayer:(ZFPlayerController *)player {
_player = player;
self.landScapeControlView.player = player;
self.portraitControlView.player = player;
///
[player.currentPlayerManager.view insertSubview:self.bgImgView atIndex:0];
[self.bgImgView addSubview:self.effectView];
self.bgImgView.frame = player.currentPlayerManager.view.bounds;
self.bgImgView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.effectView.frame = self.bgImgView.bounds;
}
- (void)setSeekToPlay:(BOOL)seekToPlay {
_seekToPlay = seekToPlay;
self.portraitControlView.seekToPlay = seekToPlay;
self.landScapeControlView.seekToPlay = seekToPlay;
}
- (void)setEffectViewShow:(BOOL)effectViewShow {
_effectViewShow = effectViewShow;
if (effectViewShow) {
self.bgImgView.hidden = NO;
} else {
self.bgImgView.hidden = YES;
}
}
- (void)setFullScreenMode:(ZFFullScreenMode)fullScreenMode {
_fullScreenMode = fullScreenMode;
self.portraitControlView.fullScreenMode = fullScreenMode;
self.landScapeControlView.fullScreenMode = fullScreenMode;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
}
- (void)setShowCustomStatusBar:(BOOL)showCustomStatusBar {
_showCustomStatusBar = showCustomStatusBar;
self.landScapeControlView.showCustomStatusBar = showCustomStatusBar;
}
#pragma mark - getter
- (UIImageView *)bgImgView {
if (!_bgImgView) {
_bgImgView = [[UIImageView alloc] init];
_bgImgView.userInteractionEnabled = YES;
}
return _bgImgView;
}
- (UIView *)effectView {
if (!_effectView) {
if (@available(iOS 8.0, *)) {
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
_effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
} else {
UIToolbar *effectView = [[UIToolbar alloc] init];
effectView.barStyle = UIBarStyleBlackTranslucent;
_effectView = effectView;
}
}
return _effectView;
}
- (ZFPortraitControlView *)portraitControlView {
if (!_portraitControlView) {
@zf_weakify(self)
_portraitControlView = [[ZFPortraitControlView alloc] init];
_portraitControlView.sliderValueChanging = ^(CGFloat value, BOOL forward) {
@zf_strongify(self)
NSString *draggedTime = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
///
[self.landScapeControlView sliderValueChanged:value currentTimeString:draggedTime];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = YES;
self.bottomPgrogress.value = value;
[self cancelAutoFadeOutControlView];
};
_portraitControlView.sliderValueChanged = ^(CGFloat value) {
@zf_strongify(self)
[self.landScapeControlView sliderChangeEnded];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = NO;
self.bottomPgrogress.value = value;
[self autoFadeOutControlView];
};
}
return _portraitControlView;
}
- (ZFLandScapeControlView *)landScapeControlView {
if (!_landScapeControlView) {
@zf_weakify(self)
_landScapeControlView = [[ZFLandScapeControlView alloc] init];
_landScapeControlView.sliderValueChanging = ^(CGFloat value, BOOL forward) {
@zf_strongify(self)
NSString *draggedTime = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
///
[self.portraitControlView sliderValueChanged:value currentTimeString:draggedTime];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = YES;
self.bottomPgrogress.value = value;
[self cancelAutoFadeOutControlView];
};
_landScapeControlView.sliderValueChanged = ^(CGFloat value) {
@zf_strongify(self)
[self.portraitControlView sliderChangeEnded];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = NO;
self.bottomPgrogress.value = value;
[self autoFadeOutControlView];
};
}
return _landScapeControlView;
}
- (ZFSpeedLoadingView *)activity {
if (!_activity) {
_activity = [[ZFSpeedLoadingView alloc] init];
}
return _activity;
}
- (UIView *)fastView {
if (!_fastView) {
_fastView = [[UIView alloc] init];
_fastView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
_fastView.layer.cornerRadius = 4;
_fastView.layer.masksToBounds = YES;
_fastView.hidden = YES;
}
return _fastView;
}
- (UIImageView *)fastImageView {
if (!_fastImageView) {
_fastImageView = [[UIImageView alloc] init];
}
return _fastImageView;
}
- (UILabel *)fastTimeLabel {
if (!_fastTimeLabel) {
_fastTimeLabel = [[UILabel alloc] init];
_fastTimeLabel.textColor = [UIColor whiteColor];
_fastTimeLabel.textAlignment = NSTextAlignmentCenter;
_fastTimeLabel.font = [UIFont systemFontOfSize:14.0];
_fastTimeLabel.adjustsFontSizeToFitWidth = YES;
}
return _fastTimeLabel;
}
- (ZFSliderView *)fastProgressView {
if (!_fastProgressView) {
_fastProgressView = [[ZFSliderView alloc] init];
_fastProgressView.maximumTrackTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.4];
_fastProgressView.minimumTrackTintColor = [UIColor whiteColor];
_fastProgressView.sliderHeight = 2;
_fastProgressView.isHideSliderBlock = NO;
}
return _fastProgressView;
}
- (UIButton *)failBtn {
if (!_failBtn) {
_failBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_failBtn setTitle:@"加载失败,点击重试" forState:UIControlStateNormal];
[_failBtn addTarget:self action:@selector(failBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[_failBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_failBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
_failBtn.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
_failBtn.hidden = YES;
}
return _failBtn;
}
- (ZFSliderView *)bottomPgrogress {
if (!_bottomPgrogress) {
_bottomPgrogress = [[ZFSliderView alloc] init];
_bottomPgrogress.maximumTrackTintColor = [UIColor clearColor];
_bottomPgrogress.minimumTrackTintColor = [UIColor whiteColor];
_bottomPgrogress.bufferTrackTintColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
_bottomPgrogress.sliderHeight = 1;
_bottomPgrogress.isHideSliderBlock = NO;
}
return _bottomPgrogress;
}
- (ZFSmallFloatControlView *)floatControlView {
if (!_floatControlView) {
_floatControlView = [[ZFSmallFloatControlView alloc] init];
@zf_weakify(self)
_floatControlView.closeClickCallback = ^{
@zf_strongify(self)
if (self.player.containerType == ZFPlayerContainerTypeCell) {
[self.player stopCurrentPlayingCell];
} else if (self.player.containerType == ZFPlayerContainerTypeView) {
[self.player stopCurrentPlayingView];
}
[self resetControlView];
};
}
return _floatControlView;
}
- (ZFVolumeBrightnessView *)volumeBrightnessView {
if (!_volumeBrightnessView) {
_volumeBrightnessView = [[ZFVolumeBrightnessView alloc] init];
_volumeBrightnessView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
_volumeBrightnessView.hidden = YES;
}
return _volumeBrightnessView;
}
- (void)setBackBtnClickCallback:(void (^)(void))backBtnClickCallback {
_backBtnClickCallback = [backBtnClickCallback copy];
self.landScapeControlView.backBtnClickCallback = _backBtnClickCallback;
}
@end

View File

@@ -0,0 +1,42 @@
//
// ZFPlayerStatusBar.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
@interface ZFPlayerStatusBar : UIView
/// 刷新时间间隔默认3秒
@property (nonatomic, assign) NSTimeInterval refreshTime;
- (void)startTimer;
- (void)destoryTimer;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,392 @@
//
// ZFPlayerStatusBar.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 "ZFPlayerStatusBar.h"
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import "UIView+ZFFrame.h"
#import "ZFReachabilityManager.h"
#import "ZFUtilities.h"
@interface ZFPlayerTimerTarget: NSProxy
@property (nonatomic, weak) id target;
@end
@implementation ZFPlayerTimerTarget
+ (instancetype)proxyWithTarget:(id)target {
ZFPlayerTimerTarget *proxy = [ZFPlayerTimerTarget alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSMethodSignature *signature = nil;
if ([self.target respondsToSelector:sel]) {
signature = [self.target methodSignatureForSelector:sel];
} else {
/// void object selector arg
/// signaturecrash
signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.target respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:self.target];
}
}
@end
@interface ZFPlayerStatusBar()
///
@property (nonatomic, strong) UILabel *dateLabel;
///
@property (nonatomic, strong) UIView *batteryView;
///
@property (nonatomic, strong) UIImageView *batteryImageView;
///
@property (nonatomic, strong) CAShapeLayer *batteryLayer;
///
@property (nonatomic, strong) CAShapeLayer *batteryBoundLayer;
///
@property (nonatomic, strong) CAShapeLayer *batteryPositiveLayer;
///
@property (nonatomic, strong) UILabel *batteryLabel;
///
@property (nonatomic, strong) UILabel *networkLabel;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@end
@implementation ZFPlayerStatusBar
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setup];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self.dateLabel sizeToFit];
[self.networkLabel sizeToFit];
[self.batteryLabel sizeToFit];
self.dateLabel.zf_size = CGSizeMake(self.dateLabel.zf_width, 16);
self.batteryView.frame = CGRectMake(self.bounds.size.width - 35 - (iPhoneX ? 44 : 0), 0, 22, 10);
self.batteryLabel.frame = CGRectMake(self.batteryView.zf_x - 42, 0, self.batteryLabel.zf_width, 16);
self.networkLabel.frame = CGRectMake(self.batteryLabel.zf_x - 40, 0, self.networkLabel.zf_width + 13, 14);
self.dateLabel.center = self.center;
self.batteryView.zf_centerY = self.zf_centerY;
self.batteryLabel.zf_right = self.batteryView.zf_x - 5;
self.batteryLabel.zf_centerY = self.batteryView.zf_centerY;
self.networkLabel.zf_right = self.batteryLabel.zf_x - 10;
self.networkLabel.zf_centerY = self.batteryView.zf_centerY;
}
- (void)dealloc {
[self destoryTimer];
}
- (void)setup {
self.refreshTime = 3.0;
///
[self addSubview:self.dateLabel];
[self addSubview:self.batteryView];
///
[self.batteryView.layer addSublayer:self.batteryBoundLayer];
///
[self.batteryView.layer addSublayer:self.batteryPositiveLayer];
///
[self.batteryView.layer addSublayer:self.batteryLayer];
[self.batteryView addSubview:self.batteryImageView];
[self addSubview:self.batteryLabel];
[self addSubview:self.networkLabel];
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(batteryLevelDidChangeNotification:)
name:UIDeviceBatteryLevelDidChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(batteryStateDidChangeNotification:)
name:UIDeviceBatteryStateDidChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(localeDidChangeNotification:)
name:NSCurrentLocaleDidChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(networkDidChangeNotification:)
name:ZFReachabilityDidChangeNotification
object:nil];
}
- (void)batteryLevelDidChangeNotification:(NSNotification *)noti {
[self updateUI];
}
- (void)batteryStateDidChangeNotification:(NSNotification *)noti {
[self updateUI];
}
- (void)localeDidChangeNotification:(NSNotification *)noti {
[self.dateFormatter setLocale:[NSLocale currentLocale]];
[self updateUI];
}
- (void)networkDidChangeNotification:(NSNotification *)noti {
self.networkLabel.text = [self networkStatus];
[self setNeedsLayout];
[self layoutIfNeeded];
}
- (void)startTimer {
self.timer = [NSTimer timerWithTimeInterval:self.refreshTime target:[ZFPlayerTimerTarget proxyWithTarget:self] selector:@selector(updateUI) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
[self.timer fire];
}
- (void)destoryTimer {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
}
#pragma mark - update UI
- (void)updateUI {
[self updateDate];
[self updateBattery];
[self setNeedsLayout];
[self layoutIfNeeded];
}
- (void)updateDate {
NSMutableString *dateString = [[NSMutableString alloc] initWithString:[self.dateFormatter stringFromDate:[NSDate date]]];
NSRange amRange = [dateString rangeOfString:[self.dateFormatter AMSymbol]];
NSRange pmRange = [dateString rangeOfString:[self.dateFormatter PMSymbol]];
if (amRange.location != NSNotFound) {
[dateString deleteCharactersInRange:amRange];
} else if (pmRange.location != NSNotFound) {
[dateString deleteCharactersInRange:pmRange];
}
self.dateLabel.text = dateString;
}
- (void)updateBattery {
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
CGFloat batteryLevel = [UIDevice currentDevice].batteryLevel;
/// -1
if (batteryLevel < 0) { batteryLevel = 1.0; }
CGRect rect = CGRectMake(1.5, 1.5, (20-3)*batteryLevel, 10-3);
UIBezierPath *batteryPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:2];
UIColor *batteryColor;
UIDeviceBatteryState batteryState = [UIDevice currentDevice].batteryState;
if (batteryState == UIDeviceBatteryStateCharging || batteryState == UIDeviceBatteryStateFull) { ///
self.batteryImageView.hidden = NO;
} else {
self.batteryImageView.hidden = YES;
}
if (@available(iOS 9.0, *)) {
if ([NSProcessInfo processInfo].lowPowerModeEnabled) { ///
batteryColor = UIColorFromHex(0xF9CF0E);
} else {
if (batteryState == UIDeviceBatteryStateCharging || batteryState == UIDeviceBatteryStateFull) { ///
batteryColor = UIColorFromHex(0x37CB46);
} else if (batteryLevel <= 0.2) { ///
batteryColor = UIColorFromHex(0xF02C2D);
} else { ///
batteryColor = [UIColor whiteColor];
}
}
} else {
if (batteryState == UIDeviceBatteryStateCharging || batteryState == UIDeviceBatteryStateFull) { ///
batteryColor = UIColorFromHex(0x37CB46);
} else if (batteryLevel <= 0.2) { ///
batteryColor = UIColorFromHex(0xF02C2D);
} else { ///
batteryColor = [UIColor whiteColor];
}
}
self.batteryLayer.strokeColor = [UIColor clearColor].CGColor;
self.batteryLayer.path = batteryPath.CGPath;
self.batteryLayer.fillColor = batteryColor.CGColor;
self.batteryLabel.text = [NSString stringWithFormat:@"%.0f%%", batteryLevel*100];
}
- (NSString *)networkStatus {
NSString *net = @"WIFI";
ZFReachabilityStatus netStatus = [ZFReachabilityManager sharedManager].networkReachabilityStatus;
switch (netStatus) {
case ZFReachabilityStatusReachableViaWiFi:
net = @"WIFI";
break;
case ZFReachabilityStatusNotReachable:
net = @"无网络";
break;
case ZFReachabilityStatusReachableVia2G:
net = @"2G";
break;
case ZFReachabilityStatusReachableVia3G:
net = @"3G";
break;
case ZFReachabilityStatusReachableVia4G:
net = @"4G";
break;
case ZFReachabilityStatusReachableVia5G:
net = @"5G";
break;
default:
net = @"未知";
break;
}
return net;
}
#pragma mark - getter
- (UILabel *)dateLabel {
if (!_dateLabel) {
_dateLabel = [UILabel new];
_dateLabel.bounds = CGRectMake(0, 0, 100, 16);
_dateLabel.textColor = [UIColor whiteColor];
_dateLabel.font = [UIFont systemFontOfSize:12];
_dateLabel.textAlignment = NSTextAlignmentCenter;
}
return _dateLabel;
}
- (NSDateFormatter*)dateFormatter {
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setLocale:[NSLocale currentLocale]];
[_dateFormatter setDateStyle:NSDateFormatterNoStyle];
[_dateFormatter setTimeStyle:NSDateFormatterShortStyle];
}
return _dateFormatter;
}
- (UIView *)batteryView {
if (!_batteryView) {
_batteryView = [[UIView alloc] init];
}
return _batteryView;
}
- (UIImageView *)batteryImageView {
if (!_batteryImageView) {
_batteryImageView = [[UIImageView alloc] init];
_batteryImageView.bounds = CGRectMake(0, 0, 8, 12);
_batteryImageView.center = CGPointMake(10, 5);
_batteryImageView.image = ZFPlayer_Image(@"ZFPlayer_battery_lightning");
}
return _batteryImageView;
}
- (CAShapeLayer *)batteryLayer {
if (!_batteryLayer) {
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
CGFloat batteryLevel = [UIDevice currentDevice].batteryLevel;
UIBezierPath *batteryPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(1.5, 1.5, (20-3)*batteryLevel, 10-3) cornerRadius:2];
_batteryLayer = [CAShapeLayer layer];
_batteryLayer.lineWidth = 1;
_batteryLayer.strokeColor = [UIColor clearColor].CGColor;
_batteryLayer.path = batteryPath.CGPath;
_batteryLayer.fillColor = [UIColor whiteColor].CGColor;
}
return _batteryLayer;
}
- (CAShapeLayer *)batteryBoundLayer {
if (!_batteryBoundLayer) {
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 20, 10) cornerRadius:2.5];
_batteryBoundLayer = [CAShapeLayer layer];
_batteryBoundLayer.lineWidth = 1;
_batteryBoundLayer.strokeColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8].CGColor;
_batteryBoundLayer.path = bezierPath.CGPath;
_batteryBoundLayer.fillColor = nil;
}
return _batteryBoundLayer;
}
- (CAShapeLayer *)batteryPositiveLayer {
if (!_batteryPositiveLayer) {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(22, 3, 1, 3) byRoundingCorners:(UIRectCornerTopRight|UIRectCornerBottomRight) cornerRadii:CGSizeMake(2, 2)];
_batteryPositiveLayer = [CAShapeLayer layer];
_batteryPositiveLayer.lineWidth = 0.5;
_batteryPositiveLayer.strokeColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8].CGColor;
_batteryPositiveLayer.path = path.CGPath;
_batteryPositiveLayer.fillColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8].CGColor;
}
return _batteryPositiveLayer;
}
- (UILabel *)batteryLabel {
if (!_batteryLabel) {
_batteryLabel = [[UILabel alloc] init];
_batteryLabel.textColor = [UIColor whiteColor];
_batteryLabel.font = [UIFont systemFontOfSize:11];
_batteryLabel.textAlignment = NSTextAlignmentRight;
}
return _batteryLabel;
}
- (UILabel *)networkLabel {
if (!_networkLabel) {
_networkLabel = [[UILabel alloc] init];
_networkLabel.layer.cornerRadius = 7;
_networkLabel.layer.borderWidth = 1;
_networkLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
_networkLabel.textColor = [UIColor whiteColor];
_networkLabel.font = [UIFont systemFontOfSize:9];
_networkLabel.textAlignment = NSTextAlignmentCenter;
_networkLabel.text = @"WIFI";
}
return _networkLabel;
}
@end

View File

@@ -0,0 +1,111 @@
//
// ZFPortraitControlView.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 "ZFSliderView.h"
#if __has_include(<ZFPlayer/ZFPlayerController.h>)
#import <ZFPlayer/ZFPlayerController.h>
#else
#import "ZFPlayerController.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface ZFPortraitControlView : UIView
/// 底部工具栏
@property (nonatomic, strong, readonly) UIView *bottomToolView;
/// 顶部工具栏
@property (nonatomic, strong, readonly) UIView *topToolView;
/// 标题
@property (nonatomic, strong, readonly) UILabel *titleLabel;
/// 播放或暂停按钮
@property (nonatomic, strong, readonly) UIButton *playOrPauseBtn;
/// 播放的当前时间
@property (nonatomic, strong, readonly) UILabel *currentTimeLabel;
/// 滑杆
@property (nonatomic, strong, readonly) ZFSliderView *slider;
/// 视频总时间
@property (nonatomic, strong, readonly) UILabel *totalTimeLabel;
/// 全屏按钮
@property (nonatomic, strong, readonly) UIButton *fullScreenBtn;
/// 播放器
@property (nonatomic, weak) ZFPlayerController *player;
/// slider滑动中
@property (nonatomic, copy, nullable) void(^sliderValueChanging)(CGFloat value,BOOL forward);
/// slider滑动结束
@property (nonatomic, copy, nullable) void(^sliderValueChanged)(CGFloat value);
/// 如果是暂停状态seek完是否播放默认YES
@property (nonatomic, assign) BOOL seekToPlay;
/// 全屏模式
@property (nonatomic, assign) ZFFullScreenMode fullScreenMode;
/// 重置控制层
- (void)resetControlView;
/// 显示控制层
- (void)showControlView;
/// 隐藏控制层
- (void)hideControlView;
/// 设置播放时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime;
/// 设置缓冲时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime;
/// 是否响应该手势
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch;
/// 标题和全屏模式
- (void)showTitle:(NSString *_Nullable)title fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/// 根据当前播放状态取反
- (void)playOrPause;
/// 播放按钮状态
- (void)playBtnSelectedState:(BOOL)selected;
/// 调节播放进度slider和当前时间更新
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString;
/// 滑杆结束滑动
- (void)sliderChangeEnded;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,387 @@
//
// ZFPortraitControlView.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 "ZFPortraitControlView.h"
#import "UIView+ZFFrame.h"
#import "ZFUtilities.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayerConst.h"
#endif
@interface ZFPortraitControlView () <ZFSliderViewDelegate>
///
@property (nonatomic, strong) UIView *bottomToolView;
///
@property (nonatomic, strong) UIView *topToolView;
///
@property (nonatomic, strong) UILabel *titleLabel;
///
@property (nonatomic, strong) UIButton *playOrPauseBtn;
///
@property (nonatomic, strong) UILabel *currentTimeLabel;
///
@property (nonatomic, strong) ZFSliderView *slider;
///
@property (nonatomic, strong) UILabel *totalTimeLabel;
///
@property (nonatomic, strong) UIButton *fullScreenBtn;
@property (nonatomic, assign) BOOL isShow;
@end
@implementation ZFPortraitControlView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
//
[self addSubview:self.topToolView];
[self addSubview:self.bottomToolView];
[self addSubview:self.playOrPauseBtn];
[self.topToolView addSubview:self.titleLabel];
[self.bottomToolView addSubview:self.currentTimeLabel];
[self.bottomToolView addSubview:self.slider];
[self.bottomToolView addSubview:self.totalTimeLabel];
[self.bottomToolView addSubview:self.fullScreenBtn];
//
[self makeSubViewsAction];
[self resetControlView];
self.clipsToBounds = YES;
}
return self;
}
- (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.bounds.size.width;
CGFloat min_view_h = self.bounds.size.height;
CGFloat min_margin = 9;
min_x = 0;
min_y = 0;
min_w = min_view_w;
min_h = 40;
self.topToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 15;
min_y = 5;
min_w = min_view_w - min_x - 15;
min_h = 30;
self.titleLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_h = 40;
min_x = 0;
min_y = min_view_h - min_h;
min_w = min_view_w;
self.bottomToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = 44;
min_h = min_w;
self.playOrPauseBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.playOrPauseBtn.center = self.center;
min_x = min_margin;
min_w = 62;
min_h = 28;
min_y = (self.bottomToolView.zf_height - min_h)/2;
self.currentTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_w = 28;
min_h = min_w;
min_x = self.bottomToolView.zf_width - min_w - min_margin;
min_y = 0;
self.fullScreenBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.fullScreenBtn.zf_centerY = self.currentTimeLabel.zf_centerY;
min_w = 62;
min_h = 28;
min_x = self.fullScreenBtn.zf_left - min_w - 4;
min_y = 0;
self.totalTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.totalTimeLabel.zf_centerY = self.currentTimeLabel.zf_centerY;
min_x = self.currentTimeLabel.zf_right + 4;
min_y = 0;
min_w = self.totalTimeLabel.zf_left - min_x - 4;
min_h = 30;
self.slider.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.slider.zf_centerY = self.currentTimeLabel.zf_centerY;
if (!self.isShow) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.playOrPauseBtn.alpha = 0;
} else {
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
self.playOrPauseBtn.alpha = 1;
}
}
- (void)makeSubViewsAction {
[self.playOrPauseBtn addTarget:self action:@selector(playPauseButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.fullScreenBtn addTarget:self action:@selector(fullScreenButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - action
- (void)playPauseButtonClickAction:(UIButton *)sender {
[self playOrPause];
}
- (void)fullScreenButtonClickAction:(UIButton *)sender {
[self.player enterFullScreen:YES animated:YES];
}
///
- (void)playOrPause {
self.playOrPauseBtn.selected = !self.playOrPauseBtn.isSelected;
self.playOrPauseBtn.isSelected? [self.player.currentPlayerManager play]: [self.player.currentPlayerManager pause];
}
- (void)playBtnSelectedState:(BOOL)selected {
self.playOrPauseBtn.selected = selected;
}
#pragma mark - ZFSliderViewDelegate
- (void)sliderTouchBegan:(float)value {
self.slider.isdragging = YES;
}
- (void)sliderTouchEnded:(float)value {
if (self.player.totalTime > 0) {
self.slider.isdragging = YES;
if (self.sliderValueChanging) self.sliderValueChanging(value, self.slider.isForward);
@zf_weakify(self)
[self.player seekToTime:self.player.totalTime*value completionHandler:^(BOOL finished) {
@zf_strongify(self)
self.slider.isdragging = NO;
if (finished) {
if (self.sliderValueChanged) self.sliderValueChanged(value);
}
}];
if (self.seekToPlay) {
[self.player.currentPlayerManager play];
}
} else {
self.slider.isdragging = NO;
self.slider.value = 0;
}
}
- (void)sliderValueChanged:(float)value {
if (self.player.totalTime == 0) {
self.slider.value = 0;
return;
}
self.slider.isdragging = YES;
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
if (self.sliderValueChanging) self.sliderValueChanging(value,self.slider.isForward);
}
- (void)sliderTapped:(float)value {
[self sliderTouchEnded:value];
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
}
#pragma mark - public method
/** ControlView */
- (void)resetControlView {
self.bottomToolView.alpha = 1;
self.slider.value = 0;
self.slider.bufferValue = 0;
self.currentTimeLabel.text = @"00:00";
self.totalTimeLabel.text = @"00:00";
self.backgroundColor = [UIColor clearColor];
self.playOrPauseBtn.selected = YES;
self.titleLabel.text = @"";
}
- (void)showControlView {
self.topToolView.alpha = 1;
self.bottomToolView.alpha = 1;
self.isShow = YES;
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
self.playOrPauseBtn.alpha = 1;
self.player.statusBarHidden = NO;
}
- (void)hideControlView {
self.isShow = NO;
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.player.statusBarHidden = NO;
self.playOrPauseBtn.alpha = 0;
self.topToolView.alpha = 0;
self.bottomToolView.alpha = 0;
}
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch {
CGRect sliderRect = [self.bottomToolView convertRect:self.slider.frame toView:self];
if (CGRectContainsPoint(sliderRect, point)) {
return NO;
}
return YES;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
if (!self.slider.isdragging) {
NSString *currentTimeString = [ZFUtilities convertTimeSecond:currentTime];
self.currentTimeLabel.text = currentTimeString;
NSString *totalTimeString = [ZFUtilities convertTimeSecond:totalTime];
self.totalTimeLabel.text = totalTimeString;
self.slider.value = videoPlayer.progress;
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime {
self.slider.bufferValue = videoPlayer.bufferProgress;
}
- (void)showTitle:(NSString *)title fullScreenMode:(ZFFullScreenMode)fullScreenMode {
self.titleLabel.text = title;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
}
/// slider
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString {
self.slider.value = value;
self.currentTimeLabel.text = timeString;
self.slider.isdragging = YES;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
}
///
- (void)sliderChangeEnded {
self.slider.isdragging = NO;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformIdentity;
}];
}
#pragma mark - setter
- (void)setFullScreenMode:(ZFFullScreenMode)fullScreenMode {
_fullScreenMode = fullScreenMode;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
}
#pragma mark - getter
- (UIView *)topToolView {
if (!_topToolView) {
_topToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_top_shadow");
_topToolView.layer.contents = (id)image.CGImage;
}
return _topToolView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.font = [UIFont systemFontOfSize:15.0];
}
return _titleLabel;
}
- (UIView *)bottomToolView {
if (!_bottomToolView) {
_bottomToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_bottom_shadow");
_bottomToolView.layer.contents = (id)image.CGImage;
}
return _bottomToolView;
}
- (UIButton *)playOrPauseBtn {
if (!_playOrPauseBtn) {
_playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"new_allPlay_44x44_") forState:UIControlStateNormal];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"new_allPause_44x44_") forState:UIControlStateSelected];
}
return _playOrPauseBtn;
}
- (UILabel *)currentTimeLabel {
if (!_currentTimeLabel) {
_currentTimeLabel = [[UILabel alloc] init];
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_currentTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _currentTimeLabel;
}
- (ZFSliderView *)slider {
if (!_slider) {
_slider = [[ZFSliderView alloc] init];
_slider.delegate = self;
_slider.maximumTrackTintColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8];
_slider.bufferTrackTintColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
_slider.minimumTrackTintColor = [UIColor whiteColor];
[_slider setThumbImage:ZFPlayer_Image(@"ZFPlayer_slider") forState:UIControlStateNormal];
_slider.sliderHeight = 2;
}
return _slider;
}
- (UILabel *)totalTimeLabel {
if (!_totalTimeLabel) {
_totalTimeLabel = [[UILabel alloc] init];
_totalTimeLabel.textColor = [UIColor whiteColor];
_totalTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_totalTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _totalTimeLabel;
}
- (UIButton *)fullScreenBtn {
if (!_fullScreenBtn) {
_fullScreenBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_fullScreenBtn setImage:ZFPlayer_Image(@"ZFPlayer_fullscreen") forState:UIControlStateNormal];
}
return _fullScreenBtn;
}
@end

View File

@@ -0,0 +1,118 @@
//
// ZFSliderView.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>
@protocol ZFSliderViewDelegate <NSObject>
@optional
// 滑块滑动开始
- (void)sliderTouchBegan:(float)value;
// 滑块滑动中
- (void)sliderValueChanged:(float)value;
// 滑块滑动结束
- (void)sliderTouchEnded:(float)value;
// 滑杆点击
- (void)sliderTapped:(float)value;
@end
@interface ZFSliderButton : UIButton
@end
@interface ZFSliderView : UIView
@property (nonatomic, weak) id<ZFSliderViewDelegate> delegate;
/** 滑块 */
@property (nonatomic, strong, readonly) ZFSliderButton *sliderBtn;
/** 默认滑杆的颜色 */
@property (nonatomic, strong) UIColor *maximumTrackTintColor;
/** 滑杆进度颜色 */
@property (nonatomic, strong) UIColor *minimumTrackTintColor;
/** 缓存进度颜色 */
@property (nonatomic, strong) UIColor *bufferTrackTintColor;
/** loading进度颜色 */
@property (nonatomic, strong) UIColor *loadingTintColor;
/** 默认滑杆的图片 */
@property (nonatomic, strong) UIImage *maximumTrackImage;
/** 滑杆进度的图片 */
@property (nonatomic, strong) UIImage *minimumTrackImage;
/** 缓存进度的图片 */
@property (nonatomic, strong) UIImage *bufferTrackImage;
/** 滑杆进度 */
@property (nonatomic, assign) float value;
/** 缓存进度 */
@property (nonatomic, assign) float bufferValue;
/** 是否允许点击默认是YES */
@property (nonatomic, assign) BOOL allowTapped;
/** 是否允许点击默认是YES */
@property (nonatomic, assign) BOOL animate;
/** 设置滑杆的高度 */
@property (nonatomic, assign) CGFloat sliderHeight;
/** 设置滑杆的圆角 */
@property (nonatomic, assign) CGFloat sliderRadius;
/** 是否隐藏滑块默认为NO */
@property (nonatomic, assign) BOOL isHideSliderBlock;
/// 是否正在拖动
@property (nonatomic, assign) BOOL isdragging;
/// 向前还是向后拖动
@property (nonatomic, assign) BOOL isForward;
@property (nonatomic, assign) CGSize thumbSize;
/**
* Starts animation of the spinner.
*/
- (void)startAnimating;
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating;
// 设置滑块背景色
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state;
// 设置滑块图片
- (void)setThumbImage:(UIImage *)image forState:(UIControlState)state;
@end

View File

@@ -0,0 +1,426 @@
//
// ZFSliderView.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 "ZFSliderView.h"
#import "UIView+ZFFrame.h"
/** */
static const CGFloat kSliderBtnWH = 19.0;
/** */
static const CGFloat kProgressH = 1.0;
/** slider*/
static const CGFloat kAnimate = 0.3;
@implementation ZFSliderButton
//
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGRect bounds = self.bounds;
//
bounds = CGRectInset(bounds, -20, -20);
// boundsyes
return CGRectContainsPoint(bounds, point);
}
@end
@interface ZFSliderView ()
/** */
@property (nonatomic, strong) UIImageView *bgProgressView;
/** */
@property (nonatomic, strong) UIImageView *bufferProgressView;
/** */
@property (nonatomic, strong) UIImageView *sliderProgressView;
/** */
@property (nonatomic, strong) ZFSliderButton *sliderBtn;
@property (nonatomic, strong) UIView *loadingBarView;
@property (nonatomic, assign) BOOL isLoading;
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;
@end
@implementation ZFSliderView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.allowTapped = YES;
self.animate = YES;
[self addSubViews];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
self.allowTapped = YES;
self.animate = YES;
[self addSubViews];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (isnan(self.value) || isnan(self.bufferValue)) return;
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.bounds.size.width;
CGFloat min_view_h = self.bounds.size.height;
min_x = 0;
min_w = min_view_w;
min_y = 0;
min_h = self.sliderHeight;
self.bgProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = self.thumbSize.width;
min_h = self.thumbSize.height;
self.sliderBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.sliderBtn.zf_centerX = self.bgProgressView.zf_width * self.value;
min_x = 0;
min_y = 0;
if (self.sliderBtn.hidden) {
min_w = self.bgProgressView.zf_width * self.value;
} else {
min_w = self.sliderBtn.zf_centerX;
}
min_h = self.sliderHeight;
self.sliderProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = self.bgProgressView.zf_width * self.bufferValue;
min_h = self.sliderHeight;
self.bufferProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_w = 0.1;
min_h = self.sliderHeight;
min_x = (min_view_w - min_w)/2;
min_y = (min_view_h - min_h)/2;
self.loadingBarView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.bgProgressView.zf_centerY = min_view_h * 0.5;
self.bufferProgressView.zf_centerY = min_view_h * 0.5;
self.sliderProgressView.zf_centerY = min_view_h * 0.5;
self.sliderBtn.zf_centerY = min_view_h * 0.5;
}
/**
*/
- (void)addSubViews {
self.thumbSize = CGSizeMake(kSliderBtnWH, kSliderBtnWH);
self.sliderHeight = kProgressH;
self.backgroundColor = [UIColor clearColor];
[self addSubview:self.bgProgressView];
[self addSubview:self.bufferProgressView];
[self addSubview:self.sliderProgressView];
[self addSubview:self.sliderBtn];
[self addSubview:self.loadingBarView];
//
self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[self addGestureRecognizer:self.tapGesture];
//
UIPanGestureRecognizer *sliderGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(sliderGesture:)];
[self addGestureRecognizer:sliderGesture];
}
#pragma mark - Setter
- (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor {
_maximumTrackTintColor = maximumTrackTintColor;
self.bgProgressView.backgroundColor = maximumTrackTintColor;
}
- (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor {
_minimumTrackTintColor = minimumTrackTintColor;
self.sliderProgressView.backgroundColor = minimumTrackTintColor;
}
- (void)setBufferTrackTintColor:(UIColor *)bufferTrackTintColor {
_bufferTrackTintColor = bufferTrackTintColor;
self.bufferProgressView.backgroundColor = bufferTrackTintColor;
}
- (void)setLoadingTintColor:(UIColor *)loadingTintColor {
_loadingTintColor = loadingTintColor;
self.loadingBarView.backgroundColor = loadingTintColor;
}
- (void)setMaximumTrackImage:(UIImage *)maximumTrackImage {
_maximumTrackImage = maximumTrackImage;
self.bgProgressView.image = maximumTrackImage;
self.maximumTrackTintColor = [UIColor clearColor];
}
- (void)setMinimumTrackImage:(UIImage *)minimumTrackImage {
_minimumTrackImage = minimumTrackImage;
self.sliderProgressView.image = minimumTrackImage;
self.minimumTrackTintColor = [UIColor clearColor];
}
- (void)setBufferTrackImage:(UIImage *)bufferTrackImage {
_bufferTrackImage = bufferTrackImage;
self.bufferProgressView.image = bufferTrackImage;
self.bufferTrackTintColor = [UIColor clearColor];
}
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state {
[self.sliderBtn setBackgroundImage:image forState:state];
}
- (void)setThumbImage:(UIImage *)image forState:(UIControlState)state {
[self.sliderBtn setImage:image forState:state];
}
- (void)setValue:(float)value {
if (isnan(value)) return;
value = MIN(1.0, value);
_value = value;
if (self.sliderBtn.hidden) {
self.sliderProgressView.zf_width = self.bgProgressView.zf_width * value;
} else {
self.sliderBtn.zf_centerX = self.bgProgressView.zf_width * value;
self.sliderProgressView.zf_width = self.sliderBtn.zf_centerX;
}
}
- (void)setBufferValue:(float)bufferValue {
if (isnan(bufferValue)) return;
bufferValue = MIN(1.0, bufferValue);
_bufferValue = bufferValue;
self.bufferProgressView.zf_width = self.bgProgressView.zf_width * bufferValue;
}
- (void)setAllowTapped:(BOOL)allowTapped {
_allowTapped = allowTapped;
if (!allowTapped) {
[self removeGestureRecognizer:self.tapGesture];
}
}
- (void)setSliderHeight:(CGFloat)sliderHeight {
if (isnan(sliderHeight)) return;
_sliderHeight = sliderHeight;
self.bgProgressView.zf_height = sliderHeight;
self.bufferProgressView.zf_height = sliderHeight;
self.sliderProgressView.zf_height = sliderHeight;
}
- (void)setSliderRadius:(CGFloat)sliderRadius {
if (isnan(sliderRadius)) return;
_sliderRadius = sliderRadius;
self.bgProgressView.layer.cornerRadius = sliderRadius;
self.bufferProgressView.layer.cornerRadius = sliderRadius;
self.sliderProgressView.layer.cornerRadius = sliderRadius;
self.bgProgressView.layer.masksToBounds = YES;
self.bufferProgressView.layer.masksToBounds = YES;
self.sliderProgressView.layer.masksToBounds = YES;
}
- (void)setIsHideSliderBlock:(BOOL)isHideSliderBlock {
_isHideSliderBlock = isHideSliderBlock;
//
if (isHideSliderBlock) {
self.sliderBtn.hidden = YES;
self.bgProgressView.zf_left = 0;
self.bufferProgressView.zf_left = 0;
self.sliderProgressView.zf_left = 0;
self.allowTapped = NO;
}
}
/**
* Starts animation of the spinner.
*/
- (void)startAnimating {
if (self.isLoading) return;
self.isLoading = YES;
self.bufferProgressView.hidden = YES;
self.sliderProgressView.hidden = YES;
self.sliderBtn.hidden = YES;
self.loadingBarView.hidden = NO;
[self.loadingBarView.layer removeAllAnimations];
CAAnimationGroup *animationGroup = [[CAAnimationGroup alloc] init];
animationGroup.duration = 0.4;
animationGroup.beginTime = CACurrentMediaTime() + 0.4;
animationGroup.repeatCount = MAXFLOAT;
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
CABasicAnimation *scaleAnimation = [CABasicAnimation animation];
scaleAnimation.keyPath = @"transform.scale.x";
scaleAnimation.fromValue = @(1000.0f);
scaleAnimation.toValue = @(self.zf_width * 10);
CABasicAnimation *alphaAnimation = [CABasicAnimation animation];
alphaAnimation.keyPath = @"opacity";
alphaAnimation.fromValue = @(1.0f);
alphaAnimation.toValue = @(0.0f);
[animationGroup setAnimations:@[scaleAnimation, alphaAnimation]];
[self.loadingBarView.layer addAnimation:animationGroup forKey:@"loading"];
}
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating {
self.isLoading = NO;
self.bufferProgressView.hidden = NO;
self.sliderProgressView.hidden = NO;
self.sliderBtn.hidden = self.isHideSliderBlock;
self.loadingBarView.hidden = YES;
[self.loadingBarView.layer removeAllAnimations];
}
#pragma mark - User Action
- (void)sliderGesture:(UIGestureRecognizer *)gesture {
switch (gesture.state) {
case UIGestureRecognizerStateBegan: {
[self sliderBtnTouchBegin:self.sliderBtn];
}
break;
case UIGestureRecognizerStateChanged: {
[self sliderBtnDragMoving:self.sliderBtn point:[gesture locationInView:self.bgProgressView]];
}
break;
case UIGestureRecognizerStateEnded: {
[self sliderBtnTouchEnded:self.sliderBtn];
}
break;
default:
break;
}
}
- (void)sliderBtnTouchBegin:(UIButton *)btn {
if ([self.delegate respondsToSelector:@selector(sliderTouchBegan:)]) {
[self.delegate sliderTouchBegan:self.value];
}
if (self.animate) {
[UIView animateWithDuration:kAnimate animations:^{
btn.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
}
}
- (void)sliderBtnTouchEnded:(UIButton *)btn {
if ([self.delegate respondsToSelector:@selector(sliderTouchEnded:)]) {
[self.delegate sliderTouchEnded:self.value];
}
if (self.animate) {
[UIView animateWithDuration:kAnimate animations:^{
btn.transform = CGAffineTransformIdentity;
}];
}
}
- (void)sliderBtnDragMoving:(UIButton *)btn point:(CGPoint)touchPoint {
//
CGPoint point = touchPoint;
// btn 0-(self.width - btn.width)
CGFloat value = (point.x - btn.zf_width * 0.5) / self.bgProgressView.zf_width;
// value0-1
value = value >= 1.0 ? 1.0 : value <= 0.0 ? 0.0 : value;
if (self.value == value) return;
self.isForward = self.value < value;
self.value = value;
if ([self.delegate respondsToSelector:@selector(sliderValueChanged:)]) {
[self.delegate sliderValueChanged:value];
}
}
- (void)tapped:(UITapGestureRecognizer *)tap {
CGPoint point = [tap locationInView:self.bgProgressView];
//
CGFloat value = (point.x - self.sliderBtn.zf_width * 0.5) * 1.0 / self.bgProgressView.zf_width;
value = value >= 1.0 ? 1.0 : value <= 0 ? 0 : value;
self.value = value;
if ([self.delegate respondsToSelector:@selector(sliderTapped:)]) {
[self.delegate sliderTapped:value];
}
}
#pragma mark - getter
- (UIView *)bgProgressView {
if (!_bgProgressView) {
_bgProgressView = [UIImageView new];
_bgProgressView.backgroundColor = [UIColor grayColor];
_bgProgressView.contentMode = UIViewContentModeScaleAspectFill;
_bgProgressView.clipsToBounds = YES;
}
return _bgProgressView;
}
- (UIView *)bufferProgressView {
if (!_bufferProgressView) {
_bufferProgressView = [UIImageView new];
_bufferProgressView.backgroundColor = [UIColor whiteColor];
_bufferProgressView.contentMode = UIViewContentModeScaleAspectFill;
_bufferProgressView.clipsToBounds = YES;
}
return _bufferProgressView;
}
- (UIView *)sliderProgressView {
if (!_sliderProgressView) {
_sliderProgressView = [UIImageView new];
_sliderProgressView.backgroundColor = [UIColor redColor];
_sliderProgressView.contentMode = UIViewContentModeScaleAspectFill;
_sliderProgressView.clipsToBounds = YES;
}
return _sliderProgressView;
}
- (ZFSliderButton *)sliderBtn {
if (!_sliderBtn) {
_sliderBtn = [ZFSliderButton buttonWithType:UIButtonTypeCustom];
[_sliderBtn setAdjustsImageWhenHighlighted:NO];
}
return _sliderBtn;
}
- (UIView *)loadingBarView {
if (!_loadingBarView) {
_loadingBarView = [[UIView alloc] init];
_loadingBarView.backgroundColor = [UIColor whiteColor];
_loadingBarView.hidden = YES;
}
return _loadingBarView;
}
@end

View File

@@ -0,0 +1,31 @@
//
// ZFSmallFloatControlView.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 ZFSmallFloatControlView : UIView
@property (nonatomic, copy, nullable) void(^closeClickCallback)(void);
@end

View File

@@ -0,0 +1,72 @@
//
// ZFSmallFloatControlView.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 "ZFSmallFloatControlView.h"
#import "ZFUtilities.h"
@interface ZFSmallFloatControlView ()
@property (nonatomic, strong) UIButton *closeBtn;
@end
@implementation ZFSmallFloatControlView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.closeBtn];
}
return self;
}
- (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.bounds.size.width;
min_x = min_view_w-20;
min_y = -10;
min_w = 30;
min_h = min_w;
self.closeBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
}
- (void)closeBtnClick:(UIButton *)sender {
if (self.closeClickCallback) self.closeClickCallback();
}
- (UIButton *)closeBtn {
if (!_closeBtn) {
_closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeBtn setImage:ZFPlayer_Image(@"ZFPlayer_close") forState:UIControlStateNormal];
[_closeBtn addTarget:self action:@selector(closeBtnClick:) forControlEvents:UIControlEventTouchUpInside];
}
return _closeBtn;
}
@end

View File

@@ -0,0 +1,27 @@
//
// ZFSpeedLoadingView.h
// Pods-ZFPlayer_Example
//
// Created by 紫枫 on 2018/6/27.
//
#import <UIKit/UIKit.h>
#import "ZFLoadingView.h"
@interface ZFSpeedLoadingView : UIView
@property (nonatomic, strong) ZFLoadingView *loadingView;
@property (nonatomic, strong) UILabel *speedTextLabel;
/**
* Starts animation of the spinner.
*/
- (void)startAnimating;
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating;
@end

View File

@@ -0,0 +1,117 @@
//
// ZFSpeedLoadingView.m
// Pods-ZFPlayer_Example
//
// Created by on 2018/6/27.
//
#import "ZFSpeedLoadingView.h"
#import "ZFNetworkSpeedMonitor.h"
#import "UIView+ZFFrame.h"
@interface ZFSpeedLoadingView ()
@property (nonatomic, strong) ZFNetworkSpeedMonitor *speedMonitor;
@end
@implementation ZFSpeedLoadingView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self initialize];
}
- (void)initialize {
self.userInteractionEnabled = NO;
[self addSubview:self.loadingView];
[self addSubview:self.speedTextLabel];
[self.speedMonitor startNetworkSpeedMonitor];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkSpeedChanged:) name:ZFDownloadNetworkSpeedNotificationKey object:nil];
}
- (void)dealloc {
[self.speedMonitor stopNetworkSpeedMonitor];
[[NSNotificationCenter defaultCenter] removeObserver:self name:ZFDownloadNetworkSpeedNotificationKey object:nil];
}
- (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.zf_width;
CGFloat min_view_h = self.zf_height;
min_w = 44;
min_h = min_w;
min_x = (min_view_w - min_w) / 2;
min_y = (min_view_h - min_h) / 2 - 10;
self.loadingView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = self.loadingView.zf_bottom+5;
min_w = min_view_w;
min_h = 20;
self.speedTextLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
}
- (void)networkSpeedChanged:(NSNotification *)sender {
NSString *downloadSpped = [sender.userInfo objectForKey:ZFNetworkSpeedNotificationKey];
self.speedTextLabel.text = downloadSpped;
}
- (void)startAnimating {
[self.loadingView startAnimating];
self.hidden = NO;
}
- (void)stopAnimating {
[self.loadingView stopAnimating];
self.hidden = YES;
}
- (UILabel *)speedTextLabel {
if (!_speedTextLabel) {
_speedTextLabel = [UILabel new];
_speedTextLabel.textColor = [UIColor whiteColor];
_speedTextLabel.font = [UIFont systemFontOfSize:12.0];
_speedTextLabel.textAlignment = NSTextAlignmentCenter;
}
return _speedTextLabel;
}
- (ZFNetworkSpeedMonitor *)speedMonitor {
if (!_speedMonitor) {
_speedMonitor = [[ZFNetworkSpeedMonitor alloc] init];
}
return _speedMonitor;
}
- (ZFLoadingView *)loadingView {
if (!_loadingView) {
_loadingView = [[ZFLoadingView alloc] init];
_loadingView.lineWidth = 0.8;
_loadingView.duration = 1;
_loadingView.hidesWhenStopped = YES;
}
return _loadingView;
}
@end

View File

@@ -0,0 +1,52 @@
//
// ZFUtilities.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>
#import <UIKit/UIKit.h>
/// iPhoneX iPhoneXS iPhoneXS Max iPhoneXR 机型判断
#define iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? ((NSInteger)(([[UIScreen mainScreen] currentMode].size.height/[[UIScreen mainScreen] currentMode].size.width)*100) == 216) : NO)
#define ZFPlayer_Image(file) [ZFUtilities imageNamed:file]
// 屏幕的宽
#define ZFPlayer_ScreenWidth [[UIScreen mainScreen] bounds].size.width
// 屏幕的高
#define ZFPlayer_ScreenHeight [[UIScreen mainScreen] bounds].size.height
#define UIColorFromHex(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
@interface ZFUtilities : NSObject
+ (NSString *)convertTimeSecond:(NSInteger)timeSecond;
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size;
+ (UIImage *)imageNamed:(NSString *)name;
@end

View File

@@ -0,0 +1,74 @@
//
// ZFUtilities.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 "ZFUtilities.h"
@implementation ZFUtilities
+ (NSString *)convertTimeSecond:(NSInteger)timeSecond {
NSString *theLastTime = nil;
long second = timeSecond;
if (timeSecond < 60) {
theLastTime = [NSString stringWithFormat:@"00:%02zd", second];
} else if(timeSecond >= 60 && timeSecond < 3600){
theLastTime = [NSString stringWithFormat:@"%02zd:%02zd", second/60, second%60];
} else if(timeSecond >= 3600){
theLastTime = [NSString stringWithFormat:@"%02zd:%02zd:%02zd", second/3600, second%3600/60, second%60];
}
return theLastTime;
}
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
if (!color || size.width <= 0 || size.height <= 0) return nil;
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (NSBundle *)bundle {
static NSBundle *bundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
bundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"ZFPlayer" ofType:@"bundle"]];
});
return bundle;
}
+ (UIImage *)imageNamed:(NSString *)name {
if (name.length == 0) return nil;
int scale = (int)UIScreen.mainScreen.scale;
if (scale < 2) scale = 2;
else if (scale > 3) scale = 3;
NSString *n = [NSString stringWithFormat:@"%@@%dx", name, scale];
UIImage *image = [UIImage imageWithContentsOfFile:[self.bundle pathForResource:n ofType:@"png"]];
if (!image) image = [UIImage imageWithContentsOfFile:[self.bundle pathForResource:name ofType:@"png"]];
return image;
}
@end

View File

@@ -0,0 +1,46 @@
//
// ZFVolumeBrightnessView.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>
typedef NS_ENUM(NSInteger, ZFVolumeBrightnessType) {
ZFVolumeBrightnessTypeVolume, // volume
ZFVolumeBrightnessTypeumeBrightness // brightness
};
@interface ZFVolumeBrightnessView : UIView
@property (nonatomic, assign, readonly) ZFVolumeBrightnessType volumeBrightnessType;
@property (nonatomic, strong, readonly) UIProgressView *progressView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
- (void)updateProgress:(CGFloat)progress withVolumeBrightnessType:(ZFVolumeBrightnessType)volumeBrightnessType;
/// 添加系统音量view
- (void)addSystemVolumeView;
/// 移除系统音量view
- (void)removeSystemVolumeView;
@end

View File

@@ -0,0 +1,162 @@
//
// ZFVolumeBrightnessView.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 "ZFVolumeBrightnessView.h"
#import <MediaPlayer/MediaPlayer.h>
#import "ZFUtilities.h"
@interface ZFVolumeBrightnessView ()
@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, assign) ZFVolumeBrightnessType volumeBrightnessType;
@property (nonatomic, strong) MPVolumeView *volumeView;
@end
@implementation ZFVolumeBrightnessView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.iconImageView];
[self addSubview:self.progressView];
[self hideTipView];
}
return self;
}
- (void)dealloc {
[self addSystemVolumeView];
}
- (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;
CGFloat margin = 10;
min_x = margin;
min_w = 20;
min_h = min_w;
min_y = (min_view_h-min_h)/2;
self.iconImageView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = CGRectGetMaxX(self.iconImageView.frame) + margin;
min_h = 2;
min_y = (min_view_h-min_h)/2;
min_w = min_view_w - min_x - margin;
self.progressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.layer.cornerRadius = min_view_h/2;
self.layer.masksToBounds = YES;
}
- (void)hideTipView {
[UIView animateWithDuration:0.5 animations:^{
self.alpha = 0;
} completion:^(BOOL finished) {
self.hidden = YES;
}];
}
/// view
- (void)addSystemVolumeView {
[self.volumeView removeFromSuperview];
}
/// view
- (void)removeSystemVolumeView {
[[UIApplication sharedApplication].keyWindow addSubview:self.volumeView];
}
- (void)updateProgress:(CGFloat)progress withVolumeBrightnessType:(ZFVolumeBrightnessType)volumeBrightnessType {
if (progress >= 1) {
progress = 1;
} else if (progress <= 0) {
progress = 0;
}
self.progressView.progress = progress;
self.volumeBrightnessType = volumeBrightnessType;
UIImage *playerImage = nil;
if (volumeBrightnessType == ZFVolumeBrightnessTypeVolume) {
if (progress == 0) {
playerImage = ZFPlayer_Image(@"ZFPlayer_muted");
} else if (progress > 0 && progress < 0.5) {
playerImage = ZFPlayer_Image(@"ZFPlayer_volume_low");
} else {
playerImage = ZFPlayer_Image(@"ZFPlayer_volume_high");
}
} else if (volumeBrightnessType == ZFVolumeBrightnessTypeumeBrightness) {
if (progress >= 0 && progress < 0.5) {
playerImage = ZFPlayer_Image(@"ZFPlayer_brightness_low");
} else {
playerImage = ZFPlayer_Image(@"ZFPlayer_brightness_high");
}
}
self.iconImageView.image = playerImage;
self.hidden = NO;
self.alpha = 1;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideTipView) object:nil];
[self performSelector:@selector(hideTipView) withObject:nil afterDelay:1.5];
}
- (void)setVolumeBrightnessType:(ZFVolumeBrightnessType)volumeBrightnessType {
_volumeBrightnessType = volumeBrightnessType;
if (volumeBrightnessType == ZFVolumeBrightnessTypeVolume) {
self.iconImageView.image = ZFPlayer_Image(@"ZFPlayer_volume");
} else {
self.iconImageView.image = ZFPlayer_Image(@"ZFPlayer_brightness");
}
}
- (UIProgressView *)progressView {
if (!_progressView) {
_progressView = [[UIProgressView alloc] init];
_progressView.progressTintColor = [UIColor whiteColor];
_progressView.trackTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.4];;
}
return _progressView;
}
- (UIImageView *)iconImageView {
if (!_iconImageView) {
_iconImageView = [UIImageView new];
}
return _iconImageView;
}
- (MPVolumeView *)volumeView {
if (!_volumeView) {
_volumeView = [[MPVolumeView alloc] init];
_volumeView.frame = CGRectMake(-1000, -1000, 100, 100);
}
return _volumeView;
}
@end

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 = isLast ? sortedIndexPaths.lastObject : 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 = isLast ? sortedIndexPaths.lastObject : 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,159 @@
//
// 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>
#import "ZFLandscapeWindow.h"
#import "ZFLandscapeRotationManager.h"
API_AVAILABLE(ios(13.0)) @implementation UIViewController (ZFPlayerFixSafeArea)
/// Hook
- (void)zf_setContentOverlayInsets:(UIEdgeInsets)insets andLeftMargin:(CGFloat)leftMargin rightMargin:(CGFloat)rightMargin {
UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow;
UIWindow *otherWindow = self.view.window;
if ([keyWindow isKindOfClass:ZFLandscapeWindow.class] && otherWindow != nil) {
ZFLandscapeRotationManager *manager = ((ZFLandscapeWindow *)keyWindow).rotationManager;
UIWindow *superviewWindow = manager.containerView.window;
if (superviewWindow != otherWindow) {
[self zf_setContentOverlayInsets:insets andLeftMargin:leftMargin rightMargin:rightMargin];
}
} else {
[self zf_setContentOverlayInsets:insets andLeftMargin:leftMargin rightMargin:rightMargin];
}
}
@end
@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)]);
[self hookClass:self.class originalSelector:originalSelector swizzledSelector:swizzledSelector];
}
double systemVersion = [UIDevice currentDevice].systemVersion.doubleValue;
if (systemVersion >= 13.0 && systemVersion < 16.0) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
/// _setContentOverlayInsets:andLeftMargin:rightMargin:
NSData *data = [NSData.alloc initWithBase64EncodedString:@"X3NldENvbnRlbnRPdmVybGF5SW5zZXRzOmFuZExlZnRNYXJnaW46cmlnaHRNYXJnaW46" options:kNilOptions];
NSString *method = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
SEL originalSelector = NSSelectorFromString(method);
#pragma clang diagnostic pop
SEL swizzledSelector = @selector(zf_setContentOverlayInsets:andLeftMargin:rightMargin:);
[self hookClass:UIViewController.class originalSelector:originalSelector swizzledSelector:swizzledSelector];
}
});
}
+ (void)hookClass:(Class)cls originalSelector:(SEL)orlSelector swizzledSelector:(SEL)swzdSelector {
Method originalMethod = class_getInstanceMethod(cls, orlSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swzdSelector);
if (class_addMethod(self, orlSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod(self, swzdSelector, 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,86 @@
//
// ZFLandscapeRotationManager.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 <Foundation/Foundation.h>
#import "ZFOrientationObserver.h"
#import "ZFLandscapeWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeRotationManager : NSObject
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(UIInterfaceOrientation orientation);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(UIInterfaceOrientation orientation);
@property (nonatomic, weak) UIView *contentView;
@property (nonatomic, weak) UIView *containerView;
@property (nonatomic, strong, nullable) ZFLandscapeWindow *window;
/// Whether allow the video orientation rotate.
/// default is YES.
@property (nonatomic, assign) BOOL allowOrientationRotation;
/// Lock screen orientation
@property (nonatomic, getter=isLockedScreen) BOOL lockedScreen;
@property (nonatomic, assign) BOOL disableAnimations;
/// The support Interface Orientation,default is ZFInterfaceOrientationMaskAllButUpsideDown
@property (nonatomic, assign) ZFInterfaceOrientationMask supportInterfaceOrientation;
/// The current orientation of the player.
/// Default is UIInterfaceOrientationPortrait.
@property (nonatomic, assign) UIInterfaceOrientation currentOrientation;
@property (nonatomic, strong, readonly, nullable) ZFLandscapeViewController *landscapeViewController;
/// current device orientation observer is activie.
@property (nonatomic, assign) BOOL activeDeviceObserver;
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation completion:(void(^ __nullable)(void))completion;
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
- (UIInterfaceOrientation)getCurrentOrientation;
- (void)handleDeviceOrientationChange;
/// update the rotateView and containerView.
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView;
- (BOOL)isSuppprtInterfaceOrientation:(UIInterfaceOrientation)orientation;
+ (ZFInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,165 @@
//
// ZFLandscapeRotationManager.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 "ZFLandscapeRotationManager.h"
@interface ZFLandscapeRotationManager () <ZFLandscapeViewControllerDelegate>
@end
@implementation ZFLandscapeRotationManager
- (instancetype)init {
self = [super init];
if (self) {
_currentOrientation = UIInterfaceOrientationPortrait;
}
return self;
}
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView {
self.contentView = rotateView;
self.containerView = containerView;
}
- (UIInterfaceOrientation)getCurrentOrientation {
if (@available(iOS 16.0, *)) {
NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
UIWindowScene *scene = [array firstObject];
return scene.interfaceOrientation;
} else {
return (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
}
}
- (void)handleDeviceOrientationChange {
if (!self.allowOrientationRotation || self.isLockedScreen) 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;
}
}
- (BOOL)isSuppprtInterfaceOrientation:(UIInterfaceOrientation)orientation {
if (orientation == UIInterfaceOrientationPortrait) {
return [self _isSupportedPortrait];
} else if (orientation == UIInterfaceOrientationLandscapeLeft) {
return [self _isSupportedLandscapeLeft];
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
return [self _isSupportedLandscapeRight];
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
return [self _isSupportedPortraitUpsideDown];
}
return NO;
}
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation completion:(void(^ __nullable)(void))completion {}
- (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 {
_currentOrientation = orientation;
if (UIInterfaceOrientationIsLandscape(orientation)) {
if (!self.window) {
self.window = [ZFLandscapeWindow new];
self.landscapeViewController.delegate = self;
self.window.rootViewController = self.landscapeViewController;
self.window.rotationManager = self;
}
}
self.disableAnimations = !animated;
if ([UIDevice currentDevice].systemVersion.doubleValue < 16.0) {
[self interfaceOrientation:UIInterfaceOrientationUnknown completion:nil];
}
[self interfaceOrientation:orientation completion:completion];
}
/// is support portrait
- (BOOL)_isSupportedPortrait {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait;
}
/// is support portraitUpsideDown
- (BOOL)_isSupportedPortraitUpsideDown {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortraitUpsideDown;
}
/// is support landscapeLeft
- (BOOL)_isSupportedLandscapeLeft {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeLeft;
}
/// is support landscapeRight
- (BOOL)_isSupportedLandscapeRight {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeRight;
}
+ (ZFInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
if ([window isKindOfClass:ZFLandscapeWindow.class]) {
ZFLandscapeRotationManager *manager = ((ZFLandscapeWindow *)window).rotationManager;
if (manager != nil) {
return (ZFInterfaceOrientationMask)[manager supportedInterfaceOrientationsForWindow:window];
}
}
return ZFInterfaceOrientationMaskUnknow;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)]
userInfo:nil];
}
@end

View File

@@ -0,0 +1,33 @@
//
// ZFLandscapeRotationManager_iOS15.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 "ZFLandscapeRotationManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeRotationManager_iOS15 : ZFLandscapeRotationManager
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,166 @@
//
// ZFLandscapeRotationManager_iOS15.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 "ZFLandscapeRotationManager_iOS15.h"
#import "ZFLandscapeViewController_iOS15.h"
@interface ZFLandscapeRotationManager_iOS15 () {
void(^_rotateCompleted)(void);
}
@property (nonatomic, strong, readonly) ZFLandscapeViewController_iOS15 *landscapeViewController;
/// Force Rotaion, default NO.
@property (nonatomic, assign) BOOL forceRotaion;
@end
@implementation ZFLandscapeRotationManager_iOS15
@synthesize landscapeViewController = _landscapeViewController;
- (ZFLandscapeViewController_iOS15 *)landscapeViewController {
if (!_landscapeViewController) {
_landscapeViewController = [[ZFLandscapeViewController_iOS15 alloc] init];
}
return _landscapeViewController;
}
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation completion:(void(^ __nullable)(void))completion {
[super interfaceOrientation:orientation completion:completion];
_rotateCompleted = completion;
self.forceRotaion = YES;
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];
}
[UIViewController attemptRotationToDeviceOrientation];
}
- (void)rotationBegin {
if (self.window.isHidden) {
self.window.hidden = NO;
[self.window makeKeyAndVisible];
}
[self.window.rootViewController setNeedsStatusBarAppearanceUpdate];
}
- (void)rotationEnd {
if (!self.window.isHidden && !UIInterfaceOrientationIsLandscape(self.currentOrientation)) {
self.window.hidden = YES;
[self.containerView.window makeKeyAndVisible];
}
self.disableAnimations = NO;
if (_rotateCompleted) {
_rotateCompleted();
_rotateCompleted = nil;
}
}
- (BOOL)allowsRotation {
if (UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
UIInterfaceOrientation toOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (![self isSuppprtInterfaceOrientation:toOrientation]) {
return NO;
}
}
if (self.forceRotaion) { return YES; }
if (!self.activeDeviceObserver) { return NO; }
if (self.allowOrientationRotation && !self.isLockedScreen) { return YES; }
return NO;
}
#pragma mark - ZFLandscapeViewControllerDelegate
- (BOOL)ls_shouldAutorotate {
if ([self allowsRotation]) {
[self rotationBegin];
return YES;
}
return NO;
}
- (void)rotationFullscreenViewController:(ZFLandscapeViewController *)viewController viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
UIInterfaceOrientation toOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (![self isSuppprtInterfaceOrientation:toOrientation]) { return; }
self.currentOrientation = toOrientation;
UIView *playerSuperview = self.landscapeViewController.playerSuperview;
if (UIInterfaceOrientationIsLandscape(toOrientation) && self.contentView.superview != playerSuperview) {
CGRect targetRect = [self.containerView convertRect:self.containerView.bounds toView:self.containerView.window];
playerSuperview.frame = targetRect;
self.contentView.frame = playerSuperview.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[playerSuperview addSubview:self.contentView];
[self.contentView layoutIfNeeded];
}
if (self.orientationWillChange) self.orientationWillChange(toOrientation);
if (self.disableAnimations) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
}
[UIView animateWithDuration:0.3 animations:^{
if (UIInterfaceOrientationIsLandscape(toOrientation)) {
playerSuperview.frame = CGRectMake(0, 0, size.width, size.height);
} else {
playerSuperview.frame = [self.containerView convertRect:self.containerView.bounds toView:self.containerView.window];
}
[self.contentView layoutIfNeeded];
} completion:^(BOOL finished) {
if (self.disableAnimations) {
[CATransaction commit];
}
self.forceRotaion = NO;
if (toOrientation == UIInterfaceOrientationPortrait) {
UIView *snapshot = [self.contentView snapshotViewAfterScreenUpdates:NO];
snapshot.frame = self.containerView.bounds;
snapshot.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.containerView addSubview:snapshot];
[UIView animateWithDuration:0.0 animations:^{} completion:^(BOOL finished) {
self.contentView.frame = self.containerView.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.containerView addSubview:self.contentView];
[self.contentView layoutIfNeeded];
[snapshot removeFromSuperview];
[self rotationEnd];
if (self.orientationDidChanged) self.orientationDidChanged(toOrientation);
}];
} else {
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.contentView.frame = self.window.bounds;
[self.contentView layoutIfNeeded];
[self rotationEnd];
if (self.orientationDidChanged) self.orientationDidChanged(toOrientation);
}
}];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
@end

View File

@@ -0,0 +1,33 @@
//
// ZFLandscapeRotationManager_iOS16.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 "ZFLandscapeRotationManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeRotationManager_iOS16 : ZFLandscapeRotationManager
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,147 @@
//
// ZFLandscapeRotationManager_iOS16.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 "ZFLandscapeRotationManager_iOS16.h"
@implementation ZFLandscapeRotationManager_iOS16
@synthesize landscapeViewController = _landscapeViewController;
- (ZFLandscapeViewController *)landscapeViewController {
if (!_landscapeViewController) {
_landscapeViewController = [[ZFLandscapeViewController alloc] init];
}
return _landscapeViewController;
}
- (void)setNeedsUpdateOfSupportedInterfaceOrientations {
if (@available(iOS 16.0, *)) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000
[UIApplication.sharedApplication.keyWindow.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations];
[self.window.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations];
#else
[(id)UIApplication.sharedApplication.keyWindow.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations];
[(id)self.window.rootViewController setNeedsUpdateOfSupportedInterfaceOrientations];
#endif
}
}
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation completion:(void(^ __nullable)(void))completion {
[super interfaceOrientation:orientation completion:completion];
UIInterfaceOrientation fromOrientation = [self getCurrentOrientation];
UIInterfaceOrientation toOrientation = orientation;
UIWindow *sourceWindow = self.containerView.window;
CGRect sourceFrame = [self.containerView convertRect:self.containerView.bounds toView:sourceWindow];
CGRect screenBounds = UIScreen.mainScreen.bounds;
CGFloat maxSize = MAX(screenBounds.size.width, screenBounds.size.height);
CGFloat minSize = MIN(screenBounds.size.width, screenBounds.size.height);
self.contentView.autoresizingMask = UIViewAutoresizingNone;
if (fromOrientation == UIInterfaceOrientationPortrait || self.contentView.superview != self.landscapeViewController.view) {
self.contentView.frame = sourceFrame;
[sourceWindow addSubview:self.contentView];
[self.contentView layoutIfNeeded];
if (!self.window.isKeyWindow) {
self.window.hidden = NO;
[self.window makeKeyAndVisible];
}
} else if (toOrientation == UIInterfaceOrientationPortrait) {
self.contentView.bounds = CGRectMake(0, 0, maxSize, minSize);
self.contentView.center = CGPointMake(minSize * 0.5, maxSize * 0.5);
self.contentView.transform = [self getRotationTransform:fromOrientation];
[sourceWindow addSubview:self.contentView];
[sourceWindow makeKeyAndVisible];
[self.contentView layoutIfNeeded];
self.window.hidden = YES;
}
[self setNeedsUpdateOfSupportedInterfaceOrientations];
CGRect rotationBounds = CGRectZero;
CGPoint rotationCenter = CGPointZero;
if (UIInterfaceOrientationIsLandscape(toOrientation)) {
rotationBounds = CGRectMake(0, 0, maxSize, minSize);
rotationCenter = (fromOrientation == UIInterfaceOrientationPortrait || self.contentView.superview != self.landscapeViewController.view) ? CGPointMake(minSize * 0.5, maxSize * 0.5): CGPointMake(maxSize * 0.5, minSize * 0.5);
}
// transform
CGAffineTransform rotationTransform = CGAffineTransformIdentity;
if (fromOrientation == UIInterfaceOrientationPortrait) {
rotationTransform = [self getRotationTransform:toOrientation];
}
self.currentOrientation = toOrientation;
if (self.orientationWillChange) self.orientationWillChange(toOrientation);
if (self.disableAnimations) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
}
[UIView animateWithDuration:0.3 animations:^{
if (toOrientation == UIInterfaceOrientationPortrait) {
[self.contentView setTransform:rotationTransform];
self.contentView.frame = sourceFrame;
} else {
[self.contentView setTransform:rotationTransform];
[self.contentView setBounds:rotationBounds];
[self.contentView setCenter:rotationCenter];
}
[self.contentView layoutIfNeeded];
} completion:^(BOOL finished) {
if (self.disableAnimations) {
[CATransaction commit];
}
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if (toOrientation == UIInterfaceOrientationPortrait) {
[self.containerView addSubview:self.contentView];
self.contentView.frame = self.containerView.bounds;
} else {
[self setNeedsUpdateOfSupportedInterfaceOrientations];
self.contentView.transform = CGAffineTransformIdentity;
[self.landscapeViewController.view addSubview:self.contentView];
self.contentView.frame = self.window.bounds;
[self.contentView layoutIfNeeded];
}
if (self.orientationDidChanged) self.orientationDidChanged(toOrientation);
if (completion) completion();
}];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
if (window == self.window) {
return 1 << self.currentOrientation;
}
return UIInterfaceOrientationMaskPortrait;
}
- (CGAffineTransform)getRotationTransform:(UIInterfaceOrientation)orientation {
CGAffineTransform rotationTransform = CGAffineTransformIdentity;
if (orientation == UIInterfaceOrientationLandscapeLeft) {
rotationTransform = CGAffineTransformMakeRotation(-M_PI_2);
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
rotationTransform = CGAffineTransformMakeRotation(M_PI_2);
}
return rotationTransform;
}
@end

View File

@@ -0,0 +1,51 @@
//
// 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>
@optional
- (BOOL)ls_shouldAutorotate;
- (void)rotationFullscreenViewController:(ZFLandscapeViewController *)viewController viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
@end
@interface ZFLandscapeViewController : UIViewController
@property (nonatomic, weak, nullable) id<ZFLandscapeViewControllerDelegate> delegate;
@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;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,69 @@
//
// ZFFullScreenViewController.m
// ZFPlayer
//
// 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"
@implementation ZFLandscapeViewController
- (instancetype)init {
self = [super init];
if (self) {
_statusBarStyle = UIStatusBarStyleLightContent;
_statusBarAnimation = UIStatusBarAnimationSlide;
}
return self;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
if ([self.delegate respondsToSelector:@selector(rotationFullscreenViewController:viewWillTransitionToSize:withTransitionCoordinator:)]) {
[self.delegate rotationFullscreenViewController:self viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (BOOL)prefersHomeIndicatorAutoHidden {
return YES;
}
- (BOOL)prefersStatusBarHidden {
return self.statusBarHidden;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return self.statusBarAnimation;
}
@end

View File

@@ -0,0 +1,35 @@
//
// ZFLandscapeViewController_iOS15.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 "ZFLandscapeViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeViewController_iOS15 : ZFLandscapeViewController
@property (nonatomic, strong, readonly) UIView *playerSuperview;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,48 @@
//
// ZFLandscapeViewController_iOS15.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_iOS15.h"
@implementation ZFLandscapeViewController_iOS15
- (void)viewDidLoad {
[super viewDidLoad];
_playerSuperview = [[UIView alloc] initWithFrame:CGRectZero];
_playerSuperview.backgroundColor = [UIColor clearColor];
[self.view addSubview:_playerSuperview];
}
- (BOOL)shouldAutorotate {
return [self.delegate ls_shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (BOOL)prefersHomeIndicatorAutoHidden {
return YES;
}
@end

View File

@@ -0,0 +1,37 @@
//
// 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"
@class ZFLandscapeRotationManager;
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeWindow : UIWindow
@property (nonatomic, weak) ZFLandscapeRotationManager *rotationManager;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,79 @@
//
// 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"
#import "ZFLandscapeRotationManager_iOS15.h"
@implementation ZFLandscapeWindow
@dynamic rootViewController;
- (void)setBackgroundColor:(nullable UIColor *)backgroundColor {}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.windowLevel = UIWindowLevelStatusBar - 1;
if (@available(iOS 13.0, *)) {
if (self.windowScene == nil) {
self.windowScene = UIApplication.sharedApplication.keyWindow.windowScene;
}
if (@available(iOS 9.0, *)) {
[self.rootViewController loadViewIfNeeded];
} else {
[self.rootViewController view];
}
}
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,166 @@
//
// 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) {
ZFInterfaceOrientationMaskUnknow = 0,
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;
/// Container view of a full screen state player.
@property (nonatomic, strong, readonly, nullable) 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,362 @@
// 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>
#import "ZFLandscapeRotationManager_iOS15.h"
#import "ZFLandscapeRotationManager_iOS16.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 ()
@property (nonatomic, weak) ZFPlayerView *view;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@property (nonatomic, strong) ZFPortraitViewController *portraitViewController;
@property (nonatomic, strong) ZFLandscapeRotationManager *landscapeRotationManager;
/// current device orientation observer is activie.
@property (nonatomic, assign) BOOL activeDeviceObserver;
@end
@implementation ZFOrientationObserver
@synthesize presentationSize = _presentationSize;
- (instancetype)init {
self = [super init];
if (self) {
_duration = 0.30;
_fullScreenMode = ZFFullScreenModeLandscape;
_portraitFullScreenMode = ZFPortraitFullScreenModeScaleToFill;
_disablePortraitGestureTypes = ZFDisablePortraitGestureTypesAll;
self.supportInterfaceOrientation = ZFInterfaceOrientationMaskAllButUpsideDown;
self.allowOrientationRotation = YES;
self.activeDeviceObserver = YES;
}
return self;
}
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView {
self.view = rotateView;
self.containerView = containerView;
[self.landscapeRotationManager updateRotateView:rotateView containerView:containerView];
}
- (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;
[self.landscapeRotationManager handleDeviceOrientationChange];
}
#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;
[self.landscapeRotationManager rotateToOrientation:orientation animated:animated completion:completion];
}
- (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 completion:nil];
}
- (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 - 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;
}
- (ZFLandscapeRotationManager *)landscapeRotationManager {
if (!_landscapeRotationManager) {
if (@available(iOS 16.0, *)) {
_landscapeRotationManager = [[ZFLandscapeRotationManager_iOS16 alloc] init];
} else {
_landscapeRotationManager = [[ZFLandscapeRotationManager_iOS15 alloc] init];
}
@zf_weakify(self)
_landscapeRotationManager.orientationWillChange = ^(UIInterfaceOrientation orientation) {
@zf_strongify(self)
self.fullScreen = UIInterfaceOrientationIsLandscape(orientation);
if (self.orientationWillChange) self.orientationWillChange(self, self.fullScreen);
};
_landscapeRotationManager.orientationDidChanged = ^(UIInterfaceOrientation orientation) {
@zf_strongify(self)
self.fullScreen = UIInterfaceOrientationIsLandscape(orientation);
if (self.orientationDidChanged) self.orientationDidChanged(self, self.fullScreen);
};
}
return _landscapeRotationManager;
}
- (UIView *)fullScreenContainerView {
if (self.fullScreenMode == ZFFullScreenModeLandscape) {
return self.landscapeRotationManager.landscapeViewController.view;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
return self.portraitViewController.view;
}
return nil;
}
- (UIInterfaceOrientation)currentOrientation {
if (self.fullScreenMode == ZFFullScreenModeLandscape) {
return self.landscapeRotationManager.currentOrientation;
}
return [self.landscapeRotationManager getCurrentOrientation];
}
#pragma mark - setter
- (void)setLockedScreen:(BOOL)lockedScreen {
_lockedScreen = lockedScreen;
self.landscapeRotationManager.lockedScreen = lockedScreen;
if (lockedScreen) {
[self removeDeviceOrientationObserver];
} else {
[self addDeviceOrientationObserver];
}
}
- (void)setFullScreen:(BOOL)fullScreen {
_fullScreen = fullScreen;
[self.landscapeRotationManager.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.landscapeRotationManager.landscapeViewController.statusBarHidden = fullScreenStatusBarHidden;
[self.landscapeRotationManager.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.landscapeRotationManager.landscapeViewController.statusBarStyle = fullScreenStatusBarStyle;
[self.landscapeRotationManager.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.landscapeRotationManager.landscapeViewController.statusBarAnimation = fullScreenStatusBarAnimation;
[self.landscapeRotationManager.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.landscapeRotationManager.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.landscapeRotationManager.containerView = containerView;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.containerView = containerView;
}
}
- (void)setAllowOrientationRotation:(BOOL)allowOrientationRotation {
_allowOrientationRotation = allowOrientationRotation;
self.landscapeRotationManager.allowOrientationRotation = allowOrientationRotation;
}
- (void)setSupportInterfaceOrientation:(ZFInterfaceOrientationMask)supportInterfaceOrientation {
_supportInterfaceOrientation = supportInterfaceOrientation;
self.landscapeRotationManager.supportInterfaceOrientation = supportInterfaceOrientation;
}
- (void)setActiveDeviceObserver:(BOOL)activeDeviceObserver {
_activeDeviceObserver = activeDeviceObserver;
self.landscapeRotationManager.activeDeviceObserver = activeDeviceObserver;
}
@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,603 @@
//
// 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, weak, nullable) UIView *containerView;
/// The currentPlayerManager must conform `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, strong) id<ZFPlayerMediaPlayback> currentPlayerManager;
/// The custom controlView must conform `ZFPlayerMediaControl` protocol.
@property (nonatomic, strong, nullable) UIView<ZFPlayerMediaControl> *controlView;
/// The notification manager class.
@property (nonatomic, strong, readonly, nullable) ZFPlayerNotification *notification;
/// The container view type.
@property (nonatomic, assign, readonly) ZFPlayerContainerType containerType;
/// The player's small container view.
@property (nonatomic, strong, readonly, nullable) ZFFloatView *smallFloatView;
/// Whether the small window is displayed.
@property (nonatomic, assign, readonly) BOOL isSmallFloatViewShow;
/// The scroll view is `tableView` or `collectionView`.
@property (nonatomic, weak, nullable) UIScrollView *scrollView;
/*!
@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, nullable) 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 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 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

Some files were not shown because too many files have changed in this diff Show More