- 语音消息

- 图片消息(显示)
This commit is contained in:
linshujie 2026-06-08 18:42:30 +08:00
parent e849222272
commit 59be003fc7
179 changed files with 30280 additions and 11952 deletions

View File

@ -23,6 +23,7 @@ target 'QuickLocation' do
pod 'KingfisherWebP'
pod 'Masonry'
#工具
#pod 'FLEX', :configurations => ['Debug', 'AdHoc']
pod 'HXPHPicker'
@ -30,7 +31,12 @@ target 'QuickLocation' do
pod 'SGQRCode', '~> 4.1.0' #扫描
#pod 'PGDatePicker'
pod 'IQKeyboardManagerSwift'
pod 'YBImageBrowser'
pod 'YBImageBrowser/Video' #视频功能
pod 'YYImage' #, :git => 'https://github.com/QiuYeHong90/YYImage.git'
pod 'YYImage/WebP'
#UI
pod 'SnapKit'
pod 'MJRefresh'

View File

@ -152,6 +152,18 @@ PODS:
- SwiftyUserDefaults (5.3.0)
- TagListView (1.4.1)
- URLNavigator (2.5.1)
- YBImageBrowser (3.0.9):
- YBImageBrowser/Core (= 3.0.9)
- YBImageBrowser/Core (3.0.9):
- SDWebImage (>= 5.0.0)
- YYImage
- YBImageBrowser/Video (3.0.9):
- YBImageBrowser/Core
- YYImage (1.0.4):
- YYImage/Core (= 1.0.4)
- YYImage/Core (1.0.4)
- YYImage/WebP (1.0.4):
- YYImage/Core
- ZXSDK (3.3.2)
DEPENDENCIES:
@ -185,6 +197,10 @@ DEPENDENCIES:
- SwiftyUserDefaults
- TagListView (~> 1.0)
- URLNavigator
- YBImageBrowser
- YBImageBrowser/Video
- YYImage
- YYImage/WebP
SPEC REPOS:
https://gitee.com/mirrors/CocoaPods-Specs.git:
@ -234,6 +250,8 @@ SPEC REPOS:
- SwiftyUserDefaults
- TagListView
- URLNavigator
- YBImageBrowser
- YYImage
- ZXSDK
EXTERNAL SOURCES:
@ -293,8 +311,10 @@ SPEC CHECKSUMS:
SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
TagListView: a1c236d17960a0cf64156725a76ce6d73ebce2be
URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d
YBImageBrowser: 7ecc8bf33ffa5f3b94c397c29b4f3638dd37f527
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
PODFILE CHECKSUM: d9389fd0d3da8895e9ddac8adbec655edaf9bb3d
PODFILE CHECKSUM: 7834e369bb92eca27bfaa0253f39e8e939801cd4
COCOAPODS: 1.16.2

22
Pods/Manifest.lock generated
View File

@ -152,6 +152,18 @@ PODS:
- SwiftyUserDefaults (5.3.0)
- TagListView (1.4.1)
- URLNavigator (2.5.1)
- YBImageBrowser (3.0.9):
- YBImageBrowser/Core (= 3.0.9)
- YBImageBrowser/Core (3.0.9):
- SDWebImage (>= 5.0.0)
- YYImage
- YBImageBrowser/Video (3.0.9):
- YBImageBrowser/Core
- YYImage (1.0.4):
- YYImage/Core (= 1.0.4)
- YYImage/Core (1.0.4)
- YYImage/WebP (1.0.4):
- YYImage/Core
- ZXSDK (3.3.2)
DEPENDENCIES:
@ -185,6 +197,10 @@ DEPENDENCIES:
- SwiftyUserDefaults
- TagListView (~> 1.0)
- URLNavigator
- YBImageBrowser
- YBImageBrowser/Video
- YYImage
- YYImage/WebP
SPEC REPOS:
https://gitee.com/mirrors/CocoaPods-Specs.git:
@ -234,6 +250,8 @@ SPEC REPOS:
- SwiftyUserDefaults
- TagListView
- URLNavigator
- YBImageBrowser
- YYImage
- ZXSDK
EXTERNAL SOURCES:
@ -293,8 +311,10 @@ SPEC CHECKSUMS:
SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
TagListView: a1c236d17960a0cf64156725a76ce6d73ebce2be
URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d
YBImageBrowser: 7ecc8bf33ffa5f3b94c397c29b4f3638dd37f527
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
PODFILE CHECKSUM: d9389fd0d3da8895e9ddac8adbec655edaf9bb3d
PODFILE CHECKSUM: 7834e369bb92eca27bfaa0253f39e8e939801cd4
COCOAPODS: 1.16.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B97FC50FB8C4390DDCCA281E85E7AD84"
BuildableName = "YBImageBrowser.framework"
BlueprintName = "YBImageBrowser"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "822E44240F2922DAB12018A6B649BD19"
BuildableName = "YYImage.framework"
BlueprintName = "YYImage"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -344,6 +344,16 @@
<key>isShown</key>
<false/>
</dict>
<key>YBImageBrowser.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>YYImage.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>ZXSDK.xcscheme</key>
<dict>
<key>isShown</key>

View File

@ -2101,6 +2101,57 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## YBImageBrowser
MIT License
Copyright (c) 2019 杨波
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.
## YYImage
The MIT License (MIT)
Copyright (c) 2015 ibireme <ibireme@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.
## libwebp
Copyright (c) 2010, Google Inc. All rights reserved.

View File

@ -2382,6 +2382,69 @@ SOFTWARE.
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2019 杨波
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>YBImageBrowser</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2015 ibireme &lt;ibireme@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>YYImage</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2010, Google Inc. All rights reserved.

View File

