// // 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 "ZFPlayerController.h" #import #import #import #import "ZFPlayerNotification.h" #import "UIScrollView+ZFPlayer.h" #import "ZFReachabilityManager.h" #import "ZFPlayer.h" @interface ZFPlayerController () @property (nonatomic, strong) UIView *containerView; @property (nonatomic, strong) ZFPlayerNotification *notification; @property (nonatomic, strong) id currentPlayerManager; @property (nonatomic, strong, nullable) UIScrollView *scrollView; @property (nonatomic, assign, getter=isPauseByUser) BOOL pauseByUser; @property (nonatomic, strong) UISlider *volumeViewSlider; @property (nonatomic, assign) NSTimeInterval currentTime; @property (nonatomic, assign) NSTimeInterval totalTime; @property (nonatomic, assign) NSTimeInterval bufferTime; /// The view tag that the player display in scrollView. @property (nonatomic, assign) NSInteger containerViewTag; @end @implementation ZFPlayerController - (instancetype)init { self = [super init]; if (self) { @weakify(self) [[ZFReachabilityManager sharedManager] startMonitoring]; [[ZFReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(ZFReachabilityStatus status) { @strongify(self) if ([self.controlView respondsToSelector:@selector(videoPlayer:reachabilityChanged:)]) { [self.controlView videoPlayer:self reachabilityChanged:status]; } }]; [self configureVolume]; } return self; } /// Get system volume - (void)configureVolume { MPVolumeView *volumeView = [[MPVolumeView alloc] init]; self.volumeViewSlider = nil; for (UIView *view in [volumeView subviews]){ if ([view.class.description isEqualToString:@"MPVolumeSlider"]){ self.volumeViewSlider = (UISlider *)view; break; } } // Apps using this category don't mute when the phone's mute button is turned on, but play sound when the phone is silent [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; [[AVAudioSession sharedInstance] setActive:YES error:nil]; } - (void)dealloc { [self.currentPlayerManager stop]; } + (instancetype)playerWithPlayerManager:(id)playerManager containerView:(nonnull UIView *)containerView { ZFPlayerController *player = [[self alloc] init]; player.currentPlayerManager = playerManager; player.containerView = containerView; return player; } + (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id)playerManager containerViewTag:(NSInteger)containerViewTag { ZFPlayerController *player = [[self alloc] init]; player.scrollView = scrollView; player.currentPlayerManager = playerManager; player.containerViewTag = containerViewTag; return player; } - (instancetype)initWithPlayerManager:(id)playerManager containerView:(nonnull UIView *)containerView { ZFPlayerController *player = [self init]; player.currentPlayerManager = playerManager; player.containerView = containerView; return player; } - (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id)playerManager containerViewTag:(NSInteger)containerViewTag { ZFPlayerController *player = [self init]; player.scrollView = scrollView; player.currentPlayerManager = playerManager; player.containerViewTag = containerViewTag; return player; } - (void)playerManagerCallbcak { @weakify(self) self.currentPlayerManager.playerPlayTimeChanged = ^(id _Nonnull asset, NSTimeInterval currentTime, NSTimeInterval duration) { @strongify(self) self.currentTime = currentTime; self.totalTime = duration; if ([self.controlView respondsToSelector:@selector(videoPlayer:currentTime:totalTime:)]) { [self.controlView videoPlayer:self currentTime:currentTime totalTime:duration]; } }; self.currentPlayerManager.playerBufferTimeChanged = ^(id _Nonnull asset, NSTimeInterval bufferTime) { @strongify(self) self.bufferTime = bufferTime; if ([self.controlView respondsToSelector:@selector(videoPlayer:bufferTime:)]) { [self.controlView videoPlayer:self bufferTime:bufferTime]; } }; self.currentPlayerManager.playerPrepareToPlay = ^(id _Nonnull asset, NSURL * _Nonnull assetURL) { @strongify(self) self.currentPlayerManager.view.hidden = NO; if ([self.controlView respondsToSelector:@selector(videoPlayer:prepareToPlay:)]) { [self.controlView videoPlayer:self prepareToPlay:assetURL]; } }; self.currentPlayerManager.playerPlayStatChanged = ^(id _Nonnull asset, ZFPlayerPlaybackState playState) { @strongify(self) if ([self.controlView respondsToSelector:@selector(videoPlayer:playStateChanged:)]) { [self.controlView videoPlayer:self playStateChanged:playState]; } }; self.currentPlayerManager.playerLoadStatChanged = ^(id _Nonnull asset, ZFPlayerLoadState loadState) { @strongify(self) if ([self.controlView respondsToSelector:@selector(videoPlayer:loadStateChanged:)]) { [self.controlView videoPlayer:self loadStateChanged:loadState]; } }; self.currentPlayerManager.playerDidToEnd = ^(id _Nonnull asset) { @strongify(self) if (self.playerDidToEnd) self.playerDidToEnd(asset); if ([self.controlView respondsToSelector:@selector(videoPlayerPlayEnd:)]) { [self.controlView videoPlayerPlayEnd:self]; } }; } - (void)layoutPlayerSubViews { self.containerView.userInteractionEnabled = YES; [self.containerView addSubview:self.currentPlayerManager.view]; [self.currentPlayerManager.view addSubview:self.controlView]; self.currentPlayerManager.view.frame = self.controlView.bounds; self.currentPlayerManager.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.controlView.frame = self.currentPlayerManager.view.bounds; self.controlView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; } #pragma mark - getter - (ZFPlayerNotification *)notification { if (!_notification) { _notification = [[ZFPlayerNotification alloc] init]; @weakify(self) _notification.willResignActive = ^(ZFPlayerNotification * _Nonnull registrar) { @strongify(self) if (self.pauseWhenAppResignActive && self.currentPlayerManager.isPlaying) { [self.currentPlayerManager pause]; self.pauseByUser = YES; } }; _notification.didBecomeActive = ^(ZFPlayerNotification * _Nonnull registrar) { @strongify(self) if (self.isPauseByUser) { [self.currentPlayerManager play]; self.pauseByUser = NO; } }; _notification.oldDeviceUnavailable = ^(ZFPlayerNotification * _Nonnull registrar) { @strongify(self) if (self.currentPlayerManager.isPlaying) { [self.currentPlayerManager play]; } }; } return _notification; } #pragma mark - setter - (void)setCurrentPlayerManager:(id)currentPlayerManager { if (!currentPlayerManager) return; _currentPlayerManager = currentPlayerManager; _currentPlayerManager.view.hidden = YES; self.gestureControl.disableTypes = self.disableGestureTypes; [self.gestureControl addGestureToView:currentPlayerManager.view]; [self playerManagerCallbcak]; [self.notification addNotification]; } - (void)setContainerView:(UIView *)containerView { if (!containerView) return; _containerView = containerView; containerView.userInteractionEnabled = YES; [containerView addSubview:self.currentPlayerManager.view]; self.currentPlayerManager.view.frame = containerView.bounds; self.currentPlayerManager.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.orientationObserver addDeviceOrientationObserver]; } - (void)setControlView:(UIView *)controlView { if (!controlView) return; _controlView = controlView; controlView.player = self; [self.currentPlayerManager.view addSubview:controlView]; controlView.frame = self.currentPlayerManager.view.bounds; controlView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; } @end @implementation ZFPlayerController (ZFPlayerTimeControl) - (float)progress { if (self.totalTime == 0) return 0; return self.currentTime/self.totalTime; } - (float)bufferProgress { if (self.totalTime == 0) return 0; return self.bufferTime/self.totalTime; } - (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^)(BOOL))completionHandler { [self.currentPlayerManager seekToTime:time completionHandler:completionHandler]; } @end @implementation ZFPlayerController (ZFPlayerPlaybackControl) - (void)stop { [self.notification removeNotification]; [self.orientationObserver removeDeviceOrientationObserver]; [self.currentPlayerManager stop]; [self.currentPlayerManager.view removeFromSuperview]; } - (void)replaceCurrentPlayerManager:(id)manager { if (self.currentPlayerManager.isPreparedToPlay) { [self.notification removeNotification]; [self.orientationObserver removeDeviceOrientationObserver]; [self stop]; } [self.gestureControl removeGestureToView:self.currentPlayerManager.view]; self.currentPlayerManager = manager; [self layoutPlayerSubViews]; } - (void)playTheNext { if (self.assetURLs.count > 0) { NSInteger index = self.currentPlayIndex + 1; if (index >= self.assetURLs.count) return; NSURL *assetURL = [self.assetURLs objectAtIndex:index]; self.assetURL = assetURL; self.currentPlayIndex = [self.assetURLs indexOfObject:assetURL]; } } - (void)playThePrevious { if (self.assetURLs.count > 0) { NSInteger index = self.currentPlayIndex - 1; if (index < 0) return; NSURL *assetURL = [self.assetURLs objectAtIndex:index]; self.assetURL = assetURL; self.currentPlayIndex = [self.assetURLs indexOfObject:assetURL]; } } - (void)playTheIndex:(NSInteger)index { if (self.assetURLs.count > 0) { if (index >= self.assetURLs.count) return; NSURL *assetURL = [self.assetURLs objectAtIndex:index]; self.assetURL = assetURL; self.currentPlayIndex = index; } } #pragma mark - getter - (NSURL *)assetURL { return objc_getAssociatedObject(self, _cmd); } - (NSArray *)assetURLs { return objc_getAssociatedObject(self, _cmd); } - (BOOL)isLastAssetURL { if (self.assetURLs.count > 0) { return self.assetURL == self.assetURLs.lastObject; } return NO; } - (BOOL)isFirstAssetURL { if (self.assetURLs.count > 0) { return self.assetURL == self.assetURLs.firstObject; } return NO; } - (BOOL)isWWANAutoPlay { NSNumber *number = objc_getAssociatedObject(self, _cmd); if (number) return number.boolValue; self.WWANAutoPlay = YES; return YES; } - (float)brightness { return [UIScreen mainScreen].brightness; } - (float)volume { CGFloat volume = self.volumeViewSlider.value; if (volume == 0) { volume = [[AVAudioSession sharedInstance] outputVolume]; } return volume; } - (BOOL)isMuted { return self.volume == 0; } - (float)lastVolumeValue { return [objc_getAssociatedObject(self, _cmd) floatValue]; } - (ZFPlayerPlaybackState)playState { return self.currentPlayerManager.playState; } - (BOOL)isPlaying { return self.currentPlayerManager.isPlaying; } - (BOOL)pauseWhenAppResignActive { NSNumber *number = objc_getAssociatedObject(self, _cmd); if (number) return number.boolValue; self.pauseWhenAppResignActive = YES; return YES; } - (void (^)(id _Nonnull))playerDidToEnd { return objc_getAssociatedObject(self, _cmd); } - (NSInteger)currentPlayIndex { return [objc_getAssociatedObject(self, _cmd) integerValue]; } #pragma mark - setter - (void)setAssetURL:(NSURL *)assetURL { objc_setAssociatedObject(self, @selector(assetURL), assetURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (self.currentPlayerManager.isPreparedToPlay) [self.currentPlayerManager stop]; if (!self.currentPlayerManager.view.superview) self.containerView = self.containerView; self.currentPlayerManager.assetURL = assetURL; } - (void)setAssetURLs:(NSArray * _Nullable)assetURLs { objc_setAssociatedObject(self, @selector(assetURLs), assetURLs, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setWWANAutoPlay:(BOOL)WWANAutoPlay { objc_setAssociatedObject(self, @selector(isWWANAutoPlay), @(WWANAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (self.scrollView) self.scrollView.zf_WWANAutoPlay = self.isWWANAutoPlay; } - (void)setVolume:(float)volume { volume = MIN(MAX(0, volume), 1); objc_setAssociatedObject(self, @selector(volume), @(volume), OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.volumeViewSlider.value = volume; } - (void)setMuted:(BOOL)muted { if (muted) { self.lastVolumeValue = self.volumeViewSlider.value; self.volumeViewSlider.value = 0; } else { self.volumeViewSlider.value = self.lastVolumeValue; } } - (void)setLastVolumeValue:(float)lastVolumeValue { objc_setAssociatedObject(self, @selector(lastVolumeValue), @(lastVolumeValue), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setBrightness:(float)brightness { brightness = MIN(MAX(0, brightness), 1); objc_setAssociatedObject(self, @selector(brightness), @(brightness), OBJC_ASSOCIATION_RETAIN_NONATOMIC); [UIScreen mainScreen].brightness = brightness; } - (void)setPauseWhenAppResignActive:(BOOL)pauseWhenAppResignActive { objc_setAssociatedObject(self, @selector(pauseWhenAppResignActive), @(pauseWhenAppResignActive), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setPlayerDidToEnd:(void (^)(id _Nonnull))playerDidToEnd { objc_setAssociatedObject(self, @selector(playerDidToEnd), playerDidToEnd, OBJC_ASSOCIATION_COPY); } - (void)setCurrentPlayIndex:(NSInteger)currentPlayIndex { objc_setAssociatedObject(self, @selector(currentPlayIndex), @(currentPlayIndex), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end @implementation ZFPlayerController (ZFPlayerOrientationRotation) - (void)addDeviceOrientationObserver { [self.orientationObserver addDeviceOrientationObserver]; } - (void)removeDeviceOrientationObserver { [self.orientationObserver removeDeviceOrientationObserver]; } - (void)enterLandscapeFullScreen:(UIInterfaceOrientation)orientation animated:(BOOL)animated { self.orientationObserver.fullScreenMode = ZFFullScreenModeLandscape; [self.orientationObserver enterLandscapeFullScreen:orientation animated:animated]; } - (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated { self.orientationObserver.fullScreenMode = ZFFullScreenModePortrait; [self.orientationObserver enterPortraitFullScreen:fullScreen animated:YES]; } - (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated { if (self.orientationObserver.fullScreenMode == ZFFullScreenModePortrait) { [self.orientationObserver enterPortraitFullScreen:fullScreen animated:YES]; } else { UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown; orientation = fullScreen? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait; [self.orientationObserver enterLandscapeFullScreen:orientation animated:animated]; } } #pragma mark - getter - (ZFOrientationObserver *)orientationObserver { if (!self.currentPlayerManager.view && !self.containerView) return nil; @weakify(self) ZFOrientationObserver *orientationObserver = objc_getAssociatedObject(self, _cmd); if (!orientationObserver) { orientationObserver = [[ZFOrientationObserver alloc] initWithRotateView:self.currentPlayerManager.view containerView:self.containerView]; orientationObserver.orientationWillChange = ^(ZFOrientationObserver * _Nonnull observer, BOOL isFullScreen) { @strongify(self) if (self.orientationWillChange) self.orientationWillChange(self, isFullScreen); if ([self.controlView respondsToSelector:@selector(videoPlayer:orientationWillChange:)]) { [self.controlView videoPlayer:self orientationWillChange:observer]; } [self.controlView setNeedsLayout]; [self.controlView layoutIfNeeded]; }; orientationObserver.orientationDidChanged = ^(ZFOrientationObserver * _Nonnull observer, BOOL isFullScreen) { @strongify(self) if (self.orientationDidChanged) self.orientationDidChanged(self, isFullScreen); if ([self.controlView respondsToSelector:@selector(videoPlayer:orientationDidChanged:)]) { [self.controlView videoPlayer:self orientationDidChanged:observer]; } }; objc_setAssociatedObject(self, _cmd, orientationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return orientationObserver; } - (void (^)(ZFPlayerController * _Nonnull, BOOL))orientationWillChange { return objc_getAssociatedObject(self, _cmd); } - (void (^)(ZFPlayerController * _Nonnull, BOOL))orientationDidChanged { return objc_getAssociatedObject(self, _cmd); } - (BOOL)isFullScreen { return self.orientationObserver.isFullScreen; } - (UIInterfaceOrientation)currentOrientation { return self.orientationObserver.currentOrientation; } - (BOOL)isStatusBarHidden { return self.orientationObserver.isStatusBarHidden; } - (BOOL)isLockedScreen { return self.orientationObserver.isLockedScreen; } - (BOOL)shouldAutorotate { return self.orientationObserver.shouldAutorotate; } #pragma mark - setter - (void)setOrientationWillChange:(void (^)(ZFPlayerController * _Nonnull, BOOL))orientationWillChange { objc_setAssociatedObject(self, @selector(orientationWillChange), orientationWillChange, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (void)setOrientationDidChanged:(void (^)(ZFPlayerController * _Nonnull, BOOL))orientationDidChanged { objc_setAssociatedObject(self, @selector(orientationDidChanged), orientationDidChanged, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (void)setStatusBarHidden:(BOOL)statusBarHidden { objc_setAssociatedObject(self, @selector(isStatusBarHidden), @(statusBarHidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.orientationObserver.statusBarHidden = statusBarHidden; } - (void)setLockedScreen:(BOOL)lockedScreen { objc_setAssociatedObject(self, @selector(isLockedScreen), @(lockedScreen), OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.orientationObserver.lockedScreen = lockedScreen; if ([self.controlView respondsToSelector:@selector(lockedVideoPlayer:lockedScreen:)]) { [self.controlView lockedVideoPlayer:self lockedScreen:lockedScreen]; } } - (void)setShouldAutorotate:(BOOL)shouldAutorotate { objc_setAssociatedObject(self, @selector(shouldAutorotate), @(shouldAutorotate), OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.orientationObserver.shouldAutorotate = shouldAutorotate; } @end @implementation ZFPlayerController (ZFPlayerViewGesture) #pragma mark - getter - (ZFPlayerGestureControl *)gestureControl { ZFPlayerGestureControl *gestureControl = objc_getAssociatedObject(self, _cmd); if (!gestureControl) { gestureControl = [[ZFPlayerGestureControl alloc] init]; @weakify(self) gestureControl.triggerCondition = ^BOOL(ZFPlayerGestureControl * _Nonnull control, ZFPlayerGestureType type, UIGestureRecognizer * _Nonnull gesture, UITouch *touch) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gestureTriggerCondition:gestureType:gestureRecognizer:touch:)]) { return [self.controlView gestureTriggerCondition:control gestureType:type gestureRecognizer:gesture touch:touch]; } return YES; }; gestureControl.singleTapped = ^(ZFPlayerGestureControl * _Nonnull control) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gestureSingleTapped:)]) { [self.controlView gestureSingleTapped:control]; } }; gestureControl.doubleTapped = ^(ZFPlayerGestureControl * _Nonnull control) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gestureDoubleTapped:)]) { [self.controlView gestureDoubleTapped:control]; } }; gestureControl.beganPan = ^(ZFPlayerGestureControl * _Nonnull control, ZFPanDirection direction, ZFPanLocation location) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gestureBeganPan:panDirection:panLocation:)]) { [self.controlView gestureBeganPan:control panDirection:direction panLocation:location]; } }; gestureControl.changedPan = ^(ZFPlayerGestureControl * _Nonnull control, ZFPanDirection direction, ZFPanLocation location, CGPoint velocity) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gestureChangedPan:panDirection:panLocation:withVelocity:)]) { [self.controlView gestureChangedPan:control panDirection:direction panLocation:location withVelocity:velocity]; } }; gestureControl.endedPan = ^(ZFPlayerGestureControl * _Nonnull control, ZFPanDirection direction, ZFPanLocation location) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gestureEndedPan:panDirection:panLocation:)]) { [self.controlView gestureEndedPan:control panDirection:direction panLocation:location]; } }; gestureControl.pinched = ^(ZFPlayerGestureControl * _Nonnull control, float scale) { @strongify(self) if ([self.controlView respondsToSelector:@selector(gesturePinched:scale:)]) { [self.controlView gesturePinched:control scale:scale]; } }; objc_setAssociatedObject(self, _cmd, gestureControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return gestureControl; } - (ZFPlayerDisableGestureTypes)disableGestureTypes { return [objc_getAssociatedObject(self, _cmd) unsignedIntegerValue]; } #pragma mark - setter - (void)setDisableGestureTypes:(ZFPlayerDisableGestureTypes)disableGestureTypes { objc_setAssociatedObject(self, @selector(disableGestureTypes), @(disableGestureTypes), OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.gestureControl.disableTypes = disableGestureTypes; } @end @implementation ZFPlayerController (ZFPlayerScrollView) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SEL selectors[] = { NSSelectorFromString(@"dealloc") }; for (NSInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) { SEL originalSelector = selectors[index]; SEL swizzledSelector = NSSelectorFromString([@"zf_" stringByAppendingString:NSStringFromSelector(originalSelector)]); Method originalMethod = class_getInstanceMethod(self, originalSelector); Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector); if (class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) { class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } }); } - (void)zf_dealloc { [self.smallFloatView removeFromSuperview]; self.smallFloatView = nil; [self zf_dealloc]; } //// Add video to the cell - (void)addPlayerViewToCell { self.isSmallFloatViewShow = NO; self.smallFloatView.hidden = YES; UIView *cell = [self.scrollView zf_getCellForIndexPath:self.playingIndexPath]; self.containerView = [cell viewWithTag:self.containerViewTag]; [self.containerView addSubview:self.currentPlayerManager.view]; self.currentPlayerManager.view.frame = self.containerView.bounds; self.currentPlayerManager.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.orientationObserver cellModelRotateView:self.currentPlayerManager.view rotateViewAtCell:cell playerViewTag:self.containerViewTag]; } /// Add to the keyWindow - (void)addPlayerViewToKeyWindow { self.isSmallFloatViewShow = YES; self.smallFloatView.hidden = NO; [self.smallFloatView addSubview:self.currentPlayerManager.view]; self.currentPlayerManager.view.frame = self.smallFloatView.bounds; self.currentPlayerManager.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.orientationObserver cellSmallModelRotateView:self.currentPlayerManager.view containerView:self.smallFloatView]; } #pragma mark - setter - (void)setScrollView:(UIScrollView *)scrollView { objc_setAssociatedObject(self, @selector(scrollView), scrollView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.scrollView.zf_WWANAutoPlay = self.isWWANAutoPlay; @weakify(self) scrollView.zf_enableScrollHook = YES; scrollView.zf_playerDidAppearInScrollView = ^(NSIndexPath * _Nonnull indexPath) { @strongify(self) if (self.isFullScreen) return; if ([self.controlView respondsToSelector:@selector(playerDidAppearInScrollView:)]) { [self.controlView playerDidAppearInScrollView:self]; } if (!self.stopWhileNotVisible) { [self addPlayerViewToCell]; } }; scrollView.zf_playerWillDisappearInScrollView = ^(NSIndexPath * _Nonnull indexPath) { @strongify(self) if (self.isFullScreen) return; if ([self.controlView respondsToSelector:@selector(playerWillDisappearInScrollView:)]) { [self.controlView playerWillDisappearInScrollView:self]; } }; scrollView.zf_playerDisappearHalfInScrollView = ^(NSIndexPath * _Nonnull indexPath) { @strongify(self) if (self.isFullScreen) return; if ([self.controlView respondsToSelector:@selector(playerDisappearHalfInScrollView:)]) { [self.controlView playerDisappearHalfInScrollView:self]; } if (self.stopWhileNotVisible) { [self stopCurrentPlayingCell]; } }; scrollView.zf_playerDidDisappearInScrollView = ^(NSIndexPath * _Nonnull indexPath) { @strongify(self) if (self.isFullScreen) return; if ([self.controlView respondsToSelector:@selector(playerDidDisappearInScrollView:)]) { [self.controlView playerDidDisappearInScrollView:self]; } if (!self.stopWhileNotVisible) { [self addPlayerViewToKeyWindow]; } else { [self stopCurrentPlayingCell]; } }; } - (void)setStopWhileNotVisible:(BOOL)stopWhileNotVisible { self.scrollView.zf_stopWhileNotVisible = stopWhileNotVisible; objc_setAssociatedObject(self, @selector(stopWhileNotVisible), @(stopWhileNotVisible), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setContainerViewTag:(NSInteger)containerViewTag { objc_setAssociatedObject(self, @selector(containerViewTag), @(containerViewTag), OBJC_ASSOCIATION_RETAIN_NONATOMIC); self.scrollView.zf_containerViewTag = containerViewTag; } - (void)setPlayingIndexPath:(NSIndexPath *)playingIndexPath { objc_setAssociatedObject(self, @selector(playingIndexPath), playingIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (playingIndexPath) { [self stopCurrentPlayingCell]; UIView *cell = [self.scrollView zf_getCellForIndexPath:playingIndexPath]; self.containerView = [cell viewWithTag:self.containerViewTag]; [self.orientationObserver cellModelRotateView:self.currentPlayerManager.view rotateViewAtCell:cell playerViewTag:self.containerViewTag]; [self.orientationObserver addDeviceOrientationObserver]; self.scrollView.zf_playingIndexPath = playingIndexPath; } } - (void)setShouldAutoPlay:(BOOL)shouldAutoPlay { self.scrollView.zf_shouldAutoPlay = shouldAutoPlay; } - (void)setSectionAssetURLs:(NSArray *> * _Nullable)sectionAssetURLs { objc_setAssociatedObject(self, @selector(sectionAssetURLs), sectionAssetURLs, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setSmallFloatView:(ZFFloatView * _Nullable)smallFloatView { objc_setAssociatedObject(self, @selector(smallFloatView), smallFloatView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setIsSmallFloatViewShow:(BOOL)isSmallFloatViewShow { objc_setAssociatedObject(self, @selector(isSmallFloatViewShow), @(isSmallFloatViewShow), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - getter - (UIScrollView *)scrollView { UIScrollView *scrollView = objc_getAssociatedObject(self, _cmd); return scrollView; } - (BOOL)stopWhileNotVisible { NSNumber *number = objc_getAssociatedObject(self, _cmd); if (number) return number.boolValue; self.stopWhileNotVisible = YES; return YES; } - (NSInteger)containerViewTag { return [objc_getAssociatedObject(self, _cmd) integerValue]; } - (ZFFloatView *)smallFloatView { ZFFloatView *smallFloatView = objc_getAssociatedObject(self, _cmd); if (!smallFloatView) { smallFloatView = [[ZFFloatView alloc] init]; smallFloatView.parentView = [UIApplication sharedApplication].keyWindow; smallFloatView.hidden = YES; self.smallFloatView = smallFloatView; } return smallFloatView; } - (BOOL)isSmallFloatViewShow { return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (NSIndexPath *)playingIndexPath { return objc_getAssociatedObject(self, _cmd); } - (NSArray *> *)sectionAssetURLs { return objc_getAssociatedObject(self, _cmd); } - (BOOL)shouldAutoPlay { return self.scrollView.zf_shouldAutoPlay; } - (void)playTheIndexPath:(NSIndexPath *)indexPath scrollToTop:(BOOL)scrollToTop completionHandler:(void (^ _Nullable)(void))completionHandler { NSURL *assetURL; if (self.sectionAssetURLs.count) { assetURL = self.sectionAssetURLs[indexPath.section][indexPath.row]; } else if (self.assetURLs.count) { assetURL = self.assetURLs[indexPath.row]; self.currentPlayIndex = indexPath.row; } if (scrollToTop) { @weakify(self) [self.scrollView zf_scrollToRowAtIndexPath:indexPath completionHandler:^{ @strongify(self) if (completionHandler) completionHandler(); self.playingIndexPath = indexPath; self.assetURL = assetURL; [self.scrollView zf_scrollViewDidStopScroll]; }]; } else { if (completionHandler) completionHandler(); self.playingIndexPath = indexPath; self.assetURL = assetURL; } } - (void)playTheIndexPath:(NSIndexPath *)indexPath scrollToTop:(BOOL)scrollToTop { self.playingIndexPath = indexPath; NSURL *assetURL; if (self.sectionAssetURLs.count) { assetURL = self.sectionAssetURLs[indexPath.section][indexPath.row]; } else if (self.assetURLs.count) { assetURL = self.assetURLs[indexPath.row]; self.currentPlayIndex = indexPath.row; } self.assetURL = assetURL; if (scrollToTop) { [self.scrollView zf_scrollToRowAtIndexPath:indexPath completionHandler:nil]; } } - (void)playTheIndexPath:(NSIndexPath *)indexPath assetURL:(NSURL *)assetURL scrollToTop:(BOOL)scrollToTop { self.playingIndexPath = indexPath; self.assetURL = assetURL; if (scrollToTop) { [self.scrollView zf_scrollToRowAtIndexPath:indexPath completionHandler:nil]; } } - (void)stopCurrentPlayingCell { if (self.scrollView.zf_playingIndexPath) { [self stop]; self.isSmallFloatViewShow = NO; self.scrollView.zf_playingIndexPath = nil; if (self.smallFloatView) self.smallFloatView.hidden = YES; } } @end