8
Podfile
|
|
@ -23,6 +23,7 @@ target 'QuickLocation' do
|
||||||
pod 'KingfisherWebP'
|
pod 'KingfisherWebP'
|
||||||
pod 'Masonry'
|
pod 'Masonry'
|
||||||
|
|
||||||
|
|
||||||
#工具
|
#工具
|
||||||
#pod 'FLEX', :configurations => ['Debug', 'AdHoc']
|
#pod 'FLEX', :configurations => ['Debug', 'AdHoc']
|
||||||
pod 'HXPHPicker'
|
pod 'HXPHPicker'
|
||||||
|
|
@ -30,7 +31,12 @@ target 'QuickLocation' do
|
||||||
pod 'SGQRCode', '~> 4.1.0' #扫描
|
pod 'SGQRCode', '~> 4.1.0' #扫描
|
||||||
#pod 'PGDatePicker'
|
#pod 'PGDatePicker'
|
||||||
pod 'IQKeyboardManagerSwift'
|
pod 'IQKeyboardManagerSwift'
|
||||||
|
pod 'YBImageBrowser'
|
||||||
|
pod 'YBImageBrowser/Video' #视频功能
|
||||||
|
pod 'YYImage' #, :git => 'https://github.com/QiuYeHong90/YYImage.git'
|
||||||
|
pod 'YYImage/WebP'
|
||||||
|
|
||||||
|
|
||||||
#UI
|
#UI
|
||||||
pod 'SnapKit'
|
pod 'SnapKit'
|
||||||
pod 'MJRefresh'
|
pod 'MJRefresh'
|
||||||
|
|
|
||||||
22
Podfile.lock
|
|
@ -152,6 +152,18 @@ PODS:
|
||||||
- SwiftyUserDefaults (5.3.0)
|
- SwiftyUserDefaults (5.3.0)
|
||||||
- TagListView (1.4.1)
|
- TagListView (1.4.1)
|
||||||
- URLNavigator (2.5.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)
|
- ZXSDK (3.3.2)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
|
@ -185,6 +197,10 @@ DEPENDENCIES:
|
||||||
- SwiftyUserDefaults
|
- SwiftyUserDefaults
|
||||||
- TagListView (~> 1.0)
|
- TagListView (~> 1.0)
|
||||||
- URLNavigator
|
- URLNavigator
|
||||||
|
- YBImageBrowser
|
||||||
|
- YBImageBrowser/Video
|
||||||
|
- YYImage
|
||||||
|
- YYImage/WebP
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://gitee.com/mirrors/CocoaPods-Specs.git:
|
https://gitee.com/mirrors/CocoaPods-Specs.git:
|
||||||
|
|
@ -234,6 +250,8 @@ SPEC REPOS:
|
||||||
- SwiftyUserDefaults
|
- SwiftyUserDefaults
|
||||||
- TagListView
|
- TagListView
|
||||||
- URLNavigator
|
- URLNavigator
|
||||||
|
- YBImageBrowser
|
||||||
|
- YYImage
|
||||||
- ZXSDK
|
- ZXSDK
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
|
@ -293,8 +311,10 @@ SPEC CHECKSUMS:
|
||||||
SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
|
SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
|
||||||
TagListView: a1c236d17960a0cf64156725a76ce6d73ebce2be
|
TagListView: a1c236d17960a0cf64156725a76ce6d73ebce2be
|
||||||
URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d
|
URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d
|
||||||
|
YBImageBrowser: 7ecc8bf33ffa5f3b94c397c29b4f3638dd37f527
|
||||||
|
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
|
||||||
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
|
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
|
||||||
|
|
||||||
PODFILE CHECKSUM: d9389fd0d3da8895e9ddac8adbec655edaf9bb3d
|
PODFILE CHECKSUM: 7834e369bb92eca27bfaa0253f39e8e939801cd4
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,18 @@ PODS:
|
||||||
- SwiftyUserDefaults (5.3.0)
|
- SwiftyUserDefaults (5.3.0)
|
||||||
- TagListView (1.4.1)
|
- TagListView (1.4.1)
|
||||||
- URLNavigator (2.5.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)
|
- ZXSDK (3.3.2)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
|
@ -185,6 +197,10 @@ DEPENDENCIES:
|
||||||
- SwiftyUserDefaults
|
- SwiftyUserDefaults
|
||||||
- TagListView (~> 1.0)
|
- TagListView (~> 1.0)
|
||||||
- URLNavigator
|
- URLNavigator
|
||||||
|
- YBImageBrowser
|
||||||
|
- YBImageBrowser/Video
|
||||||
|
- YYImage
|
||||||
|
- YYImage/WebP
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://gitee.com/mirrors/CocoaPods-Specs.git:
|
https://gitee.com/mirrors/CocoaPods-Specs.git:
|
||||||
|
|
@ -234,6 +250,8 @@ SPEC REPOS:
|
||||||
- SwiftyUserDefaults
|
- SwiftyUserDefaults
|
||||||
- TagListView
|
- TagListView
|
||||||
- URLNavigator
|
- URLNavigator
|
||||||
|
- YBImageBrowser
|
||||||
|
- YYImage
|
||||||
- ZXSDK
|
- ZXSDK
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
|
@ -293,8 +311,10 @@ SPEC CHECKSUMS:
|
||||||
SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
|
SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
|
||||||
TagListView: a1c236d17960a0cf64156725a76ce6d73ebce2be
|
TagListView: a1c236d17960a0cf64156725a76ce6d73ebce2be
|
||||||
URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d
|
URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d
|
||||||
|
YBImageBrowser: 7ecc8bf33ffa5f3b94c397c29b4f3638dd37f527
|
||||||
|
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
|
||||||
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
|
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
|
||||||
|
|
||||||
PODFILE CHECKSUM: d9389fd0d3da8895e9ddac8adbec655edaf9bb3d
|
PODFILE CHECKSUM: 7834e369bb92eca27bfaa0253f39e8e939801cd4
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|
|
||||||
58
Pods/Pods.xcodeproj/xcuserdata/yanghong.xcuserdatad/xcschemes/YBImageBrowser.xcscheme
generated
Normal 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>
|
||||||
58
Pods/Pods.xcodeproj/xcuserdata/yanghong.xcuserdatad/xcschemes/YYImage.xcscheme
generated
Normal 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>
|
||||||
|
|
@ -344,6 +344,16 @@
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</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>
|
<key>ZXSDK.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
|
|
|
||||||
|
|
@ -2101,6 +2101,57 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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
|
## libwebp
|
||||||
|
|
||||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||||
|
|
|
||||||
|
|
@ -2382,6 +2382,69 @@ SOFTWARE.
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSGroupSpecifier</string>
|
<string>PSGroupSpecifier</string>
|
||||||
</dict>
|
</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 <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.
|
||||||
|
|
||||||
|
</string>
|
||||||
|
<key>License</key>
|
||||||
|
<string>MIT</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>YYImage</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>FooterText</key>
|
<key>FooterText</key>
|
||||||
<string>Copyright (c) 2010, Google Inc. All rights reserved.
|
<string>Copyright (c) 2010, Google Inc. All rights reserved.
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,7 @@ ${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework
|
||||||
${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework
|
${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework
|
||||||
${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework
|
${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework
|
||||||
${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.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}/libwebp/libwebp.framework
|
||||||
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
|
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
|
||||||
|
|
@ -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}/SwiftyUserDefaults.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TagListView.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TagListView.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/URLNavigator.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}/libwebp.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
|
||||||
|
|
@ -36,5 +36,7 @@ ${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework
|
||||||
${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework
|
${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework
|
||||||
${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework
|
${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework
|
||||||
${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.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}/libwebp/libwebp.framework
|
||||||
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
|
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
|
||||||
|
|
@ -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}/SwiftyUserDefaults.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TagListView.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TagListView.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/URLNavigator.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}/libwebp.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
|
||||||
|
|
@ -213,6 +213,8 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.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}/libwebp/libwebp.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
|
||||||
fi
|
fi
|
||||||
|
|
@ -254,6 +256,8 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/TagListView/TagListView.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.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}/libwebp/libwebp.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||||
ARCHS = $(ARCHS_STANDARD)
|
ARCHS = $(ARCHS_STANDARD)
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = 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
|
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'
|
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
|
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_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}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
|
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
|
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||||
ARCHS = $(ARCHS_STANDARD)
|
ARCHS = $(ARCHS_STANDARD)
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = 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
|
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'
|
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
|
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_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}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
|
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
|
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_YBImageBrowser : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_YBImageBrowser
|
||||||
|
@end
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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[];
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
framework module YBImageBrowser {
|
||||||
|
umbrella header "YBImageBrowser-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_YYImage : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_YYImage
|
||||||
|
@end
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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[];
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
framework module YYImage {
|
||||||
|
umbrella header "YYImage-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,229 @@
|
||||||
|

|
||||||
|
|
||||||
|
[](https://cocoapods.org/pods/YBImageBrowser)
|
||||||
|
[](https://github.com/indulgeIn/YBImageBrowser)
|
||||||
|
[](https://github.com/indulgeIn/YBImageBrowser)
|
||||||
|
|
||||||
|
**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。
|
||||||
|
|
||||||
|
# 目录
|
||||||
|
|
||||||
|
* [预览](#预览)
|
||||||
|
* [特性](#特性)
|
||||||
|
* [安装](#安装)
|
||||||
|
* [用法](#用法)
|
||||||
|
* [常见问题](#常见问题)
|
||||||
|
|
||||||
|
|
||||||
|
# 预览
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 特性
|
||||||
|
|
||||||
|
- 支持 GIF,APNG,WebP 等本地和网络图片类型(由 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 版本的计划。
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
/// 自动播放次数,默认为 0,NSUIntegerMax 表示无限次
|
||||||
|
@property (nonatomic, assign) NSUInteger autoPlayCount;
|
||||||
|
|
||||||
|
/// 重复播放次数,默认为 0,NSUIntegerMax 表示无限次
|
||||||
|
@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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 850 B |
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_circlePoint@2x.png
generated
Normal file
|
After Width: | Height: | Size: 799 B |
BIN
Pods/YBImageBrowser/Video/YBImageBrowserVideo.bundle/ybib_circlePoint@3x.png
generated
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 961 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
39
Pods/YBImageBrowser/YBImageBrowser/AuxiliaryView/YBIBAuxiliaryViewHandler.h
generated
Normal 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
|
||||||
45
Pods/YBImageBrowser/YBImageBrowser/AuxiliaryView/YBIBAuxiliaryViewHandler.m
generated
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||