@ -36,5 +36,7 @@ ${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework
${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework
${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework
${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.framework
${BUILT_PRODUCTS_DIR}/YBImageBrowser/YBImageBrowser.framework
${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework
${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework

View File

@ -35,5 +35,7 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyUserDefaults.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TagListView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/URLNavigator.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YBImageBrowser.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework

View File

@ -36,5 +36,7 @@ ${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework
${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework
${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework
${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.framework
${BUILT_PRODUCTS_DIR}/YBImageBrowser/YBImageBrowser.framework
${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework
${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework

View File

@ -35,5 +35,7 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyUserDefaults.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TagListView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/URLNavigator.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YBImageBrowser.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework

View File

@ -213,6 +213,8 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework"
install_framework "${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YBImageBrowser/YBImageBrowser.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework"
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
fi
@ -254,6 +256,8 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework"
install_framework "${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YBImageBrowser/YBImageBrowser.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework"
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
fi

View File

@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
ARCHS = $(ARCHS_STANDARD)
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
ENABLE_USER_SCRIPT_SANDBOXING = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "Combine" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "Moya" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AssetsLibrary" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
ARCHS = $(ARCHS_STANDARD)
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
ENABLE_USER_SCRIPT_SANDBOXING = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "Combine" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "Moya" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AssetsLibrary" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@ -0,0 +1,26 @@
<?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>${PODS_DEVELOPMENT_LANGUAGE}</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>3.0.9</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_YBImageBrowser : NSObject
@end
@implementation PodsDummy_YBImageBrowser
@end

View File

@ -0,0 +1,12 @@
#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

View File

@ -0,0 +1,62 @@
#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 "YBIBAuxiliaryViewHandler.h"
#import "YBIBLoadingView.h"
#import "YBIBToastView.h"
#import "NSObject+YBImageBrowser.h"
#import "YBIBAnimatedTransition.h"
#import "YBIBCollectionView.h"
#import "YBIBCollectionViewLayout.h"
#import "YBIBContainerView.h"
#import "YBIBDataMediator.h"
#import "YBIBScreenRotationHandler.h"
#import "YBImageBrowser+Internal.h"
#import "YBIBCopywriter.h"
#import "YBIBIconManager.h"
#import "YBIBPhotoAlbumManager.h"
#import "YBIBSentinel.h"
#import "YBIBUtilities.h"
#import "YBIBImageCache+Internal.h"
#import "YBIBImageCache.h"
#import "YBIBImageCell+Internal.h"
#import "YBIBImageCell.h"
#import "YBIBImageData+Internal.h"
#import "YBIBImageData.h"
#import "YBIBImageLayout.h"
#import "YBIBImageScrollView.h"
#import "YBIBInteractionProfile.h"
#import "YBImage.h"
#import "YBIBCellProtocol.h"
#import "YBIBDataProtocol.h"
#import "YBIBGetBaseInfoProtocol.h"
#import "YBIBOperateBrowserProtocol.h"
#import "YBIBOrientationReceiveProtocol.h"
#import "YBImageBrowserDataSource.h"
#import "YBImageBrowserDelegate.h"
#import "YBIBSheetView.h"
#import "YBIBToolViewHandler.h"
#import "YBIBTopView.h"
#import "YBIBDefaultWebImageMediator.h"
#import "YBIBWebImageMediator.h"
#import "YBImageBrowser.h"
#import "YBIBVideoActionBar.h"
#import "YBIBVideoCell+Internal.h"
#import "YBIBVideoCell.h"
#import "YBIBVideoData+Internal.h"
#import "YBIBVideoData.h"
#import "YBIBVideoTopBar.h"
#import "YBIBVideoView.h"
FOUNDATION_EXPORT double YBImageBrowserVersionNumber;
FOUNDATION_EXPORT const unsigned char YBImageBrowserVersionString[];

View File

@ -0,0 +1,14 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "AssetsLibrary" -framework "CoreFoundation" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "SDWebImage" -framework "UIKit" -framework "YYImage"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/YBImageBrowser
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

View File

@ -0,0 +1,6 @@
framework module YBImageBrowser {
umbrella header "YBImageBrowser-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,14 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "AssetsLibrary" -framework "CoreFoundation" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "SDWebImage" -framework "UIKit" -framework "YYImage"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/YBImageBrowser
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

View File

@ -0,0 +1,26 @@
<?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>${PODS_DEVELOPMENT_LANGUAGE}</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>1.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_YYImage : NSObject
@end
@implementation PodsDummy_YYImage
@end

View File

@ -0,0 +1,12 @@
#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

View File

@ -0,0 +1,21 @@
#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 "YYAnimatedImageView.h"
#import "YYFrameImage.h"
#import "YYImage.h"
#import "YYImageCoder.h"
#import "YYSpriteSheetImage.h"
FOUNDATION_EXPORT double YYImageVersionNumber;
FOUNDATION_EXPORT const unsigned char YYImageVersionString[];

View File

@ -0,0 +1,14 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYImage
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -l"z" -framework "Accelerate" -framework "AssetsLibrary" -framework "CoreFoundation" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit" -framework "WebP"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYImage
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

View File

@ -0,0 +1,6 @@
framework module YYImage {
umbrella header "YYImage-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,14 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYImage
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -l"z" -framework "Accelerate" -framework "AssetsLibrary" -framework "CoreFoundation" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit" -framework "WebP"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYImage
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

21
Pods/YBImageBrowser/LICENSE generated Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 杨波
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.

229
Pods/YBImageBrowser/README.md generated Normal file
View File

@ -0,0 +1,229 @@
![](https://github.com/indulgeIn/YBImageBrowser/blob/master/Images/banner.png)
[![CocoaPods](https://img.shields.io/cocoapods/v/YBImageBrowser.svg)](https://cocoapods.org/pods/YBImageBrowser)&nbsp;
[![CocoaPods](https://img.shields.io/cocoapods/p/YBImageBrowser.svg)](https://github.com/indulgeIn/YBImageBrowser)&nbsp;
[![License](https://img.shields.io/github/license/indulgeIn/YBImageBrowser.svg)](https://github.com/indulgeIn/YBImageBrowser)&nbsp;
**iOS 图片浏览器,功能强大,易于拓展,性能优化和内存控制让其运行更加的流畅和稳健。**
##### 相关文章:
##### [YBImageBrowser 重构心得:如何优化架构、性能、内存?](https://www.jianshu.com/p/ef53d0094437)
##### [避免 iOS 组件依赖冲突的小技巧](https://www.jianshu.com/p/0e3283275300)
## 注意事项
#### 关于 3.x 版本 (使用 2.x 版本请切换到 store_2.x 分支)
为了彻底解决 2.x 版本的设计缺陷和代码漏洞,特花费大量业余时间进行了 3.x 深度重构,所以没办法做到向下兼容,希望社区朋友们能体谅,根据情况进行版本迁移。
3.x 版本有着更科学的架构,更极致的性能提升,更严格的内存控制,使用起来会更得心应手,也便于将来的迭代优化。
#### 提问须知
考虑到笔者的精力问题,遇到问题请先查看 API、效仿 Demo、阅读 README、搜索 Issues。请不要提出与组件无关的问题比如 CocoaPods 的错误,如果是 BUG 或 Feature 最好是提 Issue。
# 目录
* [预览](#预览)
* [特性](#特性)
* [安装](#安装)
* [用法](#用法)
* [常见问题](#常见问题)
# 预览
![](https://github.com/indulgeIn/YBImageBrowser/blob/master/Images/preview.gif)
# 特性
- 支持 GIFAPNGWebP 等本地和网络图片类型(由 YYImage、SDWebImage 提供支持)。
- 支持系统相册图片和视频。
- 支持简单的视频播放。
- 支持高清图浏览。
- 支持图片预处理(比如添加水印)。
- 支持根据图片的大小判断是否需要预先解码(精确控制内存)。
- 支持图片压缩、裁剪的界限设定。
- 支持修改下载图片的 NSURLRequest。
- 支持主动旋转或跟随控制器旋转。
- 支持自定义图标。
- 支持自定义 Toast/Loading。
- 支持自定义文案(默认提供中文和英文)。
- 支持自定义工具视图(比如查看原图功能)。
- 支持自定义 Cell比如添加一个广告模块
- 支持添加到其它父视图上使用(比如加到控制器上)。
- 支持转场动效、图片布局等深度定制。
- 支持数据重载、局部更新。
- 支持低粒度的内存控制和性能调优。
- 极致的性能优化和严格的内存控制让其运行更加的流畅和稳健。
# 安装
## CocoaPods
支持分库导入,核心部分就是图片浏览功能,视频播放作为拓展功能按需导入。
1. 在 Podfile 中添加:
```
pod 'YBImageBrowser'
pod 'YBImageBrowser/Video' //视频功能需添加
```
2. 执行 `pod install``pod update`
3. 导入 `<YBImageBrowser/YBImageBrowser.h>`,视频功能需导入`<YBImageBrowser/YBIBVideoData.h>`。
4. 注意:如果你需要支持 WebP可以在 Podfile 中添加 `pod 'YYImage/WebP'`
若搜索不到库,可执行`pod repo update`,或使用 `rm ~/Library/Caches/CocoaPods/search_index.json` 移除本地索引然后再执行安装,或更新一下 CocoaPods 版本。
#### 去除 SDWebImage 的依赖(版本需 >= 3.0.4
Podfile 相应的配置变为:
```
pod 'YBImageBrowser/NOSD'
pod 'YBImageBrowser/VideoNOSD' //视频功能需添加
```
这时你必须定义一个类实现`YBIBWebImageMediator`协议,并赋值给`YBImageBrowser`类的`webImageMediator`属性(可以参考 `YBIBDefaultWebImageMediator`的实现)。
## 手动导入
1. 下载 YBImageBrowser 文件夹所有内容并且拖入你的工程中,视频功能还需下载 Video 文件夹所有内容。
2. 链接以下 frameworks
* SDWebImage
* YYImage
3. 导入 `YBImageBrowser.h`,视频功能需导入`YBIBVideoData.h`
4. 注意:如果你需要支持 WebP可以在 Podfile 中添加 `pod 'YYImage/WebP'`,或者到手动下载 [YYImage 仓库](https://github.com/ibireme/YYImage) 的 webP 支持文件。
# 用法
初始化`YBImageBrowser`并且赋值数据源`id<YBIBDataProtocol>`,默认提供`YBIBImageData` (图片) 和`YBIBVideoData` (视频) 两种数据源。
图片处理是组件的核心,笔者精力有限,视频播放做得很轻量,若有更高的要求最好是自定义 Cell望体谅。
Demo 中提供了很多示例代码,演示较复杂的拓展方式,所以若需要深度定制最好是下载 Demo 查看。
建议不对`YBImageBrowser`进行复用,目前还存在一些逻辑漏洞。
## 基本使用
```
// 本地图片
YBIBImageData *data0 = [YBIBImageData new];
data0.imageName = ...;
data0.projectiveView = ...;
// 网络图片
YBIBImageData *data1 = [YBIBImageData new];
data1.imageURL = ...;
data1.projectiveView = ...;
// 视频
YBIBVideoData *data2 = [YBIBVideoData new];
data2.videoURL = ...;
data2.projectiveView = ...;
YBImageBrowser *browser = [YBImageBrowser new];
browser.dataSourceArray = @[data0, data1, data2];
browser.currentPage = ...;
[browser show];
```
## 设置支持的旋转方向
当图片浏览器依托的 UIViewController 仅支持一个方向:
这种情况通过`YBImageBrowser.new.supportedOrientations`设置图片浏览器支持的旋转方向。
否则:
上面的属性将失效,图片浏览器会跟随控制器的旋转而旋转,由于各种原因这种情况的旋转过渡有瑕疵,建议不使用这种方式。
## 自定义图标
修改`YBIBIconManager.sharedManager`实例的属性。
## 自定义文案
修改`YBIBCopywriter.sharedCopywriter`实例的属性。
## 自定义 Toast / Loading
实现`YBIBAuxiliaryViewHandler`协议,并且赋值给`YBImageBrowser.new.auxiliaryViewHandler`属性,可参考和协议同名的默认实现类。
## 自定义工具视图ToolView
默认实现的`YBImageBrowser.new.defaultToolViewHandler`处理器可以做一些属性配置,当满足不了业务需求时,最好是进行自定义,参考默认实现或 Demo 中“查看原图”功能实现。
定义一个或多个类实现`YBIBToolViewHandler`协议,并且装入`YBImageBrowser.new.toolViewHandlers`数组属性。建议使用一个中介者来实现这个协议,然后所有的工具视图都由这个中介者来管理,当然也可以让每一个自定义的工具 UIView 都实现`YBIBToolViewHandler`协议,请根据具体需求取舍。
## 自定义 Cell
当默认提供的`YBIBImageData` (图片) 和`YBIBVideoData` (视频) 满足不了需求时,可自定义拓展 Cell参考默认实现或 Demo 中的示例代码。
定义一个实现`YBIBCellProtocol`协议的`UICollectionViewCell`类和一个实现`YBIBDataProtocol`协议的数据类,当要求不高时实现必选协议方法就能跑起来了,若对交互有要求就相对比较复杂,最好是参考默认的交互动效实现。
在某些场景下,甚至可以直接继承项目中的 Cell 来做自定义。
# 常见问题
## SDWebImage Pods 版本兼容问题
SDWebImage 有两种情况会出现兼容问题:该库对 SDWebImage 采用模糊向上依赖,但将来 SDWebImage 可能没做好向下兼容;当其它库依赖 SDWebImage 更低或更高 API 不兼容版本。对于这种情况,可以尝试以下方式解决:
- Podfile 中采用去除 SDWebImage 依赖的方式导入,只需要实现一个中介者(见[安装](#安装)部分)。
- 更改其它库对 SDWebImage 的依赖版本。
- 手动导入 YBImageBrowser然后修改`YBIBDefaultWebImageMediator`文件。
为什么不去除依赖 SDWebImage 自己实现?时间成本太高。
为什么不拖入 SDWebImage 修改类名?会扩大组件的体积,若外部有 SDWebImage 就存在一份多余代码。
## 依赖的 YYImage 与项目依赖的 YYKit 冲突
实际上 YYKit 有把各个组件拆分出来,建议项目中分开导入:
```
pod 'YYModel'
pod 'YYCache'
pod 'YYImage'
pod 'YYWebImage'
pod 'YYText'
...
```
而且这样更灵活便于取舍。
## 低内存设备 OOM 问题
组件内部会降低在低内存设备上的性能,减小内存占用,但若高清图过多,可能需要手动去控制(以下是硬件消耗很低的状态):
```
YBIBImageData *data = YBIBImageData.new;
// 取消预解码
data.shouldPreDecodeAsync = NO;
// 直接设大触发裁剪比例,绘制更小的裁剪区域压力更小,不过可能会让用户感觉奇怪,放很大才开始裁剪显示高清局部(这个属性很多时候不需要显式设置,内部会动态计算)
data.cuttingZoomScale = 10;
YBImageBrowser *browser = YBImageBrowser.new;
// 调低图片的缓存数量
browser.ybib_imageCache.imageCacheCountLimit = 1;
// 预加载数量设为 0
browser.preloadCount = 0;
```
## 视频播放功能简陋
关于大家提的关于视频的需求,有些成本过高,笔者精力有限望体谅。若组件默认的视频播放器满足不了需求,就自定义一个 Cell 吧,把成熟的播放器集成到组件中肯定更加的稳定。
## 关于 Swift 版本
考虑时间成本,目前没有写 Swift 版本的计划。

View File

@ -0,0 +1,44 @@
//
// YBIBVideoActionBar.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/11.
// Copyright © 2019 杨波. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class YBIBVideoActionBar;
@protocol YBIBVideoActionBarDelegate <NSObject>
@required
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPlayButton:(UIButton *)playButton;
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPauseButton:(UIButton *)pauseButton;
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar changeValue:(float)value;
@end
@interface YBIBVideoActionBar : UIView
@property (nonatomic, weak) id<YBIBVideoActionBarDelegate> delegate;
- (void)setMaxValue:(float)value;
- (void)setCurrentValue:(float)value;
- (void)pause;
- (void)play;
+ (CGFloat)defaultHeight;
@property (nonatomic, assign, readonly) BOOL isTouchInside;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,191 @@
//
// YBIBVideoActionBar.m
// YBImageBrowserDemo
//
// Created by on 2019/7/11.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBVideoActionBar.h"
#import "YBIBIconManager.h"
@interface YBVideoBrowseActionSlider : UISlider
@end
@implementation YBVideoBrowseActionSlider
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setThumbImage:YBIBIconManager.sharedManager.videoDragCircleImage() forState:UIControlStateNormal];
self.minimumTrackTintColor = UIColor.whiteColor;
self.maximumTrackTintColor = [UIColor.whiteColor colorWithAlphaComponent:0.5];
self.layer.shadowColor = UIColor.darkGrayColor.CGColor;
self.layer.shadowOffset = CGSizeMake(0, 1);
self.layer.shadowOpacity = 1;
self.layer.shadowRadius = 4;
}
return self;
}
- (CGRect)trackRectForBounds:(CGRect)bounds {
CGRect frame = [super trackRectForBounds:bounds];
return CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 2);
}
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
CGRect frame = [super thumbRectForBounds:bounds trackRect:rect value:value];
return CGRectMake(frame.origin.x - 10, frame.origin.y - 10, frame.size.width + 20, frame.size.height + 20);
}
@end
@interface YBIBVideoActionBar ()
@property (nonatomic, strong) UIButton *playButton;
@property (nonatomic, strong) UILabel *preTimeLabel;
@property (nonatomic, strong) UILabel *sufTimeLabel;
@property (nonatomic, strong) YBVideoBrowseActionSlider *slider;
@end
@implementation YBIBVideoActionBar {
BOOL _dragging;
}
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_dragging = NO;
[self addSubview:self.playButton];
[self addSubview:self.preTimeLabel];
[self addSubview:self.sufTimeLabel];
[self addSubview:self.slider];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat width = self.bounds.size.width, height = self.bounds.size.height, labelWidth = 55, buttonWidth = 44, labelOffset = 10;
CGFloat imageWidth = YBIBIconManager.sharedManager.videoPlayImage().size.width;
CGFloat offset = (buttonWidth - imageWidth) * 0.5;
self.playButton.frame = CGRectMake(10, 0, buttonWidth, height);
self.preTimeLabel.frame = CGRectMake(CGRectGetMaxX(self.playButton.frame) + labelOffset - offset, 0, labelWidth, height);
self.sufTimeLabel.frame = CGRectMake(width - labelWidth - labelOffset, 0, labelWidth, height);
self.slider.frame = CGRectMake(CGRectGetMaxX(self.preTimeLabel.frame), 0, CGRectGetMinX(self.sufTimeLabel.frame) - CGRectGetMaxX(self.preTimeLabel.frame), height);
}
#pragma mark - public
+ (CGFloat)defaultHeight {
return 44;
}
- (void)setMaxValue:(float)value {
self.slider.maximumValue = value;
self.sufTimeLabel.attributedText = [self.class timeformatFromSeconds:value];
}
- (void)setCurrentValue:(float)value {
if (!_dragging) {
[self.slider setValue:value animated:YES];
}
self.preTimeLabel.attributedText = [self.class timeformatFromSeconds:value];
}
- (void)pause {
self.playButton.selected = NO;
}
- (void)play {
_dragging = NO;
self.playButton.selected = YES;
self.slider.userInteractionEnabled = YES;
}
#pragma mark - private
+ (NSAttributedString *)timeformatFromSeconds:(NSInteger)seconds {
NSInteger hour = seconds / 3600, min = (seconds % 3600) / 60, sec = seconds % 60;
NSString *text = seconds > 3600 ? [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hour, (long)min, (long)sec] : [NSString stringWithFormat:@"%02ld:%02ld", (long)min, (long)sec];
NSShadow *shadow = [NSShadow new];
shadow.shadowBlurRadius = 4;
shadow.shadowOffset = CGSizeMake(0, 1);
shadow.shadowColor = UIColor.darkGrayColor;
NSAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:text attributes:@{NSShadowAttributeName:shadow, NSFontAttributeName:[UIFont boldSystemFontOfSize:11]}];
return attr;
}
#pragma mark - touch event
- (void)clickPlayButton:(UIButton *)button {
button.userInteractionEnabled = NO;
if (button.selected) {
[self.delegate yb_videoActionBar:self clickPauseButton:button];
} else {
[self.delegate yb_videoActionBar:self clickPlayButton:button];
}
button.userInteractionEnabled = YES;
}
- (void)respondsToSliderTouchFinished:(UISlider *)slider {
[self.delegate yb_videoActionBar:self changeValue:slider.value];
}
- (void)respondsToSliderTouchDown:(UISlider *)slider {
_dragging = YES;
slider.userInteractionEnabled = NO;
}
#pragma mark - getters
- (UIButton *)playButton {
if (!_playButton) {
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_playButton setImage:YBIBIconManager.sharedManager.videoPlayImage() forState:UIControlStateNormal];
[_playButton setImage:YBIBIconManager.sharedManager.videoPauseImage() forState:UIControlStateSelected];
[_playButton addTarget:self action:@selector(clickPlayButton:) forControlEvents:UIControlEventTouchUpInside];
_playButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
_playButton.layer.shadowOffset = CGSizeMake(0, 1);
_playButton.layer.shadowOpacity = 1;
_playButton.layer.shadowRadius = 4;
}
return _playButton;
}
- (UILabel *)preTimeLabel {
if (!_preTimeLabel) {
_preTimeLabel = [UILabel new];
_preTimeLabel.attributedText = [self.class timeformatFromSeconds:0];
_preTimeLabel.adjustsFontSizeToFitWidth = YES;
_preTimeLabel.textAlignment = NSTextAlignmentCenter;
_preTimeLabel.textColor = [UIColor.whiteColor colorWithAlphaComponent:0.9];
}
return _preTimeLabel;
}
- (UILabel *)sufTimeLabel {
if (!_sufTimeLabel) {
_sufTimeLabel = [UILabel new];
_sufTimeLabel.attributedText = [self.class timeformatFromSeconds:0];
_sufTimeLabel.adjustsFontSizeToFitWidth = YES;
_sufTimeLabel.textAlignment = NSTextAlignmentCenter;
_sufTimeLabel.textColor = [UIColor.whiteColor colorWithAlphaComponent:0.9];
}
return _sufTimeLabel;
}
- (YBVideoBrowseActionSlider *)slider {
if (!_slider) {
_slider = [YBVideoBrowseActionSlider new];
[_slider addTarget:self action:@selector(respondsToSliderTouchFinished:) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchCancel|UIControlEventTouchUpOutside];
[_slider addTarget:self action:@selector(respondsToSliderTouchDown:) forControlEvents:UIControlEventTouchDown];
}
return _slider;
}
- (BOOL)isTouchInside {
return self.slider.isTouchInside;
}
@end

View File

@ -0,0 +1,20 @@
//
// YBIBVideoCell+Internal.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/12/23.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBIBVideoCell.h"
#import "YBIBVideoView.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBVideoCell ()
@property (nonatomic, strong) YBIBVideoView *videoView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,17 @@
//
// YBIBVideoCell.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/10.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBIBCellProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBVideoCell : UICollectionViewCell <YBIBCellProtocol>
@end
NS_ASSUME_NONNULL_END

448
Pods/YBImageBrowser/Video/YBIBVideoCell.m generated Normal file
View File

@ -0,0 +1,448 @@
//
// YBIBVideoCell.m
// YBImageBrowserDemo
//
// Created by on 2019/7/10.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBVideoCell.h"
#import "YBIBVideoData.h"
#import "YBIBVideoData+Internal.h"
#import "YBIBCopywriter.h"
#import "YBIBIconManager.h"
#import <objc/runtime.h>
#import "YBIBVideoCell+Internal.h"
@interface NSObject (YBIBVideoPlayingRecord)
- (void)ybib_videoPlayingAdd:(NSObject *)obj;
- (void)ybib_videoPlayingRemove:(NSObject *)obj;
- (BOOL)ybib_noVideoPlaying;
@end
@implementation NSObject (YBIBVideoPlayingRecord)
- (NSMutableSet *)ybib_videoPlayingSet {
static void *kRecordKey = &kRecordKey;
NSMutableSet *set = objc_getAssociatedObject(self, kRecordKey);
if (!set) {
set = [NSMutableSet set];
objc_setAssociatedObject(self, kRecordKey, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return set;
}
- (void)ybib_videoPlayingAdd:(NSObject *)obj {
[[self ybib_videoPlayingSet] addObject:[NSString stringWithFormat:@"%p", obj]];
}
- (void)ybib_videoPlayingRemove:(NSObject *)obj {
[[self ybib_videoPlayingSet] removeObject:[NSString stringWithFormat:@"%p", obj]];
}
- (BOOL)ybib_noVideoPlaying {
return [self ybib_videoPlayingSet].count == 0;
}
@end
@interface YBIBVideoCell () <YBIBVideoDataDelegate, YBIBVideoViewDelegate, UIGestureRecognizerDelegate>
@end
@implementation YBIBVideoCell {
CGPoint _interactStartPoint;
BOOL _interacting;
}
#pragma mark - life cycle
- (void)dealloc {
[self.yb_backView ybib_videoPlayingRemove:self];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initValue];
[self.contentView addSubview:self.videoView];
[self addGesture];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.videoView.frame = self.bounds;
}
- (void)initValue {
_interactStartPoint = CGPointZero;
_interacting = NO;
}
- (void)prepareForReuse {
((YBIBVideoData *)self.yb_cellData).delegate = nil;
self.videoView.thumbImageView.image = nil;
[self hideAuxiliaryView];
[self.videoView reset];
self.videoView.asset = nil;
[super prepareForReuse];
}
#pragma mark - private
- (void)hideAuxiliaryView {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
[self.yb_auxiliaryViewHandler() yb_hideToastWithContainer:self];
}
- (void)updateImageLayoutWithOrientation:(UIDeviceOrientation)orientation previousImageSize:(CGSize)previousImageSize {
YBIBVideoData *data = self.yb_cellData;
UIImage *image = self.videoView.thumbImageView.image;
CGSize imageSize = image.size;
CGRect imageViewFrame = [data yb_imageViewFrameWithContainerSize:self.yb_containerSize(orientation) imageSize:imageSize orientation:orientation];
CGFloat scale;
if (previousImageSize.width > 0 && previousImageSize.height > 0) {
scale = imageSize.width / imageSize.height - previousImageSize.width / previousImageSize.height;
} else {
scale = 0;
}
// '0.001' is admissible error.
if (ABS(scale) <= 0.001) {
self.videoView.thumbImageView.frame = imageViewFrame;
} else {
[UIView animateWithDuration:0.25 animations:^{
self.videoView.thumbImageView.frame = imageViewFrame;
}];
}
}
- (void)hideBrowser {
((YBIBVideoData *)self.yb_cellData).delegate = nil;
self.videoView.thumbImageView.hidden = NO;
self.videoView.autoPlayCount = 0;
[self.videoView reset];
[self.videoView hideToolBar:YES];
[self.videoView hidePlayButton];
self.yb_hideBrowser();
_interacting = NO;
}
- (void)hideToolViews:(BOOL)hide {
if (hide) {
self.yb_hideToolViews(YES);
} else {
if ([self.yb_backView ybib_noVideoPlaying]) {
self.yb_hideToolViews(NO);
}
}
}
#pragma mark - <YBIBCellProtocol>
@synthesize yb_currentOrientation = _yb_currentOrientation;
@synthesize yb_containerSize = _yb_containerSize;
@synthesize yb_backView = _yb_backView;
@synthesize yb_collectionView = _yb_collectionView;
@synthesize yb_isTransitioning = _yb_isTransitioning;
@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
@synthesize yb_hideStatusBar = _yb_hideStatusBar;
@synthesize yb_hideBrowser = _yb_hideBrowser;
@synthesize yb_hideToolViews = _yb_hideToolViews;
@synthesize yb_cellData = _yb_cellData;
@synthesize yb_currentPage = _yb_currentPage;
@synthesize yb_selfPage = _yb_selfPage;
@synthesize yb_cellIsInCenter = _yb_cellIsInCenter;
@synthesize yb_isRotating = _yb_isRotating;
- (void)setYb_cellData:(id<YBIBDataProtocol>)yb_cellData {
_yb_cellData = yb_cellData;
YBIBVideoData *data = (YBIBVideoData *)yb_cellData;
data.delegate = self;
UIDeviceOrientation orientation = self.yb_currentOrientation();
CGSize containerSize = self.yb_containerSize(orientation);
[self.videoView updateLayoutWithExpectOrientation:orientation containerSize:containerSize];
self.videoView.autoPlayCount = data.autoPlayCount;
self.videoView.topBar.cancelButton.hidden = data.shouldHideForkButton;
}
- (void)yb_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation {
if (_interacting) [self restoreGestureInteractionWithDuration:0];
}
- (void)yb_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation {
[self updateImageLayoutWithOrientation:orientation previousImageSize:self.videoView.thumbImageView.image.size];
CGSize containerSize = self.yb_containerSize(orientation);
[self.videoView updateLayoutWithExpectOrientation:orientation containerSize:containerSize];
}
- (UIView *)yb_foregroundView {
return self.videoView.thumbImageView;
}
- (void)yb_pageChanged {
if (self.yb_currentPage() != self.yb_selfPage()) {
[self.videoView reset];
[self hideToolViews:NO];
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
if (_interacting) [self restoreGestureInteractionWithDuration:0];
self.videoView.needAutoPlay = NO;
} else {
self.videoView.needAutoPlay = YES;
}
}
#pragma mark - <YBIBVideoDataDelegate>
- (void)yb_startLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data {}
- (void)yb_finishLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data {}
- (void)yb_startLoadingFirstFrameForData:(YBIBVideoData *)data {
if (!self.videoView.thumbImageView.image) {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
}
}
- (void)yb_finishLoadingFirstFrameForData:(YBIBVideoData *)data {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
}
- (void)yb_videoData:(YBIBVideoData *)data downloadingWithProgress:(CGFloat)progress {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self progress:progress];
}
- (void)yb_finishDownloadingForData:(YBIBVideoData *)data {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
}
- (void)yb_videoData:(YBIBVideoData *)data readyForAVAsset:(AVAsset *)asset {
self.videoView.asset = asset;
}
- (void)yb_videoData:(YBIBVideoData *)data readyForThumbImage:(UIImage *)image {
if (!self.videoView.isPlaying) {
self.videoView.thumbImageView.hidden = NO;
}
if (!self.videoView.thumbImageView.image) {
CGSize previousSize = self.videoView.thumbImageView.image.size;
self.videoView.thumbImageView.image = image;
[self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:previousSize];
}
}
- (void)yb_videoIsInvalidForData:(YBIBVideoData *)data {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
NSString *imageIsInvalid = [YBIBCopywriter sharedCopywriter].videoIsInvalid;
if (self.videoView.thumbImageView.image) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:imageIsInvalid];
} else {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self text:imageIsInvalid];
}
}
#pragma mark - <YBIBVideoViewDelegate>
- (BOOL)yb_isFreezingForVideoView:(YBIBVideoView *)view {
return self.yb_isTransitioning();
}
- (void)yb_preparePlayForVideoView:(YBIBVideoView *)view {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (!view.isPlaying && !view.isPlayFailed && self.yb_selfPage() == self.yb_currentPage()) {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
}
});
}
- (void)yb_startPlayForVideoView:(YBIBVideoView *)view {
self.videoView.thumbImageView.hidden = YES;
[self.yb_backView ybib_videoPlayingAdd:self];
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
[self hideToolViews:YES];
}
- (void)yb_didPlayToEndTimeForVideoView:(YBIBVideoView *)view {
YBIBVideoData *data = (YBIBVideoData *)self.yb_cellData;
if (data.repeatPlayCount == NSUIntegerMax) {
[view preparPlay];
} else if (data.repeatPlayCount > 0) {
--data.repeatPlayCount;
[view preparPlay];
} else {
[self hideToolViews:NO];
}
}
- (void)yb_finishPlayForVideoView:(YBIBVideoView *)view {
[self.yb_backView ybib_videoPlayingRemove:self];
[self hideToolViews:NO];
}
- (void)yb_playFailedForVideoView:(YBIBVideoView *)view {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:YBIBCopywriter.sharedCopywriter.videoError];
}
- (void)yb_respondsToTapGestureForVideoView:(YBIBVideoView *)view {
if (self.yb_isRotating()) return;
YBIBVideoData *data = self.yb_cellData;
if (data.singleTouchBlock) {
data.singleTouchBlock(data);
} else {
[self hideBrowser];
}
}
- (void)yb_cancelledForVideoView:(YBIBVideoView *)view {
if (self.yb_isRotating()) return;
[self hideBrowser];
}
- (CGSize)yb_containerSizeForVideoView:(YBIBVideoView *)view {
return self.yb_containerSize(self.yb_currentOrientation());
}
- (void)yb_autoPlayCountChanged:(NSUInteger)count {
YBIBVideoData *data = (YBIBVideoData *)self.yb_cellData;
data.autoPlayCount = count;
}
#pragma mark - <UIGestureRecognizerDelegate>
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
#pragma mark - gesture
- (void)addGesture {
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToPanGesture:)];
panGesture.cancelsTouchesInView = NO;
panGesture.delegate = self;
[self.videoView.tapGesture requireGestureRecognizerToFail:panGesture];
[self.videoView addGestureRecognizer:panGesture];
}
- (void)respondsToPanGesture:(UIPanGestureRecognizer *)pan {
if (self.yb_isRotating()) return;
if ((!self.videoView.thumbImageView.image && !self.videoView.isPlaying)) return;
YBIBInteractionProfile *profile = ((YBIBVideoData *)self.yb_cellData).interactionProfile;
if (profile.disable) return;
CGPoint point = [pan locationInView:self];
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
if (pan.state == UIGestureRecognizerStateBegan) {
_interactStartPoint = point;
} else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateRecognized || pan.state == UIGestureRecognizerStateFailed) {
// End
if (_interacting) {
CGPoint velocity = [pan velocityInView:self.videoView];
BOOL velocityArrive = ABS(velocity.y) > profile.dismissVelocityY;
BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > containerSize.height * profile.dismissScale;
BOOL shouldDismiss = distanceArrive || velocityArrive;
if (shouldDismiss) {
[self hideBrowser];
} else {
[self restoreGestureInteractionWithDuration:profile.restoreDuration];
}
}
} else if (pan.state == UIGestureRecognizerStateChanged) {
if (_interacting) {
// Change
self.videoView.center = point;
CGFloat scale = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 1.2);
if (scale > 1) scale = 1;
if (scale < 0.35) scale = 0.35;
self.videoView.transform = CGAffineTransformMakeScale(scale, scale);
CGFloat alpha = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 0.7);
if (alpha > 1) alpha = 1;
if (alpha < 0) alpha = 0;
self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:alpha];
} else {
// Start
if (CGPointEqualToPoint(_interactStartPoint, CGPointZero) || self.yb_currentPage() != self.yb_selfPage() || !self.yb_cellIsInCenter() || self.videoView.actionBar.isTouchInside) return;
CGPoint velocityPoint = [pan velocityInView:self.videoView];
CGFloat triggerDistance = profile.triggerDistance;
BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > triggerDistance && (ABS(point.x - _interactStartPoint.x) < triggerDistance && ABS(velocityPoint.x) < 500);
BOOL shouldStart = distanceArrive;
if (!shouldStart) return;
[self.videoView hideToolBar:YES];
_interactStartPoint = point;
CGRect startFrame = self.videoView.bounds;
CGFloat anchorX = (point.x - startFrame.origin.x) / startFrame.size.width,
anchorY = (point.y - startFrame.origin.y) / startFrame.size.height;
self.videoView.layer.anchorPoint = CGPointMake(anchorX, anchorY);
self.videoView.userInteractionEnabled = NO;
self.videoView.center = point;
[self hideToolViews:YES];
self.yb_hideStatusBar(NO);
self.yb_collectionView().scrollEnabled = NO;
_interacting = YES;
}
}
}
- (void)restoreGestureInteractionWithDuration:(NSTimeInterval)duration {
[self.videoView hideToolBar:NO];
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
void (^animations)(void) = ^{
self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:1];
CGPoint anchorPoint = self.videoView.layer.anchorPoint;
self.videoView.center = CGPointMake(containerSize.width * anchorPoint.x, containerSize.height * anchorPoint.y);
self.videoView.transform = CGAffineTransformIdentity;
};
void (^completion)(BOOL finished) = ^(BOOL finished){
self.videoView.layer.anchorPoint = CGPointMake(0.5, 0.5);
self.videoView.center = CGPointMake(containerSize.width * 0.5, containerSize.height * 0.5);
self.videoView.userInteractionEnabled = YES;
self.yb_hideStatusBar(YES);
self.yb_collectionView().scrollEnabled = YES;
if (!self.videoView.isPlaying) [self hideToolViews:NO];;
self->_interactStartPoint = CGPointZero;
self->_interacting = NO;
};
if (duration <= 0) {
animations();
completion(NO);
} else {
[UIView animateWithDuration:duration animations:animations completion:completion];
}
}
#pragma mark - getters & setters
- (YBIBVideoView *)videoView {
if (!_videoView) {
_videoView = [YBIBVideoView new];
_videoView.delegate = self;
}
return _videoView;
}
@end

View File

@ -0,0 +1,50 @@
//
// YBIBVideoData+Internal.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/11.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBIBVideoData.h"
NS_ASSUME_NONNULL_BEGIN
@class YBIBVideoData;
@protocol YBIBVideoDataDelegate <NSObject>
@required
- (void)yb_startLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data;
- (void)yb_finishLoadingAVAssetFromPHAssetForData:(YBIBVideoData *)data;
- (void)yb_startLoadingFirstFrameForData:(YBIBVideoData *)data;
- (void)yb_finishLoadingFirstFrameForData:(YBIBVideoData *)data;
- (void)yb_videoData:(YBIBVideoData *)data downloadingWithProgress:(CGFloat)progress;
- (void)yb_finishDownloadingForData:(YBIBVideoData *)data;
- (void)yb_videoData:(YBIBVideoData *)data readyForThumbImage:(UIImage *)image;
- (void)yb_videoData:(YBIBVideoData *)data readyForAVAsset:(AVAsset *)asset;
- (void)yb_videoIsInvalidForData:(YBIBVideoData *)data;
@end
@interface YBIBVideoData ()
@property (nonatomic, assign, getter=isLoadingAVAssetFromPHAsset) BOOL loadingAVAssetFromPHAsset;
@property (nonatomic, assign, getter=isLoadingFirstFrame) BOOL loadingFirstFrame;
@property (nonatomic, assign, getter=isDownloading) BOOL downloading;
@property (nonatomic, weak) id<YBIBVideoDataDelegate> delegate;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,64 @@
//
// YBIBVideoData.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/10.
// Copyright © 2019 杨波. All rights reserved.
//
#import <Photos/Photos.h>
#import "YBIBDataProtocol.h"
#import "YBIBInteractionProfile.h"
NS_ASSUME_NONNULL_BEGIN
@class YBIBVideoData;
/// 单击事件的处理闭包
typedef void (^YBIBVideoSingleTouchBlock)(YBIBVideoData *videoData);
/**
*/
@interface YBIBVideoData : NSObject <YBIBDataProtocol>
/// 视频 URL
@property (nonatomic, copy, nullable) NSURL *videoURL;
/// 相册视频资源
@property (nonatomic, strong, nullable) PHAsset *videoPHAsset;
/// 视频 AVAsset (通常使用 AVURLAsset)
@property (nonatomic, strong, nullable) AVAsset *videoAVAsset;
/// 投影视图,当前数据模型对应外界业务的 UIView (通常为 UIImageView),做转场动效用
@property (nonatomic, weak, nullable) __kindof UIView *projectiveView;
/// 预览图/缩约图,若 projectiveView 存在且是 UIImageView 类型将会自动获取缩约图
@property (nonatomic, strong, nullable) UIImage *thumbImage;
/// 是否允许保存到相册
@property (nonatomic, assign) BOOL allowSaveToPhotoAlbum;
/// 自动播放次数,默认为 0NSUIntegerMax 表示无限次
@property (nonatomic, assign) NSUInteger autoPlayCount;
/// 重复播放次数,默认为 0NSUIntegerMax 表示无限次
@property (nonatomic, assign) NSUInteger repeatPlayCount;
/// 预留属性可随意使用
@property (nonatomic, strong, nullable) id extraData;
/// 手势交互动效配置文件
@property (nonatomic, strong) YBIBInteractionProfile *interactionProfile;
/// 单击的处理(视频未播放时),默认是退出图片浏览器
@property (nonatomic, copy, nullable) YBIBVideoSingleTouchBlock singleTouchBlock;
/// 是否要隐藏播放时的叉叉(取消)按钮
@property (nonatomic, assign) BOOL shouldHideForkButton;
@end
NS_ASSUME_NONNULL_END

296
Pods/YBImageBrowser/Video/YBIBVideoData.m generated Normal file
View File

@ -0,0 +1,296 @@
//
// YBIBVideoData.m
// YBImageBrowserDemo
//
// Created by on 2019/7/10.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBVideoData.h"
#import "YBIBVideoCell.h"
#import "YBIBVideoData+Internal.h"
#import "YBIBUtilities.h"
#import "YBIBPhotoAlbumManager.h"
#import "YBIBCopywriter.h"
extern CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay);
@interface YBIBVideoData () <NSURLSessionDelegate>
@end
@implementation YBIBVideoData {
NSURLSessionDownloadTask *_downloadTask;
}
#pragma mark - life cycle
- (instancetype)init {
self = [super init];
if (self) {
[self initValue];
}
return self;
}
- (void)initValue {
_loadingFirstFrame = NO;
_loadingAVAssetFromPHAsset = NO;
_downloading = NO;
_interactionProfile = [YBIBInteractionProfile new];
_repeatPlayCount = 0;
_autoPlayCount = 0;
_shouldHideForkButton = NO;
_allowSaveToPhotoAlbum = YES;
}
#pragma mark - load data
- (void)loadData {
// Always load 'thumbImage'.
[self loadThumbImage];
if (self.videoAVAsset) {
[self.delegate yb_videoData:self readyForAVAsset:self.videoAVAsset];
} else if (self.videoPHAsset) {
[self loadAVAssetFromPHAsset];
} else {
[self.delegate yb_videoIsInvalidForData:self];
}
}
- (void)loadAVAssetFromPHAsset {
if (!self.videoPHAsset) return;
if (self.isLoadingAVAssetFromPHAsset) {
self.loadingAVAssetFromPHAsset = YES;
return;
}
self.loadingAVAssetFromPHAsset = YES;
[YBIBPhotoAlbumManager getAVAssetWithPHAsset:self.videoPHAsset completion:^(AVAsset * _Nullable asset) {
YBIB_DISPATCH_ASYNC_MAIN(^{
self.loadingAVAssetFromPHAsset = NO;
self.videoAVAsset = asset;
[self.delegate yb_videoData:self readyForAVAsset:self.videoAVAsset];
[self loadThumbImage];
})
}];
}
- (void)loadThumbImage {
if (self.thumbImage) {
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
} else if (self.projectiveView && [self.projectiveView isKindOfClass:UIImageView.self] && ((UIImageView *)self.projectiveView).image) {
self.thumbImage = ((UIImageView *)self.projectiveView).image;
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
} else {
[self loadThumbImage_firstFrame];
}
}
- (void)loadThumbImage_firstFrame {
if (!self.videoAVAsset) return;
if (self.isLoadingFirstFrame) {
self.loadingFirstFrame = YES;
return;
}
self.loadingFirstFrame = YES;
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
CGSize maximumSize = containerSize;
__weak typeof(self) wSelf = self;
YBIB_DISPATCH_ASYNC(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.videoAVAsset];
generator.appliesPreferredTrackTransform = YES;
generator.maximumSize = maximumSize;
NSError *error = nil;
CGImageRef cgImage = [generator copyCGImageAtTime:CMTimeMake(0, 1) actualTime:NULL error:&error];
CGImageRef decodedImage = YYCGImageCreateDecodedCopy(cgImage, YES);
UIImage *resultImage = [UIImage imageWithCGImage:decodedImage];
if (cgImage) CGImageRelease(cgImage);
if (decodedImage) CGImageRelease(decodedImage);
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingFirstFrame = NO;
if (!error && resultImage) {
self.thumbImage = resultImage;
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
}
})
})
}
#pragma mark - <YBIBDataProtocol>
@synthesize yb_currentOrientation = _yb_currentOrientation;
@synthesize yb_containerView = _yb_containerView;
@synthesize yb_containerSize = _yb_containerSize;
@synthesize yb_isHideTransitioning = _yb_isHideTransitioning;
@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
- (nonnull Class)yb_classOfCell {
return YBIBVideoCell.self;
}
- (UIView *)yb_projectiveView {
return self.projectiveView;
}
- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
if (containerSize.width <= 0 || containerSize.height <= 0 || imageSize.width <= 0 || imageSize.height <= 0) return CGRectZero;
CGFloat x = 0, y = 0, width = 0, height = 0;
if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height) {
width = containerSize.width;
height = containerSize.width * (imageSize.height / imageSize.width);
x = 0;
y = (containerSize.height - height) / 2.0;
} else {
height = containerSize.height;
width = containerSize.height * (imageSize.width / imageSize.height);
x = (containerSize.width - width) / 2.0;
y = 0;
}
return CGRectMake(x, y, width, height);
}
- (void)yb_preload {
if (!self.delegate) {
[self loadData];
}
}
- (BOOL)yb_allowSaveToPhotoAlbum {
return self.allowSaveToPhotoAlbum;
}
- (void)yb_saveToPhotoAlbum {
void(^unableToSave)(void) = ^(){
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].unableToSave];
};
if (self.videoAVAsset && [self.videoAVAsset isKindOfClass:AVURLAsset.class]) {
AVURLAsset *asset = (AVURLAsset *)self.videoAVAsset;
NSURL *URL = asset.URL;
if ([URL.scheme isEqualToString:@"file"]) {
NSString *path = URL.path;
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) {
UISaveVideoAtPathToSavedPhotosAlbum(path, self, @selector(UISaveVideoAtPathToSavedPhotosAlbum_videoPath:didFinishSavingWithError:contextInfo:), nil);
} else {
unableToSave();
}
} else if ([URL.scheme containsString:@"http"]) {
[self downloadWithURL:URL];
} else {
unableToSave();
}
} else {
unableToSave();
}
}
#pragma mark - private
- (void)UISaveVideoAtPathToSavedPhotosAlbum_videoPath:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
if (error) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
} else {
[self.yb_auxiliaryViewHandler() yb_showCorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumSuccess];
}
}
- (void)downloadWithURL:(NSURL *)URL {
if (self.isDownloading) {
self.downloading = YES;
return;
}
self.downloading = YES;
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
_downloadTask = [session downloadTaskWithURL:URL];
[_downloadTask resume];
}
#pragma mark - <NSURLSessionDelegate>
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
CGFloat progress = totalBytesWritten / (double)totalBytesExpectedToWrite;
if (progress < 0) progress = 0;
if (progress > 1) progress = 1;
[self.delegate yb_videoData:self downloadingWithProgress:progress];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error {
if (error) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].downloadFailed];
}
self.downloading = NO;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *file = [cache stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:file] error:nil];
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(file)) {
UISaveVideoAtPathToSavedPhotosAlbum(file, self, @selector(UISaveVideoAtPathToSavedPhotosAlbum_videoPath:didFinishSavingWithError:contextInfo:), nil);
} else {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
}
self.downloading = NO;
}
#pragma mark - getters & setters
- (void)setVideoURL:(NSURL *)videoURL{
_videoURL = [videoURL isKindOfClass:NSString.class] ? [NSURL URLWithString:(NSString *)videoURL] : videoURL;
self.videoAVAsset = [AVURLAsset URLAssetWithURL:_videoURL options:nil];
}
- (void)setDownloading:(BOOL)downloading {
_downloading = downloading;
if (downloading) {
[self.delegate yb_videoData:self downloadingWithProgress:0];
} else {
[self.delegate yb_finishDownloadingForData:self];
}
}
- (void)setLoadingAVAssetFromPHAsset:(BOOL)loadingAVAssetFromPHAsset {
_loadingAVAssetFromPHAsset = loadingAVAssetFromPHAsset;
if (loadingAVAssetFromPHAsset) {
[self.delegate yb_startLoadingAVAssetFromPHAssetForData:self];
} else {
[self.delegate yb_finishLoadingAVAssetFromPHAssetForData:self];
}
}
- (void)setLoadingFirstFrame:(BOOL)loadingFirstFrame {
_loadingFirstFrame = loadingFirstFrame;
if (loadingFirstFrame) {
[self.delegate yb_startLoadingFirstFrameForData:self];
} else {
[self.delegate yb_finishLoadingFirstFrameForData:self];
}
}
@synthesize delegate = _delegate;
- (void)setDelegate:(id<YBIBVideoDataDelegate>)delegate {
_delegate = delegate;
if (delegate) {
[self loadData];
}
}
- (id<YBIBVideoDataDelegate>)delegate {
// Stop sending data to the '_delegate' if it is transiting.
return self.yb_isHideTransitioning() ? nil : _delegate;
}
@end

