Commit cd31c8f9 cgx

引入ZFPlayer

1 个父辈 e0c1b4f9
正在显示 54 个修改的文件 包含 7644 行增加11 行删除
......@@ -7,6 +7,7 @@
#import "MusicPlayerController.h"
#import "MusicPlayerView.h"
#import <ZFPlayer/ZFPlayer.h>
@interface MusicPlayerController ()
@property (nonatomic, strong) MusicPlayerView *playerView;
......
......@@ -12,6 +12,7 @@ target 'DreamSleep' do
pod 'YYWebImage', '~> 1.0.5'
pod 'YYImage/WebP'
pod 'YYModel', '~> 1.0.4'
pod 'ZFPlayer', '~> 4.0.3'
end
# AFNetworking (4.0.1)
......@@ -26,3 +27,4 @@ end
# YYImage/WebP(模拟器上目前无法运行)
# YYModel (1.0.4)
# SDWebImage (5.12.5)(去掉)
# ZFPlayer(4.0.3)
......@@ -36,6 +36,9 @@ PODS:
- YYWebImage (1.0.5):
- YYCache
- YYImage
- ZFPlayer (4.0.3):
- ZFPlayer/Core (= 4.0.3)
- ZFPlayer/Core (4.0.3)
DEPENDENCIES:
- DKNightVersion (~> 2.4.3)
......@@ -48,6 +51,7 @@ DEPENDENCIES:
- YYImage/WebP
- YYModel (~> 1.0.4)
- YYWebImage (~> 1.0.5)
- ZFPlayer (~> 4.0.3)
SPEC REPOS:
trunk:
......@@ -63,6 +67,7 @@ SPEC REPOS:
- YYImage
- YYModel
- YYWebImage
- ZFPlayer
SPEC CHECKSUMS:
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
......@@ -77,7 +82,8 @@ SPEC CHECKSUMS:
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
ZFPlayer: 624c92de06eb34df322c3b53b4c9ce760f528f7a
PODFILE CHECKSUM: 5f273d0f03f58db41d7f0a6d3d96a8bd054ab744
PODFILE CHECKSUM: ebcc67379940f6d7de396520351e58af5c93d8cb
COCOAPODS: 1.11.3
......@@ -36,6 +36,9 @@ PODS:
- YYWebImage (1.0.5):
- YYCache
- YYImage
- ZFPlayer (4.0.3):
- ZFPlayer/Core (= 4.0.3)
- ZFPlayer/Core (4.0.3)
DEPENDENCIES:
- DKNightVersion (~> 2.4.3)
......@@ -48,6 +51,7 @@ DEPENDENCIES:
- YYImage/WebP
- YYModel (~> 1.0.4)
- YYWebImage (~> 1.0.5)
- ZFPlayer (~> 4.0.3)
SPEC REPOS:
trunk:
......@@ -63,6 +67,7 @@ SPEC REPOS:
- YYImage
- YYModel
- YYWebImage
- ZFPlayer
SPEC CHECKSUMS:
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
......@@ -77,7 +82,8 @@ SPEC CHECKSUMS:
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
ZFPlayer: 624c92de06eb34df322c3b53b4c9ce760f528f7a
PODFILE CHECKSUM: 5f273d0f03f58db41d7f0a6d3d96a8bd054ab744
PODFILE CHECKSUM: ebcc67379940f6d7de396520351e58af5c93d8cb
COCOAPODS: 1.11.3
......@@ -279,6 +279,29 @@ SOFTWARE.
## ZFPlayer
Copyright (c) 2018 renzifeng <zifeng1300@gmail.com>
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.
## lottie-ios
Apache License
......
......@@ -358,6 +358,35 @@ SOFTWARE.
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2018 renzifeng &lt;zifeng1300@gmail.com&gt;
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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>ZFPlayer</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string> Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
......
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer/ZFPlayer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage"
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage" -framework "ZFPlayer"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer/ZFPlayer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage"
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage" -framework "ZFPlayer"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer/ZFPlayer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage"
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage" -framework "ZFPlayer"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>4.0.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
#import <Foundation/Foundation.h>
@interface PodsDummy_ZFPlayer : NSObject
@end
@implementation PodsDummy_ZFPlayer
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "UIScrollView+ZFPlayer.h"
#import "ZFFloatView.h"
#import "ZFKVOController.h"
#import "ZFLandscapeViewController.h"
#import "ZFLandscapeWindow.h"
#import "ZFOrientationObserver.h"
#import "ZFPersentInteractiveTransition.h"
#import "ZFPlayer.h"
#import "ZFPlayerConst.h"
#import "ZFPlayerController.h"
#import "ZFPlayerGestureControl.h"
#import "ZFPlayerLogManager.h"
#import "ZFPlayerMediaControl.h"
#import "ZFPlayerMediaPlayback.h"
#import "ZFPlayerNotification.h"
#import "ZFPlayerView.h"
#import "ZFPortraitViewController.h"
#import "ZFPresentTransition.h"
#import "ZFReachabilityManager.h"
FOUNDATION_EXPORT double ZFPlayerVersionNumber;
FOUNDATION_EXPORT const unsigned char ZFPlayerVersionString[];
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/ZFPlayer
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
framework module ZFPlayer {
umbrella header "ZFPlayer-umbrella.h"
export *
module * { export * }
}
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ZFPlayer
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/ZFPlayer
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
Copyright (c) 2018 renzifeng <zifeng1300@gmail.com>
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.
<p align="center">
<img src="https://upload-images.jianshu.io/upload_images/635942-092427e571756309.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="ZFPlayer" title="ZFPlayer" width="557"/>
</p>
<p align="center">
<a href="https://img.shields.io/cocoapods/v/ZFPlayer.svg"><img src="https://img.shields.io/cocoapods/v/ZFPlayer.svg"></a>
<a href="https://img.shields.io/github/license/renzifeng/ZFPlayer.svg?style=flat"><img src="https://img.shields.io/github/license/renzifeng/ZFPlayer.svg?style=flat"></a>
<a href="https://img.shields.io/cocoapods/dt/ZFPlayer.svg?maxAge=2592000"><img src="https://img.shields.io/cocoapods/dt/ZFPlayer.svg?maxAge=2592000"></a>
<a href="https://img.shields.io/cocoapods/at/ZFPlayer.svg?maxAge=2592000"><img src="https://img.shields.io/cocoapods/at/ZFPlayer.svg?maxAge=2592000"></a>
<a href="http://cocoadocs.org/docsets/ZFPlayer"><img src="https://img.shields.io/cocoapods/p/ZFPlayer.svg?style=flat"></a>
<a href="http://weibo.com/zifeng1300"><img src="https://img.shields.io/badge/weibo-@%E4%BB%BB%E5%AD%90%E4%B8%B0-yellow.svg?style=flat"></a>
</p>
[中文说明](https://www.jianshu.com/p/90e55deb4d51)
[ZFPlayer 4.x迁移指南](https://github.com/renzifeng/ZFPlayer/wiki/ZFPlayer-4.x%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97)
Before this, you used ZFPlayer, are you worried about encapsulating avplayer instead of using or modifying the source code to support other players, the control layer is not easy to customize, and so on? In order to solve these problems, I have wrote this player template, for player SDK you can conform the `ZFPlayerMediaPlayback` protocol, for control view you can conform the `ZFPlayerMediaControl` protocol, can custom the player and control view.
![ZFPlayer思维导图](https://upload-images.jianshu.io/upload_images/635942-e99d76498cb01afb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
## 🔨 Requirements
- iOS 7+
- Xcode 8+
## 📲 Installation
ZFPlayer is available through [CocoaPods](https://cocoapods.org). To install it,use player template simply add the following line to your Podfile:
```objc
pod 'ZFPlayer', '~> 4.0'
```
Use default controlView simply add the following line to your Podfile:
```objc
pod 'ZFPlayer/ControlView', '~> 4.0'
```
Use AVPlayer simply add the following line to your Podfile:
```objc
pod 'ZFPlayer/AVPlayer', '~> 4.0'
```
Use ijkplayer simply add the following line to your Podfile:
```objc
pod 'ZFPlayer/ijkplayer', '~> 4.0'
```
[IJKMediaFramework SDK](https://gitee.com/renzifeng/IJKMediaFramework) support cocoapods
边下边播可以参考使用[KTVHTTPCache](https://github.com/ChangbaDevs/KTVHTTPCache)
## 🐒 Usage
#### ZFPlayerController
Main classes,normal style initialization and list style initialization (tableView, collection,scrollView)
Normal style initialization
```objc
ZFPlayerController *player = [ZFPlayerController playerWithPlayerManager:playerManager containerView:containerView];
ZFPlayerController *player = [[ZFPlayerController alloc] initwithPlayerManager:playerManager containerView:containerView];
```
List style initialization
```objc
ZFPlayerController *player = [ZFPlayerController playerWithScrollView:tableView playerManager:playerManager containerViewTag:containerViewTag];
ZFPlayerController *player = [ZFPlayerController alloc] initWithScrollView:tableView playerManager:playerManager containerViewTag:containerViewTag];
ZFPlayerController *player = [ZFPlayerController playerWithScrollView:scrollView playerManager:playerManager containerView:containerView];
ZFPlayerController *player = [ZFPlayerController alloc] initWithScrollView:tableView playerManager:playerManager containerView:containerView];
```
#### ZFPlayerMediaPlayback
For the playerMnager,you must conform `ZFPlayerMediaPlayback` protocol,custom playermanager can supports any player SDK,such as `AVPlayer`,`MPMoviePlayerController`,`ijkplayer`,`vlc`,`PLPlayerKit`,`KSYMediaPlayer`and so on,you can reference the `ZFAVPlayerManager`class.
```objc
Class<ZFPlayerMediaPlayback> *playerManager = ...;
```
#### ZFPlayerMediaControl
This class is used to display the control layer, and you must conform the ZFPlayerMediaControl protocol, you can reference the `ZFPlayerControlView` class.
```objc
UIView<ZFPlayerMediaControl> *controlView = ...;
player.controlView = controlView;
```
## 📷 Screenshots
![Picture effect](https://upload-images.jianshu.io/upload_images/635942-1b0e23b7f5eabd9e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
## 👨🏻‍💻 Author
- Weibo: [@任子丰](https://weibo.com/zifeng1300)
- Email: zifeng1300@gmail.com
- QQ群: 123449304
![](https://upload-images.jianshu.io/upload_images/635942-a9fbbb2710de8eff.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
## ❤️ Contributors
林界:https://github.com/GeekLee609
## 🙋🏻‍♂️🙋🏻‍♀️寻求志同道合的小伙伴
- 现寻求志同道合的小伙伴一起维护此框架,有兴趣的小伙伴可以[发邮件](zifeng1300@gmail.com)给我,非常感谢!
- 如果一切OK,我将开放框架维护权限(github、pod等)
## 💰 打赏作者
如果ZFPlayer在开发中有帮助到你、如果你需要技术支持或者你需要定制功能,都可以拼命打赏我!
![支付.jpg](https://upload-images.jianshu.io/upload_images/635942-b9b836cfbb7a5e44.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
## 👮🏻 License
ZFPlayer is available under the MIT license. See the LICENSE file for more info.
//
// 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 ZFPlayerScrollViewDirectionVertical,this value can only be ZFPlayerScrollDirectionUp or ZFPlayerScrollDirectionDown.
/// When the ZFPlayerScrollViewDirection is ZFPlayerScrollViewDirectionHorizontal,this 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 screen,defalut 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
//
// UIScrollView+ZFPlayer.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIScrollView+ZFPlayer.h"
#import <objc/runtime.h>
#import "ZFReachabilityManager.h"
#import "ZFPlayerConst.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
@interface UIScrollView ()
@property (nonatomic) CGFloat zf_lastOffsetY;
@property (nonatomic) CGFloat zf_lastOffsetX;
@property (nonatomic) ZFPlayerScrollDirection zf_scrollDirection;
@end
@implementation UIScrollView (ZFPlayer)
#pragma mark - public method
- (UIView *)zf_getCellForIndexPath:(NSIndexPath *)indexPath {
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
return cell;
} else if ([self _isCollectionView]) {
UICollectionView *collectionView = (UICollectionView *)self;
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
return cell;
}
return nil;
}
- (NSIndexPath *)zf_getIndexPathForCell:(UIView *)cell {
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)cell];
return indexPath;
} else if ([self _isCollectionView]) {
UICollectionView *collectionView = (UICollectionView *)self;
NSIndexPath *indexPath = [collectionView indexPathForCell:(UICollectionViewCell *)cell];
return indexPath;
}
return nil;
}
/**
Scroll to indexPath with position.
@param indexPath scroll the indexPath.
@param scrollPosition scrollView scroll position.
@param animated animate.
@param completionHandler Scroll completion callback.
*/
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler {
[self zf_scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animateDuration:animated ? 0.4 : 0.0 completionHandler:completionHandler];
}
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animateDuration:(NSTimeInterval)duration
completionHandler:(void (^ __nullable)(void))completionHandler {
BOOL animated = duration > 0.0;
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
UITableViewScrollPosition tableScrollPosition = UITableViewScrollPositionNone;
if (scrollPosition <= ZFPlayerScrollViewScrollPositionBottom) {
tableScrollPosition = (UITableViewScrollPosition)scrollPosition;
}
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:tableScrollPosition animated:animated];
} else if ([self _isCollectionView]) {
UICollectionView *collectionView = (UICollectionView *)self;
if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
UICollectionViewScrollPosition collectionScrollPosition = UICollectionViewScrollPositionNone;
switch (scrollPosition) {
case ZFPlayerScrollViewScrollPositionNone:
collectionScrollPosition = UICollectionViewScrollPositionNone;
break;
case ZFPlayerScrollViewScrollPositionTop:
collectionScrollPosition = UICollectionViewScrollPositionTop;
break;
case ZFPlayerScrollViewScrollPositionCenteredVertically:
collectionScrollPosition = UICollectionViewScrollPositionCenteredVertically;
break;
case ZFPlayerScrollViewScrollPositionBottom:
collectionScrollPosition = UICollectionViewScrollPositionBottom;
break;
default:
break;
}
[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:collectionScrollPosition animated:animated];
} else if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionHorizontal) {
UICollectionViewScrollPosition collectionScrollPosition = UICollectionViewScrollPositionNone;
switch (scrollPosition) {
case ZFPlayerScrollViewScrollPositionNone:
collectionScrollPosition = UICollectionViewScrollPositionNone;
break;
case ZFPlayerScrollViewScrollPositionLeft:
collectionScrollPosition = UICollectionViewScrollPositionLeft;
break;
case ZFPlayerScrollViewScrollPositionCenteredHorizontally:
collectionScrollPosition = UICollectionViewScrollPositionCenteredHorizontally;
break;
case ZFPlayerScrollViewScrollPositionRight:
collectionScrollPosition = UICollectionViewScrollPositionRight;
break;
default:
break;
}
[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:collectionScrollPosition animated:animated];
}
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (completionHandler) completionHandler();
});
}
- (void)zf_scrollViewDidEndDecelerating {
BOOL scrollToScrollStop = !self.tracking && !self.dragging && !self.decelerating;
if (scrollToScrollStop) {
[self _scrollViewDidStopScroll];
}
}
- (void)zf_scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate {
if (!decelerate) {
BOOL dragToDragStop = self.tracking && !self.dragging && !self.decelerating;
if (dragToDragStop) {
[self _scrollViewDidStopScroll];
}
}
}
- (void)zf_scrollViewDidScrollToTop {
[self _scrollViewDidStopScroll];
}
- (void)zf_scrollViewDidScroll {
if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
[self _findCorrectCellWhenScrollViewDirectionVertical:nil];
[self _scrollViewScrollingDirectionVertical];
} else {
[self _findCorrectCellWhenScrollViewDirectionHorizontal:nil];
[self _scrollViewScrollingDirectionHorizontal];
}
}
- (void)zf_scrollViewWillBeginDragging {
[self _scrollViewBeginDragging];
}
#pragma mark - private method
- (void)_scrollViewDidStopScroll {
self.zf_scrollDirection = ZFPlayerScrollDirectionNone;
@zf_weakify(self)
[self zf_filterShouldPlayCellWhileScrolled:^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.zf_scrollViewDidStopScrollCallback) self.zf_scrollViewDidStopScrollCallback(indexPath);
if (self.zf_scrollViewDidEndScrollingCallback) self.zf_scrollViewDidEndScrollingCallback(indexPath);
}];
}
- (void)_scrollViewBeginDragging {
if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
self.zf_lastOffsetY = self.contentOffset.y;
} else {
self.zf_lastOffsetX = self.contentOffset.x;
}
}
/**
The percentage of scrolling processed in vertical scrolling.
*/
- (void)_scrollViewScrollingDirectionVertical {
CGFloat offsetY = self.contentOffset.y;
self.zf_scrollDirection = (offsetY - self.zf_lastOffsetY > 0) ? ZFPlayerScrollDirectionUp : ZFPlayerScrollDirectionDown;
self.zf_lastOffsetY = offsetY;
if (self.zf_stopPlay) return;
UIView *playerView;
if (self.zf_containerType == ZFPlayerContainerTypeCell) {
// Avoid being paused the first time you play it.
if (self.contentOffset.y < 0) return;
if (!self.zf_playingIndexPath) return;
UIView *cell = [self zf_getCellForIndexPath:self.zf_playingIndexPath];
if (!cell) {
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
return;
}
playerView = [cell viewWithTag:self.zf_containerViewTag];
} else if (self.zf_containerType == ZFPlayerContainerTypeView) {
if (!self.zf_containerView) return;
playerView = self.zf_containerView;
}
CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
CGRect rect = [self convertRect:rect1 toView:self.superview];
/// playerView top to scrollView top space.
CGFloat topSpacing = CGRectGetMinY(rect) - CGRectGetMinY(self.frame) - CGRectGetMinY(playerView.frame);
/// playerView bottom to scrollView bottom space.
CGFloat bottomSpacing = CGRectGetMaxY(self.frame) - CGRectGetMaxY(rect) + CGRectGetMinY(playerView.frame);
/// The height of the content area.
CGFloat contentInsetHeight = CGRectGetMaxY(self.frame) - CGRectGetMinY(self.frame);
CGFloat playerDisapperaPercent = 0;
CGFloat playerApperaPercent = 0;
if (self.zf_scrollDirection == ZFPlayerScrollDirectionUp) { /// Scroll up
/// Player is disappearing.
if (topSpacing <= 0 && CGRectGetHeight(rect) != 0) {
playerDisapperaPercent = -topSpacing/CGRectGetHeight(rect);
if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
}
/// Top area
if (topSpacing <= 0 && topSpacing > -CGRectGetHeight(rect)/2) {
/// When the player will disappear.
if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
} else if (topSpacing <= -CGRectGetHeight(rect)) {
/// When the player did disappeared.
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
} else if (topSpacing > 0 && topSpacing <= contentInsetHeight) {
/// Player is appearing.
if (CGRectGetHeight(rect) != 0) {
playerApperaPercent = -(topSpacing-contentInsetHeight)/CGRectGetHeight(rect);
if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
}
/// In visable area
if (topSpacing <= contentInsetHeight && topSpacing > contentInsetHeight-CGRectGetHeight(rect)/2) {
/// When the player will appear.
if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
} else {
/// When the player did appeared.
if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
}
}
} else if (self.zf_scrollDirection == ZFPlayerScrollDirectionDown) { /// Scroll Down
/// Player is disappearing.
if (bottomSpacing <= 0 && CGRectGetHeight(rect) != 0) {
playerDisapperaPercent = -bottomSpacing/CGRectGetHeight(rect);
if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
}
/// Bottom area
if (bottomSpacing <= 0 && bottomSpacing > -CGRectGetHeight(rect)/2) {
/// When the player will disappear.
if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
} else if (bottomSpacing <= -CGRectGetHeight(rect)) {
/// When the player did disappeared.
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
} else if (bottomSpacing > 0 && bottomSpacing <= contentInsetHeight) {
/// Player is appearing.
if (CGRectGetHeight(rect) != 0) {
playerApperaPercent = -(bottomSpacing-contentInsetHeight)/CGRectGetHeight(rect);
if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
}
/// In visable area
if (bottomSpacing <= contentInsetHeight && bottomSpacing > contentInsetHeight-CGRectGetHeight(rect)/2) {
/// When the player will appear.
if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
} else {
/// When the player did appeared.
if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
}
}
}
}
/**
The percentage of scrolling processed in horizontal scrolling.
*/
- (void)_scrollViewScrollingDirectionHorizontal {
CGFloat offsetX = self.contentOffset.x;
self.zf_scrollDirection = (offsetX - self.zf_lastOffsetX > 0) ? ZFPlayerScrollDirectionLeft : ZFPlayerScrollDirectionRight;
self.zf_lastOffsetX = offsetX;
if (self.zf_stopPlay) return;
UIView *playerView;
if (self.zf_containerType == ZFPlayerContainerTypeCell) {
// Avoid being paused the first time you play it.
if (self.contentOffset.x < 0) return;
if (!self.zf_playingIndexPath) return;
UIView *cell = [self zf_getCellForIndexPath:self.zf_playingIndexPath];
if (!cell) {
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
return;
}
playerView = [cell viewWithTag:self.zf_containerViewTag];
} else if (self.zf_containerType == ZFPlayerContainerTypeView) {
if (!self.zf_containerView) return;
playerView = self.zf_containerView;
}
CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
CGRect rect = [self convertRect:rect1 toView:self.superview];
/// playerView left to scrollView left space.
CGFloat leftSpacing = CGRectGetMinX(rect) - CGRectGetMinX(self.frame) - CGRectGetMinX(playerView.frame);
/// playerView bottom to scrollView right space.
CGFloat rightSpacing = CGRectGetMaxX(self.frame) - CGRectGetMaxX(rect) + CGRectGetMinX(playerView.frame);
/// The height of the content area.
CGFloat contentInsetWidth = CGRectGetMaxX(self.frame) - CGRectGetMinX(self.frame);
CGFloat playerDisapperaPercent = 0;
CGFloat playerApperaPercent = 0;
if (self.zf_scrollDirection == ZFPlayerScrollDirectionLeft) { /// Scroll left
/// Player is disappearing.
if (leftSpacing <= 0 && CGRectGetWidth(rect) != 0) {
playerDisapperaPercent = -leftSpacing/CGRectGetWidth(rect);
if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
}
/// Top area
if (leftSpacing <= 0 && leftSpacing > -CGRectGetWidth(rect)/2) {
/// When the player will disappear.
if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
} else if (leftSpacing <= -CGRectGetWidth(rect)) {
/// When the player did disappeared.
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
} else if (leftSpacing > 0 && leftSpacing <= contentInsetWidth) {
/// Player is appearing.
if (CGRectGetWidth(rect) != 0) {
playerApperaPercent = -(leftSpacing-contentInsetWidth)/CGRectGetWidth(rect);
if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
}
/// In visable area
if (leftSpacing <= contentInsetWidth && leftSpacing > contentInsetWidth-CGRectGetWidth(rect)/2) {
/// When the player will appear.
if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
} else {
/// When the player did appeared.
if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
}
}
} else if (self.zf_scrollDirection == ZFPlayerScrollDirectionRight) { /// Scroll right
/// Player is disappearing.
if (rightSpacing <= 0 && CGRectGetWidth(rect) != 0) {
playerDisapperaPercent = -rightSpacing/CGRectGetWidth(rect);
if (playerDisapperaPercent > 1.0) playerDisapperaPercent = 1.0;
if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(self.zf_playingIndexPath, playerDisapperaPercent);
}
/// Bottom area
if (rightSpacing <= 0 && rightSpacing > -CGRectGetWidth(rect)/2) {
/// When the player will disappear.
if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(self.zf_playingIndexPath);
} else if (rightSpacing <= -CGRectGetWidth(rect)) {
/// When the player did disappeared.
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(self.zf_playingIndexPath);
} else if (rightSpacing > 0 && rightSpacing <= contentInsetWidth) {
/// Player is appearing.
if (CGRectGetWidth(rect) != 0) {
playerApperaPercent = -(rightSpacing-contentInsetWidth)/CGRectGetWidth(rect);
if (playerApperaPercent > 1.0) playerApperaPercent = 1.0;
if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(self.zf_playingIndexPath, playerApperaPercent);
}
/// In visable area
if (rightSpacing <= contentInsetWidth && rightSpacing > contentInsetWidth-CGRectGetWidth(rect)/2) {
/// When the player will appear.
if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(self.zf_playingIndexPath);
} else {
/// When the player did appeared.
if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(self.zf_playingIndexPath);
}
}
}
}
/**
Find the playing cell while the scrollDirection is vertical.
*/
- (void)_findCorrectCellWhenScrollViewDirectionVertical:(void (^ __nullable)(NSIndexPath *indexPath))handler {
if (!self.zf_shouldAutoPlay) return;
if (self.zf_containerType == ZFPlayerContainerTypeView) return;
if (!self.zf_stopWhileNotVisible) {
/// If you have a cell that is playing, stop the traversal.
if (self.zf_playingIndexPath) {
NSIndexPath *finalIndexPath = self.zf_playingIndexPath;
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(finalIndexPath);
if (handler) handler(finalIndexPath);
self.zf_shouldPlayIndexPath = finalIndexPath;
return;
}
}
NSArray *visiableCells = nil;
NSIndexPath *indexPath = nil;
BOOL isLast = self.contentOffset.y + self.frame.size.height >= self.contentSize.height;
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
visiableCells = [tableView visibleCells];
// First visible cell indexPath
indexPath = tableView.indexPathsForVisibleRows.firstObject;
if ((self.contentOffset.y <= 0 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
if (handler) handler(indexPath);
self.zf_shouldPlayIndexPath = indexPath;
return;
}
}
} else if ([self _isCollectionView]) {
UICollectionView *collectionView = (UICollectionView *)self;
visiableCells = [collectionView visibleCells];
NSArray *sortedIndexPaths = [collectionView.indexPathsForVisibleItems sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
visiableCells = [visiableCells sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSIndexPath *path1 = (NSIndexPath *)[collectionView indexPathForCell:obj1];
NSIndexPath *path2 = (NSIndexPath *)[collectionView indexPathForCell:obj2];
return [path1 compare:path2];
}];
// First visible cell indexPath
indexPath = sortedIndexPaths.firstObject;
if ((self.contentOffset.y <= 0 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
if (handler) handler(indexPath);
self.zf_shouldPlayIndexPath = indexPath;
return;
}
}
}
NSArray *cells = nil;
if (self.zf_scrollDirection == ZFPlayerScrollDirectionUp) {
cells = visiableCells;
} else {
cells = [visiableCells reverseObjectEnumerator].allObjects;
}
/// Mid line.
CGFloat scrollViewMidY = CGRectGetHeight(self.frame)/2;
/// The final playing indexPath.
__block NSIndexPath *finalIndexPath = nil;
/// The final distance from the center line.
__block CGFloat finalSpace = 0;
@zf_weakify(self)
[cells enumerateObjectsUsingBlock:^(UIView *cell, NSUInteger idx, BOOL * _Nonnull stop) {
@zf_strongify(self)
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (!playerView || playerView.hidden || playerView.alpha <= 0.01) return;
CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
CGRect rect = [self convertRect:rect1 toView:self.superview];
/// playerView top to scrollView top space.
CGFloat topSpacing = CGRectGetMinY(rect) - CGRectGetMinY(self.frame) - CGRectGetMinY(playerView.frame);
/// playerView bottom to scrollView bottom space.
CGFloat bottomSpacing = CGRectGetMaxY(self.frame) - CGRectGetMaxY(rect) + CGRectGetMinY(playerView.frame);
CGFloat centerSpacing = ABS(scrollViewMidY - CGRectGetMidY(rect));
NSIndexPath *indexPath = [self zf_getIndexPathForCell:cell];
/// Play when the video playback section is visible.
if ((topSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetHeight(rect)) && (bottomSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetHeight(rect))) {
if (!finalIndexPath || centerSpacing < finalSpace) {
finalIndexPath = indexPath;
finalSpace = centerSpacing;
}
}
}];
/// if find the playing indexPath.
if (finalIndexPath) {
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
if (handler) handler(finalIndexPath);
}
self.zf_shouldPlayIndexPath = finalIndexPath;
}
/**
Find the playing cell while the scrollDirection is horizontal.
*/
- (void)_findCorrectCellWhenScrollViewDirectionHorizontal:(void (^ __nullable)(NSIndexPath *indexPath))handler {
if (!self.zf_shouldAutoPlay) return;
if (self.zf_containerType == ZFPlayerContainerTypeView) return;
if (!self.zf_stopWhileNotVisible) {
/// If you have a cell that is playing, stop the traversal.
if (self.zf_playingIndexPath) {
NSIndexPath *finalIndexPath = self.zf_playingIndexPath;
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(finalIndexPath);
if (handler) handler(finalIndexPath);
self.zf_shouldPlayIndexPath = finalIndexPath;
return;
}
}
NSArray *visiableCells = nil;
NSIndexPath *indexPath = nil;
BOOL isLast = self.contentOffset.x + self.frame.size.width >= self.contentSize.width;
if ([self _isTableView]) {
UITableView *tableView = (UITableView *)self;
visiableCells = [tableView visibleCells];
// First visible cell indexPath
indexPath = tableView.indexPathsForVisibleRows.firstObject;
if ((self.contentOffset.x <= 0 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
if (handler) handler(indexPath);
self.zf_shouldPlayIndexPath = indexPath;
return;
}
}
} else if ([self _isCollectionView]) {
UICollectionView *collectionView = (UICollectionView *)self;
visiableCells = [collectionView visibleCells];
NSArray *sortedIndexPaths = [collectionView.indexPathsForVisibleItems sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
visiableCells = [visiableCells sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSIndexPath *path1 = (NSIndexPath *)[collectionView indexPathForCell:obj1];
NSIndexPath *path2 = (NSIndexPath *)[collectionView indexPathForCell:obj2];
return [path1 compare:path2];
}];
// First visible cell indexPath
indexPath = sortedIndexPaths.firstObject;
if ((self.contentOffset.x <= 0 || isLast) && (!self.zf_playingIndexPath || [indexPath compare:self.zf_playingIndexPath] == NSOrderedSame)) {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (playerView && !playerView.hidden && playerView.alpha > 0.01) {
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
if (handler) handler(indexPath);
self.zf_shouldPlayIndexPath = indexPath;
return;
}
}
}
NSArray *cells = nil;
if (self.zf_scrollDirection == ZFPlayerScrollDirectionUp) {
cells = visiableCells;
} else {
cells = [visiableCells reverseObjectEnumerator].allObjects;
}
/// Mid line.
CGFloat scrollViewMidX = CGRectGetWidth(self.frame)/2;
/// The final playing indexPath.
__block NSIndexPath *finalIndexPath = nil;
/// The final distance from the center line.
__block CGFloat finalSpace = 0;
@zf_weakify(self)
[cells enumerateObjectsUsingBlock:^(UIView *cell, NSUInteger idx, BOOL * _Nonnull stop) {
@zf_strongify(self)
UIView *playerView = [cell viewWithTag:self.zf_containerViewTag];
if (!playerView || playerView.hidden || playerView.alpha <= 0.01) return;
CGRect rect1 = [playerView convertRect:playerView.frame toView:self];
CGRect rect = [self convertRect:rect1 toView:self.superview];
/// playerView left to scrollView top space.
CGFloat leftSpacing = CGRectGetMinX(rect) - CGRectGetMinX(self.frame) - CGRectGetMinX(playerView.frame);
/// playerView right to scrollView top space.
CGFloat rightSpacing = CGRectGetMaxX(self.frame) - CGRectGetMaxX(rect) + CGRectGetMinX(playerView.frame);
CGFloat centerSpacing = ABS(scrollViewMidX - CGRectGetMidX(rect));
NSIndexPath *indexPath = [self zf_getIndexPathForCell:cell];
/// Play when the video playback section is visible.
if ((leftSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetWidth(rect)) && (rightSpacing >= -(1 - self.zf_playerApperaPercent) * CGRectGetWidth(rect))) {
if (!finalIndexPath || centerSpacing < finalSpace) {
finalIndexPath = indexPath;
finalSpace = centerSpacing;
}
}
}];
/// if find the playing indexPath.
if (finalIndexPath) {
if (self.zf_scrollViewDidScrollCallback) self.zf_scrollViewDidScrollCallback(indexPath);
if (handler) handler(finalIndexPath);
self.zf_shouldPlayIndexPath = finalIndexPath;
}
}
- (BOOL)_isTableView {
return [self isKindOfClass:[UITableView class]];
}
- (BOOL)_isCollectionView {
return [self isKindOfClass:[UICollectionView class]];
}
#pragma mark - getter
- (ZFPlayerScrollDirection)zf_scrollDirection {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
- (ZFPlayerScrollViewDirection)zf_scrollViewDirection {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
- (CGFloat)zf_lastOffsetY {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
- (CGFloat)zf_lastOffsetX {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
#pragma mark - setter
- (void)setZf_scrollDirection:(ZFPlayerScrollDirection)zf_scrollDirection {
objc_setAssociatedObject(self, @selector(zf_scrollDirection), @(zf_scrollDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_scrollViewDirection:(ZFPlayerScrollViewDirection)zf_scrollViewDirection {
objc_setAssociatedObject(self, @selector(zf_scrollViewDirection), @(zf_scrollViewDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_lastOffsetY:(CGFloat)zf_lastOffsetY {
objc_setAssociatedObject(self, @selector(zf_lastOffsetY), @(zf_lastOffsetY), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_lastOffsetX:(CGFloat)zf_lastOffsetX {
objc_setAssociatedObject(self, @selector(zf_lastOffsetX), @(zf_lastOffsetX), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@implementation UIScrollView (ZFPlayerCannotCalled)
- (void)zf_filterShouldPlayCellWhileScrolling:(void (^ __nullable)(NSIndexPath *indexPath))handler {
if (self.zf_scrollViewDirection == ZFPlayerScrollViewDirectionVertical) {
[self _findCorrectCellWhenScrollViewDirectionVertical:handler];
} else {
[self _findCorrectCellWhenScrollViewDirectionHorizontal:handler];
}
}
- (void)zf_filterShouldPlayCellWhileScrolled:(void (^ __nullable)(NSIndexPath *indexPath))handler {
if (!self.zf_shouldAutoPlay) return;
@zf_weakify(self)
[self zf_filterShouldPlayCellWhileScrolling:^(NSIndexPath *indexPath) {
@zf_strongify(self)
/// 如果当前控制器已经消失,直接return
if (self.zf_viewControllerDisappear) return;
if ([ZFReachabilityManager sharedManager].isReachableViaWWAN && !self.zf_WWANAutoPlay) {
/// 移动网络
self.zf_shouldPlayIndexPath = indexPath;
return;
}
if (handler) handler(indexPath);
self.zf_playingIndexPath = indexPath;
}];
}
#pragma mark - getter
- (void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerDisappearingInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerAppearingInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_playerDidAppearInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_playerWillDisappearInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_playerWillAppearInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_playerDidDisappearInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidEndScrollingCallback {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidScrollCallback {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_playerShouldPlayInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (CGFloat)zf_playerApperaPercent {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
- (CGFloat)zf_playerDisapperaPercent {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
- (BOOL)zf_viewControllerDisappear {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (BOOL)zf_stopPlay {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.boolValue;
self.zf_stopPlay = YES;
return YES;
}
- (BOOL)zf_stopWhileNotVisible {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (NSIndexPath *)zf_playingIndexPath {
return objc_getAssociatedObject(self, _cmd);
}
- (NSIndexPath *)zf_shouldPlayIndexPath {
return objc_getAssociatedObject(self, _cmd);
}
- (NSInteger)zf_containerViewTag {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
- (BOOL)zf_isWWANAutoPlay {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (BOOL)zf_shouldAutoPlay {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.boolValue;
self.zf_shouldAutoPlay = YES;
return YES;
}
- (ZFPlayerContainerType)zf_containerType {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
- (UIView *)zf_containerView {
return objc_getAssociatedObject(self, _cmd);
}
#pragma mark - setter
- (void)setZf_playerDisappearingInScrollView:(void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerDisappearingInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerDisappearingInScrollView), zf_playerDisappearingInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerAppearingInScrollView:(void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerAppearingInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerAppearingInScrollView), zf_playerAppearingInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerDidAppearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerDidAppearInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerDidAppearInScrollView), zf_playerDidAppearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerWillDisappearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerWillDisappearInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerWillDisappearInScrollView), zf_playerWillDisappearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerWillAppearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerWillAppearInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerWillAppearInScrollView), zf_playerWillAppearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerDidDisappearInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerDidDisappearInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerDidDisappearInScrollView), zf_playerDidDisappearInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_scrollViewDidEndScrollingCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidEndScrollingCallback {
objc_setAssociatedObject(self, @selector(zf_scrollViewDidEndScrollingCallback), zf_scrollViewDidEndScrollingCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_scrollViewDidScrollCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidScrollCallback {
objc_setAssociatedObject(self, @selector(zf_scrollViewDidScrollCallback), zf_scrollViewDidScrollCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerShouldPlayInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerShouldPlayInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerShouldPlayInScrollView), zf_playerShouldPlayInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerApperaPercent:(CGFloat)zf_playerApperaPercent {
objc_setAssociatedObject(self, @selector(zf_playerApperaPercent), @(zf_playerApperaPercent), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerDisapperaPercent:(CGFloat)zf_playerDisapperaPercent {
objc_setAssociatedObject(self, @selector(zf_playerDisapperaPercent), @(zf_playerDisapperaPercent), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_viewControllerDisappear:(BOOL)zf_viewControllerDisappear {
objc_setAssociatedObject(self, @selector(zf_viewControllerDisappear), @(zf_viewControllerDisappear), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_stopPlay:(BOOL)zf_stopPlay {
objc_setAssociatedObject(self, @selector(zf_stopPlay), @(zf_stopPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_stopWhileNotVisible:(BOOL)zf_stopWhileNotVisible {
objc_setAssociatedObject(self, @selector(zf_stopWhileNotVisible), @(zf_stopWhileNotVisible), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_playingIndexPath:(NSIndexPath *)zf_playingIndexPath {
objc_setAssociatedObject(self, @selector(zf_playingIndexPath), zf_playingIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (zf_playingIndexPath && [zf_playingIndexPath compare:self.zf_shouldPlayIndexPath] != NSOrderedSame) {
self.zf_shouldPlayIndexPath = zf_playingIndexPath;
}
}
- (void)setZf_shouldPlayIndexPath:(NSIndexPath *)zf_shouldPlayIndexPath {
if (self.zf_playerShouldPlayInScrollView) self.zf_playerShouldPlayInScrollView(zf_shouldPlayIndexPath);
if (self.zf_shouldPlayIndexPathCallback) self.zf_shouldPlayIndexPathCallback(zf_shouldPlayIndexPath);
objc_setAssociatedObject(self, @selector(zf_shouldPlayIndexPath), zf_shouldPlayIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_containerViewTag:(NSInteger)zf_containerViewTag {
objc_setAssociatedObject(self, @selector(zf_containerViewTag), @(zf_containerViewTag), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_containerType:(ZFPlayerContainerType)zf_containerType {
objc_setAssociatedObject(self, @selector(zf_containerType), @(zf_containerType), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_containerView:(UIView *)zf_containerView {
objc_setAssociatedObject(self, @selector(zf_containerView), zf_containerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_shouldAutoPlay:(BOOL)zf_shouldAutoPlay {
objc_setAssociatedObject(self, @selector(zf_shouldAutoPlay), @(zf_shouldAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_WWANAutoPlay:(BOOL)zf_WWANAutoPlay {
objc_setAssociatedObject(self, @selector(zf_isWWANAutoPlay), @(zf_WWANAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@implementation UIScrollView (ZFPlayerDeprecated)
#pragma mark - getter
- (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidStopScrollCallback {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_shouldPlayIndexPathCallback {
return objc_getAssociatedObject(self, _cmd);
}
#pragma mark - setter
- (void)setZf_scrollViewDidStopScrollCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidStopScrollCallback {
objc_setAssociatedObject(self, @selector(zf_scrollViewDidStopScrollCallback), zf_scrollViewDidStopScrollCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_shouldPlayIndexPathCallback:(void (^)(NSIndexPath * _Nonnull))zf_shouldPlayIndexPathCallback {
objc_setAssociatedObject(self, @selector(zf_shouldPlayIndexPathCallback), zf_shouldPlayIndexPathCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
#pragma mark - method
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^ __nullable)(void))completionHandler {
[self zf_scrollToRowAtIndexPath:indexPath animated:YES completionHandler:completionHandler];
}
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated completionHandler:(void (^ __nullable)(void))completionHandler {
[self zf_scrollToRowAtIndexPath:indexPath animateWithDuration:animated ? 0.4 : 0.0 completionHandler:completionHandler];
}
/// Scroll to indexPath with animations duration.
- (void)zf_scrollToRowAtIndexPath:(NSIndexPath *)indexPath animateWithDuration:(NSTimeInterval)duration completionHandler:(void (^ __nullable)(void))completionHandler {
[self zf_scrollToRowAtIndexPath:indexPath atScrollPosition:ZFPlayerScrollViewScrollPositionTop animateDuration:duration completionHandler:completionHandler];
}
@end
#pragma clang diagnostic pop
//
// UIViewController+ZFPlayerRotation.m
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@implementation UITabBarController (ZFPlayerRotation)
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL selectors[] = {
@selector(selectedIndex)
};
for (NSUInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) {
SEL originalSelector = selectors[index];
SEL swizzledSelector = NSSelectorFromString([@"zf_" stringByAppendingString:NSStringFromSelector(originalSelector)]);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
if (class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
});
}
- (NSInteger)zf_selectedIndex {
NSInteger index = [self zf_selectedIndex];
if (index > self.viewControllers.count) return 0;
return index;
}
/**
* If the root view of the window is a UINavigationController, you call this Category first, and then UIViewController called.
* All you need to do is revisit the following three methods on a page that supports directions other than portrait.
*/
// Whether automatic screen rotation is supported.
- (BOOL)shouldAutorotate {
return [[self viewControllerRotation] shouldAutorotate];
}
// Which screen directions are supported.
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [[self viewControllerRotation] supportedInterfaceOrientations];
}
// The default screen direction (the current ViewController must be represented by a modal UIViewController (which is not valid with modal navigation) to call this method).
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self viewControllerRotation] preferredInterfaceOrientationForPresentation];
}
// Return ViewController which decide rotation,if 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
//
// 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
//
// 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
//
// 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
//
// 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
//
// ZFFullscreenViewController.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@class ZFLandscapeViewController;
NS_ASSUME_NONNULL_BEGIN
@protocol ZFLandscapeViewControllerDelegate <NSObject>
- (BOOL)ls_shouldAutorotate;
- (void)ls_willRotateToOrientation:(UIInterfaceOrientation)orientation;
- (void)ls_didRotateFromOrientation:(UIInterfaceOrientation)orientation;
- (CGRect)ls_targetRect;
@end
@interface ZFLandscapeViewController : UIViewController
@property (nonatomic, weak) UIView *contentView;
@property (nonatomic, weak) UIView *containerView;
@property (nonatomic, assign) CGRect targetRect;
@property (nonatomic, weak, nullable) id<ZFLandscapeViewControllerDelegate> delegate;
@property (nonatomic, readonly) BOOL isFullscreen;
@property (nonatomic, getter=isRotating) BOOL rotating;
@property (nonatomic, assign) BOOL disableAnimations;
@property (nonatomic, assign) BOOL statusBarHidden;
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle statusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation statusBarAnimation;
@property (nonatomic, copy) void(^rotatingCompleted)(void);
@end
NS_ASSUME_NONNULL_END
//
// ZFFullScreenViewController.m
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFLandscapeViewController.h"
@interface ZFLandscapeViewController ()
@property (nonatomic, assign) UIInterfaceOrientation currentOrientation;
@end
@implementation ZFLandscapeViewController
- (instancetype)init {
self = [super init];
if (self) {
_currentOrientation = UIInterfaceOrientationPortrait;
_statusBarStyle = UIStatusBarStyleLightContent;
_statusBarAnimation = UIStatusBarAnimationSlide;
}
return self;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
self.rotating = YES;
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
if (!UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
return;
}
UIInterfaceOrientation newOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
UIInterfaceOrientation oldOrientation = _currentOrientation;
if (UIInterfaceOrientationIsLandscape(newOrientation)) {
if (self.contentView.superview != self.view) {
[self.view addSubview:self.contentView];
}
}
if (oldOrientation == UIInterfaceOrientationPortrait) {
self.contentView.frame = [self.delegate ls_targetRect];
[self.contentView layoutIfNeeded];
}
self.currentOrientation = newOrientation;
[self.delegate ls_willRotateToOrientation:self.currentOrientation];
BOOL isFullscreen = size.width > size.height;
if (self.disableAnimations) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
}
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if (isFullscreen) {
self.contentView.frame = CGRectMake(0, 0, size.width, size.height);
} else {
self.contentView.frame = [self.delegate ls_targetRect];
}
[self.contentView layoutIfNeeded];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if (self.disableAnimations) {
[CATransaction commit];
}
[self.delegate ls_didRotateFromOrientation:self.currentOrientation];
if (!isFullscreen) {
self.contentView.frame = self.containerView.bounds;
[self.contentView layoutIfNeeded];
}
self.disableAnimations = NO;
self.rotating = NO;
}];
}
- (BOOL)isFullscreen {
return UIInterfaceOrientationIsLandscape(_currentOrientation);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate {
return [self.delegate ls_shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (UIInterfaceOrientationIsLandscape(currentOrientation)) {
return UIInterfaceOrientationMaskLandscape;
}
return UIInterfaceOrientationMaskAll;
}
- (BOOL)prefersHomeIndicatorAutoHidden {
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (UIInterfaceOrientationIsLandscape(currentOrientation)) {
return YES;
}
return NO;
}
- (BOOL)prefersStatusBarHidden {
return self.statusBarHidden;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return self.statusBarAnimation;
}
- (void)setRotating:(BOOL)rotating {
_rotating = rotating;
if (!rotating && self.rotatingCompleted) {
self.rotatingCompleted();
}
}
@end
//
// ZFLandScaprWindow.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFLandscapeViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandscapeWindow : UIWindow
@property (nonatomic, strong, readonly) ZFLandscapeViewController *landscapeViewController;
@end
NS_ASSUME_NONNULL_END
//
// ZFLandScaprWindow.m
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFLandscapeWindow.h"
@implementation ZFLandscapeWindow
@dynamic rootViewController;
- (void)setBackgroundColor:(nullable UIColor *)backgroundColor {}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.windowLevel = UIWindowLevelNormal;
_landscapeViewController = [[ZFLandscapeViewController alloc] init];
self.rootViewController = _landscapeViewController;
if (@available(iOS 13.0, *)) {
if (self.windowScene == nil) {
self.windowScene = UIApplication.sharedApplication.keyWindow.windowScene;
}
}
self.hidden = YES;
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *_Nullable)event {
return YES;
}
- (void)layoutSubviews {
[super layoutSubviews];
static CGRect bounds;
if (!CGRectEqualToRect(bounds, self.bounds)) {
UIView *superview = self;
if (@available(iOS 13.0, *)) {
superview = self.subviews.firstObject;
}
[UIView performWithoutAnimation:^{
for (UIView *view in superview.subviews) {
if (view != self.rootViewController.view && [view isMemberOfClass:UIView.class]) {
view.backgroundColor = UIColor.clearColor;
for (UIView *subview in view.subviews) {
subview.backgroundColor = UIColor.clearColor;
}
}
}
}];
}
bounds = self.bounds;
self.rootViewController.view.frame = bounds;
}
@end
//
// ZFOrentationObserver.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFPlayerView.h"
NS_ASSUME_NONNULL_BEGIN
/// Full screen mode
typedef NS_ENUM(NSUInteger, ZFFullScreenMode) {
ZFFullScreenModeAutomatic, // Determine full screen mode automatically
ZFFullScreenModeLandscape, // Landscape full screen mode
ZFFullScreenModePortrait // Portrait full screen Model
};
/// Portrait full screen mode.
typedef NS_ENUM(NSUInteger, ZFPortraitFullScreenMode) {
ZFPortraitFullScreenModeScaleToFill, // Full fill
ZFPortraitFullScreenModeScaleAspectFit // contents scaled to fit with fixed aspect. remainder is transparent
};
/// Player view mode
typedef NS_ENUM(NSUInteger, ZFRotateType) {
ZFRotateTypeNormal, // Normal
ZFRotateTypeCell // Cell
};
/**
Rotation of support direction
*/
typedef NS_OPTIONS(NSUInteger, ZFInterfaceOrientationMask) {
ZFInterfaceOrientationMaskPortrait = (1 << 0),
ZFInterfaceOrientationMaskLandscapeLeft = (1 << 1),
ZFInterfaceOrientationMaskLandscapeRight = (1 << 2),
ZFInterfaceOrientationMaskPortraitUpsideDown = (1 << 3),
ZFInterfaceOrientationMaskLandscape = (ZFInterfaceOrientationMaskLandscapeLeft | ZFInterfaceOrientationMaskLandscapeRight),
ZFInterfaceOrientationMaskAll = (ZFInterfaceOrientationMaskPortrait | ZFInterfaceOrientationMaskLandscape | ZFInterfaceOrientationMaskPortraitUpsideDown),
ZFInterfaceOrientationMaskAllButUpsideDown = (ZFInterfaceOrientationMaskPortrait | ZFInterfaceOrientationMaskLandscape),
};
/// This enumeration lists some of the gesture types that the player has by default.
typedef NS_OPTIONS(NSUInteger, ZFDisablePortraitGestureTypes) {
ZFDisablePortraitGestureTypesNone = 0,
ZFDisablePortraitGestureTypesTap = 1 << 0,
ZFDisablePortraitGestureTypesPan = 1 << 1,
ZFDisablePortraitGestureTypesAll = (ZFDisablePortraitGestureTypesTap | ZFDisablePortraitGestureTypesPan)
};
@protocol ZFPortraitOrientationDelegate <NSObject>
- (void)zf_orientationWillChange:(BOOL)isFullScreen;
- (void)zf_orientationDidChanged:(BOOL)isFullScreen;
- (void)zf_interationState:(BOOL)isDragging;
@end
@interface ZFOrientationObserver : NSObject
/// update the rotateView and containerView.
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView;
/// list play
- (void)updateRotateView:(ZFPlayerView *)rotateView
rotateViewAtCell:(UIView *)cell
playerViewTag:(NSInteger)playerViewTag;
/// Container view of a full screen state player.
@property (nonatomic, strong) UIView *fullScreenContainerView;
/// Container view of a small screen state player.
@property (nonatomic, weak) UIView *containerView;
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(ZFOrientationObserver *observer, BOOL isFullScreen);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(ZFOrientationObserver *observer, BOOL isFullScreen);
/// Full screen mode, the default landscape into full screen
@property (nonatomic) ZFFullScreenMode fullScreenMode;
@property (nonatomic, assign) ZFPortraitFullScreenMode portraitFullScreenMode;
/// rotate duration, default is 0.30
@property (nonatomic) NSTimeInterval duration;
/// If the full screen.
@property (nonatomic, readonly, getter=isFullScreen) BOOL fullScreen;
/// Lock screen orientation
@property (nonatomic, getter=isLockedScreen) BOOL lockedScreen;
/// The fullscreen statusbar hidden.
@property (nonatomic, assign) BOOL fullScreenStatusBarHidden;
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle fullScreenStatusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation fullScreenStatusBarAnimation;
@property (nonatomic, assign) CGSize presentationSize;
/// default is ZFDisablePortraitGestureTypesAll.
@property (nonatomic, assign) ZFDisablePortraitGestureTypes disablePortraitGestureTypes;
/// The current orientation of the player.
/// Default is UIInterfaceOrientationPortrait.
@property (nonatomic, readonly) UIInterfaceOrientation currentOrientation;
/// Whether allow the video orientation rotate.
/// default is YES.
@property (nonatomic, assign) BOOL allowOrientationRotation;
/// The support Interface Orientation,default is ZFInterfaceOrientationMaskAllButUpsideDown
@property (nonatomic, assign) ZFInterfaceOrientationMask supportInterfaceOrientation;
/// Add the device orientation observer.
- (void)addDeviceOrientationObserver;
/// Remove the device orientation observer.
- (void)removeDeviceOrientationObserver;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/// FullScreen mode is determined by ZFFullScreenMode.
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
/// FullScreen mode is determined by ZFFullScreenMode.
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion;
@end
NS_ASSUME_NONNULL_END
// ZFOrentationObserver.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFOrientationObserver.h"
#import "ZFLandscapeWindow.h"
#import "ZFPortraitViewController.h"
#import "ZFPlayerConst.h"
#import <objc/runtime.h>
@interface UIWindow (CurrentViewController)
/*!
@method currentViewController
@return Returns the topViewController in stack of topMostController.
*/
+ (UIViewController*)zf_currentViewController;
@end
@implementation UIWindow (CurrentViewController)
+ (UIViewController*)zf_currentViewController; {
__block UIWindow *window;
if (@available(iOS 13, *)) {
[[UIApplication sharedApplication].connectedScenes enumerateObjectsUsingBlock:^(UIScene * _Nonnull scene, BOOL * _Nonnull scenesStop) {
if ([scene isKindOfClass: [UIWindowScene class]]) {
UIWindowScene * windowScene = (UIWindowScene *)scene;
[windowScene.windows enumerateObjectsUsingBlock:^(UIWindow * _Nonnull windowTemp, NSUInteger idx, BOOL * _Nonnull windowStop) {
if (windowTemp.isKeyWindow) {
window = windowTemp;
*windowStop = YES;
*scenesStop = YES;
}
}];
}
}];
} else {
window = [[UIApplication sharedApplication].delegate window];
}
UIViewController *topViewController = [window rootViewController];
while (true) {
if (topViewController.presentedViewController) {
topViewController = topViewController.presentedViewController;
} else if ([topViewController isKindOfClass:[UINavigationController class]] && [(UINavigationController *)topViewController topViewController]) {
topViewController = [(UINavigationController *)topViewController topViewController];
} else if ([topViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tab = (UITabBarController *)topViewController;
topViewController = tab.selectedViewController;
} else {
break;
}
}
return topViewController;
}
@end
@interface ZFOrientationObserver () <ZFLandscapeViewControllerDelegate>
@property (nonatomic, weak) ZFPlayerView *view;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@property (nonatomic, strong) UIView *cell;
@property (nonatomic, assign) NSInteger playerViewTag;
@property (nonatomic, assign) ZFRotateType rotateType;
@property (nonatomic, strong) UIWindow *previousKeyWindow;
@property (nonatomic, strong) ZFLandscapeWindow *window;
@property (nonatomic, readonly, getter=isRotating) BOOL rotating;
@property (nonatomic, strong) ZFPortraitViewController *portraitViewController;
/// current device orientation observer is activie.
@property (nonatomic, assign) BOOL activeDeviceObserver;
/// Force Rotaion, default NO.
@property (nonatomic, assign) BOOL forceRotaion;
@end
@implementation ZFOrientationObserver
@synthesize presentationSize = _presentationSize;
- (instancetype)init {
self = [super init];
if (self) {
_duration = 0.30;
_fullScreenMode = ZFFullScreenModeLandscape;
_supportInterfaceOrientation = ZFInterfaceOrientationMaskAllButUpsideDown;
_allowOrientationRotation = YES;
_rotateType = ZFRotateTypeNormal;
_currentOrientation = UIInterfaceOrientationPortrait;
_portraitFullScreenMode = ZFPortraitFullScreenModeScaleToFill;
_disablePortraitGestureTypes = ZFDisablePortraitGestureTypesAll;
}
return self;
}
- (void)updateRotateView:(ZFPlayerView *)rotateView
containerView:(UIView *)containerView {
self.rotateType = ZFRotateTypeNormal;
self.view = rotateView;
self.containerView = containerView;
}
- (void)updateRotateView:(ZFPlayerView *)rotateView rotateViewAtCell:(UIView *)cell playerViewTag:(NSInteger)playerViewTag {
self.rotateType = ZFRotateTypeCell;
self.view = rotateView;
self.cell = cell;
self.playerViewTag = playerViewTag;
}
- (void)dealloc {
[self removeDeviceOrientationObserver];
}
- (void)addDeviceOrientationObserver {
if (self.allowOrientationRotation) {
self.activeDeviceObserver = YES;
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeviceOrientationChange) name:UIDeviceOrientationDidChangeNotification object:nil];
}
}
- (void)removeDeviceOrientationObserver {
self.activeDeviceObserver = NO;
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)handleDeviceOrientationChange {
if (self.fullScreenMode == ZFFullScreenModePortrait || !self.allowOrientationRotation) return;
if (!UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
return;
}
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
// Determine that if the current direction is the same as the direction you want to rotate, do nothing
if (currentOrientation == _currentOrientation) return;
_currentOrientation = currentOrientation;
if (_currentOrientation == UIInterfaceOrientationPortraitUpsideDown) return;
switch (currentOrientation) {
case UIInterfaceOrientationPortrait: {
if ([self _isSupportedPortrait]) {
[self rotateToOrientation:UIInterfaceOrientationPortrait animated:YES];
}
}
break;
case UIInterfaceOrientationLandscapeLeft: {
if ([self _isSupportedLandscapeLeft]) {
[self rotateToOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];
}
}
break;
case UIInterfaceOrientationLandscapeRight: {
if ([self _isSupportedLandscapeRight]) {
[self rotateToOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
}
}
break;
default: break;
}
}
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation {
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
UIInterfaceOrientation val = orientation;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
#pragma mark - public
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
[self rotateToOrientation:orientation animated:animated completion:nil];
}
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion {
if (self.fullScreenMode == ZFFullScreenModePortrait) return;
_currentOrientation = orientation;
self.forceRotaion = YES;
if (UIInterfaceOrientationIsLandscape(orientation)) {
if (!self.fullScreen) {
UIView *containerView = nil;
if (self.rotateType == ZFRotateTypeCell) {
containerView = [self.cell viewWithTag:self.playerViewTag];
} else {
containerView = self.containerView;
}
CGRect targetRect = [self.view convertRect:self.view.frame toView:containerView.window];
if (!self.window) {
self.window = [ZFLandscapeWindow new];
self.window.landscapeViewController.delegate = self;
if (@available(iOS 9.0, *)) {
[self.window.rootViewController loadViewIfNeeded];
} else {
[self.window.rootViewController view];
}
}
self.window.landscapeViewController.targetRect = targetRect;
self.window.landscapeViewController.contentView = self.view;
self.window.landscapeViewController.containerView = self.containerView;
self.fullScreen = YES;
}
if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
} else {
self.fullScreen = NO;
}
self.window.landscapeViewController.disableAnimations = !animated;
@zf_weakify(self)
self.window.landscapeViewController.rotatingCompleted = ^{
@zf_strongify(self)
self.forceRotaion = NO;
if (completion) completion();
};
[self interfaceOrientation:UIInterfaceOrientationUnknown];
[self interfaceOrientation:orientation];
}
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
[self enterPortraitFullScreen:fullScreen animated:animated completion:nil];
}
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion {
self.fullScreen = fullScreen;
if (fullScreen) {
self.portraitViewController.contentView = self.view;
self.portraitViewController.containerView = self.containerView;
self.portraitViewController.duration = self.duration;
if (self.portraitFullScreenMode == ZFPortraitFullScreenModeScaleAspectFit) {
self.portraitViewController.presentationSize = self.presentationSize;
} else if (self.portraitFullScreenMode == ZFPortraitFullScreenModeScaleToFill) {
self.portraitViewController.presentationSize = CGSizeMake(ZFPlayerScreenWidth, ZFPlayerScreenHeight);
}
self.portraitViewController.fullScreenAnimation = animated;
[[UIWindow zf_currentViewController] presentViewController:self.portraitViewController animated:animated completion:^{
if (completion) completion();
}];
} else {
self.portraitViewController.fullScreenAnimation = animated;
[self.portraitViewController dismissViewControllerAnimated:animated completion:^{
if (completion) completion();
}];
}
}
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
[self enterFullScreen:fullScreen animated:animated];
}
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion {
if (self.fullScreenMode == ZFFullScreenModePortrait) {
[self enterPortraitFullScreen:fullScreen animated:animated completion:completion];
} else {
UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
orientation = fullScreen? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait;
[self rotateToOrientation:orientation animated:animated completion:completion];
}
}
#pragma mark - private
/// is support portrait
- (BOOL)_isSupportedPortrait {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait;
}
/// is support landscapeLeft
- (BOOL)_isSupportedLandscapeLeft {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeLeft;
}
/// is support landscapeRight
- (BOOL)_isSupportedLandscapeRight {
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeRight;
}
- (BOOL)_isSupported:(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationPortrait:
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait;
case UIInterfaceOrientationLandscapeLeft:
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeRight;
default:
return NO;
}
return NO;
}
- (void)_rotationToLandscapeOrientation:(UIInterfaceOrientation)orientation {
if (UIInterfaceOrientationIsLandscape(orientation)) {
UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow;
if (keyWindow != self.window && self.previousKeyWindow != keyWindow) {
self.previousKeyWindow = UIApplication.sharedApplication.keyWindow;
}
if (!self.window.isKeyWindow) {
self.window.hidden = NO;
[self.window makeKeyAndVisible];
}
}
}
- (void)_rotationToPortraitOrientation:(UIInterfaceOrientation)orientation {
if (orientation == UIInterfaceOrientationPortrait && !self.window.hidden) {
UIView *containerView = nil;
if (self.rotateType == ZFRotateTypeCell) {
containerView = [self.cell viewWithTag:self.playerViewTag];
} else {
containerView = self.containerView;
}
UIView *snapshot = [self.view snapshotViewAfterScreenUpdates:NO];
snapshot.frame = containerView.bounds;
[containerView addSubview:snapshot];
[self performSelector:@selector(_contentViewAdd:) onThread:NSThread.mainThread withObject:containerView waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
[self performSelector:@selector(_makeKeyAndVisible:) onThread:NSThread.mainThread withObject:snapshot waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
}
}
- (void)_contentViewAdd:(UIView *)containerView {
[containerView addSubview:self.view];
self.view.frame = containerView.bounds;
[self.view layoutIfNeeded];
}
- (void)_makeKeyAndVisible:(UIView *)snapshot {
[snapshot removeFromSuperview];
UIWindow *previousKeyWindow = self.previousKeyWindow ?: UIApplication.sharedApplication.windows.firstObject;
[previousKeyWindow makeKeyAndVisible];
self.previousKeyWindow = nil;
self.window.hidden = YES;
}
#pragma mark - ZFLandscapeViewControllerDelegate
- (BOOL)ls_shouldAutorotate {
if (self.fullScreenMode == ZFFullScreenModePortrait) {
return NO;
}
UIInterfaceOrientation currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
if (![self _isSupported:currentOrientation]) {
return NO;
}
if (self.forceRotaion) {
[self _rotationToLandscapeOrientation:currentOrientation];
return YES;
}
if (!self.activeDeviceObserver) {
return NO;
}
[self _rotationToLandscapeOrientation:currentOrientation];
return YES;
}
- (void)ls_willRotateToOrientation:(UIInterfaceOrientation)orientation {
self.fullScreen = UIInterfaceOrientationIsLandscape(orientation);
if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
}
- (void)ls_didRotateFromOrientation:(UIInterfaceOrientation)orientation {
if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
if (!self.isFullScreen) {
[self _rotationToPortraitOrientation:UIInterfaceOrientationPortrait];
}
}
- (CGRect)ls_targetRect {
UIView *containerView = nil;
if (self.rotateType == ZFRotateTypeCell) {
containerView = [self.cell viewWithTag:self.playerViewTag];
} else {
containerView = self.containerView;
}
CGRect targetRect = [containerView convertRect:containerView.bounds toView:containerView.window];
return targetRect;
}
#pragma mark - getter
- (ZFPortraitViewController *)portraitViewController {
if (!_portraitViewController) {
@zf_weakify(self)
_portraitViewController = [[ZFPortraitViewController alloc] init];
if (@available(iOS 9.0, *)) {
[_portraitViewController loadViewIfNeeded];
} else {
[_portraitViewController view];
}
_portraitViewController.orientationWillChange = ^(BOOL isFullScreen) {
@zf_strongify(self)
self.fullScreen = isFullScreen;
if (self.orientationWillChange) self.orientationWillChange(self, isFullScreen);
};
_portraitViewController.orientationDidChanged = ^(BOOL isFullScreen) {
@zf_strongify(self)
self.fullScreen = isFullScreen;
if (self.orientationDidChanged) self.orientationDidChanged(self, isFullScreen);
};
}
return _portraitViewController;
}
#pragma mark - setter
- (void)setLockedScreen:(BOOL)lockedScreen {
_lockedScreen = lockedScreen;
if (lockedScreen) {
[self removeDeviceOrientationObserver];
} else {
[self addDeviceOrientationObserver];
}
}
- (UIView *)fullScreenContainerView {
if (self.fullScreenMode == ZFFullScreenModeLandscape) {
return self.window.landscapeViewController.view;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
return self.portraitViewController.view;
}
return nil;
}
- (void)setFullScreen:(BOOL)fullScreen {
_fullScreen = fullScreen;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
[UIViewController attemptRotationToDeviceOrientation];
}
- (void)setFullScreenStatusBarHidden:(BOOL)fullScreenStatusBarHidden {
_fullScreenStatusBarHidden = fullScreenStatusBarHidden;
if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.statusBarHidden = fullScreenStatusBarHidden;
[self.portraitViewController setNeedsStatusBarAppearanceUpdate];
} else if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.statusBarHidden = fullScreenStatusBarHidden;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
}
}
- (void)setFullScreenStatusBarStyle:(UIStatusBarStyle)fullScreenStatusBarStyle {
_fullScreenStatusBarStyle = fullScreenStatusBarStyle;
if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.statusBarStyle = fullScreenStatusBarStyle;
[self.portraitViewController setNeedsStatusBarAppearanceUpdate];
} else if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.statusBarStyle = fullScreenStatusBarStyle;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
}
}
- (void)setFullScreenStatusBarAnimation:(UIStatusBarAnimation)fullScreenStatusBarAnimation {
_fullScreenStatusBarAnimation = fullScreenStatusBarAnimation;
if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.statusBarAnimation = fullScreenStatusBarAnimation;
[self.portraitViewController setNeedsStatusBarAppearanceUpdate];
} else if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.statusBarAnimation = fullScreenStatusBarAnimation;
[self.window.landscapeViewController setNeedsStatusBarAppearanceUpdate];
}
}
- (void)setDisablePortraitGestureTypes:(ZFDisablePortraitGestureTypes)disablePortraitGestureTypes {
_disablePortraitGestureTypes = disablePortraitGestureTypes;
self.portraitViewController.disablePortraitGestureTypes = disablePortraitGestureTypes;
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
if (self.fullScreenMode == ZFFullScreenModePortrait && self.portraitFullScreenMode == ZFPortraitFullScreenModeScaleAspectFit) {
self.portraitViewController.presentationSize = presentationSize;
}
}
- (void)setView:(ZFPlayerView *)view {
if (view == _view) {
return;
}
_view = view;
if (self.fullScreenMode == ZFFullScreenModeLandscape && self.window) {
self.window.landscapeViewController.contentView = view;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.contentView = view;
}
}
- (void)setContainerView:(UIView *)containerView {
if (containerView == _containerView) {
return;
}
_containerView = containerView;
if (self.fullScreenMode == ZFFullScreenModeLandscape) {
self.window.landscapeViewController.containerView = containerView;
} else if (self.fullScreenMode == ZFFullScreenModePortrait) {
self.portraitViewController.containerView = containerView;
}
}
- (void)setAllowOrientationRotation:(BOOL)allowOrientationRotation {
_allowOrientationRotation = allowOrientationRotation;
if (allowOrientationRotation) {
[self addDeviceOrientationObserver];
} else {
[self removeDeviceOrientationObserver];
}
}
@end
//
// 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
//
// 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
//
// 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"
//
// 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
//
// ZFPlayerController.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "ZFPlayerMediaPlayback.h"
#import "ZFOrientationObserver.h"
#import "ZFPlayerMediaControl.h"
#import "ZFPlayerGestureControl.h"
#import "ZFPlayerNotification.h"
#import "ZFFloatView.h"
#import "UIScrollView+ZFPlayer.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFPlayerController : NSObject
/// The video contrainerView in normal model.
@property (nonatomic, strong) UIView *containerView;
/// The currentPlayerManager must conform `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, strong) id<ZFPlayerMediaPlayback> currentPlayerManager;
/// The custom controlView must conform `ZFPlayerMediaControl` protocol.
@property (nonatomic, strong) UIView<ZFPlayerMediaControl> *controlView;
/// The notification manager class.
@property (nonatomic, strong, readonly) ZFPlayerNotification *notification;
/// The container view type.
@property (nonatomic, assign, readonly) ZFPlayerContainerType containerType;
/// The player's small container view.
@property (nonatomic, strong, readonly) ZFFloatView *smallFloatView;
/// Whether the small window is displayed.
@property (nonatomic, assign, readonly) BOOL isSmallFloatViewShow;
/*!
@method playerWithPlayerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video frames must set the contrainerView.
@result An instance of ZFPlayerController.
*/
+ (instancetype)playerWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
/*!
@method initWithPlayerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video frames must set the contrainerView.
@result An instance of ZFPlayerController.
*/
- (instancetype)initWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
/*!
@method playerWithScrollView:playerManager:containerViewTag:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UITableView` or `UICollectionView`.
@param scrollView is `tableView` or `collectionView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerViewTag to see the video at scrollView must set the contrainerViewTag.
@result An instance of ZFPlayerController.
*/
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag;
/*!
@method initWithScrollView:playerManager:containerViewTag:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UITableView` or `UICollectionView`.
@param scrollView is `tableView` or `collectionView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerViewTag to see the video at scrollView must set the contrainerViewTag.
@result An instance of ZFPlayerController.
*/
- (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag;
/*!
@method playerWithScrollView:playerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UIScrollView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video at the scrollView.
@result An instance of ZFPlayerController.
*/
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
/*!
@method initWithScrollView:playerManager:containerView:
@abstract Create an ZFPlayerController that plays a single audiovisual item. Use in `UIScrollView`.
@param playerManager must conform `ZFPlayerMediaPlayback` protocol.
@param containerView to see the video at the scrollView.
@result An instance of ZFPlayerController.
*/
- (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;
@end
@interface ZFPlayerController (ZFPlayerTimeControl)
/// The player current play time.
@property (nonatomic, readonly) NSTimeInterval currentTime;
/// The player total time.
@property (nonatomic, readonly) NSTimeInterval totalTime;
/// The player buffer time.
@property (nonatomic, readonly) NSTimeInterval bufferTime;
/// The player progress, 0...1
@property (nonatomic, readonly) float progress;
/// The player bufferProgress, 0...1
@property (nonatomic, readonly) float bufferProgress;
/**
Use this method to seek to a specified time for the current player and to be notified when the seek operation is complete.
@param time seek time.
@param completionHandler completion handler.
*/
- (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
@end
@interface ZFPlayerController (ZFPlayerPlaybackControl)
/// Resume playback record.default is NO.
/// Memory storage playback records.
@property (nonatomic, assign) BOOL resumePlayRecord;
/// 0...1.0
/// Only affects audio volume for the device instance and not for the player.
/// You can change device volume or player volume as needed,change the player volume you can conform the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic) float volume;
/// The device muted.
/// Only affects audio muting for the device instance and not for the player.
/// You can change device mute or player mute as needed,change the player mute you can conform the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, getter=isMuted) BOOL muted;
// 0...1.0, where 1.0 is maximum brightness. Only supported by main screen.
@property (nonatomic) float brightness;
/// The play asset URL.
@property (nonatomic) NSURL *assetURL;
/// If tableView or collectionView has only one section , use `assetURLs`.
/// If tableView or collectionView has more sections , use `sectionAssetURLs`.
/// Set this you can use `playTheNext` `playThePrevious` `playTheIndex:` method.
@property (nonatomic, copy, nullable) NSArray <NSURL *>*assetURLs;
/// The currently playing index,limited to one-dimensional arrays.
@property (nonatomic) NSInteger currentPlayIndex;
/// is the last asset URL in `assetURLs`.
@property (nonatomic, readonly) BOOL isLastAssetURL;
/// is the first asset URL in `assetURLs`.
@property (nonatomic, readonly) BOOL isFirstAssetURL;
/// If Yes, player will be called pause method When Received `UIApplicationWillResignActiveNotification` notification.
/// default is YES.
@property (nonatomic) BOOL pauseWhenAppResignActive;
/// When the player is playing, it is paused by some event,not by user click to pause.
/// For example, when the player is playing, application goes into the background or pushed to another viewController
@property (nonatomic, getter=isPauseByEvent) BOOL pauseByEvent;
/// The current player controller is disappear, not dealloc
@property (nonatomic, getter=isViewControllerDisappear) BOOL viewControllerDisappear;
/// You can custom the AVAudioSession,
/// default is NO.
@property (nonatomic, assign) BOOL customAudioSession;
/// The block invoked when the player is Prepare to play.
@property (nonatomic, copy, nullable) void(^playerPrepareToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player is Ready to play.
@property (nonatomic, copy, nullable) void(^playerReadyToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player play progress changed.
@property (nonatomic, copy, nullable) void(^playerPlayTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval currentTime, NSTimeInterval duration);
/// The block invoked when the player play buffer changed.
@property (nonatomic, copy, nullable) void(^playerBufferTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval bufferTime);
/// The block invoked when the player playback state changed.
@property (nonatomic, copy, nullable) void(^playerPlayStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerPlaybackState playState);
/// The block invoked when the player load state changed.
@property (nonatomic, copy, nullable) void(^playerLoadStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerLoadState loadState);
/// The block invoked when the player play failed.
@property (nonatomic, copy, nullable) void(^playerPlayFailed)(id<ZFPlayerMediaPlayback> asset, id error);
/// The block invoked when the player play end.
@property (nonatomic, copy, nullable) void(^playerDidToEnd)(id<ZFPlayerMediaPlayback> asset);
// The block invoked when video size changed.
@property (nonatomic, copy, nullable) void(^presentationSizeChanged)(id<ZFPlayerMediaPlayback> asset, CGSize size);
/**
Play the next url ,while the `assetURLs` is not NULL.
*/
- (void)playTheNext;
/**
Play the previous url ,while the `assetURLs` is not NULL.
*/
- (void)playThePrevious;
/**
Play the index of url ,while the `assetURLs` is not NULL.
@param index play the index.
*/
- (void)playTheIndex:(NSInteger)index;
/**
Player stop and playerView remove from super view,remove other notification.
*/
- (void)stop;
/*!
@method replaceCurrentPlayerManager:
@abstract Replaces the player's current playeranager with the specified player item.
@param manager must conform `ZFPlayerMediaPlayback` protocol
@discussion The playerManager that will become the player's current playeranager.
*/
- (void)replaceCurrentPlayerManager:(id<ZFPlayerMediaPlayback>)manager;
/**
Add video to cell.
*/
- (void)addPlayerViewToCell;
/**
Add video to container view.
*/
- (void)addPlayerViewToContainerView:(UIView *)containerView;
/**
Add to small float view.
*/
- (void)addPlayerViewToSmallFloatView;
/**
Stop the current playing video and remove the playerView.
*/
- (void)stopCurrentPlayingView;
/**
stop the current playing video on cell.
*/
- (void)stopCurrentPlayingCell;
@end
@interface ZFPlayerController (ZFPlayerOrientationRotation)
@property (nonatomic, readonly) ZFOrientationObserver *orientationObserver;
/// Whether automatic screen rotation is supported.
/// The value is NO.
/// This property is used for the return value of UIViewController `shouldAutorotate` method.
@property (nonatomic, readonly) BOOL shouldAutorotate;
/// Whether allow the video orientation rotate.
/// default is YES.
@property (nonatomic) BOOL allowOrentitaionRotation;
/// When ZFFullScreenMode is ZFFullScreenModeLandscape the orientation is LandscapeLeft or LandscapeRight, this value is YES.
/// When ZFFullScreenMode is ZFFullScreenModePortrait, while the player fullSceen this value is YES.
@property (nonatomic, readonly) BOOL isFullScreen;
/// when call the `stop` method, exit the fullScreen model, default is YES.
@property (nonatomic, assign) BOOL exitFullScreenWhenStop;
/// Lock the screen orientation.
@property (nonatomic, getter=isLockedScreen) BOOL lockedScreen;
/// The current orientation of the player.
/// Default is UIInterfaceOrientationPortrait.
@property (nonatomic, readonly) UIInterfaceOrientation currentOrientation;
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(ZFPlayerController *player, BOOL isFullScreen);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(ZFPlayerController *player, BOOL isFullScreen);
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle fullScreenStatusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation fullScreenStatusBarAnimation;
/// The fullscreen statusbar hidden.
@property (nonatomic, getter=isStatusBarHidden) BOOL statusBarHidden;
/**
Add the device orientation observer.
*/
- (void)addDeviceOrientationObserver;
/**
Remove the device orientation observer.
*/
- (void)removeDeviceOrientationObserver;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
@param orientation is UIInterfaceOrientation.
@param animated is animated.
*/
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
@param orientation is UIInterfaceOrientation.
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
@param fullScreen is fullscreen.
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/**
Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModePortrait.
@param fullScreen is fullscreen.
@param animated is animated.
*/
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
/**
FullScreen mode is determined by ZFFullScreenMode.
@param fullScreen is fullscreen.
@param animated is animated.
@param completion rotating completed callback.
*/
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void(^ __nullable)(void))completion;
/**
FullScreen mode is determined by ZFFullScreenMode.
@param fullScreen is fullscreen.
@param animated is animated.
*/
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated;
@end
@interface ZFPlayerController (ZFPlayerViewGesture)
/// An instance of ZFPlayerGestureControl.
@property (nonatomic, readonly) ZFPlayerGestureControl *gestureControl;
/// The gesture types that the player not support.
@property (nonatomic, assign) ZFPlayerDisableGestureTypes disableGestureTypes;
/// The pan gesture moving direction that the player not support.
@property (nonatomic) ZFPlayerDisablePanMovingDirection disablePanMovingDirection;
@end
@interface ZFPlayerController (ZFPlayerScrollView)
/// The scroll view is `tableView` or `collectionView`.
@property (nonatomic, readonly, nullable) UIScrollView *scrollView;
/// The scrollView player should auto player, default is YES.
@property (nonatomic) BOOL shouldAutoPlay;
/// WWAN network auto play, only support in scrollView mode when the `shouldAutoPlay` is YES, default is NO.
@property (nonatomic, getter=isWWANAutoPlay) BOOL WWANAutoPlay;
/// The indexPath is playing.
@property (nonatomic, readonly, nullable) NSIndexPath *playingIndexPath;
/// The indexPath should be play while scrolling.
@property (nonatomic, readonly, nullable) NSIndexPath *shouldPlayIndexPath;
/// The view tag that the player display in scrollView.
@property (nonatomic, readonly) NSInteger containerViewTag;
/// The current playing cell stop playing when the cell has out off the screen,defalut 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
//
// ZFPlayerController.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 <objc/runtime.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>
#import "UIScrollView+ZFPlayer.h"
#import "ZFReachabilityManager.h"
#import "ZFPlayerConst.h"
static NSMutableDictionary <NSString* ,NSNumber *> *_zfPlayRecords;
@interface ZFPlayerController ()
@property (nonatomic, strong) ZFPlayerNotification *notification;
@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, strong) UISlider *volumeViewSlider;
@property (nonatomic, assign) NSInteger containerViewTag;
@property (nonatomic, assign) ZFPlayerContainerType containerType;
/// The player's small container view.
@property (nonatomic, strong) ZFFloatView *smallFloatView;
/// Whether the small window is displayed.
@property (nonatomic, assign) BOOL isSmallFloatViewShow;
/// The indexPath is playing.
@property (nonatomic, nullable) NSIndexPath *playingIndexPath;
@end
@implementation ZFPlayerController
@dynamic scrollView;
@dynamic containerViewTag;
@dynamic playingIndexPath;
- (instancetype)init {
self = [super init];
if (self) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_zfPlayRecords = @{}.mutableCopy;
});
@zf_weakify(self)
[[ZFReachabilityManager sharedManager] startMonitoring];
[[ZFReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(ZFReachabilityStatus status) {
@zf_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;
}
}
}
- (void)dealloc {
[self.currentPlayerManager stop];
}
+ (instancetype)playerWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(nonnull UIView *)containerView {
ZFPlayerController *player = [[self alloc] initWithPlayerManager:playerManager containerView:containerView];
return player;
}
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag {
ZFPlayerController *player = [[self alloc] initWithScrollView:scrollView playerManager:playerManager containerViewTag:containerViewTag];
return player;
}
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView {
ZFPlayerController *player = [[self alloc] initWithScrollView:scrollView playerManager:playerManager containerView:containerView];
return player;
}
- (instancetype)initWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(nonnull UIView *)containerView {
ZFPlayerController *player = [self init];
player.containerView = containerView;
player.currentPlayerManager = playerManager;
player.containerType = ZFPlayerContainerTypeView;
return player;
}
- (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag {
ZFPlayerController *player = [self init];
player.scrollView = scrollView;
player.containerViewTag = containerViewTag;
player.currentPlayerManager = playerManager;
player.containerType = ZFPlayerContainerTypeCell;
return player;
}
- (instancetype)initWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView {
ZFPlayerController *player = [self init];
player.scrollView = scrollView;
player.containerView = containerView;
player.currentPlayerManager = playerManager;
player.containerType = ZFPlayerContainerTypeView;
return player;
}
- (void)playerManagerCallbcak {
@zf_weakify(self)
self.currentPlayerManager.playerPrepareToPlay = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, NSURL * _Nonnull assetURL) {
@zf_strongify(self)
if (self.resumePlayRecord && [_zfPlayRecords valueForKey:assetURL.absoluteString]) {
NSTimeInterval seekTime = [_zfPlayRecords valueForKey:assetURL.absoluteString].doubleValue;
self.currentPlayerManager.seekTime = seekTime;
}
[self.notification addNotification];
[self addDeviceOrientationObserver];
if (self.scrollView) {
self.scrollView.zf_stopPlay = NO;
}
[self layoutPlayerSubViews];
if (self.playerPrepareToPlay) self.playerPrepareToPlay(asset,assetURL);
if ([self.controlView respondsToSelector:@selector(videoPlayer:prepareToPlay:)]) {
[self.controlView videoPlayer:self prepareToPlay:assetURL];
}
};
self.currentPlayerManager.playerReadyToPlay = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, NSURL * _Nonnull assetURL) {
@zf_strongify(self)
if (self.playerReadyToPlay) self.playerReadyToPlay(asset,assetURL);
if (!self.customAudioSession) {
// 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 withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
}
if (self.viewControllerDisappear) self.pauseByEvent = YES;
};
self.currentPlayerManager.playerPlayTimeChanged = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, NSTimeInterval currentTime, NSTimeInterval duration) {
@zf_strongify(self)
if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(asset,currentTime,duration);
if ([self.controlView respondsToSelector:@selector(videoPlayer:currentTime:totalTime:)]) {
[self.controlView videoPlayer:self currentTime:currentTime totalTime:duration];
}
if (self.currentPlayerManager.assetURL.absoluteString) {
[_zfPlayRecords setValue:@(currentTime) forKey:self.currentPlayerManager.assetURL.absoluteString];
}
};
self.currentPlayerManager.playerBufferTimeChanged = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, NSTimeInterval bufferTime) {
@zf_strongify(self)
if ([self.controlView respondsToSelector:@selector(videoPlayer:bufferTime:)]) {
[self.controlView videoPlayer:self bufferTime:bufferTime];
}
if (self.playerBufferTimeChanged) self.playerBufferTimeChanged(asset,bufferTime);
};
self.currentPlayerManager.playerPlayStateChanged = ^(id _Nonnull asset, ZFPlayerPlaybackState playState) {
@zf_strongify(self)
if (self.playerPlayStateChanged) self.playerPlayStateChanged(asset, playState);
if ([self.controlView respondsToSelector:@selector(videoPlayer:playStateChanged:)]) {
[self.controlView videoPlayer:self playStateChanged:playState];
}
};
self.currentPlayerManager.playerLoadStateChanged = ^(id _Nonnull asset, ZFPlayerLoadState loadState) {
@zf_strongify(self)
if (loadState == ZFPlayerLoadStatePrepare && CGSizeEqualToSize(CGSizeZero, self.currentPlayerManager.presentationSize)) {
CGSize size = self.currentPlayerManager.view.frame.size;
self.orientationObserver.presentationSize = size;
}
if (self.playerLoadStateChanged) self.playerLoadStateChanged(asset, loadState);
if ([self.controlView respondsToSelector:@selector(videoPlayer:loadStateChanged:)]) {
[self.controlView videoPlayer:self loadStateChanged:loadState];
}
};
self.currentPlayerManager.playerDidToEnd = ^(id _Nonnull asset) {
@zf_strongify(self)
if (self.currentPlayerManager.assetURL.absoluteString) {
[_zfPlayRecords setValue:@(0) forKey:self.currentPlayerManager.assetURL.absoluteString];
}
if (self.playerDidToEnd) self.playerDidToEnd(asset);
if ([self.controlView respondsToSelector:@selector(videoPlayerPlayEnd:)]) {
[self.controlView videoPlayerPlayEnd:self];
}
};
self.currentPlayerManager.playerPlayFailed = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, id _Nonnull error) {
@zf_strongify(self)
if (self.playerPlayFailed) self.playerPlayFailed(asset, error);
if ([self.controlView respondsToSelector:@selector(videoPlayerPlayFailed:error:)]) {
[self.controlView videoPlayerPlayFailed:self error:error];
}
};
self.currentPlayerManager.presentationSizeChanged = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, CGSize size){
@zf_strongify(self)
self.orientationObserver.presentationSize = size;
if (self.orientationObserver.fullScreenMode == ZFFullScreenModeAutomatic) {
if (size.width > size.height) {
self.orientationObserver.fullScreenMode = ZFFullScreenModeLandscape;
} else {
self.orientationObserver.fullScreenMode = ZFFullScreenModePortrait;
}
}
if (self.presentationSizeChanged) self.presentationSizeChanged(asset, size);
if ([self.controlView respondsToSelector:@selector(videoPlayer:presentationSizeChanged:)]) {
[self.controlView videoPlayer:self presentationSizeChanged:size];
}
};
}
- (void)layoutPlayerSubViews {
if (self.containerView && self.currentPlayerManager.view && self.currentPlayerManager.isPreparedToPlay) {
UIView *superview = nil;
if (self.isFullScreen) {
superview = self.orientationObserver.fullScreenContainerView;
} else if (self.containerView) {
superview = self.containerView;
}
[superview addSubview:self.currentPlayerManager.view];
[self.currentPlayerManager.view addSubview:self.controlView];
self.currentPlayerManager.view.frame = superview.bounds;
self.currentPlayerManager.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.controlView.frame = self.currentPlayerManager.view.bounds;
self.controlView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.orientationObserver updateRotateView:self.currentPlayerManager.view containerView:self.containerView];
}
}
#pragma mark - getter
- (ZFPlayerNotification *)notification {
if (!_notification) {
_notification = [[ZFPlayerNotification alloc] init];
@zf_weakify(self)
_notification.willResignActive = ^(ZFPlayerNotification * _Nonnull registrar) {
@zf_strongify(self)
if (self.isViewControllerDisappear) return;
if (self.pauseWhenAppResignActive && self.currentPlayerManager.isPlaying) {
self.pauseByEvent = YES;
}
self.orientationObserver.lockedScreen = YES;
[[UIApplication sharedApplication].keyWindow endEditing:YES];
if (!self.pauseWhenAppResignActive) {
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
}
};
_notification.didBecomeActive = ^(ZFPlayerNotification * _Nonnull registrar) {
@zf_strongify(self)
if (self.isViewControllerDisappear) return;
if (self.isPauseByEvent) self.pauseByEvent = NO;
self.orientationObserver.lockedScreen = NO;
};
_notification.oldDeviceUnavailable = ^(ZFPlayerNotification * _Nonnull registrar) {
@zf_strongify(self)
if (self.currentPlayerManager.isPlaying) {
[self.currentPlayerManager play];
}
};
}
return _notification;
}
- (ZFFloatView *)smallFloatView {
if (!_smallFloatView) {
_smallFloatView = [[ZFFloatView alloc] init];
_smallFloatView.parentView = [UIApplication sharedApplication].keyWindow;
_smallFloatView.hidden = YES;
}
return _smallFloatView;
}
#pragma mark - setter
- (void)setCurrentPlayerManager:(id<ZFPlayerMediaPlayback>)currentPlayerManager {
if (!currentPlayerManager) return;
if (_currentPlayerManager.isPreparedToPlay) {
[_currentPlayerManager stop];
[_currentPlayerManager.view removeFromSuperview];
[self removeDeviceOrientationObserver];
[self.gestureControl removeGestureToView:self.currentPlayerManager.view];
}
_currentPlayerManager = currentPlayerManager;
self.gestureControl.disableTypes = self.disableGestureTypes;
[self.gestureControl addGestureToView:currentPlayerManager.view];
[self playerManagerCallbcak];
self.controlView.player = self;
[self layoutPlayerSubViews];
if (currentPlayerManager.isPreparedToPlay) {
[self addDeviceOrientationObserver];
}
[self.orientationObserver updateRotateView:currentPlayerManager.view containerView:self.containerView];
}
- (void)setContainerView:(UIView *)containerView {
_containerView = containerView;
if (self.scrollView) {
self.scrollView.zf_containerView = containerView;
}
if (!containerView) return;
containerView.userInteractionEnabled = YES;
[self layoutPlayerSubViews];
[self.orientationObserver updateRotateView:self.currentPlayerManager.view containerView:containerView];
}
- (void)setControlView:(UIView<ZFPlayerMediaControl> *)controlView {
if (controlView && controlView != _controlView) {
[_controlView removeFromSuperview];
}
_controlView = controlView;
if (!controlView) return;
controlView.player = self;
[self layoutPlayerSubViews];
}
- (void)setContainerType:(ZFPlayerContainerType)containerType {
_containerType = containerType;
if (self.scrollView) {
self.scrollView.zf_containerType = containerType;
}
}
@end
@implementation ZFPlayerController (ZFPlayerTimeControl)
- (NSTimeInterval)currentTime {
return self.currentPlayerManager.currentTime;
}
- (NSTimeInterval)totalTime {
return self.currentPlayerManager.totalTime;
}
- (NSTimeInterval)bufferTime {
return self.currentPlayerManager.bufferTime;
}
- (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)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;
}
}
- (void)stop {
if (self.isFullScreen && self.exitFullScreenWhenStop) {
@zf_weakify(self)
[self.orientationObserver enterFullScreen:NO animated:NO completion:^{
@zf_strongify(self)
[self.currentPlayerManager stop];
[self.currentPlayerManager.view removeFromSuperview];
}];
} else {
[self.currentPlayerManager stop];
[self.currentPlayerManager.view removeFromSuperview];
}
if (self.scrollView) self.scrollView.zf_stopPlay = YES;
[self.notification removeNotification];
[self.orientationObserver removeDeviceOrientationObserver];
}
- (void)replaceCurrentPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager {
self.currentPlayerManager = playerManager;
}
//// 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 updateRotateView:self.currentPlayerManager.view rotateViewAtCell:cell playerViewTag:self.containerViewTag];
if ([self.controlView respondsToSelector:@selector(videoPlayer:floatViewShow:)]) {
[self.controlView videoPlayer:self floatViewShow:NO];
}
}
//// Add video to the container view
- (void)addPlayerViewToContainerView:(UIView *)containerView {
self.isSmallFloatViewShow = NO;
self.smallFloatView.hidden = YES;
self.containerView = containerView;
[self.containerView addSubview:self.currentPlayerManager.view];
self.currentPlayerManager.view.frame = self.containerView.bounds;
self.currentPlayerManager.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.orientationObserver updateRotateView:self.currentPlayerManager.view containerView:self.containerView];
if ([self.controlView respondsToSelector:@selector(videoPlayer:floatViewShow:)]) {
[self.controlView videoPlayer:self floatViewShow:NO];
}
}
- (void)addPlayerViewToSmallFloatView {
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 updateRotateView:self.currentPlayerManager.view containerView:self.smallFloatView];
if ([self.controlView respondsToSelector:@selector(videoPlayer:floatViewShow:)]) {
[self.controlView videoPlayer:self floatViewShow:YES];
}
}
- (void)stopCurrentPlayingView {
if (self.containerView) {
[self stop];
self.isSmallFloatViewShow = NO;
if (self.smallFloatView) self.smallFloatView.hidden = YES;
}
}
- (void)stopCurrentPlayingCell {
if (self.scrollView.zf_playingIndexPath) {
[self stop];
self.isSmallFloatViewShow = NO;
self.playingIndexPath = nil;
if (self.smallFloatView) self.smallFloatView.hidden = YES;
}
}
#pragma mark - getter
- (BOOL)resumePlayRecord {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (NSURL *)assetURL {
return objc_getAssociatedObject(self, _cmd);
}
- (NSArray<NSURL *> *)assetURLs {
return objc_getAssociatedObject(self, _cmd);
}
- (BOOL)isLastAssetURL {
if (self.assetURLs.count > 0) {
return [self.assetURL isEqual:self.assetURLs.lastObject];
}
return NO;
}
- (BOOL)isFirstAssetURL {
if (self.assetURLs.count > 0) {
return [self.assetURL isEqual:self.assetURLs.firstObject];
}
return NO;
}
- (BOOL)isPauseByEvent {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (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<ZFPlayerMediaPlayback> _Nonnull, NSURL * _Nonnull))playerPrepareToPlay {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSURL * _Nonnull))playerReadyToPlay {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSTimeInterval, NSTimeInterval))playerPlayTimeChanged {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSTimeInterval))playerBufferTimeChanged {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, ZFPlayerPlaybackState))playerPlayStateChanged {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, ZFPlayerLoadState))playerLoadStateChanged {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull))playerDidToEnd {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, id _Nonnull))playerPlayFailed {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(id<ZFPlayerMediaPlayback> _Nonnull, CGSize ))presentationSizeChanged {
return objc_getAssociatedObject(self, _cmd);
}
- (NSInteger)currentPlayIndex {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
- (BOOL)isViewControllerDisappear {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (BOOL)customAudioSession {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
#pragma mark - setter
- (void)setResumePlayRecord:(BOOL)resumePlayRecord {
objc_setAssociatedObject(self, @selector(resumePlayRecord), @(resumePlayRecord), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setAssetURL:(NSURL *)assetURL {
objc_setAssociatedObject(self, @selector(assetURL), assetURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.currentPlayerManager.assetURL = assetURL;
}
- (void)setAssetURLs:(NSArray<NSURL *> * _Nullable)assetURLs {
objc_setAssociatedObject(self, @selector(assetURLs), assetURLs, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (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) {
if (self.volumeViewSlider.value > 0) {
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)setPauseByEvent:(BOOL)pauseByEvent {
objc_setAssociatedObject(self, @selector(isPauseByEvent), @(pauseByEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (pauseByEvent) {
[self.currentPlayerManager pause];
} else {
[self.currentPlayerManager play];
}
}
- (void)setPauseWhenAppResignActive:(BOOL)pauseWhenAppResignActive {
objc_setAssociatedObject(self, @selector(pauseWhenAppResignActive), @(pauseWhenAppResignActive), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setPlayerPrepareToPlay:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSURL * _Nonnull))playerPrepareToPlay {
objc_setAssociatedObject(self, @selector(playerPrepareToPlay), playerPrepareToPlay, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerReadyToPlay:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSURL * _Nonnull))playerReadyToPlay {
objc_setAssociatedObject(self, @selector(playerReadyToPlay), playerReadyToPlay, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerPlayTimeChanged:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSTimeInterval, NSTimeInterval))playerPlayTimeChanged {
objc_setAssociatedObject(self, @selector(playerPlayTimeChanged), playerPlayTimeChanged, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerBufferTimeChanged:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, NSTimeInterval))playerBufferTimeChanged {
objc_setAssociatedObject(self, @selector(playerBufferTimeChanged), playerBufferTimeChanged, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerPlayStateChanged:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, ZFPlayerPlaybackState))playerPlayStateChanged {
objc_setAssociatedObject(self, @selector(playerPlayStateChanged), playerPlayStateChanged, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerLoadStateChanged:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, ZFPlayerLoadState))playerLoadStateChanged {
objc_setAssociatedObject(self, @selector(playerLoadStateChanged), playerLoadStateChanged, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerDidToEnd:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull))playerDidToEnd {
objc_setAssociatedObject(self, @selector(playerDidToEnd), playerDidToEnd, OBJC_ASSOCIATION_COPY);
}
- (void)setPlayerPlayFailed:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, id _Nonnull))playerPlayFailed {
objc_setAssociatedObject(self, @selector(playerPlayFailed), playerPlayFailed, OBJC_ASSOCIATION_COPY);
}
- (void)setPresentationSizeChanged:(void (^)(id<ZFPlayerMediaPlayback> _Nonnull, CGSize))presentationSizeChanged {
objc_setAssociatedObject(self, @selector(presentationSizeChanged), presentationSizeChanged, OBJC_ASSOCIATION_COPY);
}
- (void)setCurrentPlayIndex:(NSInteger)currentPlayIndex {
objc_setAssociatedObject(self, @selector(currentPlayIndex), @(currentPlayIndex), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setViewControllerDisappear:(BOOL)viewControllerDisappear {
objc_setAssociatedObject(self, @selector(isViewControllerDisappear), @(viewControllerDisappear), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.scrollView) self.scrollView.zf_viewControllerDisappear = viewControllerDisappear;
if (!self.currentPlayerManager.isPreparedToPlay) return;
if (viewControllerDisappear) {
[self removeDeviceOrientationObserver];
if (self.currentPlayerManager.isPlaying) self.pauseByEvent = YES;
if (self.isSmallFloatViewShow) self.smallFloatView.hidden = YES;
} else {
[self addDeviceOrientationObserver];
if (self.isPauseByEvent) self.pauseByEvent = NO;
if (self.isSmallFloatViewShow) self.smallFloatView.hidden = NO;
}
}
- (void)setCustomAudioSession:(BOOL)customAudioSession {
objc_setAssociatedObject(self, @selector(customAudioSession), @(customAudioSession), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@implementation ZFPlayerController (ZFPlayerOrientationRotation)
- (void)addDeviceOrientationObserver {
if (self.allowOrentitaionRotation) {
[self.orientationObserver addDeviceOrientationObserver];
}
}
- (void)removeDeviceOrientationObserver {
[self.orientationObserver removeDeviceOrientationObserver];
}
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
[self rotateToOrientation:orientation animated:animated completion:nil];
}
/// Enter the fullScreen while the ZFFullScreenMode is ZFFullScreenModeLandscape.
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void(^ __nullable)(void))completion {
self.orientationObserver.fullScreenMode = ZFFullScreenModeLandscape;
[self.orientationObserver rotateToOrientation:orientation animated:animated completion:nil];
}
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion {
self.orientationObserver.fullScreenMode = ZFFullScreenModePortrait;
[self.orientationObserver enterPortraitFullScreen:fullScreen animated:animated completion:completion];
}
- (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
[self enterPortraitFullScreen:fullScreen animated:animated completion:nil];
}
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion {
if (self.orientationObserver.fullScreenMode == ZFFullScreenModePortrait) {
[self.orientationObserver enterPortraitFullScreen:fullScreen animated:animated completion:completion];
} else {
UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
orientation = fullScreen? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait;
[self.orientationObserver rotateToOrientation:orientation animated:animated completion:completion];
}
}
- (void)enterFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
[self enterFullScreen:fullScreen animated:animated completion:nil];
}
#pragma mark - getter
- (ZFOrientationObserver *)orientationObserver {
@zf_weakify(self)
ZFOrientationObserver *orientationObserver = objc_getAssociatedObject(self, _cmd);
if (!orientationObserver) {
orientationObserver = [[ZFOrientationObserver alloc] init];
orientationObserver.orientationWillChange = ^(ZFOrientationObserver * _Nonnull observer, BOOL isFullScreen) {
@zf_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) {
@zf_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;
}
- (BOOL)exitFullScreenWhenStop {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.boolValue;
self.exitFullScreenWhenStop = YES;
return YES;
}
- (UIInterfaceOrientation)currentOrientation {
return self.orientationObserver.currentOrientation;
}
- (BOOL)isStatusBarHidden {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (BOOL)isLockedScreen {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (BOOL)shouldAutorotate {
return NO;
}
- (BOOL)allowOrentitaionRotation {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.boolValue;
self.allowOrentitaionRotation = YES;
return YES;
}
- (UIStatusBarStyle)fullScreenStatusBarStyle {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.integerValue;
self.fullScreenStatusBarStyle = UIStatusBarStyleLightContent;
return UIStatusBarStyleLightContent;
}
- (UIStatusBarAnimation)fullScreenStatusBarAnimation {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.integerValue;
self.fullScreenStatusBarAnimation = UIStatusBarAnimationSlide;
return UIStatusBarAnimationSlide;
}
#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.fullScreenStatusBarHidden = 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)setAllowOrentitaionRotation:(BOOL)allowOrentitaionRotation {
objc_setAssociatedObject(self, @selector(allowOrentitaionRotation), @(allowOrentitaionRotation), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.orientationObserver.allowOrientationRotation = allowOrentitaionRotation;
}
- (void)setExitFullScreenWhenStop:(BOOL)exitFullScreenWhenStop {
objc_setAssociatedObject(self, @selector(exitFullScreenWhenStop), @(exitFullScreenWhenStop), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setFullScreenStatusBarStyle:(UIStatusBarStyle)fullScreenStatusBarStyle {
objc_setAssociatedObject(self, @selector(fullScreenStatusBarStyle), @(fullScreenStatusBarStyle), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.orientationObserver.fullScreenStatusBarStyle = fullScreenStatusBarStyle;
}
- (void)setFullScreenStatusBarAnimation:(UIStatusBarAnimation)fullScreenStatusBarAnimation {
objc_setAssociatedObject(self, @selector(fullScreenStatusBarAnimation), @(fullScreenStatusBarAnimation), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.orientationObserver.fullScreenStatusBarAnimation = fullScreenStatusBarAnimation;
}
@end
@implementation ZFPlayerController (ZFPlayerViewGesture)
#pragma mark - getter
- (ZFPlayerGestureControl *)gestureControl {
ZFPlayerGestureControl *gestureControl = objc_getAssociatedObject(self, _cmd);
if (!gestureControl) {
gestureControl = [[ZFPlayerGestureControl alloc] init];
@zf_weakify(self)
gestureControl.triggerCondition = ^BOOL(ZFPlayerGestureControl * _Nonnull control, ZFPlayerGestureType type, UIGestureRecognizer * _Nonnull gesture, UITouch *touch) {
@zf_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) {
@zf_strongify(self)
if ([self.controlView respondsToSelector:@selector(gestureSingleTapped:)]) {
[self.controlView gestureSingleTapped:control];
}
};
gestureControl.doubleTapped = ^(ZFPlayerGestureControl * _Nonnull control) {
@zf_strongify(self)
if ([self.controlView respondsToSelector:@selector(gestureDoubleTapped:)]) {
[self.controlView gestureDoubleTapped:control];
}
};
gestureControl.beganPan = ^(ZFPlayerGestureControl * _Nonnull control, ZFPanDirection direction, ZFPanLocation location) {
@zf_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) {
@zf_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) {
@zf_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) {
@zf_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) integerValue];
}
- (ZFPlayerDisablePanMovingDirection)disablePanMovingDirection {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
#pragma mark - setter
- (void)setDisableGestureTypes:(ZFPlayerDisableGestureTypes)disableGestureTypes {
objc_setAssociatedObject(self, @selector(disableGestureTypes), @(disableGestureTypes), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.gestureControl.disableTypes = disableGestureTypes;
}
- (void)setDisablePanMovingDirection:(ZFPlayerDisablePanMovingDirection)disablePanMovingDirection {
objc_setAssociatedObject(self, @selector(disablePanMovingDirection), @(disablePanMovingDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.gestureControl.disablePanMovingDirection = disablePanMovingDirection;
}
@end
@implementation ZFPlayerController (ZFPlayerScrollView)
+ (void)initialize {
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];
}
#pragma mark - setter
- (void)setScrollView:(UIScrollView *)scrollView {
objc_setAssociatedObject(self, @selector(scrollView), scrollView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.scrollView.zf_WWANAutoPlay = self.isWWANAutoPlay;
@zf_weakify(self)
scrollView.zf_playerWillAppearInScrollView = ^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.isFullScreen) return;
if (self.zf_playerWillAppearInScrollView) self.zf_playerWillAppearInScrollView(indexPath);
if ([self.controlView respondsToSelector:@selector(playerDidAppearInScrollView:)]) {
[self.controlView playerDidAppearInScrollView:self];
}
};
scrollView.zf_playerDidAppearInScrollView = ^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.isFullScreen) return;
if (self.zf_playerDidAppearInScrollView) self.zf_playerDidAppearInScrollView(indexPath);
if ([self.controlView respondsToSelector:@selector(playerDidAppearInScrollView:)]) {
[self.controlView playerDidAppearInScrollView:self];
}
};
scrollView.zf_playerWillDisappearInScrollView = ^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.isFullScreen) return;
if (self.zf_playerWillDisappearInScrollView) self.zf_playerWillDisappearInScrollView(indexPath);
if ([self.controlView respondsToSelector:@selector(playerWillDisappearInScrollView:)]) {
[self.controlView playerWillDisappearInScrollView:self];
}
};
scrollView.zf_playerDidDisappearInScrollView = ^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.isFullScreen) return;
if (self.zf_playerDidDisappearInScrollView) self.zf_playerDidDisappearInScrollView(indexPath);
if ([self.controlView respondsToSelector:@selector(playerDidDisappearInScrollView:)]) {
[self.controlView playerDidDisappearInScrollView:self];
}
if (self.stopWhileNotVisible) { /// stop playing
if (self.containerType == ZFPlayerContainerTypeView) {
[self stopCurrentPlayingView];
} else if (self.containerType == ZFPlayerContainerTypeCell) {
[self stopCurrentPlayingCell];
}
} else { /// add to window
if (!self.isSmallFloatViewShow) {
[self addPlayerViewToSmallFloatView];
}
}
};
scrollView.zf_playerAppearingInScrollView = ^(NSIndexPath * _Nonnull indexPath, CGFloat playerApperaPercent) {
@zf_strongify(self)
if (self.isFullScreen) return;
if (self.zf_playerAppearingInScrollView) self.zf_playerAppearingInScrollView(indexPath, playerApperaPercent);
if ([self.controlView respondsToSelector:@selector(playerAppearingInScrollView:playerApperaPercent:)]) {
[self.controlView playerAppearingInScrollView:self playerApperaPercent:playerApperaPercent];
}
if (!self.stopWhileNotVisible && playerApperaPercent >= self.playerApperaPercent) {
if (self.containerType == ZFPlayerContainerTypeView) {
if (self.isSmallFloatViewShow) {
[self addPlayerViewToContainerView:self.containerView];
}
} else if (self.containerType == ZFPlayerContainerTypeCell) {
if (self.isSmallFloatViewShow) {
[self addPlayerViewToCell];
}
}
}
};
scrollView.zf_playerDisappearingInScrollView = ^(NSIndexPath * _Nonnull indexPath, CGFloat playerDisapperaPercent) {
@zf_strongify(self)
if (self.isFullScreen) return;
if (self.zf_playerDisappearingInScrollView) self.zf_playerDisappearingInScrollView(indexPath, playerDisapperaPercent);
if ([self.controlView respondsToSelector:@selector(playerDisappearingInScrollView:playerDisapperaPercent:)]) {
[self.controlView playerDisappearingInScrollView:self playerDisapperaPercent:playerDisapperaPercent];
}
if (playerDisapperaPercent >= self.playerDisapperaPercent) {
if (self.stopWhileNotVisible) { /// stop playing
if (self.containerType == ZFPlayerContainerTypeView) {
[self stopCurrentPlayingView];
} else if (self.containerType == ZFPlayerContainerTypeCell) {
[self stopCurrentPlayingCell];
}
} else { /// add to window
if (!self.isSmallFloatViewShow) {
[self addPlayerViewToSmallFloatView];
}
}
}
};
scrollView.zf_playerShouldPlayInScrollView = ^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.zf_playerShouldPlayInScrollView) self.zf_playerShouldPlayInScrollView(indexPath);
};
scrollView.zf_scrollViewDidEndScrollingCallback = ^(NSIndexPath * _Nonnull indexPath) {
@zf_strongify(self)
if (self.zf_scrollViewDidEndScrollingCallback) self.zf_scrollViewDidEndScrollingCallback(indexPath);
};
}
- (void)setWWANAutoPlay:(BOOL)WWANAutoPlay {
objc_setAssociatedObject(self, @selector(isWWANAutoPlay), @(WWANAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.scrollView) self.scrollView.zf_WWANAutoPlay = self.isWWANAutoPlay;
}
- (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.isSmallFloatViewShow = NO;
if (self.smallFloatView) self.smallFloatView.hidden = YES;
UIView *cell = [self.scrollView zf_getCellForIndexPath:playingIndexPath];
self.containerView = [cell viewWithTag:self.containerViewTag];
[self.orientationObserver updateRotateView:self.currentPlayerManager.view rotateViewAtCell:cell playerViewTag:self.containerViewTag];
[self addDeviceOrientationObserver];
self.scrollView.zf_playingIndexPath = playingIndexPath;
[self layoutPlayerSubViews];
} else {
self.scrollView.zf_playingIndexPath = playingIndexPath;
}
}
- (void)setShouldAutoPlay:(BOOL)shouldAutoPlay {
objc_setAssociatedObject(self, @selector(shouldAutoPlay), @(shouldAutoPlay), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.scrollView.zf_shouldAutoPlay = shouldAutoPlay;
}
- (void)setSectionAssetURLs:(NSArray<NSArray<NSURL *> *> * _Nullable)sectionAssetURLs {
objc_setAssociatedObject(self, @selector(sectionAssetURLs), sectionAssetURLs, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setPlayerDisapperaPercent:(CGFloat)playerDisapperaPercent {
playerDisapperaPercent = MIN(MAX(0.0, playerDisapperaPercent), 1.0);
self.scrollView.zf_playerDisapperaPercent = playerDisapperaPercent;
objc_setAssociatedObject(self, @selector(playerDisapperaPercent), @(playerDisapperaPercent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setPlayerApperaPercent:(CGFloat)playerApperaPercent {
playerApperaPercent = MIN(MAX(0.0, playerApperaPercent), 1.0);
self.scrollView.zf_playerApperaPercent = playerApperaPercent;
objc_setAssociatedObject(self, @selector(playerApperaPercent), @(playerApperaPercent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setZf_playerAppearingInScrollView:(void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerAppearingInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerAppearingInScrollView), zf_playerAppearingInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_playerDisappearingInScrollView:(void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerDisappearingInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerDisappearingInScrollView), zf_playerDisappearingInScrollView, 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_playerShouldPlayInScrollView:(void (^)(NSIndexPath * _Nonnull))zf_playerShouldPlayInScrollView {
objc_setAssociatedObject(self, @selector(zf_playerShouldPlayInScrollView), zf_playerShouldPlayInScrollView, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setZf_scrollViewDidEndScrollingCallback:(void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidEndScrollingCallback {
objc_setAssociatedObject(self, @selector(zf_scrollViewDidEndScrollingCallback), zf_scrollViewDidEndScrollingCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
#pragma mark - getter
- (UIScrollView *)scrollView {
UIScrollView *scrollView = objc_getAssociatedObject(self, _cmd);
return scrollView;
}
- (BOOL)isWWANAutoPlay {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (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];
}
- (NSIndexPath *)playingIndexPath {
return objc_getAssociatedObject(self, _cmd);
}
- (NSIndexPath *)shouldPlayIndexPath {
return self.scrollView.zf_shouldPlayIndexPath;
}
- (NSArray<NSArray<NSURL *> *> *)sectionAssetURLs {
return objc_getAssociatedObject(self, _cmd);
}
- (BOOL)shouldAutoPlay {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (CGFloat)playerDisapperaPercent {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.floatValue;
self.playerDisapperaPercent = 0.5;
return 0.5;
}
- (CGFloat)playerApperaPercent {
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) return number.floatValue;
self.playerApperaPercent = 0.0;
return 0.0;
}
- (void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerAppearingInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull, CGFloat))zf_playerDisappearingInScrollView {
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_playerShouldPlayInScrollView {
return objc_getAssociatedObject(self, _cmd);
}
- (void (^)(NSIndexPath * _Nonnull))zf_scrollViewDidEndScrollingCallback {
return objc_getAssociatedObject(self, _cmd);
}
#pragma mark - Public method
- (void)zf_filterShouldPlayCellWhileScrolled:(void (^ __nullable)(NSIndexPath *indexPath))handler {
[self.scrollView zf_filterShouldPlayCellWhileScrolled:handler];
}
- (void)zf_filterShouldPlayCellWhileScrolling:(void (^ __nullable)(NSIndexPath *indexPath))handler {
[self.scrollView zf_filterShouldPlayCellWhileScrolling:handler];
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath {
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;
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition animated:(BOOL)animated {
[self playTheIndexPath:indexPath scrollPosition:scrollPosition animated:animated completionHandler:nil];
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition animated:(BOOL)animated 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;
}
@zf_weakify(self)
[self.scrollView zf_scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated completionHandler:^{
@zf_strongify(self)
if (completionHandler) completionHandler();
self.playingIndexPath = indexPath;
self.assetURL = assetURL;
}];
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath assetURL:(NSURL *)assetURL {
self.playingIndexPath = indexPath;
self.assetURL = assetURL;
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath
assetURL:(NSURL *)assetURL
scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated {
[self playTheIndexPath:indexPath assetURL:assetURL scrollPosition:scrollPosition animated:animated completionHandler:nil];
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath
assetURL:(NSURL *)assetURL
scrollPosition:(ZFPlayerScrollViewScrollPosition)scrollPosition
animated:(BOOL)animated
completionHandler:(void (^ __nullable)(void))completionHandler {
@zf_weakify(self)
[self.scrollView zf_scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated completionHandler:^{
@zf_strongify(self)
if (completionHandler) completionHandler();
self.playingIndexPath = indexPath;
self.assetURL = assetURL;
}];
}
@end
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@implementation ZFPlayerController (ZFPlayerDeprecated)
- (void)updateScrollViewPlayerToCell {
if (self.currentPlayerManager.view && self.playingIndexPath && self.containerViewTag) {
UIView *cell = [self.scrollView zf_getCellForIndexPath:self.playingIndexPath];
self.containerView = [cell viewWithTag:self.containerViewTag];
[self.orientationObserver updateRotateView:self.currentPlayerManager.view rotateViewAtCell:cell playerViewTag:self.containerViewTag];
[self layoutPlayerSubViews];
}
}
- (void)updateNoramlPlayerWithContainerView:(UIView *)containerView {
if (self.currentPlayerManager.view && self.containerView) {
self.containerView = containerView;
[self.orientationObserver updateRotateView:self.currentPlayerManager.view containerView:self.containerView];
[self layoutPlayerSubViews];
}
}
- (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) {
@zf_weakify(self)
[self.scrollView zf_scrollToRowAtIndexPath:indexPath completionHandler:^{
@zf_strongify(self)
if (completionHandler) completionHandler();
self.playingIndexPath = indexPath;
self.assetURL = assetURL;
}];
} else {
if (completionHandler) completionHandler();
self.playingIndexPath = indexPath;
self.assetURL = assetURL;
}
}
- (void)playTheIndexPath:(NSIndexPath *)indexPath scrollToTop:(BOOL)scrollToTop {
if ([indexPath compare:self.playingIndexPath] == NSOrderedSame) return;
if (scrollToTop) {
@zf_weakify(self)
[self.scrollView zf_scrollToRowAtIndexPath:indexPath completionHandler:^{
@zf_strongify(self)
[self playTheIndexPath:indexPath];
}];
} else {
[self playTheIndexPath:indexPath];
}
}
- (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)enterLandscapeFullScreen:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion {
self.orientationObserver.fullScreenMode = ZFFullScreenModeLandscape;
[self.orientationObserver rotateToOrientation:orientation animated:animated completion:completion];
}
- (void)enterLandscapeFullScreen:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
[self enterLandscapeFullScreen:orientation animated:animated completion:nil];
}
/// Add to the keyWindow
- (void)addPlayerViewToKeyWindow {
[self addPlayerViewToSmallFloatView];
}
@end
#pragma clang diagnostic pop
//
// 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
//
// ZFPlayerGestureControl.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPlayerGestureControl.h"
@interface ZFPlayerGestureControl ()<UIGestureRecognizerDelegate>
@property (nonatomic, strong) UITapGestureRecognizer *singleTap;
@property (nonatomic, strong) UITapGestureRecognizer *doubleTap;
@property (nonatomic, strong) UIPanGestureRecognizer *panGR;
@property (nonatomic, strong) UIPinchGestureRecognizer *pinchGR;
@property (nonatomic) ZFPanDirection panDirection;
@property (nonatomic) ZFPanLocation panLocation;
@property (nonatomic) ZFPanMovingDirection panMovingDirection;
@property (nonatomic, weak) UIView *targetView;
@end
@implementation ZFPlayerGestureControl
- (void)addGestureToView:(UIView *)view {
self.targetView = view;
self.targetView.multipleTouchEnabled = YES;
[self.singleTap requireGestureRecognizerToFail:self.doubleTap];
[self.singleTap requireGestureRecognizerToFail:self.panGR];
[self.targetView addGestureRecognizer:self.singleTap];
[self.targetView addGestureRecognizer:self.doubleTap];
[self.targetView addGestureRecognizer:self.panGR];
[self.targetView addGestureRecognizer:self.pinchGR];
}
- (void)removeGestureToView:(UIView *)view {
[view removeGestureRecognizer:self.singleTap];
[view removeGestureRecognizer:self.doubleTap];
[view removeGestureRecognizer:self.panGR];
[view removeGestureRecognizer:self.pinchGR];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.panGR) {
CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:self.targetView];
CGFloat x = fabs(translation.x);
CGFloat y = fabs(translation.y);
if (x < y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionVertical) { /// up and down moving direction.
return NO;
} else if (x > y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionHorizontal) { /// left and right moving direction.
return NO;
}
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
ZFPlayerGestureType type = ZFPlayerGestureTypeUnknown;
if (gestureRecognizer == self.singleTap) type = ZFPlayerGestureTypeSingleTap;
else if (gestureRecognizer == self.doubleTap) type = ZFPlayerGestureTypeDoubleTap;
else if (gestureRecognizer == self.panGR) type = ZFPlayerGestureTypePan;
else if (gestureRecognizer == self.pinchGR) type = ZFPlayerGestureTypePinch;
CGPoint locationPoint = [touch locationInView:touch.view];
if (locationPoint.x > _targetView.bounds.size.width / 2) {
self.panLocation = ZFPanLocationRight;
} else {
self.panLocation = ZFPanLocationLeft;
}
switch (type) {
case ZFPlayerGestureTypeUnknown: break;
case ZFPlayerGestureTypePan: {
if (self.disableTypes & ZFPlayerDisableGestureTypesPan) {
return NO;
}
}
break;
case ZFPlayerGestureTypePinch: {
if (self.disableTypes & ZFPlayerDisableGestureTypesPinch) {
return NO;
}
}
break;
case ZFPlayerGestureTypeDoubleTap: {
if (self.disableTypes & ZFPlayerDisableGestureTypesDoubleTap) {
return NO;
}
}
break;
case ZFPlayerGestureTypeSingleTap: {
if (self.disableTypes & ZFPlayerDisableGestureTypesSingleTap) {
return NO;
}
}
break;
}
if (self.triggerCondition) return self.triggerCondition(self, type, gestureRecognizer, touch);
return YES;
}
// Whether to support multi-trigger, return YES, you can trigger a method with multiple gestures, return NO is mutually exclusive
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (otherGestureRecognizer != self.singleTap &&
otherGestureRecognizer != self.doubleTap &&
otherGestureRecognizer != self.panGR &&
otherGestureRecognizer != self.pinchGR) return NO;
if (gestureRecognizer == self.panGR) {
CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:self.targetView];
CGFloat x = fabs(translation.x);
CGFloat y = fabs(translation.y);
if (x < y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionVertical) {
return YES;
} else if (x > y && self.disablePanMovingDirection & ZFPlayerDisablePanMovingDirectionHorizontal) {
return YES;
}
}
if (gestureRecognizer.numberOfTouches >= 2) {
return NO;
}
return YES;
}
- (UITapGestureRecognizer *)singleTap {
if (!_singleTap){
_singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
_singleTap.delegate = self;
_singleTap.delaysTouchesBegan = YES;
_singleTap.delaysTouchesEnded = YES;
_singleTap.numberOfTouchesRequired = 1;
_singleTap.numberOfTapsRequired = 1;
}
return _singleTap;
}
- (UITapGestureRecognizer *)doubleTap {
if (!_doubleTap) {
_doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
_doubleTap.delegate = self;
_doubleTap.delaysTouchesBegan = YES;
_singleTap.delaysTouchesEnded = YES;
_doubleTap.numberOfTouchesRequired = 1;
_doubleTap.numberOfTapsRequired = 2;
}
return _doubleTap;
}
- (UIPanGestureRecognizer *)panGR {
if (!_panGR) {
_panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
_panGR.delegate = self;
_panGR.delaysTouchesBegan = YES;
_panGR.delaysTouchesEnded = YES;
_panGR.maximumNumberOfTouches = 1;
_panGR.cancelsTouchesInView = YES;
}
return _panGR;
}
- (UIPinchGestureRecognizer *)pinchGR {
if (!_pinchGR) {
_pinchGR = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
_pinchGR.delegate = self;
_pinchGR.delaysTouchesBegan = YES;
}
return _pinchGR;
}
- (void)handleSingleTap:(UITapGestureRecognizer *)tap {
if (self.singleTapped) self.singleTapped(self);
}
- (void)handleDoubleTap:(UITapGestureRecognizer *)tap {
if (self.doubleTapped) self.doubleTapped(self);
}
- (void)handlePan:(UIPanGestureRecognizer *)pan {
CGPoint translate = [pan translationInView:pan.view];
CGPoint velocity = [pan velocityInView:pan.view];
switch (pan.state) {
case UIGestureRecognizerStateBegan: {
self.panMovingDirection = ZFPanMovingDirectionUnkown;
CGFloat x = fabs(velocity.x);
CGFloat y = fabs(velocity.y);
if (x > y) {
self.panDirection = ZFPanDirectionH;
} else if (x < y) {
self.panDirection = ZFPanDirectionV;
} else {
self.panDirection = ZFPanDirectionUnknown;
}
if (self.beganPan) self.beganPan(self, self.panDirection, self.panLocation);
}
break;
case UIGestureRecognizerStateChanged: {
switch (_panDirection) {
case ZFPanDirectionH: {
if (translate.x > 0) {
self.panMovingDirection = ZFPanMovingDirectionRight;
} else {
self.panMovingDirection = ZFPanMovingDirectionLeft;
}
}
break;
case ZFPanDirectionV: {
if (translate.y > 0) {
self.panMovingDirection = ZFPanMovingDirectionBottom;
} else {
self.panMovingDirection = ZFPanMovingDirectionTop;
}
}
break;
case ZFPanDirectionUnknown:
break;
}
if (self.changedPan) self.changedPan(self, self.panDirection, self.panLocation, velocity);
}
break;
case UIGestureRecognizerStateFailed:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded: {
if (self.endedPan) self.endedPan(self, self.panDirection, self.panLocation);
}
break;
default:
break;
}
[pan setTranslation:CGPointZero inView:pan.view];
}
- (void)handlePinch:(UIPinchGestureRecognizer *)pinch {
switch (pinch.state) {
case UIGestureRecognizerStateEnded: {
if (self.pinched) self.pinched(self, pinch.scale);
}
break;
default:
break;
}
}
@end
//
// ZFPlayerLogManager.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#define ZFPlayerLog(format,...) [ZFPlayerLogManager logWithFunction:__FUNCTION__ lineNumber:__LINE__ formatString:[NSString stringWithFormat:format, ##__VA_ARGS__]]
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ZFPlayerLogManager : NSObject
// Set the log output status.
+ (void)setLogEnable:(BOOL)enable;
// Gets the log output status.
+ (BOOL)getLogEnable;
/// Get ZFPlayer version.
+ (NSString *)version;
// Log output method.
+ (void)logWithFunction:(const char *)function lineNumber:(int)lineNumber formatString:(NSString *)formatString;
@end
//
// ZFPlayerLogManager.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPlayerLogManager.h"
static BOOL kLogEnable = NO;
@implementation ZFPlayerLogManager
+ (void)setLogEnable:(BOOL)enable {
kLogEnable = enable;
}
+ (BOOL)getLogEnable {
return kLogEnable;
}
+ (NSString *)version {
return @"4.0.2";
}
+ (void)logWithFunction:(const char *)function lineNumber:(int)lineNumber formatString:(NSString *)formatString {
if ([self getLogEnable]) {
NSLog(@"%s[%d]%@", function, lineNumber, formatString);
}
}
@end
//
// ZFPlayerMediaControl.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "ZFPlayerMediaPlayback.h"
#import "ZFOrientationObserver.h"
#import "ZFPlayerGestureControl.h"
#import "ZFReachabilityManager.h"
@class ZFPlayerController;
NS_ASSUME_NONNULL_BEGIN
@protocol ZFPlayerMediaControl <NSObject>
@required
/// Current playerController
@property (nonatomic, weak) ZFPlayerController *player;
@optional
#pragma mark - Playback state
/// When the player prepare to play the video.
- (void)videoPlayer:(ZFPlayerController *)videoPlayer prepareToPlay:(NSURL *)assetURL;
/// When th player playback state changed.
- (void)videoPlayer:(ZFPlayerController *)videoPlayer playStateChanged:(ZFPlayerPlaybackState)state;
/// When th player loading state changed.
- (void)videoPlayer:(ZFPlayerController *)videoPlayer loadStateChanged:(ZFPlayerLoadState)state;
#pragma mark - progress
/**
When the playback changed.
@param videoPlayer the player.
@param currentTime the current play time.
@param totalTime the video total time.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer
currentTime:(NSTimeInterval)currentTime
totalTime:(NSTimeInterval)totalTime;
/**
When buffer progress changed.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer
bufferTime:(NSTimeInterval)bufferTime;
/**
When you are dragging to change the video progress.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer
draggingTime:(NSTimeInterval)seekTime
totalTime:(NSTimeInterval)totalTime;
/**
When play end.
*/
- (void)videoPlayerPlayEnd:(ZFPlayerController *)videoPlayer;
/**
When play failed.
*/
- (void)videoPlayerPlayFailed:(ZFPlayerController *)videoPlayer error:(id)error;
#pragma mark - lock screen
/**
When set `videoPlayer.lockedScreen`.
*/
- (void)lockedVideoPlayer:(ZFPlayerController *)videoPlayer lockedScreen:(BOOL)locked;
#pragma mark - Screen rotation
/**
When the fullScreen maode will changed.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationWillChange:(ZFOrientationObserver *)observer;
/**
When the fullScreen maode did changed.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationDidChanged:(ZFOrientationObserver *)observer;
#pragma mark - The network changed
/**
When the network changed
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer reachabilityChanged:(ZFReachabilityStatus)status;
#pragma mark - The video size changed
/**
When the video size changed
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size;
#pragma mark - Gesture
/**
When the gesture condition
*/
- (BOOL)gestureTriggerCondition:(ZFPlayerGestureControl *)gestureControl
gestureType:(ZFPlayerGestureType)gestureType
gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
touch:(UITouch *)touch;
/**
When the gesture single tapped
*/
- (void)gestureSingleTapped:(ZFPlayerGestureControl *)gestureControl;
/**
When the gesture double tapped
*/
- (void)gestureDoubleTapped:(ZFPlayerGestureControl *)gestureControl;
/**
When the gesture begin panGesture
*/
- (void)gestureBeganPan:(ZFPlayerGestureControl *)gestureControl
panDirection:(ZFPanDirection)direction
panLocation:(ZFPanLocation)location;
/**
When the gesture paning
*/
- (void)gestureChangedPan:(ZFPlayerGestureControl *)gestureControl
panDirection:(ZFPanDirection)direction
panLocation:(ZFPanLocation)location
withVelocity:(CGPoint)velocity;
/**
When the end panGesture
*/
- (void)gestureEndedPan:(ZFPlayerGestureControl *)gestureControl
panDirection:(ZFPanDirection)direction
panLocation:(ZFPanLocation)location;
/**
When the pinchGesture changed
*/
- (void)gesturePinched:(ZFPlayerGestureControl *)gestureControl
scale:(float)scale;
#pragma mark - scrollview
/**
When the player will appear in scrollView.
*/
- (void)playerWillAppearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player did appear in scrollView.
*/
- (void)playerDidAppearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player will disappear in scrollView.
*/
- (void)playerWillDisappearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player did disappear in scrollView.
*/
- (void)playerDidDisappearInScrollView:(ZFPlayerController *)videoPlayer;
/**
When the player appearing in scrollView.
*/
- (void)playerAppearingInScrollView:(ZFPlayerController *)videoPlayer playerApperaPercent:(CGFloat)playerApperaPercent;
/**
When the player disappearing in scrollView.
*/
- (void)playerDisappearingInScrollView:(ZFPlayerController *)videoPlayer playerDisapperaPercent:(CGFloat)playerDisapperaPercent;
/**
When the small float view show.
*/
- (void)videoPlayer:(ZFPlayerController *)videoPlayer floatViewShow:(BOOL)show;
@end
NS_ASSUME_NONNULL_END
//
// ZFMediaPlayback.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "ZFPlayerView.h"
#import "ZFPlayerConst.h"
NS_ASSUME_NONNULL_BEGIN
@protocol ZFPlayerMediaPlayback <NSObject>
@required
/// The view must inherited `ZFPlayerView`,this view deals with some gesture conflicts.
@property (nonatomic) ZFPlayerView *view;
/// The player volume.
/// Only affects audio volume for the player instance and not for the device.
/// You can change device volume or player volume as needed,change the player volume you can follow the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic) float volume;
/// The player muted.
/// indicates whether or not audio output of the player is muted. Only affects audio muting for the player instance and not for the device.
/// You can change device volume or player muted as needed,change the player muted you can follow the `ZFPlayerMediaPlayback` protocol.
@property (nonatomic, getter=isMuted) BOOL muted;
/// Playback speed,0.5...2
@property (nonatomic) float rate;
/// The player current play time.
@property (nonatomic, readonly) NSTimeInterval currentTime;
/// The player total time.
@property (nonatomic, readonly) NSTimeInterval totalTime;
/// The player buffer time.
@property (nonatomic, readonly) NSTimeInterval bufferTime;
/// The player seek time.
@property (nonatomic) NSTimeInterval seekTime;
/// The player play state,playing or not playing.
@property (nonatomic, readonly) BOOL isPlaying;
/// Determines how the content scales to fit the view. Defaults to ZFPlayerScalingModeNone.
@property (nonatomic) ZFPlayerScalingMode scalingMode;
/**
@abstract Check whether video preparation is complete.
@discussion isPreparedToPlay processing logic
* If isPreparedToPlay is true, you can call [ZFPlayerMediaPlayback play] API start playing;
* If isPreparedToPlay to false, direct call [ZFPlayerMediaPlayback play], in the play the internal automatic call [ZFPlayerMediaPlayback prepareToPlay] API.
* Returns true if prepared for playback.
*/
@property (nonatomic, readonly) BOOL isPreparedToPlay;
/// The player should auto player, default is YES.
@property (nonatomic) BOOL shouldAutoPlay;
/// The play asset URL.
@property (nonatomic) NSURL *assetURL;
/// The video size.
@property (nonatomic) CGSize presentationSize;
/// The playback state.
@property (nonatomic, readonly) ZFPlayerPlaybackState playState;
/// The player load state.
@property (nonatomic, readonly) ZFPlayerLoadState loadState;
///------------------------------------
/// If you don't appoint the controlView, you can called the following blocks.
/// If you appoint the controlView, The following block cannot be called outside, only for `ZFPlayerController` calls.
///------------------------------------
/// The block invoked when the player is Prepare to play.
@property (nonatomic, copy, nullable) void(^playerPrepareToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player is Ready to play.
@property (nonatomic, copy, nullable) void(^playerReadyToPlay)(id<ZFPlayerMediaPlayback> asset, NSURL *assetURL);
/// The block invoked when the player play progress changed.
@property (nonatomic, copy, nullable) void(^playerPlayTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval currentTime, NSTimeInterval duration);
/// The block invoked when the player play buffer changed.
@property (nonatomic, copy, nullable) void(^playerBufferTimeChanged)(id<ZFPlayerMediaPlayback> asset, NSTimeInterval bufferTime);
/// The block invoked when the player playback state changed.
@property (nonatomic, copy, nullable) void(^playerPlayStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerPlaybackState playState);
/// The block invoked when the player load state changed.
@property (nonatomic, copy, nullable) void(^playerLoadStateChanged)(id<ZFPlayerMediaPlayback> asset, ZFPlayerLoadState loadState);
/// The block invoked when the player play failed.
@property (nonatomic, copy, nullable) void(^playerPlayFailed)(id<ZFPlayerMediaPlayback> asset, id error);
/// The block invoked when the player play end.
@property (nonatomic, copy, nullable) void(^playerDidToEnd)(id<ZFPlayerMediaPlayback> asset);
// The block invoked when video size changed.
@property (nonatomic, copy, nullable) void(^presentationSizeChanged)(id<ZFPlayerMediaPlayback> asset, CGSize size);
///------------------------------------
/// end
///------------------------------------
/// Prepares the current queue for playback, interrupting any active (non-mixible) audio sessions.
- (void)prepareToPlay;
/// Reload player.
- (void)reloadPlayer;
/// Play playback.
- (void)play;
/// Pauses playback.
- (void)pause;
/// Replay playback.
- (void)replay;
/// Stop playback.
- (void)stop;
/// Use this method to seek to a specified time for the current player and to be notified when the seek operation is complete.
- (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
@optional
/// Video UIImage at the current time.
- (UIImage *)thumbnailImageAtCurrentTime;
/// Video UIImage at the current time.
- (void)thumbnailImageAtCurrentTime:(void(^)(UIImage *))handler;
@end
NS_ASSUME_NONNULL_END
//
// ZFPlayerNotification.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, ZFPlayerBackgroundState) {
ZFPlayerBackgroundStateForeground, // Enter the foreground from the background.
ZFPlayerBackgroundStateBackground, // From the foreground to the background.
};
@interface ZFPlayerNotification : NSObject
@property (nonatomic, readonly) ZFPlayerBackgroundState backgroundState;
@property (nonatomic, copy, nullable) void(^willResignActive)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^didBecomeActive)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^newDeviceAvailable)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^oldDeviceUnavailable)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^categoryChange)(ZFPlayerNotification *registrar);
@property (nonatomic, copy, nullable) void(^volumeChanged)(float volume);
@property (nonatomic, copy, nullable) void(^audioInterruptionCallback)(AVAudioSessionInterruptionType interruptionType);
- (void)addNotification;
- (void)removeNotification;
@end
NS_ASSUME_NONNULL_END
//
// ZFPlayerNotification.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPlayerNotification.h"
@interface ZFPlayerNotification ()
@property (nonatomic, assign) ZFPlayerBackgroundState backgroundState;
@end
@implementation ZFPlayerNotification
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioSessionRouteChangeNotification:)
name:AVAudioSessionRouteChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActiveNotification)
name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActiveNotification)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(volumeDidChangeNotification:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioSessionInterruptionNotification:)
name:AVAudioSessionInterruptionNotification
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
}
- (void)dealloc {
[self removeNotification];
}
- (void)audioSessionRouteChangeNotification:(NSNotification*)notification {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
if (self.newDeviceAvailable) self.newDeviceAvailable(self);
}
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
if (self.oldDeviceUnavailable) self.oldDeviceUnavailable(self);
}
break;
case AVAudioSessionRouteChangeReasonCategoryChange: {
if (self.categoryChange) self.categoryChange(self);
}
break;
}
});
}
- (void)volumeDidChangeNotification:(NSNotification *)notification {
float volume = [[[notification userInfo] objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
if (self.volumeChanged) self.volumeChanged(volume);
}
- (void)applicationWillResignActiveNotification {
self.backgroundState = ZFPlayerBackgroundStateBackground;
if (_willResignActive) _willResignActive(self);
}
- (void)applicationDidBecomeActiveNotification {
self.backgroundState = ZFPlayerBackgroundStateForeground;
if (_didBecomeActive) _didBecomeActive(self);
}
- (void)audioSessionInterruptionNotification:(NSNotification *)notification {
NSDictionary *interuptionDict = notification.userInfo;
AVAudioSessionInterruptionType interruptionType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
if (self.audioInterruptionCallback) self.audioInterruptionCallback(interruptionType);
}
@end
//
// ZFPlayerView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFPlayerConst.h"
@interface ZFPlayerView : UIView
/// player content view.
@property (nonatomic, strong) UIView *playerView;
/// Determines how the content scales to fit the view.
@property (nonatomic, assign) ZFPlayerScalingMode scalingMode;
/// The video size.
@property (nonatomic, assign) CGSize presentationSize;
/// The cover for playerView.
@property (nonatomic, strong, readonly) UIImageView *coverImageView;
@end
//
// ZFPlayerView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPlayerView.h"
#import "ZFPlayerConst.h"
@implementation ZFPlayerView
@synthesize presentationSize = _presentationSize;
@synthesize coverImageView = _coverImageView;
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor blackColor];
[self addSubview:self.coverImageView];
}
return self;
}
- (void)setPlayerView:(UIView *)playerView {
if (_playerView) {
[_playerView removeFromSuperview];
self.presentationSize = CGSizeZero;
}
_playerView = playerView;
if (playerView != nil) {
[self addSubview:playerView];
}
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.frame.size.width;
CGFloat min_view_h = self.frame.size.height;
CGSize playerViewSize = CGSizeZero;
CGFloat videoWidth = self.presentationSize.width;
CGFloat videoHeight = self.presentationSize.height;
if (videoHeight == 0) return;
CGFloat screenScale = min_view_w/min_view_h;
CGFloat videoScale = videoWidth/videoHeight;
if (screenScale > videoScale) {
CGFloat height = min_view_h;
CGFloat width = height * videoScale;
playerViewSize = CGSizeMake(width, height);
} else {
CGFloat width = min_view_w;
CGFloat height = width / videoScale;
playerViewSize = CGSizeMake(width, height);
}
if (self.scalingMode == ZFPlayerScalingModeNone || self.scalingMode == ZFPlayerScalingModeAspectFit) {
min_w = playerViewSize.width;
min_h = playerViewSize.height;
min_x = (min_view_w - min_w) / 2.0;
min_y = (min_view_h - min_h) / 2.0;
self.playerView.frame = CGRectMake(min_x, min_y, min_w, min_h);
} else if (self.scalingMode == ZFPlayerScalingModeAspectFill || self.scalingMode == ZFPlayerScalingModeFill) {
self.playerView.frame = self.bounds;
}
self.coverImageView.frame = self.playerView.frame;
}
- (CGSize)presentationSize {
if (CGSizeEqualToSize(_presentationSize, CGSizeZero)) {
_presentationSize = self.frame.size;
}
return _presentationSize;
}
- (UIImageView *)coverImageView {
if (!_coverImageView) {
_coverImageView = [[UIImageView alloc] init];
_coverImageView.userInteractionEnabled = YES;
_coverImageView.clipsToBounds = YES;
_coverImageView.contentMode = UIViewContentModeScaleAspectFit;
}
return _coverImageView;
}
- (void)setScalingMode:(ZFPlayerScalingMode)scalingMode {
_scalingMode = scalingMode;
if (scalingMode == ZFPlayerScalingModeNone || scalingMode == ZFPlayerScalingModeAspectFit) {
self.coverImageView.contentMode = UIViewContentModeScaleAspectFit;
} else if (scalingMode == ZFPlayerScalingModeAspectFill) {
self.coverImageView.contentMode = UIViewContentModeScaleAspectFill;
} else if (scalingMode == ZFPlayerScalingModeFill) {
self.coverImageView.contentMode = UIViewContentModeScaleToFill;
}
[self layoutIfNeeded];
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
if (CGSizeEqualToSize(CGSizeZero, presentationSize)) return;
[self setNeedsLayout];
[self layoutIfNeeded];
}
@end
//
// ZFPortraitViewController.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFOrientationObserver.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZFPortraitViewController : UIViewController
/// The block invoked When player will rotate.
@property (nonatomic, copy, nullable) void(^orientationWillChange)(BOOL isFullScreen);
/// The block invoked when player rotated.
@property (nonatomic, copy, nullable) void(^orientationDidChanged)(BOOL isFullScreen);
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, assign) BOOL statusBarHidden;
/// default is UIStatusBarStyleLightContent.
@property (nonatomic, assign) UIStatusBarStyle statusBarStyle;
/// defalut is UIStatusBarAnimationSlide.
@property (nonatomic, assign) UIStatusBarAnimation statusBarAnimation;
/// default is ZFDisablePortraitGestureTypesNone.
@property (nonatomic, assign) ZFDisablePortraitGestureTypes disablePortraitGestureTypes;
@property (nonatomic, assign) CGSize presentationSize;
@property (nonatomic, assign) BOOL fullScreenAnimation;
@property (nonatomic, assign) NSTimeInterval duration;
@end
NS_ASSUME_NONNULL_END
//
// ZFPortraitViewController.m
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPortraitViewController.h"
#import "ZFPersentInteractiveTransition.h"
#import "ZFPresentTransition.h"
@interface ZFPortraitViewController ()<UIViewControllerTransitioningDelegate,ZFPortraitOrientationDelegate>
@property (nonatomic, strong) ZFPresentTransition *transition;
@property (nonatomic, strong) ZFPersentInteractiveTransition *interactiveTransition;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@end
@implementation ZFPortraitViewController
- (instancetype)init {
self = [super init];
if (self) {
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
self.modalPresentationCapturesStatusBarAppearance = YES;
_statusBarStyle = UIStatusBarStyleLightContent;
_statusBarAnimation = UIStatusBarAnimationSlide;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (!self.fullScreenAnimation) {
if (self.orientationWillChange) {
self.orientationWillChange(YES);
}
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (!self.fullScreenAnimation) {
self.view.alpha = 1;
[self.view addSubview:self.contentView];
self.contentView.frame = [self contentFullScreenRect];
if (self.orientationDidChanged) {
self.orientationDidChanged(YES);
}
}
self.fullScreen = YES;
[self.interactiveTransition updateContentView:self.contentView
containerView:self.containerView];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (!self.fullScreenAnimation) {
if (self.orientationWillChange) {
self.orientationWillChange(NO);
}
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.fullScreen = NO;
if (!self.fullScreenAnimation) {
[self.containerView addSubview:self.contentView];
self.contentView.frame = self.containerView.bounds;
if (self.orientationDidChanged) {
self.orientationDidChanged(NO);
}
}
}
#pragma mark - transition delegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
[self.transition transitionWithTransitionType:ZFPresentTransitionTypePresent contentView:self.contentView containerView:self.containerView];
return self.transition;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
[self.transition transitionWithTransitionType:ZFPresentTransitionTypeDismiss contentView:self.contentView containerView:self.containerView];
return self.transition;
}
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
return self.interactiveTransition.interation ? self.interactiveTransition : nil;
}
- (BOOL)shouldAutorotate {
return NO;
}
- (BOOL)prefersStatusBarHidden {
return self.statusBarHidden;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return self.statusBarAnimation;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#pragma mark - ZFPortraitOrientationDelegate
- (void)zf_orientationWillChange:(BOOL)isFullScreen {
if (self.orientationWillChange) {
self.orientationWillChange(isFullScreen);
}
}
- (void)zf_orientationDidChanged:(BOOL)isFullScreen {
if (self.orientationDidChanged) {
self.orientationDidChanged(isFullScreen);
}
}
- (void)zf_interationState:(BOOL)isDragging {
self.transition.interation = isDragging;
}
#pragma mark - getter
- (ZFPresentTransition *)transition {
if (!_transition) {
_transition = [[ZFPresentTransition alloc] init];
_transition.contentFullScreenRect = [self contentFullScreenRect];
_transition.delagate = self;
}
return _transition;
}
- (ZFPersentInteractiveTransition *)interactiveTransition {
if (!_interactiveTransition) {
_interactiveTransition = [[ZFPersentInteractiveTransition alloc] init];
_interactiveTransition.contentFullScreenRect = [self contentFullScreenRect];
_interactiveTransition.viewController = self;
_interactiveTransition.delagate = self;
}
return _interactiveTransition;;
}
- (void)setDisablePortraitGestureTypes:(ZFDisablePortraitGestureTypes)disablePortraitGestureTypes {
_disablePortraitGestureTypes = disablePortraitGestureTypes;
self.interactiveTransition.disablePortraitGestureTypes = disablePortraitGestureTypes;
}
- (void)setPresentationSize:(CGSize)presentationSize {
_presentationSize = presentationSize;
self.transition.contentFullScreenRect = [self contentFullScreenRect];
self.interactiveTransition.contentFullScreenRect = [self contentFullScreenRect];
if (!self.fullScreenAnimation && self.isFullScreen) {
self.contentView.frame = [self contentFullScreenRect];
}
}
- (void)setFullScreen:(BOOL)fullScreen {
_fullScreen = fullScreen;
self.transition.fullScreen = fullScreen;
}
- (void)setFullScreenAnimation:(BOOL)fullScreenAnimation {
_fullScreenAnimation = fullScreenAnimation;
self.interactiveTransition.fullScreenAnimation = fullScreenAnimation;
}
- (void)setDuration:(NSTimeInterval)duration {
_duration = duration;
self.transition.duration = duration;
}
- (CGRect)contentFullScreenRect {
CGFloat videoWidth = self.presentationSize.width;
CGFloat videoHeight = self.presentationSize.height;
if (videoHeight == 0) {
return CGRectZero;
}
CGSize fullScreenScaleSize = CGSizeZero;
CGFloat screenScale = ZFPlayerScreenWidth/ZFPlayerScreenHeight;
CGFloat videoScale = videoWidth/videoHeight;
if (screenScale > videoScale) {
CGFloat height = ZFPlayerScreenHeight;
CGFloat width = height * videoScale;
fullScreenScaleSize = CGSizeMake(width, height);
} else {
CGFloat width = ZFPlayerScreenWidth;
CGFloat height = (CGFloat)(width / videoScale);
fullScreenScaleSize = CGSizeMake(width, height);
}
videoWidth = fullScreenScaleSize.width;
videoHeight = fullScreenScaleSize.height;
CGRect rect = CGRectMake((ZFPlayerScreenWidth - videoWidth) / 2.0, (ZFPlayerScreenHeight - videoHeight) / 2.0, videoWidth, videoHeight);
return rect;
}
@end
//
// ZFPresentTransition.h
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFOrientationObserver.h"
typedef NS_ENUM(NSUInteger, ZFPresentTransitionType) {
ZFPresentTransitionTypePresent,
ZFPresentTransitionTypeDismiss,
};
@interface ZFPresentTransition : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic, weak) id<ZFPortraitOrientationDelegate> delagate;
@property (nonatomic, assign) CGRect contentFullScreenRect;
@property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
@property (nonatomic, assign) BOOL interation;
@property (nonatomic, assign) NSTimeInterval duration;
- (void)transitionWithTransitionType:(ZFPresentTransitionType)type
contentView:(UIView *)contentView
containerView:(UIView *)containerView;
@end
//
// ZFPresentTransition.m
// ZFPlayer
//
// Copyright (c) 2020年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPresentTransition.h"
#import "ZFPlayerConst.h"
@interface ZFPresentTransition ()
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, assign) ZFPresentTransitionType type;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, assign, getter=isTransiting) BOOL transiting;
@end
@implementation ZFPresentTransition
- (void)transitionWithTransitionType:(ZFPresentTransitionType)type
contentView:(UIView *)contentView
containerView:(UIView *)containerView {
self.type = type;
self.contentView = contentView;
self.containerView = containerView;
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return self.duration == 0 ? 0.25f : self.duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
switch (self.type) {
case ZFPresentTransitionTypePresent: {
[self presentAnimation:transitionContext];
}
break;
case ZFPresentTransitionTypeDismiss: {
[self dismissAnimation:transitionContext];
}
break;
}
}
- (void)presentAnimation:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if ([fromVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)fromVC;
fromVC = nav.viewControllers.lastObject;
} else if ([fromVC isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBar = (UITabBarController *)fromVC;
if ([tabBar.selectedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)tabBar.selectedViewController;
fromVC = nav.viewControllers.lastObject;
} else {
fromVC = tabBar.selectedViewController;
}
}
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView addSubview:self.contentView];
CGRect originRect = [self.containerView convertRect:self.contentView.frame toView:toVC.view];
self.contentView.frame = originRect;
UIColor *tempColor = toVC.view.backgroundColor;
toVC.view.backgroundColor = [tempColor colorWithAlphaComponent:0];
toVC.view.alpha = 1;
[self.delagate zf_orientationWillChange:YES];
CGRect toRect = self.contentFullScreenRect;
self.transiting = YES;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
self.contentView.frame = toRect;
[self.contentView layoutIfNeeded];
toVC.view.backgroundColor = [tempColor colorWithAlphaComponent:1.f];
} completion:^(BOOL finished) {
self.transiting = NO;
[toVC.view addSubview:self.contentView];
[transitionContext completeTransition:YES];
[self.delagate zf_orientationDidChanged:YES];
if (!CGRectEqualToRect(toRect, self.contentFullScreenRect)) {
self.contentView.frame = self.contentFullScreenRect;
[self.contentView layoutIfNeeded];
}
}];
}
- (void)dismissAnimation:(id<UIViewControllerContextTransitioning>)transitionContext {
UIView *containerView = [transitionContext containerView];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if ([toVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)toVC;
toVC = nav.viewControllers.lastObject;
} else if ([toVC isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBar = (UITabBarController *)toVC;
if ([tabBar.selectedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)tabBar.selectedViewController;
toVC = nav.viewControllers.lastObject;
} else {
toVC = tabBar.selectedViewController;
}
}
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
fromVC.view.frame = containerView.bounds;
[containerView addSubview:fromVC.view];
[containerView addSubview:self.contentView];
CGRect originRect = [fromVC.view convertRect:self.contentView.frame toView:toVC.view];
self.contentView.frame = originRect;
CGRect toRect = [self.containerView convertRect:self.containerView.bounds toView:toVC.view];
[fromVC.view convertRect:self.contentView.bounds toView:self.containerView.window];
[self.delagate zf_orientationWillChange:NO];
self.transiting = YES;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromVC.view.alpha = 0;
self.contentView.frame = toRect;
[self.contentView layoutIfNeeded];
} completion:^(BOOL finished) {
[self.containerView addSubview:self.contentView];
self.contentView.frame = self.containerView.bounds;
[transitionContext completeTransition:YES];
[self.delagate zf_orientationDidChanged:NO];
self.transiting = NO;
}];
}
- (void)setContentFullScreenRect:(CGRect)contentFullScreenRect {
_contentFullScreenRect = contentFullScreenRect;
if (!self.transiting && self.isFullScreen && !self.interation) {
self.contentView.frame = contentFullScreenRect;
}
}
@end
//
// ZFReachabilityManager.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#if !TARGET_OS_WATCH
#import <SystemConfiguration/SystemConfiguration.h>
typedef NS_ENUM(NSInteger, ZFReachabilityStatus) {
ZFReachabilityStatusUnknown = -1,
ZFReachabilityStatusNotReachable = 0,
ZFReachabilityStatusReachableViaWiFi = 1,
ZFReachabilityStatusReachableVia2G = 2,
ZFReachabilityStatusReachableVia3G = 3,
ZFReachabilityStatusReachableVia4G = 4,
};
NS_ASSUME_NONNULL_BEGIN
/**
`ZFReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability.
See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ )
@warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined.
*/
@interface ZFReachabilityManager : NSObject
/**
The current network reachability status.
*/
@property (readonly, nonatomic, assign) ZFReachabilityStatus networkReachabilityStatus;
/**
Whether or not the network is currently reachable.
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
Whether or not the network is currently reachable via WWAN.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
Whether or not the network is currently reachable via WiFi.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
///---------------------
/// @name Initialization
///---------------------
/**
Returns the shared network reachability manager.
*/
+ (instancetype)sharedManager;
/**
Creates and returns a network reachability manager with the default socket address.
@return An initialized network reachability manager, actively monitoring the default socket address.
*/
+ (instancetype)manager;
/**
Creates and returns a network reachability manager for the specified domain.
@param domain The domain used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified domain.
*/
+ (instancetype)managerForDomain:(NSString *)domain;
/**
Creates and returns a network reachability manager for the socket address.
@param address The socket address (`sockaddr_in6`) used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified socket address.
*/
+ (instancetype)managerForAddress:(const void *)address;
/**
Initializes an instance of a network reachability manager from the specified reachability object.
@param reachability The reachability object to monitor.
@return An initialized network reachability manager, actively monitoring the specified reachability.
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
///--------------------------------------------------
/// @name Starting & Stopping Reachability Monitoring
///--------------------------------------------------
/**
Starts monitoring for changes in network reachability status.
*/
- (void)startMonitoring;
/**
Stops monitoring for changes in network reachability status.
*/
- (void)stopMonitoring;
///-------------------------------------------------
/// @name Getting Localized Reachability Description
///-------------------------------------------------
/**
Returns a localized string representation of the current network reachability status.
*/
- (NSString *)localizedNetworkReachabilityStatusString;
///---------------------------------------------------
/// @name Setting Network Reachability Change Callback
///---------------------------------------------------
/**
Sets a callback to be executed when the network availability of the `baseURL` host changes.
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(ZFReachabilityStatus status))block;
@end
///--------------------
/// @name Notifications
///--------------------
FOUNDATION_EXPORT NSString * const ZFReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const ZFReachabilityNotificationStatusItem;
///--------------------
/// @name Functions
///--------------------
/**
Returns a localized string representation of an `ZFReachabilityStatus` value.
*/
FOUNDATION_EXPORT NSString * ZFStringFromNetworkReachabilityStatus(ZFReachabilityStatus status);
NS_ASSUME_NONNULL_END
#endif
//
// ZFReachabilityManager.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFReachabilityManager.h"
#if !TARGET_OS_WATCH
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
NSString * const ZFReachabilityDidChangeNotification = @"com.ZFPlayer.reachability.change";
NSString * const ZFReachabilityNotificationStatusItem = @"ZFNetworkingReachabilityNotificationStatusItem";
typedef void (^ZFReachabilityStatusBlock)(ZFReachabilityStatus status);
NSString * ZFStringFromNetworkReachabilityStatus(ZFReachabilityStatus status) {
switch (status) {
case ZFReachabilityStatusNotReachable:
return NSLocalizedStringFromTable(@"Not Reachable", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableViaWiFi:
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableVia2G:
return NSLocalizedStringFromTable(@"Reachable via 2G", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableVia3G:
return NSLocalizedStringFromTable(@"Reachable via 3G", @"ZFPlayer", nil);
case ZFReachabilityStatusReachableVia4G:
return NSLocalizedStringFromTable(@"Reachable via 4G", @"ZFPlayer", nil);
case ZFReachabilityStatusUnknown:
default:
return NSLocalizedStringFromTable(@"Unknown", @"ZFPlayer", nil);
}
}
static ZFReachabilityStatus ZFReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
ZFReachabilityStatus status = ZFReachabilityStatusUnknown;
if (isNetworkReachable == NO) {
status = ZFReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc] init];
NSString *currentRadioAccessTechnology = info.currentRadioAccessTechnology;
if (currentRadioAccessTechnology) {
if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
status = ZFReachabilityStatusReachableVia4G;
} else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
status = ZFReachabilityStatusReachableVia2G;
} else {
status = ZFReachabilityStatusReachableVia3G;
}
}
}
#endif
else {
status = ZFReachabilityStatusReachableViaWiFi;
}
return status;
}
/**
* Queue a status change notification for the main thread.
*
* This is done to ensure that the notifications are received in the same order
* as they are sent. If notifications are sent directly, it is possible that
* a queued notification (for an earlier status condition) is processed after
* the later update, resulting in the listener being left in the wrong state.
*/
static void ZFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, ZFReachabilityStatusBlock block) {
ZFReachabilityStatus status = ZFReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
if (block) block(status);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSMutableDictionary *userInfo = @{}.mutableCopy;
userInfo[ZFReachabilityNotificationStatusItem] = @(status);
[notificationCenter postNotificationName:ZFReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
ZFPostReachabilityStatusChange(flags, (__bridge ZFReachabilityStatusBlock)info);
}
static const void * ZFReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
static void ZFReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
@interface ZFReachabilityManager ()
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
@property (readwrite, nonatomic, assign) ZFReachabilityStatus networkReachabilityStatus;
@property (readwrite, nonatomic, copy) ZFReachabilityStatusBlock networkReachabilityStatusBlock;
@end
@implementation ZFReachabilityManager
+ (instancetype)sharedManager {
static ZFReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)manager {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = ZFReachabilityStatusUnknown;
return self;
}
- (instancetype)init NS_UNAVAILABLE
{
return nil;
}
- (void)dealloc {
[self stopMonitoring];
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
}
}
#pragma mark -
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return (self.networkReachabilityStatus == ZFReachabilityStatusReachableVia2G ||self.networkReachabilityStatus == ZFReachabilityStatusReachableVia3G || self.networkReachabilityStatus == ZFReachabilityStatusReachableVia4G);
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == ZFReachabilityStatusReachableViaWiFi;
}
#pragma mark -
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
ZFReachabilityStatusBlock callback = ^(ZFReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, ZFReachabilityRetainCallback, ZFReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
ZFPostReachabilityStatusChange(flags, callback);
}
});
}
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
#pragma mark -
- (NSString *)localizedNetworkReachabilityStatusString {
return ZFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}
#pragma mark -
- (void)setReachabilityStatusChangeBlock:(void (^)(ZFReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
#pragma mark - NSKeyValueObserving
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
return [NSSet setWithObject:@"networkReachabilityStatus"];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
@end
#endif
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!