View File

@ -0,0 +1,21 @@
//
// YBIBVideoTopBar.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/11.
// Copyright © 2019 杨波. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface YBIBVideoTopBar : UIView
@property (nonatomic, strong, readonly) UIButton *cancelButton;
+ (CGFloat)defaultHeight;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,54 @@
//
// YBIBVideoTopBar.m
// YBImageBrowserDemo
//
// Created by on 2019/7/11.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBVideoTopBar.h"
#import "YBIBIconManager.h"
@interface YBIBVideoTopBar ()
@property (nonatomic, strong) UIButton *cancelButton;
@end
@implementation YBIBVideoTopBar
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.cancelButton];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat buttonWidth = 54;
self.cancelButton.frame = CGRectMake(0, 0, buttonWidth, self.bounds.size.height);
}
#pragma mark - public
+ (CGFloat)defaultHeight {
return 50;
}
#pragma mark - getter
- (UIButton *)cancelButton {
if (!_cancelButton) {
_cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_cancelButton setImage:YBIBIconManager.sharedManager.videoCancelImage() forState:UIControlStateNormal];
_cancelButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
_cancelButton.layer.shadowOffset = CGSizeMake(0, 1);
_cancelButton.layer.shadowOpacity = 1;
_cancelButton.layer.shadowRadius = 4;
}
return _cancelButton;
}
@end

View File

@ -0,0 +1,76 @@
//
// YBIBVideoView.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/11.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBIBVideoActionBar.h"
#import "YBIBVideoTopBar.h"
NS_ASSUME_NONNULL_BEGIN
@class YBIBVideoView;
@protocol YBIBVideoViewDelegate <NSObject>
@required
- (BOOL)yb_isFreezingForVideoView:(YBIBVideoView *)view;
- (void)yb_preparePlayForVideoView:(YBIBVideoView *)view;
- (void)yb_startPlayForVideoView:(YBIBVideoView *)view;
- (void)yb_finishPlayForVideoView:(YBIBVideoView *)view;
- (void)yb_didPlayToEndTimeForVideoView:(YBIBVideoView *)view;
- (void)yb_playFailedForVideoView:(YBIBVideoView *)view;
- (void)yb_respondsToTapGestureForVideoView:(YBIBVideoView *)view;
- (void)yb_cancelledForVideoView:(YBIBVideoView *)view;
- (CGSize)yb_containerSizeForVideoView:(YBIBVideoView *)view;
- (void)yb_autoPlayCountChanged:(NSUInteger)count;
@end
@interface YBIBVideoView : UIView
@property (nonatomic, strong) UIImageView *thumbImageView;
@property (nonatomic, weak) id<YBIBVideoViewDelegate> delegate;
- (void)updateLayoutWithExpectOrientation:(UIDeviceOrientation)orientation containerSize:(CGSize)containerSize;
@property (nonatomic, strong, nullable) AVAsset *asset;
@property (nonatomic, assign, readonly, getter=isPlaying) BOOL playing;
@property (nonatomic, assign, readonly, getter=isPlayFailed) BOOL playFailed;
@property (nonatomic, assign, readonly, getter=isPreparingPlay) BOOL preparingPlay;
@property (nonatomic, strong, readonly) UITapGestureRecognizer *tapGesture;
- (void)reset;
- (void)hideToolBar:(BOOL)hide;
- (void)hidePlayButton;
- (void)preparPlay;
@property (nonatomic, assign) BOOL needAutoPlay;
@property (nonatomic, assign) NSUInteger autoPlayCount;
@property (nonatomic, strong, readonly) YBIBVideoTopBar *topBar;
@property (nonatomic, strong, readonly) YBIBVideoActionBar *actionBar;
@end
NS_ASSUME_NONNULL_END

394
Pods/YBImageBrowser/Video/YBIBVideoView.m generated Normal file
View File

@ -0,0 +1,394 @@
//
// YBIBVideoView.m
// YBImageBrowserDemo
//
// Created by on 2019/7/11.
// Copyright © 2019 . All rights reserved.
//
#import <AVFoundation/AVFoundation.h>
#import "YBIBVideoView.h"
#import "YBIBVideoActionBar.h"
#import "YBIBVideoTopBar.h"
#import "YBIBUtilities.h"
#import "YBIBIconManager.h"
@interface YBIBVideoView () <YBIBVideoActionBarDelegate>
@property (nonatomic, strong) YBIBVideoTopBar *topBar;
@property (nonatomic, strong) YBIBVideoActionBar *actionBar;
@property (nonatomic, strong) UIButton *playButton;
@end
@implementation YBIBVideoView {
AVPlayer *_player;
AVPlayerItem *_playerItem;
AVPlayerLayer *_playerLayer;
BOOL _active;
}
#pragma mark - life cycle
- (void)dealloc {
[self removeObserverForSystem];
[self reset];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initValue];
self.backgroundColor = UIColor.clearColor;
[self addSubview:self.thumbImageView];
[self addSubview:self.topBar];
[self addSubview:self.actionBar];
[self addSubview:self.playButton];
[self addObserverForSystem];
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapGesture:)];
[self addGestureRecognizer:_tapGesture];
}
return self;
}
- (void)initValue {
_playing = NO;
_active = YES;
_needAutoPlay = NO;
_autoPlayCount = 0;
_playFailed = NO;
_preparingPlay = NO;
}
#pragma mark - public
- (void)updateLayoutWithExpectOrientation:(UIDeviceOrientation)orientation containerSize:(CGSize)containerSize {
UIEdgeInsets padding = YBIBPaddingByBrowserOrientation(orientation);
CGFloat width = containerSize.width - padding.left - padding.right, height = containerSize.height;
self.topBar.frame = CGRectMake(padding.left, padding.top, width, [YBIBVideoTopBar defaultHeight]);
self.actionBar.frame = CGRectMake(padding.left, height - [YBIBVideoActionBar defaultHeight] - padding.bottom - 10, width, [YBIBVideoActionBar defaultHeight]);
self.playButton.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
_playerLayer.frame = (CGRect){CGPointZero, containerSize};
}
- (void)reset {
[self removeObserverForPlayer];
// If set '_playerLayer.player = nil' or '_player = nil', can not cancel observeing of 'addPeriodicTimeObserverForInterval'.
[_player pause];
_playerItem = nil;
[_playerLayer removeFromSuperlayer];
_playerLayer = nil;
[self finishPlay];
}
- (void)hideToolBar:(BOOL)hide {
if (hide) {
self.actionBar.hidden = YES;
self.topBar.hidden = YES;
} else if (self.isPlaying) {
self.actionBar.hidden = NO;
self.topBar.hidden = NO;
}
}
- (void)hidePlayButton {
self.playButton.hidden = YES;
}
#pragma mark - private
- (void)videoJumpWithScale:(float)scale {
CMTime startTime = CMTimeMakeWithSeconds(scale, _player.currentTime.timescale);
AVPlayer *tmpPlayer = _player;
if (CMTIME_IS_INDEFINITE(startTime) || CMTIME_IS_INVALID(startTime)) return;
[_player seekToTime:startTime toleranceBefore:CMTimeMake(1, 1000) toleranceAfter:CMTimeMake(1, 1000) completionHandler:^(BOOL finished) {
if (finished && tmpPlayer == self->_player) {
[self startPlay];
}
}];
}
- (void)preparPlay {
_preparingPlay = YES;
_playFailed = NO;
self.playButton.hidden = YES;
[self.delegate yb_preparePlayForVideoView:self];
if (!_playerLayer) {
_playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
_player = [AVPlayer playerWithPlayerItem:_playerItem];
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
_playerLayer.frame = (CGRect){CGPointZero, [self.delegate yb_containerSizeForVideoView:self]};
[self.layer insertSublayer:_playerLayer above:self.thumbImageView.layer];
[self addObserverForPlayer];
} else {
[self videoJumpWithScale:0];
}
}
- (void)startPlay {
if (_player) {
_playing = YES;
[_player play];
[self.actionBar play];
self.topBar.hidden = NO;
self.actionBar.hidden = NO;
[self.delegate yb_startPlayForVideoView:self];
}
}
- (void)finishPlay {
self.playButton.hidden = NO;
[self.actionBar setCurrentValue:0];
self.actionBar.hidden = YES;
self.topBar.hidden = YES;
_playing = NO;
[self.delegate yb_finishPlayForVideoView:self];
}
- (void)playerPause {
if (_player) {
[_player pause];
[self.actionBar pause];
}
}
- (BOOL)autoPlay {
if (self.autoPlayCount == NSUIntegerMax) {
[self preparPlay];
} else if (self.autoPlayCount > 0) {
--self.autoPlayCount;
[self.delegate yb_autoPlayCountChanged:self.autoPlayCount];
[self preparPlay];
} else {
return NO;
}
return YES;
}
#pragma mark - <YBIBVideoActionBarDelegate>
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPlayButton:(UIButton *)playButton {
[self startPlay];
}
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPauseButton:(UIButton *)pauseButton {
[self playerPause];
}
- (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar changeValue:(float)value {
[self videoJumpWithScale:value];
}
#pragma mark - observe
- (void)addObserverForPlayer {
[_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
__weak typeof(self) wSelf = self;
[_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
float currentTime = time.value / time.timescale;
[self.actionBar setCurrentValue:currentTime];
}];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
}
- (void)removeObserverForPlayer {
[_playerItem removeObserver:self forKeyPath:@"status"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if (![self.delegate yb_isFreezingForVideoView:self]) {
if (object == _playerItem) {
if ([keyPath isEqualToString:@"status"]) {
[self playerItemStatusChanged];
}
}
}
}
- (void)didPlayToEndTime:(NSNotification *)noti {
if (noti.object == _playerItem) {
[self finishPlay];
[self.delegate yb_didPlayToEndTimeForVideoView:self];
}
}
- (void)playerItemStatusChanged {
if (!_active) return;
_preparingPlay = NO;
switch (_playerItem.status) {
case AVPlayerItemStatusReadyToPlay: {
// Delay to update UI.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self startPlay];
double max = CMTimeGetSeconds(self->_playerItem.duration);
[self.actionBar setMaxValue:(isnan(max) || isinf(max)) ? 0 : max];
});
}
break;
case AVPlayerItemStatusUnknown: {
_playFailed = YES;
[self.delegate yb_playFailedForVideoView:self];
[self reset];
}
break;
case AVPlayerItemStatusFailed: {
_playFailed = YES;
[self.delegate yb_playFailedForVideoView:self];
[self reset];
}
break;
}
}
- (void)removeObserverForSystem {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
}
- (void)addObserverForSystem {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarFrame) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
_active = NO;
[self playerPause];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
_active = YES;
}
- (void)didChangeStatusBarFrame {
if ([UIApplication sharedApplication].statusBarFrame.size.height > YBIBStatusbarHeight()) {
[self playerPause];
}
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification {
YBIB_DISPATCH_ASYNC_MAIN(^{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
[self playerPause];
break;
}
})
}
#pragma mark - event
- (void)respondsToTapGesture:(UITapGestureRecognizer *)tap {
if (self.isPlaying) {
self.actionBar.hidden = !self.actionBar.isHidden;
self.topBar.hidden = !self.topBar.isHidden;
} else {
[self.delegate yb_respondsToTapGestureForVideoView:self];
}
}
- (void)clickCancelButton:(UIButton *)button {
[self.delegate yb_cancelledForVideoView:self];
}
- (void)clickPlayButton:(UIButton *)button {
[self preparPlay];
}
#pragma mark - getters & setters
- (void)setNeedAutoPlay:(BOOL)needAutoPlay {
if (needAutoPlay && _asset && !self.isPlaying) {
[self autoPlay];
} else {
_needAutoPlay = needAutoPlay;
}
}
@synthesize asset = _asset;
- (void)setAsset:(AVAsset *)asset {
_asset = asset;
if (!asset) return;
if (self.needAutoPlay) {
if (![self autoPlay]) {
self.playButton.hidden = NO;
}
self.needAutoPlay = NO;
} else {
self.playButton.hidden = NO;
}
}
- (AVAsset *)asset {
if ([_asset isKindOfClass:AVURLAsset.class]) {
_asset = [AVURLAsset assetWithURL:((AVURLAsset *)_asset).URL];
}
return _asset;
}
- (YBIBVideoTopBar *)topBar {
if (!_topBar) {
_topBar = [YBIBVideoTopBar new];
[_topBar.cancelButton addTarget:self action:@selector(clickCancelButton:) forControlEvents:UIControlEventTouchUpInside];
_topBar.hidden = YES;
}
return _topBar;
}
- (YBIBVideoActionBar *)actionBar {
if (!_actionBar) {
_actionBar = [YBIBVideoActionBar new];
_actionBar.delegate = self;
_actionBar.hidden = YES;
}
return _actionBar;
}
- (UIButton *)playButton {
if (!_playButton) {
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
_playButton.bounds = CGRectMake(0, 0, 100, 100);
[_playButton setImage:YBIBIconManager.sharedManager.videoBigPlayImage() forState:UIControlStateNormal];
[_playButton addTarget:self action:@selector(clickPlayButton:) forControlEvents:UIControlEventTouchUpInside];
_playButton.hidden = YES;
_playButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
_playButton.layer.shadowOffset = CGSizeMake(0, 1);
_playButton.layer.shadowOpacity = 1;
_playButton.layer.shadowRadius = 4;
}
return _playButton;
}
- (UIImageView *)thumbImageView {
if (!_thumbImageView) {
_thumbImageView = [UIImageView new];
_thumbImageView.contentMode = UIViewContentModeScaleAspectFit;
_thumbImageView.layer.masksToBounds = YES;
}
return _thumbImageView;
}
@end

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,39 @@
//
// YBIBAuxiliaryViewHandler.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/27.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol YBIBAuxiliaryViewHandler <NSObject>
@required
/// 展示正确情况的提示
- (void)yb_showCorrectToastWithContainer:(UIView *)container text:(NSString *)text;
/// 展示错误情况的提示
- (void)yb_showIncorrectToastWithContainer:(UIView *)container text:(NSString *)text;
/// 隐藏所有提示
- (void)yb_hideToastWithContainer:(UIView *)container;
/// 展示加载视图
- (void)yb_showLoadingWithContainer:(UIView *)container;
/// 展示带进度的加载视图
- (void)yb_showLoadingWithContainer:(UIView *)container progress:(CGFloat)progress;
/// 展示带文字的视图
- (void)yb_showLoadingWithContainer:(UIView *)container text:(NSString *)text;
/// 隐藏所有视图
- (void)yb_hideLoadingWithContainer:(UIView *)container;
@end
@interface YBIBAuxiliaryViewHandler : NSObject <YBIBAuxiliaryViewHandler>
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,45 @@
//
// YBIBAuxiliaryViewHandler.m
// YBImageBrowserDemo
//
// Created by on 2019/6/27.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBAuxiliaryViewHandler.h"
#import "YBIBToastView.h"
#import "YBIBLoadingView.h"
@implementation YBIBAuxiliaryViewHandler
#pragma mark - <YBIBAuxiliaryViewHandler>
- (void)yb_showCorrectToastWithContainer:(UIView *)container text:(NSString *)text {
[container ybib_showHookToast:text];
}
- (void)yb_showIncorrectToastWithContainer:(UIView *)container text:(NSString *)text {
[container ybib_showForkToast:text];
}
- (void)yb_hideToastWithContainer:(UIView *)container {
[container ybib_hideToast];
}
- (void)yb_showLoadingWithContainer:(UIView *)container {
[container ybib_showLoading];
}
- (void)yb_showLoadingWithContainer:(UIView *)container progress:(CGFloat)progress {
[container ybib_showLoadingWithProgress:progress];
}
- (void)yb_showLoadingWithContainer:(UIView *)container text:(NSString *)text {
[container ybib_showLoadingWithText:text click:nil];
}
- (void)yb_hideLoadingWithContainer:(UIView *)container {
[container ybib_hideLoading];
}
@end

View File

@ -0,0 +1,36 @@
//
// YBIBLoadingView.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2018/9/1.
// Copyright © 2018年 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (YBIBLoading)
- (void)ybib_showLoading;
- (void)ybib_showLoadingWithProgress:(CGFloat)progress;
- (void)ybib_showLoadingWithText:(NSString *)text click:(nullable void(^)(void))click;
- (void)ybib_hideLoading;
@end
@interface YBIBLoadingView : UIView
- (void)show;
- (void)showProgress:(CGFloat)progress;
- (void)showText:(NSString *)text click:(void(^)(void))click;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,258 @@
//
// YBIBLoadingView.m
// YBImageBrowserDemo
//
// Created by on 2018/9/1.
// Copyright © 2018 . All rights reserved.
//
#import "YBIBLoadingView.h"
#import "YBIBIconManager.h"
#import <objc/runtime.h>
@interface UIView ()
@property (nonatomic, strong, readonly) YBIBLoadingView *ybib_loading;
@end
@implementation UIView (YBIBLoading)
- (void)ybib_showLoadingWithProgress:(CGFloat)progress {
[self ybib_addLoadingView];
[self.ybib_loading showProgress:progress];
}
- (void)ybib_showLoading {
[self ybib_addLoadingView];
[self.ybib_loading show];
}
- (void)ybib_showLoadingWithText:(NSString *)text click:(void (^)(void))click {
[self ybib_addLoadingView];
[self.ybib_loading showText:text click:click];
}
- (void)ybib_addLoadingView {
YBIBLoadingView *loading = self.ybib_loading;
if (!loading.superview) {
[self addSubview:loading];
loading.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:0];
NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *layD = [NSLayoutConstraint constraintWithItem:loading attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[self addConstraints:@[layA, layB, layC, layD]];
}
}
- (void)ybib_hideLoading {
YBIBLoadingView *loading = self.ybib_loading;
if (loading && loading.superview) {
[loading removeFromSuperview];
}
}
static void *YBIBLoadingKey = &YBIBLoadingKey;
- (void)setYbib_loading:(YBIBLoadingView *)ybib_loading {
objc_setAssociatedObject(self, YBIBLoadingKey, ybib_loading, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (YBIBLoadingView *)ybib_loading {
YBIBLoadingView *loading = objc_getAssociatedObject(self, YBIBLoadingKey);
if (!loading) {
loading = [YBIBLoadingView new];
self.ybib_loading = loading;
}
return loading;
}
@end
@interface YBIBProgressDrawView : UIView
@property (nonatomic, assign) CGFloat progress;
@end
@implementation YBIBProgressDrawView
- (void)drawRect:(CGRect)rect {
if (self.isHidden) return;
CGFloat progress = (isnan(_progress) || isinf(_progress) || _progress < 0) ? 0 : _progress;
CGFloat radius = 17;
CGFloat strokeWidth = 3;
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[[UIColor lightGrayColor] setStroke];
UIBezierPath *bottomPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
bottomPath.lineWidth = 4.0;
bottomPath.lineCapStyle = kCGLineCapRound;
bottomPath.lineJoinStyle = kCGLineCapRound;
[bottomPath stroke];
[[UIColor whiteColor] setStroke];
UIBezierPath *activePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:-M_PI / 2.0 endAngle:M_PI * 2 * progress - M_PI / 2.0 clockwise:true];
activePath.lineWidth = strokeWidth;
activePath.lineCapStyle = kCGLineCapRound;
activePath.lineJoinStyle = kCGLineCapRound;
[activePath stroke];
NSShadow *shadow = [NSShadow new];
shadow.shadowBlurRadius = 4;
shadow.shadowOffset = CGSizeMake(0, 1);
shadow.shadowColor = UIColor.darkGrayColor;
NSString *string = [NSString stringWithFormat:@"%.0lf%@", progress * 100, @"%"];
NSMutableAttributedString *atts = [[NSMutableAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:10], NSForegroundColorAttributeName:[UIColor whiteColor], NSShadowAttributeName:shadow}];
CGSize size = atts.size;
[atts drawAtPoint:CGPointMake(center.x - size.width / 2.0, center.y - size.height / 2.0)];
}
@end
typedef NS_ENUM(NSUInteger, YBImageBrowserProgressType) {
YBImageBrowserProgressTypeProgress,
YBImageBrowserProgressTypeLoad,
YBImageBrowserProgressTypeText
};
@interface YBIBLoadingView () {
YBImageBrowserProgressType _type;
}
@property (nonatomic, strong) UILabel *textLabel;
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) YBIBProgressDrawView *drawView;
@property (nonatomic, copy) void(^clickTextLabelBlock)(void);
@end
@implementation YBIBLoadingView
#pragma mark life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];
self.userInteractionEnabled = NO;
[self addSubview:self.drawView];
[self addSubview:self.textLabel];
[self addSubview:self.imageView];
}
return self;
}
- (void)updateConstraints {
self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:-20];
NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *layE = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *layF = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
self.drawView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *layG = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *layH = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *layI = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
NSLayoutConstraint *layJ = [NSLayoutConstraint constraintWithItem:self.drawView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
[self addConstraints:@[layA, layB, layC, layE, layF, layG, layH, layI, layJ]];
[super updateConstraints];
}
#pragma mark public
- (void)showProgress:(CGFloat)progress {
self.userInteractionEnabled = NO;
_type = YBImageBrowserProgressTypeProgress;
self.drawView.hidden = NO;
self.textLabel.hidden = YES;
self.imageView.hidden = YES;
[self stopImageViewAnimation];
self.drawView.progress = progress;
[self.drawView setNeedsDisplay];
}
- (void)show {
self.userInteractionEnabled = NO;
_type = YBImageBrowserProgressTypeLoad;
self.drawView.hidden = YES;
self.textLabel.hidden = YES;
self.imageView.hidden = NO;
[self startImageViewAnimation];
[self.drawView setNeedsDisplay];
}
- (void)startImageViewAnimation {
CABasicAnimation *ra = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
ra.toValue = [NSNumber numberWithFloat:M_PI * 2];
ra.duration = 1;
ra.cumulative = YES;
ra.repeatCount = HUGE_VALF;
ra.removedOnCompletion = NO;
ra.fillMode = kCAFillModeForwards;
[self.imageView.layer addAnimation:ra forKey:@"ra"];
}
- (void)stopImageViewAnimation {
[self.imageView.layer removeAllAnimations];
}
- (void)showText:(NSString *)text click:(void (^)(void))click {
self.userInteractionEnabled = click ? YES : NO;
_type = YBImageBrowserProgressTypeText;
self.drawView.hidden = YES;
self.textLabel.hidden = NO;
self.imageView.hidden = YES;
[self stopImageViewAnimation];
self.textLabel.text = text;
self.clickTextLabelBlock = click;
[self.drawView setNeedsDisplay];
}
#pragma mark - touch event
- (void)respondsToTapTextlabel {
if (self.clickTextLabelBlock) {
self.clickTextLabelBlock();
}
}
#pragma mark - getter
- (YBIBProgressDrawView *)drawView {
if (!_drawView) {
_drawView = [YBIBProgressDrawView new];
_drawView.backgroundColor = [UIColor clearColor];
}
return _drawView;
}
- (UILabel *)textLabel {
if (!_textLabel) {
_textLabel = [UILabel new];
_textLabel.textColor = [UIColor whiteColor];
_textLabel.numberOfLines = 0;
_textLabel.font = [UIFont systemFontOfSize:14];
_textLabel.textAlignment = NSTextAlignmentCenter;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapTextlabel)];
[_textLabel addGestureRecognizer:tapGesture];
_textLabel.userInteractionEnabled = YES;
}
return _textLabel;
}
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [UIImageView new];
_imageView.image = [YBIBIconManager sharedManager].loadingImage();
_imageView.layer.shadowColor = UIColor.darkGrayColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(0, 1);
_imageView.layer.shadowOpacity = 1;
_imageView.layer.shadowRadius = 4;
}
return _imageView;
}
@end

View File

@ -0,0 +1,36 @@
//
// YBIBToastView.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/20.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (YBIBToast)
- (void)ybib_showHookToast:(NSString *)text;
- (void)ybib_showForkToast:(NSString *)text;
- (void)ybib_hideToast;
@end
typedef NS_ENUM(NSInteger, YBIBToastType) {
YBIBToastTypeNone,
YBIBToastTypeHook,
YBIBToastTypeFork
};
@interface YBIBToastView : UIView
- (void)showWithText:(NSString *)text type:(YBIBToastType)type;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,179 @@
//
// YBIBToastView.m
// YBImageBrowserDemo
//
// Created by on 2019/6/20.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBToastView.h"
#import <objc/runtime.h>
@interface UIView ()
@property (nonatomic, strong, readonly) YBIBToastView *ybib_toast;
@end
@implementation UIView (YBIBToast)
- (void)ybib_showHookToast:(NSString *)text {
[self ybib_showToastWithText:text type:YBIBToastTypeHook hideAfterDelay:1.7];
}
- (void)ybib_showForkToast:(NSString *)text {
[self ybib_showToastWithText:text type:YBIBToastTypeFork hideAfterDelay:1.7];
}
- (void)ybib_showToastWithText:(NSString *)text type:(YBIBToastType)type hideAfterDelay:(NSTimeInterval)delay {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(ybib_hideToast) object:nil];
YBIBToastView *toast = self.ybib_toast;
if (!toast.superview) {
[self addSubview:toast];
toast.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:40];
NSLayoutConstraint *layD = [NSLayoutConstraint constraintWithItem:toast attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:-40];
[self addConstraints:@[layA, layB, layC, layD]];
}
[toast showWithText:text type:type];
[self performSelector:@selector(ybib_hideToast) withObject:nil afterDelay:delay];
}
- (void)ybib_hideToast {
YBIBToastView *toast = self.ybib_toast;
if (toast && toast.superview) {
[UIView animateWithDuration:0.25 animations:^{
toast.alpha = 0;
} completion:^(BOOL finished) {
[toast removeFromSuperview];
toast.alpha = 1;
}];
}
}
static void *YBIBToastKey = &YBIBToastKey;
- (void)setYbib_toast:(YBIBToastView *)ybib_toast {
objc_setAssociatedObject(self, YBIBToastKey, ybib_toast, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (YBIBToastView *)ybib_toast {
YBIBToastView *toast = objc_getAssociatedObject(self, YBIBToastKey);
if (!toast) {
toast = [YBIBToastView new];
self.ybib_toast = toast;
}
return toast;
}
@end
@interface YBIBToastView () {
YBIBToastType _type;
CAShapeLayer *_shapeLayer;
}
@property (nonatomic, strong) UILabel *textLabel;
@end
@implementation YBIBToastView
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
self.userInteractionEnabled = NO;
self.layer.cornerRadius = 7;
[self addSubview:self.textLabel];
}
return self;
}
- (void)updateConstraints {
self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *layA = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
NSLayoutConstraint *layB = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:-20];
NSLayoutConstraint *layC = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1 constant:-15];
NSLayoutConstraint *layD = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:70];
NSLayoutConstraint *layE = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:60];
[self addConstraints:@[layA, layB, layC, layD, layE]];
[super updateConstraints];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self startAnimation];
}
#pragma mark - animation
- (void)showWithText:(NSString *)text type:(YBIBToastType)type {
self.textLabel.text = text;
_type = type;
[self setNeedsLayout];
}
- (void)startAnimation {
if (_shapeLayer && _shapeLayer.superlayer) {
[_shapeLayer removeFromSuperlayer];
}
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.strokeColor = [UIColor whiteColor].CGColor;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.lineWidth = 5.0;
_shapeLayer.lineCap = @"round";
_shapeLayer.lineJoin = @"round";
_shapeLayer.strokeStart = 0.0;
_shapeLayer.strokeEnd = 0.0;
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGFloat r = 13.0;
CGFloat x = self.bounds.size.width / 2.0;
CGFloat y = 38.0;
switch (_type) {
case YBIBToastTypeHook: {
[bezierPath moveToPoint:CGPointMake(x - r - r / 2, y)];
[bezierPath addLineToPoint:CGPointMake(x - r / 2, y + r)];
[bezierPath addLineToPoint:CGPointMake(x + r * 2 - r / 2, y - r)];
}
break;
case YBIBToastTypeFork: {
[bezierPath moveToPoint:CGPointMake(x - r, y - r)];
[bezierPath addLineToPoint:CGPointMake(x + r, y + r)];
[bezierPath moveToPoint:CGPointMake(x - r, y + r)];
[bezierPath addLineToPoint:CGPointMake(x + r, y - r)];
}
break;
default:break;
}
CABasicAnimation *baseAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
[baseAnimation setFromValue:@0.0];
[baseAnimation setToValue:@1.0];
[baseAnimation setDuration:0.3];
baseAnimation.removedOnCompletion = NO;
baseAnimation.fillMode = kCAFillModeBoth;
_shapeLayer.path = bezierPath.CGPath;
[self.layer addSublayer:_shapeLayer];
[_shapeLayer addAnimation:baseAnimation forKey:@"strokeEnd"];
}
#pragma mark - getter
- (UILabel *)textLabel {
if (!_textLabel) {
_textLabel = [UILabel new];
_textLabel.textColor = [UIColor whiteColor];
_textLabel.font = [UIFont systemFontOfSize:14];
_textLabel.textAlignment = NSTextAlignmentCenter;
_textLabel.numberOfLines = 0;
}
return _textLabel;
}
@end

View File

@ -0,0 +1,19 @@
//
// NSObject+YBImageBrowser.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/9/26.
// Copyright © 2019 杨波. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (YBImageBrowser)
@property (nonatomic, assign) CGFloat ybib_originAlpha;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,23 @@
//
// NSObject+YBImageBrowser.m
// YBImageBrowserDemo
//
// Created by on 2019/9/26.
// Copyright © 2019 . All rights reserved.
//
#import "NSObject+YBImageBrowser.h"
#import <objc/runtime.h>
@implementation NSObject (YBImageBrowser)
static void *YBIBOriginAlphaKey = &YBIBOriginAlphaKey;
- (void)setYbib_originAlpha:(CGFloat)ybib_originAlpha {
objc_setAssociatedObject(self, YBIBOriginAlphaKey, @(ybib_originAlpha), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (CGFloat)ybib_originAlpha {
NSNumber *alpha = objc_getAssociatedObject(self, YBIBOriginAlphaKey);
return alpha ? alpha.floatValue : 1;
}
@end

View File

@ -0,0 +1,46 @@
//
// YBIBAnimatedTransition.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/6.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol YBIBAnimatedTransition <NSObject>
@required
- (void)yb_showTransitioningWithContainer:(UIView *)container startView:(nullable __kindof UIView *)startView startImage:(nullable UIImage *)startImage endFrame:(CGRect)endFrame orientation:(UIDeviceOrientation)orientation completion:(void(^)(void))completion;
- (void)yb_hideTransitioningWithContainer:(UIView *)container startView:(nullable __kindof UIView *)startView endView:(UIView *)endView orientation:(UIDeviceOrientation)orientation completion:(void(^)(void))completion;
@end
typedef NS_ENUM(NSInteger, YBIBTransitionType) {
/// 无动效
YBIBTransitionTypeNone,
/// 渐隐
YBIBTransitionTypeFade,
/// 连贯移动
YBIBTransitionTypeCoherent
};
@interface YBIBAnimatedTransition : NSObject <YBIBAnimatedTransition>
/// 入场动效类型
@property (nonatomic, assign) YBIBTransitionType showType;
/// 出场动效类型
@property (nonatomic, assign) YBIBTransitionType hideType;
/// 入场动效持续时间
@property (nonatomic, assign) NSTimeInterval showDuration;
/// 出场动效持续时间
@property (nonatomic, assign) NSTimeInterval hideDuration;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,175 @@
//
// YBIBAnimatedTransition.m
// YBImageBrowserDemo
//
// Created by on 2019/6/6.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBAnimatedTransition.h"
extern CGFloat YBIBRotationAngle(UIDeviceOrientation startOrientation, UIDeviceOrientation endOrientation);
@implementation YBIBAnimatedTransition
#pragma mark - life cycle
- (instancetype)init {
self = [super init];
if (self) {
_showType = _hideType = YBIBTransitionTypeCoherent;
_showDuration = _hideDuration = 0.25;
}
return self;
}
#pragma mark - <YBIBAnimationHandler>
- (void)yb_showTransitioningWithContainer:(UIView *)container startView:(__kindof UIView *)startView startImage:(UIImage *)startImage endFrame:(CGRect)endFrame orientation:(UIDeviceOrientation)orientation completion:(void (^)(void))completion {
YBIBTransitionType type = self.showType;
if (type == YBIBTransitionTypeCoherent) {
if (CGRectIsEmpty(endFrame) || !startView || orientation != (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation) {
type = YBIBTransitionTypeFade;
}
}
switch (type) {
case YBIBTransitionTypeNone: {
completion();
}
break;
case YBIBTransitionTypeFade: {
BOOL animateValid = !CGRectIsEmpty(endFrame) && startView;
UIImageView *animateImageView;
if (animateValid) {
animateImageView = [self imageViewAssimilateToView:startView];
animateImageView.frame = endFrame;
animateImageView.image = startImage;
[container addSubview:animateImageView];
}
CGFloat rawAlpha = container.alpha;
container.alpha = 0;
if (!animateValid) completion();
[UIView animateWithDuration:self.showDuration animations:^{
container.alpha = rawAlpha;
} completion:^(BOOL finished) {
if (animateValid) {
[animateImageView removeFromSuperview];
completion();
}
}];
}
break;
case YBIBTransitionTypeCoherent: {
UIImageView *animateImageView = [self imageViewAssimilateToView:startView];
animateImageView.frame = [startView convertRect:startView.bounds toView:container];
animateImageView.image = startImage;
[container addSubview:animateImageView];
UIColor *rawBackgroundColor = container.backgroundColor;
container.backgroundColor = [rawBackgroundColor colorWithAlphaComponent:0];
[UIView animateWithDuration:self.showDuration animations:^{
animateImageView.frame = endFrame;
container.backgroundColor = rawBackgroundColor;
} completion:^(BOOL finished) {
completion();
// Disappear smoothly.
[UIView animateWithDuration:0.2 animations:^{
animateImageView.alpha = 0;
} completion:^(BOOL finished) {
[animateImageView removeFromSuperview];
}];
}];
}
break;
}
}
- (void)yb_hideTransitioningWithContainer:(UIView *)container startView:(__kindof UIView *)startView endView:(UIView *)endView orientation:(UIDeviceOrientation)orientation completion:(void (^)(void))completion {
YBIBTransitionType type = self.hideType;
if (type == YBIBTransitionTypeCoherent && (!startView || !endView)) {
type = YBIBTransitionTypeFade;
}
switch (type) {
case YBIBTransitionTypeNone: {
completion();
}
break;
case YBIBTransitionTypeFade: {
CGFloat rawAlpha = container.alpha;
[UIView animateWithDuration:self.hideDuration animations:^{
container.alpha = 0;
} completion:^(BOOL finished) {
completion();
container.alpha = rawAlpha;
}];
}
break;
case YBIBTransitionTypeCoherent: {
CGRect startFrame = startView.frame;
CGRect endFrame = [endView convertRect:endView.bounds toView:startView.superview];
UIColor *rawBackgroundColor = container.backgroundColor;
[UIView animateWithDuration:self.hideDuration animations:^{
container.backgroundColor = [rawBackgroundColor colorWithAlphaComponent:0];
startView.contentMode = endView.contentMode;
CGAffineTransform transform = startView.transform;
UIDeviceOrientation statusBarOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
if (orientation != statusBarOrientation) {
transform = CGAffineTransformRotate(transform, YBIBRotationAngle(orientation, statusBarOrientation));
}
if ([startView isKindOfClass:UIImageView.self]) {
startView.frame = endFrame;
startView.transform = transform;
} else {
CGFloat scale = MAX(endFrame.size.width / startFrame.size.width, endFrame.size.height / startFrame.size.height);
startView.center = CGPointMake(endFrame.size.width * startView.layer.anchorPoint.x + endFrame.origin.x, endFrame.size.height * startView.layer.anchorPoint.y + endFrame.origin.y);
startView.transform = CGAffineTransformScale(transform, scale, scale);
}
} completion:^(BOOL finished) {
completion();
container.backgroundColor = rawBackgroundColor;
}];
}
break;
}
}
#pragma mark - private
- (UIImageView *)imageViewAssimilateToView:(nullable __kindof UIView *)view {
UIImageView *animateImageView = [UIImageView new];
if ([view isKindOfClass:UIImageView.self]) {
animateImageView.contentMode = view.contentMode;
} else {
animateImageView.contentMode = UIViewContentModeScaleAspectFill;
}
animateImageView.layer.masksToBounds = view.layer.masksToBounds;
animateImageView.layer.cornerRadius = view.layer.cornerRadius;
animateImageView.layer.backgroundColor = view.layer.backgroundColor;
return animateImageView;
}
@end

View File

@ -0,0 +1,25 @@
//
// YBIBCollectionView.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/6.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBIBCollectionViewLayout.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBCollectionView : UICollectionView
@property (nonatomic, strong, readonly) YBIBCollectionViewLayout *layout;
- (NSString *)reuseIdentifierForCellClass:(Class)cellClass;
- (nullable UICollectionViewCell *)centerCell;
- (void)scrollToPage:(NSInteger)page;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,83 @@
//
// YBIBCollectionView.m
// YBImageBrowserDemo
//
// Created by on 2019/6/6.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBCollectionView.h"
@implementation YBIBCollectionView {
NSMutableSet *_reuseSet;
}
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
_layout = [YBIBCollectionViewLayout new];
return [self initWithFrame:frame collectionViewLayout:_layout];
}
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(nonnull UICollectionViewLayout *)layout {
self = [super initWithFrame:frame collectionViewLayout:layout];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.pagingEnabled = YES;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.alwaysBounceVertical = NO;
self.alwaysBounceHorizontal = NO;
self.decelerationRate = UIScrollViewDecelerationRateFast;
if (@available(iOS 11.0, *)) {
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
_reuseSet = [NSMutableSet set];
}
return self;
}
#pragma mark - public
- (NSString *)reuseIdentifierForCellClass:(Class)cellClass {
NSString *identifier = NSStringFromClass(cellClass);
if (![_reuseSet containsObject:identifier]) {
NSString *path = [[NSBundle mainBundle] pathForResource:identifier ofType:@"nib"];
if (path) {
[self registerNib:[UINib nibWithNibName:identifier bundle:nil] forCellWithReuseIdentifier:identifier];
} else {
[self registerClass:cellClass forCellWithReuseIdentifier:identifier];
}
[_reuseSet addObject:identifier];
}
return identifier;
}
- (UICollectionViewCell *)centerCell {
NSArray<UICollectionViewCell *> *cells = [self visibleCells];
if (cells.count == 0) return nil;
UICollectionViewCell *res = cells[0];
CGFloat centerX = self.contentOffset.x + (self.bounds.size.width / 2.0);
for (int i = 1; i < cells.count; ++i) {
if (ABS(cells[i].center.x - centerX) < ABS(res.center.x - centerX)) {
res = cells[i];
}
}
return res;
}
- (void)scrollToPage:(NSInteger)page {
[self setContentOffset:CGPointMake(self.bounds.size.width * page, 0)];
}
#pragma mark - hit test
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
// When the hit-test view is 'UISlider', set '_scrollEnabled' to 'NO', avoid gesture conflicts.
self.scrollEnabled = ![view isKindOfClass:UISlider.class];
return view;
}
@end

View File

@ -0,0 +1,16 @@
//
// YBIBCollectionViewLayout.h
// YBImageBrowserDemo
//
// Created by 杨少 on 2018/4/17.
// Copyright © 2018年 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface YBIBCollectionViewLayout : UICollectionViewFlowLayout
@property (nonatomic, assign) CGFloat distanceBetweenPages;
@end

View File

@ -0,0 +1,44 @@
//
// YBIBCollectionViewLayout.m
// YBImageBrowserDemo
//
// Created by on 2018/4/17.
// Copyright © 2018 . All rights reserved.
//
#import "YBIBCollectionViewLayout.h"
@implementation YBIBCollectionViewLayout
- (instancetype)init {
self = [super init];
if (self) {
self.minimumLineSpacing = 0;
self.minimumInteritemSpacing = 0;
self.sectionInset = UIEdgeInsetsZero;
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_distanceBetweenPages = 20;
}
return self;
}
- (void)prepareLayout {
[super prepareLayout];
self.itemSize = self.collectionView.bounds.size;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray<UICollectionViewLayoutAttributes *> *layoutAttsArray = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
CGFloat halfWidth = self.collectionView.bounds.size.width / 2.0;
CGFloat centerX = self.collectionView.contentOffset.x + halfWidth;
[layoutAttsArray enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj.center = CGPointMake(obj.center.x + (obj.center.x - centerX) / halfWidth * self.distanceBetweenPages / 2, obj.center.y);
}];
return layoutAttsArray;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
@end

View File

@ -0,0 +1,17 @@
//
// YBIBContainerView.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/11.
// Copyright © 2019 杨波. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface YBIBContainerView : UIView
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,22 @@
//
// YBIBContainerView.m
// YBImageBrowserDemo
//
// Created by on 2019/7/11.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBContainerView.h"
@implementation YBIBContainerView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *originView = [super hitTest:point withEvent:event];
if ([originView isKindOfClass:self.class]) {
// Continue hit-testing if the view is kind of 'self.class'.
return nil;
}
return originView;
}
@end

View File

@ -0,0 +1,31 @@
//
// YBIBDataMediator.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/6.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBImageBrowser.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBDataMediator : NSObject
- (instancetype)initWithBrowser:(YBImageBrowser *)browser;
@property (nonatomic, assign) NSUInteger dataCacheCountLimit;
- (NSInteger)numberOfCells;
- (id<YBIBDataProtocol>)dataForCellAtIndex:(NSInteger)index;
- (void)clear;
@property (nonatomic, assign) NSUInteger preloadCount;
- (void)preloadWithPage:(NSInteger)page;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,69 @@
//
// YBIBDataMediator.m
// YBImageBrowserDemo
//
// Created by on 2019/6/6.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBDataMediator.h"
#import "YBImageBrowser+Internal.h"
@implementation YBIBDataMediator {
__weak YBImageBrowser *_browser;
NSCache<NSNumber *, id<YBIBDataProtocol>> *_dataCache;
}
#pragma mark - life cycle
- (instancetype)initWithBrowser:(YBImageBrowser *)browser {
if (self = [super init]) {
_browser = browser;
_dataCache = [NSCache new];
}
return self;
}
#pragma mark - public
- (NSInteger)numberOfCells {
return _browser.dataSource ? [_browser.dataSource yb_numberOfCellsInImageBrowser:_browser] : _browser.dataSourceArray.count;
}
- (id<YBIBDataProtocol>)dataForCellAtIndex:(NSInteger)index {
if (index < 0 || index > self.numberOfCells - 1) return nil;
id<YBIBDataProtocol> data = [_dataCache objectForKey:@(index)];
if (!data) {
data = _browser.dataSource ? [_browser.dataSource yb_imageBrowser:_browser dataForCellAtIndex:index] : _browser.dataSourceArray[index];
[_dataCache setObject:data forKey:@(index)];
[_browser implementGetBaseInfoProtocol:data];
}
return data;
}
- (void)clear {
[_dataCache removeAllObjects];
}
- (void)preloadWithPage:(NSInteger)page {
if (_preloadCount == 0) return;
NSInteger left = -(_preloadCount / 2), right = _preloadCount - ABS(left);
for (NSInteger i = left; i <= right; ++i) {
if (i == 0) continue;
id<YBIBDataProtocol> targetData = [self dataForCellAtIndex:page + i];
if ([targetData respondsToSelector:@selector(yb_preload)]) {
[targetData yb_preload];
}
}
}
#pragma mark - getters & setters
- (void)setDataCacheCountLimit:(NSUInteger)dataCacheCountLimit {
_dataCacheCountLimit = dataCacheCountLimit;
_dataCache.countLimit = dataCacheCountLimit;
}
@end

View File

@ -0,0 +1,37 @@
//
// YBIBScreenRotationHandler.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/8.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBImageBrowser.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBScreenRotationHandler : NSObject
- (instancetype)initWithBrowser:(YBImageBrowser *)browser;
- (void)startObserveStatusBarOrientation;
- (void)startObserveDeviceOrientation;
- (void)clear;
- (void)configContainerSize:(CGSize)size;
- (CGSize)containerSizeWithOrientation:(UIDeviceOrientation)orientation;
@property (nonatomic, assign, readonly, getter=isRotating) BOOL rotating;
@property (nonatomic, assign, readonly) UIDeviceOrientation currentOrientation;
@property (nonatomic, assign) UIInterfaceOrientationMask supportedOrientations;
@property (nonatomic, assign) NSTimeInterval rotationDuration;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,328 @@
//
// YBIBScreenRotationHandler.m
// YBImageBrowserDemo
//
// Created by on 2019/6/8.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBScreenRotationHandler.h"
#import "YBIBUtilities.h"
#import "YBIBCellProtocol.h"
#import "YBImageBrowser+Internal.h"
BOOL YBIBValidDeviceOrientation(UIDeviceOrientation orientation) {
static NSSet *validSet;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
validSet = [NSSet setWithObjects:@(UIDeviceOrientationPortrait), @(UIDeviceOrientationPortraitUpsideDown), @(UIDeviceOrientationLandscapeLeft), @(UIDeviceOrientationLandscapeRight), nil];
});
return [validSet containsObject:@(orientation)];
}
CGFloat YBIBRotationAngle(UIDeviceOrientation startOrientation, UIDeviceOrientation endOrientation) {
static NSDictionary<NSNumber*, NSNumber*> *angleMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
angleMap = @{@(UIDeviceOrientationPortrait):@(0), @(UIDeviceOrientationPortraitUpsideDown):@(M_PI), @(UIDeviceOrientationLandscapeLeft):@(M_PI_2), @(UIDeviceOrientationLandscapeRight): @(-M_PI_2)};
});
NSNumber *start = angleMap[@(startOrientation)], *end = angleMap[@(endOrientation)];
CGFloat res = CGFLOAT_IS_DOUBLE ? end.doubleValue - start.doubleValue : end.floatValue - start.floatValue;
if (ABS(res) > M_PI) {
return res > 0 ? res - M_PI * 2 : M_PI * 2 + res;
}
return res;
}
static NSUInteger const kMaskNull = 10000;
@interface YBIBScreenRotationHandler ()
@property (nonatomic, assign) BOOL rotating;
@property (nonatomic, assign) UIDeviceOrientation currentOrientation;
@end
@implementation YBIBScreenRotationHandler {
__weak YBImageBrowser *_browser;
CGSize _verticalContainerSize;
CGSize _horizontalContainerSize;
NSInteger _recordPage;
}
#pragma mark - life cycle
- (void)dealloc {
[self clear];
}
- (instancetype)initWithBrowser:(YBImageBrowser *)browser {
if (self = [super init]) {
_browser = browser;
_rotating = NO;
_supportedOrientations = UIInterfaceOrientationMaskAllButUpsideDown;
_rotationDuration = 0.25;
}
return self;
}
#pragma mark - public
- (void)startObserveStatusBarOrientation {
self.currentOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangedStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillChangeStatusBarOrientationNotification:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)startObserveDeviceOrientation {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChangeNotification:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)clear {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)configContainerSize:(CGSize)size {
if (UIDeviceOrientationIsLandscape(self.currentOrientation)) {
// Now is horizontal.
_verticalContainerSize = CGSizeMake(size.height, size.width);
_horizontalContainerSize = size;
} else {
// Now is vertical.
_verticalContainerSize = size;
_horizontalContainerSize = CGSizeMake(size.height, size.width);
}
}
- (CGSize)containerSizeWithOrientation:(UIDeviceOrientation)orientation {
return UIDeviceOrientationIsLandscape(orientation) ? _horizontalContainerSize : _verticalContainerSize;
}
#pragma mark - private
- (BOOL)supportedOfOrientation:(UIDeviceOrientation)orientation {
if (!YBIBValidDeviceOrientation(orientation)) return NO;
NSMutableSet *set = [NSMutableSet set];
if (_supportedOrientations & UIInterfaceOrientationMaskPortrait) [set addObject:@(UIDeviceOrientationPortrait)];
if (_supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown) [set addObject:@(UIDeviceOrientationPortraitUpsideDown)];
if (_supportedOrientations & UIInterfaceOrientationMaskLandscapeRight) [set addObject:@(UIDeviceOrientationLandscapeLeft)];
if (_supportedOrientations & UIInterfaceOrientationMaskLandscapeLeft) [set addObject:@(UIDeviceOrientationLandscapeRight)];
return [set containsObject:@(orientation)];
}
- (BOOL)supportedOnlyOneSystemOrientation {
UIInterfaceOrientationMask mask = [self supportSystemOrientationMask];
return mask == (mask & (-mask));
}
- (void)orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation centerCell:(UICollectionViewCell<YBIBCellProtocol> *)centerCell {
if ([centerCell respondsToSelector:@selector(yb_orientationWillChangeWithExpectOrientation:)]) {
[centerCell yb_orientationWillChangeWithExpectOrientation:orientation];
}
for (id<YBIBToolViewHandler> handler in _browser.toolViewHandlers) {
if ([handler respondsToSelector:@selector(yb_orientationWillChangeWithExpectOrientation:)]) {
[handler yb_orientationWillChangeWithExpectOrientation:orientation];
}
}
}
- (void)orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation centerCell:(UICollectionViewCell<YBIBCellProtocol> *)centerCell {
if ([centerCell respondsToSelector:@selector(yb_orientationChangeAnimationWithExpectOrientation:)]) {
[centerCell yb_orientationChangeAnimationWithExpectOrientation:orientation];
[centerCell layoutIfNeeded]; // Compatible with autolayout.
}
for (id<YBIBToolViewHandler> handler in _browser.toolViewHandlers) {
if ([handler respondsToSelector:@selector(yb_orientationChangeAnimationWithExpectOrientation:)]) {
[handler yb_orientationChangeAnimationWithExpectOrientation:orientation];
}
}
}
- (void)orientationDidChangedWithOrientation:(UIDeviceOrientation)orientation centerCell:(UICollectionViewCell<YBIBCellProtocol> *)centerCell {
if ([centerCell respondsToSelector:@selector(yb_orientationDidChangedWithOrientation:)]) {
[centerCell yb_orientationDidChangedWithOrientation:orientation];
}
for (id<YBIBToolViewHandler> handler in _browser.toolViewHandlers) {
if ([handler respondsToSelector:@selector(yb_orientationDidChangedWithOrientation:)]) {
[handler yb_orientationDidChangedWithOrientation:orientation];
}
}
}
#pragma mark - event
- (void)deviceOrientationDidChangeNotification:(NSNotification *)note {
if (![self supportedOnlyOneSystemOrientation]) return;
if (_browser.isTransitioning || self.rotating) return;
UIDeviceOrientation expectOrientation = [UIDevice currentDevice].orientation;
if (expectOrientation == self.currentOrientation || ![self supportedOfOrientation:expectOrientation]) return;
self.rotating = YES;
// Align.
[_browser.collectionView scrollToPage:_browser.currentPage];
// Record current page number before transforming.
NSInteger currentPage = _browser.currentPage;
UIDeviceOrientation statusBarOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
CGFloat angleStatusBarToExpect = YBIBRotationAngle(statusBarOrientation, expectOrientation);
CGFloat angleCurrentToExpect = YBIBRotationAngle(_currentOrientation, expectOrientation);
CGRect expectBounds = (CGRect){CGPointZero, [self containerSizeWithOrientation:expectOrientation]};
UICollectionViewCell<YBIBCellProtocol> *centerCell = (UICollectionViewCell<YBIBCellProtocol> *)self->_browser.collectionView.centerCell;
// Animate smoothly if bigger rotation angle.
NSTimeInterval duration = self.rotationDuration * (ABS(angleCurrentToExpect) > M_PI_2 ? 2 : 1);
// 'collectionView' transformation.
self->_browser.collectionView.bounds = expectBounds;
self->_browser.collectionView.transform = CGAffineTransformMakeRotation(angleStatusBarToExpect);
centerCell.contentView.transform = CGAffineTransformMakeRotation(-angleCurrentToExpect);
// Reset to prevent the page number change after transforming.
[self->_browser.collectionView scrollToPage:currentPage];
[self orientationWillChangeWithExpectOrientation:expectOrientation centerCell:centerCell];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
// Maybe the internal UI need to transform.
[self orientationChangeAnimationWithExpectOrientation:expectOrientation centerCell:centerCell];
centerCell.contentView.bounds = expectBounds;
centerCell.contentView.transform = CGAffineTransformIdentity;
self->_browser.containerView.bounds = expectBounds;
self->_browser.containerView.transform = CGAffineTransformMakeRotation(angleStatusBarToExpect);
} completion:^(BOOL finished) {
self.currentOrientation = expectOrientation;
self.rotating = NO;
[self orientationDidChangedWithOrientation:expectOrientation centerCell:centerCell];
}];
}
- (void)applicationWillChangeStatusBarOrientationNotification:(NSNotification *)noti {
if ([self supportedOnlyOneSystemOrientation]) return;
self.rotating = YES;
// Record current page number before transforming.
_recordPage = _browser.currentPage;
UICollectionViewCell<YBIBCellProtocol> *centerCell = (UICollectionViewCell<YBIBCellProtocol> *)self->_browser.collectionView.centerCell;
UIDeviceOrientation expectOrientation = ((NSNumber *)noti.userInfo[UIApplicationStatusBarOrientationUserInfoKey]).integerValue;
[self orientationWillChangeWithExpectOrientation:expectOrientation centerCell:centerCell];
}
- (void)applicationDidChangedStatusBarOrientationNotification:(NSNotification *)noti {
if ([self supportedOnlyOneSystemOrientation]) return;
UIDeviceOrientation expectOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
UICollectionViewCell<YBIBCellProtocol> *centerCell = (UICollectionViewCell<YBIBCellProtocol> *)self->_browser.collectionView.centerCell;
[self orientationChangeAnimationWithExpectOrientation:expectOrientation centerCell:centerCell];
CGRect expectBounds = (CGRect){CGPointZero, [self containerSizeWithOrientation:expectOrientation]};
self->_browser.collectionView.layout.itemSize = expectBounds.size;
// Reset to prevent the page number change after transforming.
[_browser.collectionView scrollToPage:_recordPage];
self.currentOrientation = expectOrientation;
self.rotating = NO;
[self orientationDidChangedWithOrientation:expectOrientation centerCell:centerCell];
}
#pragma mark - getters & setters
- (void)setRotating:(BOOL)rotating {
_rotating = rotating;
_browser.containerView.userInteractionEnabled = !rotating;
_browser.collectionView.userInteractionEnabled = !rotating;
_browser.collectionView.panGestureRecognizer.enabled = !rotating;
}
#pragma mark - calculate supported orientation of system
- (UIInterfaceOrientationMask)supportSystemOrientationMask {
UIInterfaceOrientationMask limitMask = 0;
// IphoneX series do not support UIInterfaceOrientationMaskPortraitUpsideDown, except selector '-application:supportedInterfaceOrientationsForWindow:' of '[UIApplication sharedApplication].delegate' return 0. Maybe it is BUG of Apple.
BOOL ignoreUpsideDownIfIphoneX = YES;
UIInterfaceOrientationMask delegateMask = [self maskOfApplicationDelegate];
if (delegateMask != kMaskNull) {
if (delegateMask == 0) {
// Apple do.
limitMask = UIInterfaceOrientationMaskAll;
ignoreUpsideDownIfIphoneX = NO;
} else {
limitMask = delegateMask;
}
} else {
// Lower priority.
limitMask = [self maskOfInfoPlist];
}
UIInterfaceOrientationMask supportMask = limitMask & [self maskOfViewController];
if (ignoreUpsideDownIfIphoneX && YBIBIsIphoneXSeries() && (supportMask & UIInterfaceOrientationMaskPortraitUpsideDown)) {
supportMask ^= UIInterfaceOrientationMaskPortraitUpsideDown;
}
return supportMask;
}
- (UIInterfaceOrientationMask)maskOfInfoPlist {
// 'Info.plist' will not change in a process.
static UIInterfaceOrientationMask mask = 0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *path = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
NSArray *array = dict[@"UISupportedInterfaceOrientations"];
NSSet *set = [NSSet setWithArray:array];
if ([set containsObject:@"UIInterfaceOrientationPortrait"]) mask |= UIInterfaceOrientationMaskPortrait;
if ([set containsObject:@"UIInterfaceOrientationLandscapeRight"]) mask |= UIInterfaceOrientationMaskLandscapeRight;
if ([set containsObject:@"UIInterfaceOrientationLandscapeLeft"]) mask |= UIInterfaceOrientationMaskLandscapeLeft;
if ([set containsObject:@"UIInterfaceOrientationPortraitUpsideDown"]) mask |= UIInterfaceOrientationMaskPortraitUpsideDown;
});
return mask == 0 ? kMaskNull : mask;
}
- (UIInterfaceOrientationMask)maskOfApplicationDelegate {
UIInterfaceOrientationMask mask = kMaskNull;
id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
if ([delegate respondsToSelector:@selector(application:supportedInterfaceOrientationsForWindow:)]) {
mask = [delegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:_browser.window];
}
return mask;
}
- (UIInterfaceOrientationMask)maskOfViewController {
UIInterfaceOrientationMask mask = kMaskNull;
// Find the UIViewController whitch 'browser' followed.
UIViewController *target = nil;
id next = _browser;
while (next) {
if ([next isKindOfClass:UIViewController.self]) {
target = next;
break;
}
if ([next isKindOfClass:UIWindow.self]) {
target = YBIBTopControllerByWindow(next);
break;
}
next = [next nextResponder];
}
// Cover directly.
if (target.tabBarController) {
mask = target.tabBarController.shouldAutorotate ? target.tabBarController.supportedInterfaceOrientations : 0;
} else if (target.navigationController) {
mask = target.navigationController.shouldAutorotate ? target.navigationController.supportedInterfaceOrientations : 0;
} else {
mask = target.shouldAutorotate ? target.supportedInterfaceOrientations : 0;
}
return mask;
}
@end

View File

@ -0,0 +1,24 @@
//
// YBImageBrowser+Internal.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/1.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBImageBrowser.h"
#import "YBIBContainerView.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBImageBrowser ()
@property (nonatomic, strong) YBIBContainerView *containerView;
- (void)implementGetBaseInfoProtocol:(id<YBIBGetBaseInfoProtocol>)obj;
@property (nonatomic, weak, nullable) NSObject *hiddenProjectiveView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,57 @@
//
// YBIBCopywriter.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2018/9/13.
// Copyright © 2018年 波儿菜. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, YBIBCopywriterType) {
/// 简体中文
YBIBCopywriterTypeSimplifiedChinese,
/// 英文
YBIBCopywriterTypeEnglish
};
/**
*/
@interface YBIBCopywriter : NSObject
/**
*/
+ (instancetype)sharedCopywriter;
/// 语言类型
@property (nonatomic, assign) YBIBCopywriterType type;
#pragma - 以下文案可更改
@property (nonatomic, copy) NSString *videoIsInvalid;
@property (nonatomic, copy) NSString *videoError;
@property (nonatomic, copy) NSString *unableToSave;
@property (nonatomic, copy) NSString *imageIsInvalid;
@property (nonatomic, copy) NSString *downloadFailed;
@property (nonatomic, copy) NSString *getPhotoAlbumAuthorizationFailed;
@property (nonatomic, copy) NSString *saveToPhotoAlbumSuccess;
@property (nonatomic, copy) NSString *saveToPhotoAlbumFailed;
@property (nonatomic, copy) NSString *saveToPhotoAlbum;
@property (nonatomic, copy) NSString *cancel;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,65 @@
//
// YBIBCopywriter.m
// YBImageBrowserDemo
//
// Created by on 2018/9/13.
// Copyright © 2018 . All rights reserved.
//
#import "YBIBCopywriter.h"
@implementation YBIBCopywriter
#pragma mark - life cycle
+ (instancetype)sharedCopywriter {
static YBIBCopywriter *copywriter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
copywriter = [YBIBCopywriter new];
});
return copywriter;
}
- (instancetype)init {
self = [super init];
if (self) {
_type = YBIBCopywriterTypeSimplifiedChinese;
NSArray *appleLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
if (appleLanguages && appleLanguages.count > 0) {
NSString *languages = appleLanguages[0];
if (![languages hasPrefix:@"zh-Hans"]) {
_type = YBIBCopywriterTypeEnglish;
}
}
[self initCopy];
}
return self;
}
#pragma mark - private
- (void)initCopy {
BOOL en = self.type == YBIBCopywriterTypeEnglish;
self.videoIsInvalid = en ? @"Video is invalid" : @"视频无效";
self.videoError = en ? @"Video error" : @"视频错误";
self.unableToSave = en ? @"Unable to save" : @"无法保存";
self.imageIsInvalid = en ? @"Image is invalid" : @"图片无效";
self.downloadFailed = en ? @"Download failed" : @"加载图片失败";
self.getPhotoAlbumAuthorizationFailed = en ? @"Failed to get album authorization" : @"获取相册权限失败";
self.saveToPhotoAlbumSuccess = en ? @"Save successful" : @"已保存到系统相册";
self.saveToPhotoAlbumFailed = en ? @"Save failed" : @"保存失败";
self.saveToPhotoAlbum = en ? @"Save" : @"保存到相册";
self.cancel = en ? @"Cancel" : @"取消";
}
#pragma mark - public
- (void)setType:(YBIBCopywriterType)type {
_type = type;
[self initCopy];
}
@end

View File

@ -0,0 +1,63 @@
//
// YBIBIconManager.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2018/8/29.
// Copyright © 2018年 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIImage (YBImageBrowser)
/**
便
@param name
@param bundle
@return
*/
+ (instancetype)ybib_imageNamed:(NSString *)name bundle:(NSBundle *)bundle;
@end
/// 获取图片闭包
typedef UIImage * _Nullable (^YBIBIconBlock)(void);
/**
*/
@interface YBIBIconManager : NSObject
/**
*/
+ (instancetype)sharedManager;
#pragma - 以下图片可更改
/// 基本-加载
@property (nonatomic, copy) YBIBIconBlock loadingImage;
/// 工具视图-保存
@property (nonatomic, copy) YBIBIconBlock toolSaveImage;
/// 工具视图-更多
@property (nonatomic, copy) YBIBIconBlock toolMoreImage;
/// 视频-播放
@property (nonatomic, copy) YBIBIconBlock videoPlayImage;
/// 视频-暂停
@property (nonatomic, copy) YBIBIconBlock videoPauseImage;
/// 视频-取消
@property (nonatomic, copy) YBIBIconBlock videoCancelImage;
/// 视频-播放大图
@property (nonatomic, copy) YBIBIconBlock videoBigPlayImage;
/// 视频-拖动圆点
@property (nonatomic, copy) YBIBIconBlock videoDragCircleImage;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,138 @@
//
// YBIBIconManager.m
// YBImageBrowserDemo
//
// Created by on 2018/8/29.
// Copyright © 2018 . All rights reserved.
//
#import "YBIBIconManager.h"
// The best order for path scale search.
static NSArray *_NSBundlePreferredScales() {
static NSArray *scales;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CGFloat screenScale = [UIScreen mainScreen].scale;
if (screenScale <= 1) {
scales = @[@1,@2,@3];
} else if (screenScale <= 2) {
scales = @[@2,@3,@1];
} else {
scales = @[@3,@2,@1];
}
});
return scales;
}
// Add scale modifier to the file name (without path extension), from @"name" to @"name@2x".
static NSString *_NSStringByAppendingNameScale(NSString *string, CGFloat scale) {
if (!string) return nil;
if (fabs(scale - 1) <= __FLT_EPSILON__ || string.length == 0 || [string hasSuffix:@"/"]) return string.copy;
return [string stringByAppendingFormat:@"@%@x", @(scale)];
}
@implementation UIImage (YBImageBrowser)
+ (instancetype)ybib_imageNamed:(NSString *)name bundle:(NSBundle *)bundle {
if (name.length == 0) return nil;
if ([name hasSuffix:@"/"]) return nil;
NSString *res = name.stringByDeletingPathExtension;
NSString *ext = name.pathExtension;
NSString *path = nil;
CGFloat scale = 1;
// If no extension, guess by system supported (same as UIImage).
NSArray *exts = ext.length > 0 ? @[ext] : @[@"", @"png", @"jpeg", @"jpg", @"gif", @"webp", @"apng"];
NSArray *scales = _NSBundlePreferredScales();
for (int s = 0; s < scales.count; s++) {
scale = ((NSNumber *)scales[s]).floatValue;
NSString *scaledName = _NSStringByAppendingNameScale(res, scale);
for (NSString *e in exts) {
path = [bundle pathForResource:scaledName ofType:e];
if (path) break;
}
if (path) break;
}
if (path.length == 0) {
// Assets.xcassets supported.
return [self imageNamed:name];
}
NSData *data = [NSData dataWithContentsOfFile:path];
if (data.length == 0) return nil;
return [[self alloc] initWithData:data scale:scale];
}
@end
NSBundle *YBIBDefaultBundle(void) {
static NSBundle *bundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *_bundle = [NSBundle bundleForClass:NSClassFromString(@"YBImageBrowser")];
NSString *path = [_bundle pathForResource:@"YBImageBrowser" ofType:@"bundle"];
bundle = [NSBundle bundleWithPath:path];
});
return bundle;
}
NSBundle *YBIBVideoBundle(void) {
static NSBundle *bundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *_bundle = [NSBundle bundleForClass:NSClassFromString(@"YBImageBrowser")];
NSString *path = [_bundle pathForResource:@"YBImageBrowserVideo" ofType:@"bundle"];
bundle = [NSBundle bundleWithPath:path];
});
return bundle;
}
@implementation YBIBIconManager
+ (instancetype)sharedManager {
static YBIBIconManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [YBIBIconManager new];
});
return manager;
}
- (instancetype)init {
self = [super init];
if (self) {
_loadingImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_loading" bundle:YBIBDefaultBundle()];
};
_toolSaveImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_save" bundle:YBIBDefaultBundle()];
};
_toolMoreImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_more" bundle:YBIBDefaultBundle()];
};
_videoPlayImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_play" bundle:YBIBVideoBundle()];
};
_videoPauseImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_pause" bundle:YBIBVideoBundle()];
};
_videoCancelImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_cancel" bundle:YBIBVideoBundle()];
};
_videoBigPlayImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_bigPlay" bundle:YBIBVideoBundle()];
};
_videoDragCircleImage = ^UIImage * _Nullable{
return [UIImage ybib_imageNamed:@"ybib_circlePoint" bundle:YBIBVideoBundle()];
};
}
return self;
}
@end

View File

@ -0,0 +1,32 @@
//
// YBIBPhotoAlbumManager.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2018/8/28.
// Copyright © 2018年 波儿菜. All rights reserved.
//
#import <Photos/Photos.h>
NS_ASSUME_NONNULL_BEGIN
@interface YBIBPhotoAlbumManager : NSObject
/**
Get photo album authorization.
*/
+ (void)getPhotoAlbumAuthorizationSuccess:(void(^)(void))success failed:(void(^)(void))failed;
/**
Get 'AVAsset' by 'PHAsset' asynchronously, callback is in child thread.
*/
+ (void)getAVAssetWithPHAsset:(PHAsset *)phAsset completion:(void(^)(AVAsset * _Nullable asset))completion;
/**
Get 'ImageData' by 'PHAsset' synchronously, callback is in child thread.
*/
+ (void)getImageDataWithPHAsset:(PHAsset *)phAsset completion:(void(^)(NSData * _Nullable data))completion;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,65 @@
//
// YBIBPhotoAlbumManager.m
// YBImageBrowserDemo
//
// Created by on 2018/8/28.
// Copyright © 2018 . All rights reserved.
//
#import "YBIBPhotoAlbumManager.h"
#import "YBIBUtilities.h"
@implementation YBIBPhotoAlbumManager
+ (void)getAVAssetWithPHAsset:(PHAsset *)phAsset completion:(nonnull void (^)(AVAsset * _Nullable))completion {
PHVideoRequestOptions *options = [PHVideoRequestOptions new];
options.networkAccessAllowed = YES;
[[PHImageManager defaultManager] requestAVAssetForVideo:phAsset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
completion(asset);
}];
}
+ (void)getImageDataWithPHAsset:(PHAsset *)phAsset completion:(nonnull void (^)(NSData * _Nullable))completion {
PHImageRequestOptions *options = [PHImageRequestOptions new];
options.networkAccessAllowed = YES;
options.resizeMode = PHImageRequestOptionsResizeModeNone;
// Only when this property is YES, the callback will in child thread.
options.synchronous = YES;
[[PHImageManager defaultManager] requestImageDataForAsset:phAsset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
BOOL complete = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue];
if (complete && imageData) {
completion(imageData);
} else {
completion(nil);
}
}];
}
+ (void)getPhotoAlbumAuthorizationSuccess:(void(^)(void))success failed:(void(^)(void))failed {
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
switch (status) {
case PHAuthorizationStatusDenied:
if (failed) failed();
break;
case PHAuthorizationStatusRestricted:
if (failed) failed();
break;
case PHAuthorizationStatusNotDetermined: {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status){
YBIB_DISPATCH_ASYNC_MAIN(^{
if (status == PHAuthorizationStatusAuthorized) {
if (success) success();
} else {
if (failed) failed();
}
})
}];
}
break;
case PHAuthorizationStatusAuthorized:
if (success) success();
break;
}
}
@end

View File

@ -0,0 +1,29 @@
//
// YBIBSentinel.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/18.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Thread safe.
*/
@interface YBIBSentinel : NSObject
/// Returns the current value of the counter.
@property (readonly) int32_t value;
/**
Increase the value atomically.
@return The new value.
*/
- (int32_t)increase;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,24 @@
//
// YBIBSentinel.m
// YBImageBrowserDemo
//
// Created by on 2019/6/18.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBSentinel.h"
#import <libkern/OSAtomic.h>
@implementation YBIBSentinel {
int32_t _value;
}
- (int32_t)value {
return _value;
}
- (int32_t)increase {
return OSAtomicIncrement32(&_value);
}
@end

View File

@ -0,0 +1,52 @@
//
// YBIBUtilities.h
// YBImageBrowserDemo
//
// Created by 杨少 on 2018/4/11.
// Copyright © 2018年 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#define YBIB_DISPATCH_ASYNC(queue, block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(queue)) == 0) {\
block();\
} else {\
dispatch_async(queue, block);\
}
#define YBIB_DISPATCH_ASYNC_MAIN(block) YBIB_DISPATCH_ASYNC(dispatch_get_main_queue(), block)
#define YBIB_CODE_EXEC_TIME(KEY, ...) \
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); \
__VA_ARGS__ \
CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime); \
NSLog(@"%@ Time-Consuming: %fms", KEY, linkTime * 1000.0);
UIWindow * _Nonnull YBIBNormalWindow(void);
UIViewController * _Nullable YBIBTopController(void);
UIViewController * _Nullable YBIBTopControllerByWindow(UIWindow *);
BOOL YBIBLowMemory(void);
BOOL YBIBIsIphoneXSeries(void);
CGFloat YBIBStatusbarHeight(void);
CGFloat YBIBSafeAreaBottomHeight(void);
UIImage *YBIBSnapshotView(UIView *);
/// This is orientation of 'YBImageBrowser' not 'UIDevice'.
UIEdgeInsets YBIBPaddingByBrowserOrientation(UIDeviceOrientation);
@interface YBIBUtilities : NSObject
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,134 @@
//
// YBIBUtilities.m
// YBImageBrowserDemo
//
// Created by on 2018/4/11.
// Copyright © 2018 . All rights reserved.
//
#import "YBIBUtilities.h"
#import <sys/utsname.h>
UIWindow * _Nullable YBIBNormalWindow(void) {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal) {
NSArray *windows = [[UIApplication sharedApplication] windows];
for(UIWindow *temp in windows) {
if (temp.windowLevel == UIWindowLevelNormal) {
return temp;
}
}
}
return window;
}
UIViewController * _Nullable YBIBTopController(void) {
return YBIBTopControllerByWindow(YBIBNormalWindow());
}
UIViewController * _Nullable YBIBTopControllerByWindow(UIWindow *window) {
if (!window) return nil;
UIViewController *top = nil;
id nextResponder;
if (window.subviews.count > 0) {
UIView *frontView = [window.subviews objectAtIndex:0];
nextResponder = frontView.nextResponder;
}
if (nextResponder && [nextResponder isKindOfClass:UIViewController.class]) {
top = nextResponder;
} else {
top = window.rootViewController;
}
while ([top isKindOfClass:UITabBarController.class] || [top isKindOfClass:UINavigationController.class] || top.presentedViewController) {
if ([top isKindOfClass:UITabBarController.class]) {
top = ((UITabBarController *)top).selectedViewController;
} else if ([top isKindOfClass:UINavigationController.class]) {
top = ((UINavigationController *)top).topViewController;
} else if (top.presentedViewController) {
top = top.presentedViewController;
}
}
return top;
}
BOOL YBIBLowMemory(void) {
static BOOL lowMemory = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
unsigned long long physicalMemory = [[NSProcessInfo processInfo] physicalMemory];
lowMemory = physicalMemory > 0 && physicalMemory < 1024 * 1024 * 1500;
});
return lowMemory;
}
BOOL YBIBIsIphoneXSeries(void) {
return YBIBStatusbarHeight() > 20;
}
CGFloat YBIBStatusbarHeight(void) {
CGFloat height = 0;
if (@available(iOS 11.0, *)) {
height = UIApplication.sharedApplication.delegate.window.safeAreaInsets.top;
}
if (height <= 0) {
height = UIApplication.sharedApplication.statusBarFrame.size.height;
}
if (height <= 0) {
height = 20;
}
return height;
}
CGFloat YBIBSafeAreaBottomHeight(void) {
CGFloat bottom = 0;
if (@available(iOS 11.0, *)) {
bottom = UIApplication.sharedApplication.delegate.window.safeAreaInsets.bottom;
}
return bottom;
}
UIImage *YBIBSnapshotView(UIView *view) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, [UIScreen mainScreen].scale);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
UIEdgeInsets YBIBPaddingByBrowserOrientation(UIDeviceOrientation orientation) {
UIEdgeInsets padding = UIEdgeInsetsZero;
if (!YBIBIsIphoneXSeries()) return padding;
UIDeviceOrientation barOrientation = (UIDeviceOrientation)UIApplication.sharedApplication.statusBarOrientation;
if (UIDeviceOrientationIsLandscape(orientation)) {
BOOL same = orientation == barOrientation;
BOOL reverse = !same && UIDeviceOrientationIsLandscape(barOrientation);
if (same) {
padding.bottom = YBIBSafeAreaBottomHeight();
padding.top = 0;
} else if (reverse) {
padding.top = YBIBSafeAreaBottomHeight();
padding.bottom = 0;
}
padding.left = padding.right = MAX(YBIBSafeAreaBottomHeight(), YBIBStatusbarHeight());
} else {
if (orientation == UIDeviceOrientationPortrait) {
padding.top = YBIBStatusbarHeight();
padding.bottom = barOrientation == UIDeviceOrientationPortrait ? YBIBSafeAreaBottomHeight() : 0;
} else {
padding.bottom = YBIBStatusbarHeight();
padding.top = barOrientation == UIDeviceOrientationPortrait ? YBIBSafeAreaBottomHeight() : 0;
}
padding.left = padding.right = UIDeviceOrientationIsLandscape(barOrientation) ? YBIBSafeAreaBottomHeight() : 0 ;
}
return padding;
}
@implementation YBIBUtilities
@end

View File

@ -0,0 +1,33 @@
//
// YBIBImageCache+Internal.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/13.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBIBImageCache.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, YBIBImageCacheType) {
YBIBImageCacheTypeOrigin,
YBIBImageCacheTypeCompressed
};
/**
Not thread safe.
*/
@interface YBIBImageCache ()
- (void)setImage:(UIImage *)image type:(YBIBImageCacheType)type forKey:(NSString *)key resident:(BOOL)resident;
- (nullable UIImage *)imageForKey:(NSString *)key type:(YBIBImageCacheType)type;
- (void)removeForKey:(NSString *)key;
- (void)removeResidentForKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,30 @@
//
// YBIBImageCache.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/13.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class YBIBImageCache;
@interface NSObject (YBIBImageCache)
/// 图片浏览器持有的图片缓存管理类
@property (nonatomic, strong, readonly) YBIBImageCache *ybib_imageCache;
@end
@interface YBIBImageCache : NSObject
/// 缓存数量限制(一个单位表示一个 YBIBImageData 产生的所有图片数据)
@property (nonatomic, assign) NSUInteger imageCacheCountLimit;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,112 @@
//
// YBIBImageCache.m
// YBImageBrowserDemo
//
// Created by on 2019/6/13.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBImageCache.h"
#import "YBIBImageCache+Internal.h"
#import "YBIBUtilities.h"
#import <objc/runtime.h>
@implementation NSObject (YBIBImageCache)
static void *YBIBImageCacheKey = &YBIBImageCacheKey;
- (void)setYbib_imageCache:(YBIBImageCache *)ybib_imageCache {
objc_setAssociatedObject(self, YBIBImageCacheKey, ybib_imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (YBIBImageCache *)ybib_imageCache {
YBIBImageCache *cache = objc_getAssociatedObject(self, YBIBImageCacheKey);
if (!cache) {
cache = [YBIBImageCache new];
self.ybib_imageCache = cache;
}
return cache;
}
@end
@interface YBIBImageCachePack : NSObject
@property (nonatomic, strong) UIImage *originImage;
@property (nonatomic, strong) UIImage *compressedImage;
@end
@implementation YBIBImageCachePack
@end
@implementation YBIBImageCache {
NSCache<NSString *, YBIBImageCachePack *> *_imageCache;
NSMutableDictionary<NSString *, YBIBImageCachePack *> *_residentCache;
}
#pragma mark - life cycle
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
- (instancetype)init {
self = [super init];
if (self) {
_imageCache = [NSCache new];
_imageCache.countLimit = _imageCacheCountLimit = YBIBLowMemory() ? 6 : 12;
_residentCache = [NSMutableDictionary dictionary];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
return self;
}
#pragma mark - event
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
[_imageCache removeAllObjects];
[_residentCache removeAllObjects];
}
#pragma mark - public
- (void)setImage:(UIImage *)image type:(YBIBImageCacheType)type forKey:(NSString *)key resident:(BOOL)resident {
YBIBImageCachePack *pack = [_imageCache objectForKey:key];
if (!pack) {
pack = [YBIBImageCachePack new];
[_imageCache setObject:pack forKey:key];
}
switch (type) {
case YBIBImageCacheTypeOrigin:
pack.originImage = image;
break;
case YBIBImageCacheTypeCompressed:
pack.compressedImage = image;
break;
}
[_residentCache setObject:pack forKey:key];
}
- (UIImage *)imageForKey:(NSString *)key type:(YBIBImageCacheType)type {
YBIBImageCachePack *pack = [_imageCache objectForKey:key] ?: [_residentCache objectForKey:key];
switch (type) {
case YBIBImageCacheTypeOrigin: return pack.originImage;
case YBIBImageCacheTypeCompressed: return pack.compressedImage;
default: return nil;
}
}
- (void)removeForKey:(NSString *)key {
[_imageCache removeObjectForKey:key];
[_residentCache removeObjectForKey:key];
}
- (void)removeResidentForKey:(NSString *)key {
[_residentCache removeObjectForKey:key];
}
#pragma mark - setter
- (void)setImageCacheCountLimit:(NSUInteger)imageCacheCountLimit {
_imageCacheCountLimit = imageCacheCountLimit;
_imageCache.countLimit = imageCacheCountLimit;
}
@end

View File

@ -0,0 +1,22 @@
//
// YBIBImageCell+Internal.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/12/23.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBIBImageCell.h"
#import "YBIBImageScrollView.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBImageCell ()
@property (nonatomic, strong) YBIBImageScrollView *imageScrollView;
@property (nonatomic, strong) UIImageView *tailoringImageView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,17 @@
//
// YBIBImageCell.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/5.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBIBCellProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface YBIBImageCell : UICollectionViewCell <YBIBCellProtocol>
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,522 @@
//
// YBIBImageCell.m
// YBImageBrowserDemo
//
// Created by on 2019/6/5.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBImageCell.h"
#import "YBIBImageData.h"
#import "YBIBIconManager.h"
#import "YBIBImageCell+Internal.h"
#import "YBIBImageData+Internal.h"
#import "YBIBCopywriter.h"
#import "YBIBUtilities.h"
@interface YBIBImageCell () <YBIBImageDataDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate>
@end
@implementation YBIBImageCell {
CGPoint _interactStartPoint;
BOOL _interacting;
}
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initValue];
[self.contentView addSubview:self.imageScrollView];
[self addGesture];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.imageScrollView.frame = self.bounds;
}
- (void)initValue {
_interactStartPoint = CGPointZero;
_interacting = NO;
}
- (void)prepareForReuse {
((YBIBImageData *)self.yb_cellData).delegate = nil;
[self.imageScrollView reset];
[self hideTailoringImageView];
[self hideAuxiliaryView];
[super prepareForReuse];
}
#pragma mark - <YBIBCellProtocol>
@synthesize yb_currentOrientation = _yb_currentOrientation;
@synthesize yb_containerSize = _yb_containerSize;
@synthesize yb_backView = _yb_backView;
@synthesize yb_collectionView = _yb_collectionView;
@synthesize yb_isTransitioning = _yb_isTransitioning;
@synthesize yb_isRotating = _yb_isRotating;
@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
@synthesize yb_hideStatusBar = _yb_hideStatusBar;
@synthesize yb_hideBrowser = _yb_hideBrowser;
@synthesize yb_hideToolViews = _yb_hideToolViews;
@synthesize yb_cellData = _yb_cellData;
@synthesize yb_cellIsInCenter = _yb_cellIsInCenter;
@synthesize yb_selfPage = _yb_selfPage;
@synthesize yb_currentPage = _yb_currentPage;
- (void)setYb_cellData:(id<YBIBDataProtocol>)yb_cellData {
_yb_cellData = yb_cellData;
((YBIBImageData *)yb_cellData).delegate = self;
}
- (UIView *)yb_foregroundView {
return self.imageScrollView.imageView;
}
- (void)yb_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation {
[self hideTailoringImageView];
}
- (void)yb_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation {
[self updateImageLayoutWithOrientation:orientation previousImageSize:self.imageScrollView.imageView.image.size];
}
#pragma mark - private
- (CGSize)contentSizeWithContainerSize:(CGSize)containerSize imageViewFrame:(CGRect)imageViewFrame {
return CGSizeMake(MAX(containerSize.width, imageViewFrame.size.width), MAX(containerSize.height, imageViewFrame.size.height));
}
- (void)updateImageLayoutWithOrientation:(UIDeviceOrientation)orientation previousImageSize:(CGSize)previousImageSize {
if (_interacting) [self restoreInteractionWithDuration:0];
YBIBImageData *data = self.yb_cellData;
CGSize imageSize;
UIImage *image = self.imageScrollView.imageView.image;
YBIBScrollImageType imageType = self.imageScrollView.imageType;
if (imageType == YBIBScrollImageTypeCompressed) {
imageSize = data.originImage ? data.originImage.size : image.size;
} else {
imageSize = image.size;
}
CGSize containerSize = self.yb_containerSize(orientation);
CGRect imageViewFrame = [data.layout yb_imageViewFrameWithContainerSize:containerSize imageSize:imageSize orientation:orientation];
CGSize contentSize = [self contentSizeWithContainerSize:containerSize imageViewFrame:imageViewFrame];
CGFloat maxZoomScale = imageType == YBIBScrollImageTypeThumb ? 1 : [data.layout yb_maximumZoomScaleWithContainerSize:containerSize imageSize:imageSize orientation:orientation];
// 'zoomScale' must set before 'contentSize' and 'imageView.frame'.
self.imageScrollView.zoomScale = 1;
self.imageScrollView.contentSize = contentSize;
self.imageScrollView.minimumZoomScale = 1;
self.imageScrollView.maximumZoomScale = maxZoomScale;
CGFloat scale;
if (previousImageSize.width > 0 && previousImageSize.height > 0) {
scale = imageSize.width / imageSize.height - previousImageSize.width / previousImageSize.height;
} else {
scale = 0;
}
// '0.001' is admissible error.
if (ABS(scale) <= 0.001) {
self.imageScrollView.imageView.frame = imageViewFrame;
} else {
[UIView animateWithDuration:0.25 animations:^{
self.imageScrollView.imageView.frame = imageViewFrame;
}];
}
}
- (void)cuttingImage {
// This method has been delayed called, so 'browser' may be in transit now.
if (self.yb_isTransitioning()) return;
if (_interacting) return;
YBIBImageData *data = self.yb_cellData;
if (!data.originImage) return;
if (self.imageScrollView.zoomScale < data.cuttingZoomScale) return;
if ([data shouldCompress]) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cuttingImage_) object:nil];
[self performSelector:@selector(cuttingImage_) withObject:nil afterDelay:0.15];
}
}
- (void)cuttingImage_ {
YBIBImageData *data = self.yb_cellData;
if (!data.originImage) return;
CGFloat scale = data.originImage.size.width / self.imageScrollView.contentSize.width;
CGFloat x = self.imageScrollView.contentOffset.x * scale,
y = self.imageScrollView.contentOffset.y * scale,
width = self.imageScrollView.bounds.size.width * scale,
height = self.imageScrollView.bounds.size.height * scale;
__weak typeof(self) wSelf = self;
[data cuttingImageToRect:CGRectMake(x, y, width, height) complete:^(UIImage *image) {
if (!image) return;
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
if (data == self.yb_cellData && !self.imageScrollView.isDragging && !self->_interacting && !self.yb_isTransitioning()) {
[self showTailoringImageView:image];
}
})
}];
}
- (void)showTailoringImageView:(UIImage *)image {
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
if (!self.tailoringImageView.superview) {
[self.contentView addSubview:self.tailoringImageView];
}
self.tailoringImageView.frame = CGRectMake(0, 0, containerSize.width, containerSize.height);
self.tailoringImageView.hidden = NO;
self.tailoringImageView.image = image;
}
- (void)hideTailoringImageView {
// Don't use 'getter' method, because it's according to the need to load.
if (_tailoringImageView) {
self.tailoringImageView.hidden = YES;
}
}
- (void)hideAuxiliaryView {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
[self.yb_auxiliaryViewHandler() yb_hideToastWithContainer:self];
}
- (void)hideBrowser {
((YBIBImageData *)self.yb_cellData).delegate = nil;
[self hideTailoringImageView];
[self hideAuxiliaryView];
self.yb_hideBrowser();
_interacting = NO;
}
#pragma mark - <YBIBImageDataDelegate>
- (void)yb_imageData:(YBIBImageData *)data startLoadingWithStatus:(YBIBImageLoadingStatus)status {
switch (status) {
case YBIBImageLoadingStatusDecoding: {
if (!self.imageScrollView.imageView.image) {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
}
}
break;
case YBIBImageLoadingStatusProcessing: {
if (!self.imageScrollView.imageView.image) {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
}
}
break;
case YBIBImageLoadingStatusCompressing: {
if (!self.imageScrollView.imageView.image) {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
}
}
break;
case YBIBImageLoadingStatusReadingPHAsset: {
if (!self.imageScrollView.imageView.image) {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self];
}
}
break;
case YBIBImageLoadingStatusNone: {
[self hideAuxiliaryView];
}
break;
default:
break;
}
}
- (void)yb_imageData:(YBIBImageData *)data readyForImage:(__kindof UIImage *)image {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
if (self.imageScrollView.imageView.image == image) return;
CGSize size = self.imageScrollView.imageView.image.size;
[self.imageScrollView setImage:image type:YBIBScrollImageTypeOriginal];
[self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:size];
}
- (void)yb_imageData:(YBIBImageData *)data readyForCompressedImage:(__kindof UIImage *)image {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
if (self.imageScrollView.imageView.image == image) return;
CGSize size = self.imageScrollView.imageView.image.size;
[self.imageScrollView setImage:image type:YBIBScrollImageTypeCompressed];
[self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:size];
}
- (void)yb_imageData:(YBIBImageData *)data readyForThumbImage:(__kindof UIImage *)image {
if (self.imageScrollView.imageView.image) return;
[self.imageScrollView setImage:image type:YBIBScrollImageTypeThumb];
[self updateImageLayoutWithOrientation:self.yb_currentOrientation() previousImageSize:image.size];
}
- (void)yb_imageIsInvalidForData:(YBIBImageData *)data {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
NSString *imageIsInvalid = [YBIBCopywriter sharedCopywriter].imageIsInvalid;
if (self.imageScrollView.imageView.image) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:imageIsInvalid];
} else {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self text:imageIsInvalid];
}
}
- (void)yb_imageData:(YBIBImageData *)data downloadProgress:(CGFloat)progress {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self progress:progress];
}
- (void)yb_imageDownloadFailedForData:(YBIBImageData *)data {
if (self.imageScrollView.imageView.image) {
[self.yb_auxiliaryViewHandler() yb_hideLoadingWithContainer:self];
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self text:[YBIBCopywriter sharedCopywriter].downloadFailed];
} else {
[self.yb_auxiliaryViewHandler() yb_showLoadingWithContainer:self text:[YBIBCopywriter sharedCopywriter].downloadFailed];
}
}
#pragma mark - gesture
- (void)addGesture {
UITapGestureRecognizer *tapSingle = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapSingle:)];
tapSingle.numberOfTapsRequired = 1;
UITapGestureRecognizer *tapDouble = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapDouble:)];
tapDouble.numberOfTapsRequired = 2;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToPan:)];
pan.maximumNumberOfTouches = 1;
pan.delegate = self;
[tapSingle requireGestureRecognizerToFail:tapDouble];
[tapSingle requireGestureRecognizerToFail:pan];
[tapDouble requireGestureRecognizerToFail:pan];
[self addGestureRecognizer:tapSingle];
[self addGestureRecognizer:tapDouble];
[self addGestureRecognizer:pan];
}
- (void)respondsToTapSingle:(UITapGestureRecognizer *)tap {
if (self.yb_isRotating()) return;
YBIBImageData *data = self.yb_cellData;
if (data.singleTouchBlock) {
data.singleTouchBlock(data);
} else {
[self hideTailoringImageView];
[self hideAuxiliaryView];
self.yb_hideBrowser();
}
}
- (void)respondsToTapDouble:(UITapGestureRecognizer *)tap {
if (self.yb_isRotating()) return;
[self hideTailoringImageView];
UIScrollView *scrollView = self.imageScrollView;
UIView *zoomView = [self viewForZoomingInScrollView:scrollView];
CGPoint point = [tap locationInView:zoomView];
if (!CGRectContainsPoint(zoomView.bounds, point)) return;
if (scrollView.zoomScale == scrollView.maximumZoomScale) {
[scrollView setZoomScale:1 animated:YES];
} else {
[scrollView zoomToRect:CGRectMake(point.x, point.y, 1, 1) animated:YES];
}
}
- (void)respondsToPan:(UIPanGestureRecognizer *)pan {
if (self.yb_isRotating()) return;
YBIBInteractionProfile *profile = ((YBIBImageData *)self.yb_cellData).interactionProfile;
if (profile.disable) return;
if ((CGRectIsEmpty(self.imageScrollView.imageView.frame) || !self.imageScrollView.imageView.image)) return;
CGPoint point = [pan locationInView:self];
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
if (pan.state == UIGestureRecognizerStateBegan) {
_interactStartPoint = point;
} else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateRecognized || pan.state == UIGestureRecognizerStateFailed) {
// End.
if (_interacting) {
CGPoint velocity = [pan velocityInView:self.imageScrollView];
BOOL velocityArrive = ABS(velocity.y) > profile.dismissVelocityY;
BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > containerSize.height * profile.dismissScale;
BOOL shouldDismiss = distanceArrive || velocityArrive;
if (shouldDismiss) {
[self hideBrowser];
} else {
[self restoreInteractionWithDuration:profile.restoreDuration];
}
}
} else if (pan.state == UIGestureRecognizerStateChanged) {
if (_interacting) {
// Change.
self.imageScrollView.center = point;
CGFloat scale = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 1.2);
if (scale > 1) scale = 1;
if (scale < 0.35) scale = 0.35;
self.imageScrollView.transform = CGAffineTransformMakeScale(scale, scale);
CGFloat alpha = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 0.7);
if (alpha > 1) alpha = 1;
if (alpha < 0) alpha = 0;
self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:alpha];
} else {
// Start.
if (CGPointEqualToPoint(_interactStartPoint, CGPointZero) || self.yb_currentPage() != self.yb_selfPage() || !self.yb_cellIsInCenter() || self.imageScrollView.isZooming) return;
CGPoint velocity = [pan velocityInView:self.imageScrollView];
CGFloat triggerDistance = profile.triggerDistance;
CGFloat offsetY = self.imageScrollView.contentOffset.y, height = self.imageScrollView.bounds.size.height;
BOOL distanceArrive = ABS(point.x - _interactStartPoint.x) < triggerDistance && ABS(velocity.x) < 500;
BOOL upArrive = point.y - _interactStartPoint.y > triggerDistance && offsetY <= 1;
BOOL downArrive = point.y - _interactStartPoint.y < -triggerDistance && offsetY + height >= MAX(self.imageScrollView.contentSize.height, height) - 1;
BOOL shouldStart = (upArrive || downArrive) && distanceArrive;
if (!shouldStart) return;
_interactStartPoint = point;
CGRect startFrame = self.imageScrollView.frame;
CGFloat anchorX = point.x / startFrame.size.width, anchorY = point.y / startFrame.size.height;
self.imageScrollView.layer.anchorPoint = CGPointMake(anchorX, anchorY);
self.imageScrollView.userInteractionEnabled = NO;
self.imageScrollView.scrollEnabled = NO;
self.imageScrollView.center = point;
self.yb_hideToolViews(YES);
self.yb_hideStatusBar(NO);
self.yb_collectionView().scrollEnabled = NO;
[self hideTailoringImageView];
_interacting = YES;
}
}
}
- (void)restoreInteractionWithDuration:(NSTimeInterval)duration {
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
void (^animations)(void) = ^{
self.yb_backView.backgroundColor = [self.yb_backView.backgroundColor colorWithAlphaComponent:1];
CGPoint anchorPoint = self.imageScrollView.layer.anchorPoint;
self.imageScrollView.center = CGPointMake(containerSize.width * anchorPoint.x, containerSize.height * anchorPoint.y);
self.imageScrollView.transform = CGAffineTransformIdentity;
};
void (^completion)(BOOL finished) = ^(BOOL finished){
self.imageScrollView.layer.anchorPoint = CGPointMake(0.5, 0.5);
self.imageScrollView.center = CGPointMake(containerSize.width * 0.5, containerSize.height * 0.5);
self.imageScrollView.userInteractionEnabled = YES;
self.imageScrollView.scrollEnabled = YES;
self.yb_hideToolViews(NO);
self.yb_hideStatusBar(YES);
self.yb_collectionView().scrollEnabled = YES;
[self cuttingImage];
self->_interactStartPoint = CGPointZero;
self->_interacting = NO;
};
if (duration <= 0) {
animations();
completion(NO);
} else {
[UIView animateWithDuration:duration animations:animations completion:completion];
}
}
#pragma mark - <UIScrollViewDelegate>
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
YBIBImageData *data = self.yb_cellData;
if (data.imageDidZoomBlock) {
data.imageDidZoomBlock(data, scrollView);
}
CGRect imageViewFrame = self.imageScrollView.imageView.frame;
CGFloat width = imageViewFrame.size.width,
height = imageViewFrame.size.height,
sHeight = scrollView.bounds.size.height,
sWidth = scrollView.bounds.size.width;
if (height > sHeight) {
imageViewFrame.origin.y = 0;
} else {
imageViewFrame.origin.y = (sHeight - height) / 2.0;
}
if (width > sWidth) {
imageViewFrame.origin.x = 0;
} else {
imageViewFrame.origin.x = (sWidth - width) / 2.0;
}
self.imageScrollView.imageView.frame = imageViewFrame;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageScrollView.imageView;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
YBIBImageData *data = self.yb_cellData;
if (data.imageDidScrollBlock) {
data.imageDidScrollBlock(data, scrollView);
}
[self cuttingImage];
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
[self hideTailoringImageView];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self hideTailoringImageView];
}
#pragma mark - <UIGestureRecognizerDelegate>
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
#pragma mark - getters
- (YBIBImageScrollView *)imageScrollView {
if (!_imageScrollView) {
_imageScrollView = [YBIBImageScrollView new];
_imageScrollView.delegate = self;
}
return _imageScrollView;
}
- (UIImageView *)tailoringImageView {
if (!_tailoringImageView) {
_tailoringImageView = [UIImageView new];
_tailoringImageView.contentMode = UIViewContentModeScaleAspectFit;
}
return _tailoringImageView;
}
@end

View File

@ -0,0 +1,63 @@
//
// YBIBImageData+Internal.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/12.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBIBImageData.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, YBIBImageLoadingStatus) {
YBIBImageLoadingStatusNone,
YBIBImageLoadingStatusCompressing,
YBIBImageLoadingStatusDecoding,
YBIBImageLoadingStatusQuerying,
YBIBImageLoadingStatusProcessing,
YBIBImageLoadingStatusDownloading,
YBIBImageLoadingStatusReadingPHAsset,
};
@class YBIBImageData;
@protocol YBIBImageDataDelegate <NSObject>
@required
- (void)yb_imageData:(YBIBImageData *)data startLoadingWithStatus:(YBIBImageLoadingStatus)status;
- (void)yb_imageIsInvalidForData:(YBIBImageData *)data;
- (void)yb_imageData:(YBIBImageData *)data readyForImage:(__kindof UIImage *)image;
- (void)yb_imageData:(YBIBImageData *)data readyForThumbImage:(__kindof UIImage *)image;
- (void)yb_imageData:(YBIBImageData *)data readyForCompressedImage:(__kindof UIImage *)image;
- (void)yb_imageData:(YBIBImageData *)data downloadProgress:(CGFloat)progress;
- (void)yb_imageDownloadFailedForData:(YBIBImageData *)data;
@end
@interface YBIBImageData ()
@property (nonatomic, weak) id<YBIBImageDataDelegate> delegate;
/// The status of asynchronous tasks.
@property (nonatomic, assign) YBIBImageLoadingStatus loadingStatus;
/// The origin image.
@property (nonatomic, strong) UIImage *originImage;
/// The image compressed by 'originImage' if need.
@property (nonatomic, strong) UIImage *compressedImage;
- (BOOL)shouldCompress;
- (void)cuttingImageToRect:(CGRect)rect complete:(void(^)(UIImage * _Nullable image))complete;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,157 @@
//
// YBIBImageData.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/5.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <Photos/Photos.h>
#import "YBIBDataProtocol.h"
#import "YBIBImageLayout.h"
#import "YBIBImageCache.h"
#import "YBIBInteractionProfile.h"
NS_ASSUME_NONNULL_BEGIN
@class YBIBImageData;
/// 获取 NSData 的闭包
typedef NSData * _Nullable (^YBIBImageDataBlock)(void);
/// 获取 UIImage 的闭包
typedef UIImage * _Nullable (^YBIBImageBlock)(void);
/// 修改 NSURLRequest 并返回的闭包
typedef NSURLRequest * _Nullable (^YBIBRequestModifierBlock)(YBIBImageData *imageData, NSURLRequest *request);
/// 根据图片逻辑像素和 scale 判断是否需要预解码的闭包
typedef BOOL (^YBIBPreDecodeDecisionBlock)(YBIBImageData *imageData, CGSize imageSize, CGFloat scale);
/// 修改 UIImage 并返回的闭包
typedef void (^YBIBImageModifierBlock)(YBIBImageData *imageData, UIImage *image, void(^completion)(UIImage *processedImage));
/// 单击事件的处理闭包
typedef void (^YBIBImageSingleTouchBlock)(YBIBImageData *imageData);
/// 内部图片滚动视图状态回调闭包
typedef void (^YBIBImageScrollViewStatusBlock)(YBIBImageData *imageData, UIScrollView *scrollView);
/**
*/
@interface YBIBImageData : NSObject <YBIBDataProtocol>
/// 本地图片名字
@property (nonatomic, copy, nullable) NSString *imageName;
/// 本地图片路径
@property (nonatomic, copy, nullable) NSString *imagePath;
/// 本地图片字节码,返回 NSData
@property (nonatomic, copy, nullable) YBIBImageDataBlock imageData;
/// 本地图片,返回 UIImage 及其衍生类 (若不是遵循'YYAnimatedImage'协议的类型,将失去对动图和拓展格式的支持)
@property (nonatomic, copy, nullable) YBIBImageBlock image;
/// 网络图片资源
@property (nonatomic, copy, nullable) NSURL *imageURL;
/// 修改 NSURLRequest 并返回
@property (nonatomic, copy, nullable) YBIBRequestModifierBlock requestModifier;
/// 相册图片资源
@property (nonatomic, strong, nullable) PHAsset *imagePHAsset;
/// 投影视图,当前数据模型对应外界业务的 UIView (通常为 UIImageView),做转场动效用
@property (nonatomic, weak, nullable) __kindof UIView *projectiveView;
/// 预览图/缩约图,注意若这个图片过大会导致内存压力(若 projectiveView 存在且是 UIImageView 类型将会自动获取缩约图)
@property (nonatomic, strong, nullable) UIImage *thumbImage;
/// 预览图/缩约图 URL缓存中未找到则忽略若 projectiveView 存在且是 UIImageView 类型将会自动获取缩约图)
@property (nonatomic, copy, nullable) NSURL *thumbURL;
/// 是否允许保存到相册
@property (nonatomic, assign) BOOL allowSaveToPhotoAlbum;
/// 根据图片信息判断是否需要预解码
@property (nonatomic, copy, nullable) YBIBPreDecodeDecisionBlock preDecodeDecision;
/// 是否异步预解码,默认为 YES
@property (nonatomic, assign) BOOL shouldPreDecodeAsync;
/// 压缩物理像素界限大小,当图片超过这个值将会被压缩显示,默认为 4096*4096
@property (nonatomic, assign) CGFloat compressingSize;
/// 触发裁剪的缩放比例,必须大于等于 1默认情况内部会动态计算 (仅当图片需要压缩显示时有效)
@property (nonatomic, assign) CGFloat cuttingZoomScale;
/**
*/
- (BOOL)shouldCompressWithImage:(UIImage *)image;
/**
( image OOM)
Example:
[... setImageModifier:^(YBIBImageData *imageData, UIImage * _Nonnull image, void (^ _Nonnull completion)(UIImage * _Nonnull)) {
//step 1 : Add watermark, trademark, etc. (Sync or async).
... processing code ...
//step 2 : Return the processed UIImage.
completion(image);
}];
*/
@property (nonatomic, copy, nullable) YBIBImageModifierBlock originImageModifier;
/// 修改压缩图片并返回处理后的图片 (仅在当前图片需要被压缩时有效)
@property (nonatomic, copy, nullable) YBIBImageModifierBlock compressedImageModifier;
/// 修改裁剪图片并返回处理后的图片 (仅在当前图片需要被裁剪时有效)
@property (nonatomic, copy, nullable) YBIBImageModifierBlock cuttedImageModifier;
/// 预留属性可随意使用
@property (nonatomic, strong, nullable) id extraData;
/// 手势交互动效配置文件
@property (nonatomic, strong) YBIBInteractionProfile *interactionProfile;
/// 单击的处理,默认是退出图片浏览器
@property (nonatomic, copy, nullable) YBIBImageSingleTouchBlock singleTouchBlock;
/// 图片滚动的回调
@property (nonatomic, copy, nullable) YBIBImageScrollViewStatusBlock imageDidScrollBlock;
/// 图片缩放的回调
@property (nonatomic, copy, nullable) YBIBImageScrollViewStatusBlock imageDidZoomBlock;
/// 图片布局类 (赋值可自定义)
@property (nonatomic, strong) id<YBIBImageLayout> layout;
/// 默认图片布局类 (可配置其属性)
@property (nonatomic, weak, readonly) YBIBImageLayout *defaultLayout;
/**
*/
- (void)stopLoading;
/**
*/
- (void)clearCache;
/**
*/
- (void)loadData;
/// 处理后的原始图片
@property (nonatomic, strong, readonly) UIImage *originImage;
/// 处理后的压缩图片
@property (nonatomic, strong, readonly) UIImage *compressedImage;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,695 @@
//
// YBIBImageData.m
// YBImageBrowserDemo
//
// Created by on 2019/6/5.
// Copyright © 2019 . All rights reserved.
//
#import "YBImage.h"
#import "YBIBImageData.h"
#import "YBIBImageCell.h"
#import "YBIBPhotoAlbumManager.h"
#import "YBIBImageData+Internal.h"
#import "YBIBUtilities.h"
#import "YBIBImageCache+Internal.h"
#import "YBIBSentinel.h"
#import "YBIBCopywriter.h"
#import <AssetsLibrary/AssetsLibrary.h>
extern CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay);
static dispatch_queue_t YBIBImageProcessingQueue(void) {
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("com.yangbo.imagebrowser.imageprocessing", DISPATCH_QUEUE_CONCURRENT);
});
return queue;
}
@implementation YBIBImageData {
__weak id _downloadToken;
YBIBSentinel *_cuttingSentinel;
/// Stop processing tasks when in freeze.
BOOL _freezing;
}
#pragma mark - life cycle
- (void)dealloc {
if (_downloadToken && [self.yb_webImageMediator() respondsToSelector:@selector(yb_cancelTaskWithDownloadToken:)]) {
[self.yb_webImageMediator() yb_cancelTaskWithDownloadToken:_downloadToken];
}
[self.imageCache removeForKey:self.cacheKey];
}
- (instancetype)init {
self = [super init];
if (self) {
[self initValue];
}
return self;
}
- (void)initValue {
_defaultLayout = _layout = [YBIBImageLayout new];
_loadingStatus = YBIBImageLoadingStatusNone;
_compressingSize = 4096 * 4096;
_shouldPreDecodeAsync = YES;
_freezing = NO;
_cuttingSentinel = [YBIBSentinel new];
_interactionProfile = [YBIBInteractionProfile new];
_allowSaveToPhotoAlbum = YES;
}
#pragma mark - load data
- (void)loadData {
_freezing = NO;
// Avoid handling asynchronous tasks repeatedly.
if (self.loadingStatus != YBIBImageLoadingStatusNone) {
[self loadThumbImage];
self.loadingStatus = self.loadingStatus;
return;
}
if (self.originImage) {
[self loadOriginImage];
} else if (self.imageName || self.imagePath || self.imageData) {
[self loadYBImage];
} else if (self.image) {
[self loadImageBlock];
} else if (self.imageURL) {
[self loadThumbImage];
[self loadURL];
} else if (self.imagePHAsset) {
[self loadThumbImage];
[self loadPHAsset];
} else {
[self.delegate yb_imageIsInvalidForData:self];
}
}
- (void)loadOriginImage {
if (_freezing) return;
if (!self.originImage) return;
if ([self shouldCompress]) {
if (self.compressedImage) {
[self.delegate yb_imageData:self readyForCompressedImage:self.compressedImage];
} else {
[self loadThumbImage];
[self loadOriginImage_compress];
}
} else {
[self.delegate yb_imageData:self readyForImage:self.originImage];
}
}
- (void)loadOriginImage_compress {
if (_freezing) return;
if (!self.originImage) return;
self.loadingStatus = YBIBImageLoadingStatusCompressing;
__weak typeof(self) wSelf = self;
CGSize size = [self bestSizeOfCompressing];
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
if (self->_freezing) {
self.loadingStatus = YBIBImageLoadingStatusNone;
return;
}
// Ensure the best display effect.
UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
[self.originImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
if (self->_freezing) {
UIGraphicsEndImageContext();
self.loadingStatus = YBIBImageLoadingStatusNone;
return;
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
[self modifyImageWithModifier:self.compressedImageModifier image:resultImage completion:^(UIImage *processedImage) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.compressedImage = processedImage ?: self.originImage;
[self.delegate yb_imageData:self readyForCompressedImage:self.compressedImage];
}];
})
})
}
- (void)loadYBImage {
if (_freezing) return;
NSString *name = self.imageName.copy;
NSString *path = self.imagePath.copy;
NSData *data = self.imageData ? self.imageData().copy : nil;
if (name.length == 0 && path.length == 0 && data.length == 0) return;
YBImageDecodeDecision decision = [self defaultDecodeDecision];
__block YBImage *image;
__weak typeof(self) wSelf = self;
void(^dealBlock)(void) = ^{
if (name.length > 0) {
image = [YBImage imageNamed:name decodeDecision:decision];
} else if (path.length > 0) {
image = [YBImage imageWithContentsOfFile:path decodeDecision:decision];
} else if (data.length > 0) {
image = [YBImage imageWithData:data scale:UIScreen.mainScreen.scale decodeDecision:decision];
}
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
if (image) {
[self setOriginImageAndLoadWithImage:image];
} else {
[self.delegate yb_imageIsInvalidForData:self];
}
})
};
if (self.shouldPreDecodeAsync) {
[self loadThumbImage];
self.loadingStatus = YBIBImageLoadingStatusDecoding;
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), dealBlock)
} else {
self.loadingStatus = YBIBImageLoadingStatusDecoding;
dealBlock();
}
}
- (void)loadImageBlock {
if (_freezing) return;
__block UIImage *image = self.image ? self.image() : nil;
if (!image) return;
BOOL shouldPreDecode = self.preDecodeDecision ? self.preDecodeDecision(self, image.size, image.scale) : ![self shouldCompressWithImage:image];
__weak typeof(self) wSelf = self;
void(^dealBlock)(void) = ^{
// Do not need to decode If 'image' conformed 'YYAnimatedImage'. (Not entirely accurate.)
if (![image conformsToProtocol:@protocol(YYAnimatedImage)]) {
CGImageRef cgImage = YYCGImageCreateDecodedCopy(image.CGImage, shouldPreDecode);
image = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
if (cgImage) CGImageRelease(cgImage);
}
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
[self setOriginImageAndLoadWithImage:image];
})
};
if (self.shouldPreDecodeAsync) {
[self loadThumbImage];
self.loadingStatus = YBIBImageLoadingStatusDecoding;
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), dealBlock)
} else {
self.loadingStatus = YBIBImageLoadingStatusDecoding;
dealBlock();
}
}
- (void)loadURL {
if (!self.imageURL || self.imageURL.absoluteString.length == 0) return;
[self loadURL_queryCache];
}
- (void)loadURL_queryCache {
if (_freezing) return;
if (!self.imageURL || self.imageURL.absoluteString.length == 0) return;
YBImageDecodeDecision decision = [self defaultDecodeDecision];
self.loadingStatus = YBIBImageLoadingStatusQuerying;
[self.yb_webImageMediator() yb_queryCacheOperationForKey:self.imageURL completed:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
if (!imageData || imageData.length == 0) {
YBIB_DISPATCH_ASYNC_MAIN(^{
self.loadingStatus = YBIBImageLoadingStatusNone;
[self loadURL_download];
})
return;
}
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
if (self->_freezing) {
self.loadingStatus = YBIBImageLoadingStatusNone;
return;
}
YBImage *image = [YBImage imageWithData:imageData scale:UIScreen.mainScreen.scale decodeDecision:decision];
__weak typeof(self) wSelf = self;
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
if (image) { // Maybe the image data is invalid.
[self setOriginImageAndLoadWithImage:image];
} else {
[self loadURL_download];
}
})
})
}];
}
- (void)loadURL_download {
if (_freezing) return;
if (!self.imageURL || self.imageURL.absoluteString.length == 0) return;
YBImageDecodeDecision decision = [self defaultDecodeDecision];
self.loadingStatus = YBIBImageLoadingStatusDownloading;
__weak typeof(self) wSelf = self;
_downloadToken = [self.yb_webImageMediator() yb_downloadImageWithURL:self.imageURL requestModifier:^NSURLRequest * _Nullable(NSURLRequest * _Nonnull request) {
return self.requestModifier ? self.requestModifier(self, request) : request;
} progress:^(NSInteger receivedSize, NSInteger expectedSize) {
CGFloat progress = receivedSize * 1.0 / expectedSize ?: 0;
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
[self.delegate yb_imageData:self downloadProgress:progress];
})
} success:^(NSData * _Nullable imageData, BOOL finished) {
if (!finished) return;
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
if (self->_freezing) {
self.loadingStatus = YBIBImageLoadingStatusNone;
return;
}
YBImage *image = [YBImage imageWithData:imageData scale:UIScreen.mainScreen.scale decodeDecision:decision];
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
[self.yb_webImageMediator() yb_storeToDiskWithImageData:imageData forKey:self.imageURL];
self.loadingStatus = YBIBImageLoadingStatusNone;
if (image) {
[self setOriginImageAndLoadWithImage:image];
} else {
[self.delegate yb_imageIsInvalidForData:self];
}
})
})
} failed:^(NSError * _Nullable error, BOOL finished) {
if (!finished) return;
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
[self.delegate yb_imageDownloadFailedForData:self];
}];
}
- (void)loadPHAsset {
if (_freezing) return;
if (!self.imagePHAsset) return;
YBImageDecodeDecision decision = [self defaultDecodeDecision];
self.loadingStatus = YBIBImageLoadingStatusReadingPHAsset;
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
[YBIBPhotoAlbumManager getImageDataWithPHAsset:self.imagePHAsset completion:^(NSData * _Nullable data) {
if (self->_freezing) {
self.loadingStatus = YBIBImageLoadingStatusNone;
return;
}
YBImage *image = [YBImage imageWithData:data scale:UIScreen.mainScreen.scale decodeDecision:decision];
__weak typeof(self) wSelf = self;
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
if (image) {
[self setOriginImageAndLoadWithImage:image];
} else {
[self.delegate yb_imageIsInvalidForData:self];
}
})
}];
})
}
- (void)loadThumbImage {
if (_freezing) return;
if (self.thumbImage) {
[self.delegate yb_imageData:self readyForThumbImage:self.thumbImage];
} else if (self.thumbURL) {
__weak typeof(self) wSelf = self;
[self.yb_webImageMediator() yb_queryCacheOperationForKey:self.thumbURL completed:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
UIImage *thumbImage;
if (image) {
thumbImage = image;
} else if (imageData) {
thumbImage = [UIImage imageWithData:imageData];
}
// If the target image is ready, ignore the thumb image.
BOOL shouldIgnore = [self shouldCompress] ? (self.compressedImage != nil) : (self.originImage != nil);
if (!shouldIgnore && thumbImage) {
[self.delegate yb_imageData:self readyForThumbImage:thumbImage];
}
}];
} else if (self.projectiveView && [self.projectiveView isKindOfClass:UIImageView.self] && ((UIImageView *)self.projectiveView).image) {
UIImage *thumbImage = ((UIImageView *)self.projectiveView).image;
[self.delegate yb_imageData:self readyForThumbImage:thumbImage];
}
}
#pragma mark - internal
- (void)cuttingImageToRect:(CGRect)rect complete:(void (^)(UIImage * _Nullable))complete {
if (_freezing) return;
if (!self.originImage) return;
int32_t value = [_cuttingSentinel increase];
BOOL (^isCancelled)(void) = ^BOOL(void) {
if (self->_freezing) return YES;
return value != self->_cuttingSentinel.value;
};
YBIB_DISPATCH_ASYNC(YBIBImageProcessingQueue(), ^{
if (isCancelled()) {
complete(nil);
return;
}
CGFloat scale = self.originImage.scale;
CGFloat width = self.originImage.size.width;
CGFloat height = self.originImage.size.height;
BOOL reverseWidthHeight = NO;
CGAffineTransform transform = CGAffineTransformIdentity;
switch (self.originImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
CGAffineTransformTranslate(CGAffineTransformMakeRotation(-M_PI), -width, -height);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(M_PI_2), 0, -height);
reverseWidthHeight = YES;
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(-M_PI_2), -width, 0);
reverseWidthHeight = YES;
break;
default: break;
}
transform = CGAffineTransformScale(transform, scale, scale);
CGRect correctRect = CGRectApplyAffineTransform(rect, transform);
CGImageRef cgImage = CGImageCreateWithImageInRect(self.originImage.CGImage, correctRect);
if (isCancelled()) {
complete(nil);
if (cgImage) CGImageRelease(cgImage);
return;
}
CGFloat cgWidth = reverseWidthHeight ? CGImageGetHeight(cgImage) : CGImageGetWidth(cgImage);
CGFloat cgHeight = reverseWidthHeight ? CGImageGetWidth(cgImage) : CGImageGetHeight(cgImage);
CGSize size = [self bestSizeOfCuttingWithOriginSize:CGSizeMake(cgWidth / scale, cgHeight / scale)];
UIImage *tmpImage = [UIImage imageWithCGImage:cgImage scale:self.originImage.scale orientation:self.originImage.imageOrientation];
if (isCancelled()) {
complete(nil);
if (cgImage) CGImageRelease(cgImage);
return;
}
// Ensure the best display effect.
UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
[tmpImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
if (isCancelled()) {
complete(nil);
UIGraphicsEndImageContext();
if (cgImage) CGImageRelease(cgImage);
return;
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (cgImage) CGImageRelease(cgImage);
__weak typeof(self) wSelf = self;
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
[self modifyImageWithModifier:self.cuttedImageModifier image:resultImage completion:^(UIImage *image) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
complete(image);
}];
})
})
}
- (BOOL)shouldCompress {
return [self shouldCompressWithImage:self.originImage];
}
#pragma mark - public
- (BOOL)shouldCompressWithImage:(UIImage *)image {
if (!image) return NO;
return [self shouldCompressWithImageSize:image.size scale:image.scale];
}
- (void)stopLoading {
_freezing = YES;
self.loadingStatus = YBIBImageLoadingStatusNone;
}
- (void)clearCache {
[self.imageCache removeForKey:self.cacheKey];
}
#pragma mark - private
/// 'size': logic pixel.
- (BOOL)shouldCompressWithImageSize:(CGSize)size scale:(CGFloat)scale {
return size.width * scale * size.height * scale > self.compressingSize;
}
/// Logic pixel.
- (CGSize)bestSizeOfCompressing {
if (!self.originImage) return CGSizeZero;
UIDeviceOrientation orientation = self.yb_currentOrientation();
CGRect imageViewFrame = [self.layout yb_imageViewFrameWithContainerSize:self.yb_containerSize(orientation) imageSize:self.originImage.size orientation:orientation];
return imageViewFrame.size;
}
/// Logic pixel.
- (CGSize)bestSizeOfCuttingWithOriginSize:(CGSize)originSize {
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
CGFloat maxWidth = containerSize.width, maxHeight = containerSize.height;
CGFloat oWidth = originSize.width, oHeight = originSize.height;
if (maxWidth <= 0 || maxHeight <= 0 || oWidth <= 0 || oHeight <= 0) return CGSizeZero;
if (oWidth <= maxWidth && oHeight <= maxHeight) {
return originSize;
}
CGFloat rWidth = 0, rHeight = 0;
if (oWidth / maxWidth < oHeight / maxHeight) {
rHeight = maxHeight;
rWidth = oWidth / oHeight * rHeight;
} else {
rWidth = maxWidth;
rHeight = oHeight / oWidth * rWidth;
}
return CGSizeMake(rWidth, rHeight);
}
- (YBImageDecodeDecision)defaultDecodeDecision {
__weak typeof(self) wSelf = self;
return ^BOOL(CGSize imageSize, CGFloat scale) {
__strong typeof(wSelf) self = wSelf;
if (!self) return NO;
CGSize logicSize = CGSizeMake(imageSize.width / scale, imageSize.height / scale);
if (self.preDecodeDecision) return self.preDecodeDecision(self, logicSize, scale);
if ([self shouldCompressWithImageSize:logicSize scale:scale]) return NO;
return YES;
};
}
- (void)modifyImageWithModifier:(YBIBImageModifierBlock)modifier image:(UIImage *)image completion:(void(^)(UIImage *processedImage))completion {
if (modifier) {
self.loadingStatus = YBIBImageLoadingStatusProcessing;
__weak typeof(self) wSelf = self;
modifier(self, image, ^(UIImage *processedImage){
// This step is necessary, maybe 'self' is already 'dealloc' if processing code takes too much time.
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingStatus = YBIBImageLoadingStatusNone;
completion(processedImage);
});
} else {
completion(image);
}
}
- (void)setOriginImageAndLoadWithImage:(UIImage *)image {
__weak typeof(self) wSelf = self;
[self modifyImageWithModifier:self.originImageModifier image:image completion:^(UIImage *processedImage) {
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.originImage = processedImage;
[self loadOriginImage];
}];
}
- (void)saveToPhotoAlbumCompleteWithError:(nullable NSError *)error {
if (error) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
} else {
[self.yb_auxiliaryViewHandler() yb_showCorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumSuccess];
}
}
- (void)UIImageWriteToSavedPhotosAlbum_completedWithImage:(UIImage *)image error:(NSError *)error context:(void *)context {
[self saveToPhotoAlbumCompleteWithError:error];
}
- (YBIBImageCache *)imageCache {
return self.yb_backView.ybib_imageCache;
}
#pragma mark - <YBIBDataProtocol>
@synthesize yb_isHideTransitioning = _yb_isHideTransitioning;
@synthesize yb_currentOrientation = _yb_currentOrientation;
@synthesize yb_containerSize = _yb_containerSize;
@synthesize yb_containerView = _yb_containerView;
@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
@synthesize yb_webImageMediator = _yb_webImageMediator;
@synthesize yb_backView = _yb_backView;
- (nonnull Class)yb_classOfCell {
return YBIBImageCell.self;
}
- (UIView *)yb_projectiveView {
return self.projectiveView;
}
- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
return [self.layout yb_imageViewFrameWithContainerSize:containerSize imageSize:imageSize orientation:orientation];
}
- (void)yb_preload {
if (!self.delegate) {
[self loadData];
}
}
- (BOOL)yb_allowSaveToPhotoAlbum {
return self.allowSaveToPhotoAlbum;
}
- (void)yb_saveToPhotoAlbum {
void(^saveData)(NSData *) = ^(NSData * _Nonnull data){
[[ALAssetsLibrary new] writeImageDataToSavedPhotosAlbum:data metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
[self saveToPhotoAlbumCompleteWithError:error];
}];
};
void(^saveImage)(UIImage *) = ^(UIImage * _Nonnull image){
UIImageWriteToSavedPhotosAlbum(image, self, @selector(UIImageWriteToSavedPhotosAlbum_completedWithImage:error:context:), NULL);
};
void(^unableToSave)(void) = ^(){
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].unableToSave];
};
[YBIBPhotoAlbumManager getPhotoAlbumAuthorizationSuccess:^{
if ([self.originImage conformsToProtocol:@protocol(YYAnimatedImage)] && [self.originImage respondsToSelector:@selector(animatedImageData)] && [self.originImage performSelector:@selector(animatedImageData)]) {
NSData *data = [self.originImage performSelector:@selector(animatedImageData)];
data ? saveData(data) : unableToSave();
} else if (self.originImage) {
saveImage(self.originImage);
} else if (self.imageURL) {
[self.yb_webImageMediator() yb_queryCacheOperationForKey:self.imageURL completed:^(UIImage * _Nullable image, NSData * _Nullable data) {
if (data) {
saveData(data);
} else if (image) {
saveImage(image);
} else {
unableToSave();
}
}];
} else {
unableToSave();
}
} failed:^{
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].getPhotoAlbumAuthorizationFailed];
}];
}
#pragma mark - getters & setters
@synthesize delegate = _delegate;
- (void)setDelegate:(id<YBIBImageDataDelegate>)delegate {
_delegate = delegate;
if (delegate) {
[self loadData];
} else {
_freezing = YES;
// Remove the resident cache if '_delegate' is nil.
[self.imageCache removeResidentForKey:self.cacheKey];
}
}
- (id<YBIBImageDataDelegate>)delegate {
// Stop sending data to the '_delegate' if it is transiting.
return self.yb_isHideTransitioning() ? nil : _delegate;
}
- (void)setImageURL:(NSURL *)imageURL {
_imageURL = [imageURL isKindOfClass:NSString.class] ? [NSURL URLWithString:(NSString *)imageURL] : imageURL;
}
- (NSString *)cacheKey {
return [NSString stringWithFormat:@"%p", self];
}
- (void)setOriginImage:(__kindof UIImage *)originImage {
// 'image' should be resident if '_delegate' exists.
[self.imageCache setImage:originImage type:YBIBImageCacheTypeOrigin forKey:self.cacheKey resident:self->_delegate != nil];
}
- (UIImage *)originImage {
return [self.imageCache imageForKey:self.cacheKey type:YBIBImageCacheTypeOrigin];
}
- (void)setCompressedImage:(UIImage *)compressedImage {
// 'image' should be resident if '_delegate' exists.
[self.imageCache setImage:compressedImage type:YBIBImageCacheTypeCompressed forKey:self.cacheKey resident:_delegate != nil];
}
- (UIImage *)compressedImage {
return [self.imageCache imageForKey:self.cacheKey type:YBIBImageCacheTypeCompressed];
}
- (void)setLoadingStatus:(YBIBImageLoadingStatus)loadingStatus {
// Ensure thread safety.
YBIB_DISPATCH_ASYNC_MAIN(^{
self->_loadingStatus = loadingStatus;
[self.delegate yb_imageData:self startLoadingWithStatus:loadingStatus];
});
}
- (CGFloat)cuttingZoomScale {
if (_cuttingZoomScale >= 1) return _cuttingZoomScale;
_cuttingZoomScale = 1.1;
if (!self.originImage) return _cuttingZoomScale;
CGFloat imagePixel = self.originImage.size.width * self.originImage.size.height * self.originImage.scale * self.originImage.scale;
// The larger the image size, the larger the '_cuttingZoomScale', in order to reduce the burden of CPU and memory.
CGFloat scale = YBIBLowMemory() ? 0.28 : 0.19;
_cuttingZoomScale += (imagePixel / self.compressingSize * scale);
return _cuttingZoomScale;
}
@end

View File

@ -0,0 +1,61 @@
//
// YBIBImageLayout.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/12.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol YBIBImageLayout <NSObject>
@required
/**
@param containerSize
@param imageSize ()
@param orientation
@return (frame)
*/
- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation;
/**
@param containerSize
@param imageSize ()
@param orientation
@return
*/
- (CGFloat)yb_maximumZoomScaleWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation;
@end
typedef NS_ENUM(NSUInteger, YBIBImageFillType) {
/// 宽度优先填充满
YBIBImageFillTypeFullWidth,
/// 完整显示
YBIBImageFillTypeCompletely
};
@interface YBIBImageLayout : NSObject <YBIBImageLayout>
/// 纵向的填充方式,默认 YBIBImageFillTypeCompletely
@property (nonatomic, assign) YBIBImageFillType verticalFillType;
/// 横向的填充方式,默认 YBIBImageFillTypeFullWidth
@property (nonatomic, assign) YBIBImageFillType horizontalFillType;
/// 最大缩放比例 (必须大于 1 才有效,若不指定内部会自动计算)
@property (nonatomic, assign) CGFloat maxZoomScale;
/// 自动计算严格缩放比例后,再乘以这个值作为最终缩放比例,默认 1.5
@property (nonatomic, assign) CGFloat zoomScaleSurplus;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,90 @@
//
// YBIBImageLayout.m
// YBImageBrowserDemo
//
// Created by on 2019/6/12.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBImageLayout.h"
@implementation YBIBImageLayout
#pragma mark - life cycle
- (instancetype)init {
self = [super init];
if (self) {
_verticalFillType = YBIBImageFillTypeCompletely;
_horizontalFillType = YBIBImageFillTypeFullWidth;
_zoomScaleSurplus = 1.5;
}
return self;
}
#pragma mark - private
- (YBIBImageFillType)fillTypeByOrientation:(UIDeviceOrientation)orientation {
return UIDeviceOrientationIsLandscape(orientation) ? self.horizontalFillType : self.verticalFillType;
}
#pragma mark - <YBIBImageLayout>
- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
if (containerSize.width <= 0 || containerSize.height <= 0 || imageSize.width <= 0 || imageSize.height <= 0) return CGRectZero;
CGFloat x = 0, y = 0, width = 0, height = 0;
switch ([self fillTypeByOrientation:orientation]) {
case YBIBImageFillTypeFullWidth: {
x = 0;
width = containerSize.width;
height = containerSize.width * (imageSize.height / imageSize.width);
if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height)
y = (containerSize.height - height) / 2.0;
else
y = 0;
}
break;
case YBIBImageFillTypeCompletely: {
if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height) {
width = containerSize.width;
height = containerSize.width * (imageSize.height / imageSize.width);
x = 0;
y = (containerSize.height - height) / 2.0;
} else {
height = containerSize.height;
width = containerSize.height * (imageSize.width / imageSize.height);
x = (containerSize.width - width) / 2.0;
y = 0;
}
}
break;
default: return CGRectZero;
}
return CGRectMake(x, y, width, height);
}
- (CGFloat)yb_maximumZoomScaleWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
if (self.maxZoomScale >= 1) return self.maxZoomScale;
if (containerSize.width <= 0 || containerSize.height <= 0) return 0;
CGFloat scale = [UIScreen mainScreen].scale;
if (scale <= 0) return 0;
CGFloat widthScale = imageSize.width / scale / containerSize.width,
heightScale = imageSize.height / scale / containerSize.height,
maxScale = 1;
switch ([self fillTypeByOrientation:orientation]) {
case YBIBImageFillTypeFullWidth:
maxScale = widthScale;
break;
case YBIBImageFillTypeCompletely:
maxScale = MAX(widthScale, heightScale);
break;
default: return 0;
}
return MAX(maxScale, 1) * self.zoomScaleSurplus;
}
@end

View File

@ -0,0 +1,32 @@
//
// YBIBImageScrollView.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/10.
// Copyright © 2019 波儿菜. All rights reserved.
//
#import "YBImage.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, YBIBScrollImageType) {
YBIBScrollImageTypeNone,
YBIBScrollImageTypeOriginal,
YBIBScrollImageTypeCompressed,
YBIBScrollImageTypeThumb
};
@interface YBIBImageScrollView : UIScrollView
- (void)setImage:(__kindof UIImage *)image type:(YBIBScrollImageType)type;
@property (nonatomic, strong, readonly) YYAnimatedImageView *imageView;
@property (nonatomic, assign) YBIBScrollImageType imageType;
- (void)reset;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,64 @@
//
// YBIBImageScrollView.m
// YBImageBrowserDemo
//
// Created by on 2019/6/10.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBImageScrollView.h"
@interface YBIBImageScrollView ()
@property (nonatomic, strong) YYAnimatedImageView *imageView;
@end
@implementation YBIBImageScrollView
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.decelerationRate = UIScrollViewDecelerationRateFast;
self.maximumZoomScale = 1;
self.minimumZoomScale = 1;
self.alwaysBounceHorizontal = NO;
self.alwaysBounceVertical = NO;
self.layer.masksToBounds = NO;
if (@available(iOS 11.0, *)) {
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
[self addSubview:self.imageView];
}
return self;
}
#pragma mark - public
- (void)setImage:(__kindof UIImage *)image type:(YBIBScrollImageType)type {
self.imageView.image = image;
self.imageType = type;
}
- (void)reset {
self.zoomScale = 1;
self.imageView.image = nil;
self.imageView.frame = CGRectZero;
self.imageType = YBIBScrollImageTypeNone;
}
#pragma mark - getters
- (YYAnimatedImageView *)imageView {
if (!_imageView) {
_imageView = [YYAnimatedImageView new];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.layer.masksToBounds = YES;
}
return _imageView;
}
@end

View File

@ -0,0 +1,32 @@
//
// YBIBInteractionProfile.h
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/6/30.
// Copyright © 2019 杨波. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface YBIBInteractionProfile : NSObject
/// 是否取消手势交互动效
@property (nonatomic, assign) BOOL disable;
/// 拖动的距离与最大高度的比例,达到这个比例就会出场
@property (nonatomic, assign) CGFloat dismissScale;
/// 拖动的速度,达到这个值就会出场
@property (nonatomic, assign) CGFloat dismissVelocityY;
/// 拖动动效复位时的时长
@property (nonatomic, assign) CGFloat restoreDuration;
/// 拖动触发手势交互动效的起始距离
@property (nonatomic, assign) CGFloat triggerDistance;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,25 @@
//
// YBIBInteractionProfile.m
// YBImageBrowserDemo
//
// Created by on 2019/6/30.
// Copyright © 2019 . All rights reserved.
//
#import "YBIBInteractionProfile.h"
@implementation YBIBInteractionProfile
- (instancetype)init {
self = [super init];
if (self) {
_disable = NO;
_dismissScale = 0.22;
_dismissVelocityY = 800;
_restoreDuration = 0.15;
_triggerDistance = 3;
}
return self;
}
@end

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