├── .gitignore ├── .travis.yml ├── Example ├── Podfile ├── Podfile.lock ├── Pods │ ├── Local Podspecs │ │ └── TIMFlowView.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── Pods-TIMFlowView_Example │ │ ├── Pods-TIMFlowView_Example-Info.plist │ │ ├── Pods-TIMFlowView_Example-acknowledgements.markdown │ │ ├── Pods-TIMFlowView_Example-acknowledgements.plist │ │ ├── Pods-TIMFlowView_Example-dummy.m │ │ ├── Pods-TIMFlowView_Example-frameworks.sh │ │ ├── Pods-TIMFlowView_Example-umbrella.h │ │ ├── Pods-TIMFlowView_Example.debug.xcconfig │ │ ├── Pods-TIMFlowView_Example.modulemap │ │ └── Pods-TIMFlowView_Example.release.xcconfig │ │ ├── Pods-TIMFlowView_Tests │ │ ├── Pods-TIMFlowView_Tests-Info.plist │ │ ├── Pods-TIMFlowView_Tests-acknowledgements.markdown │ │ ├── Pods-TIMFlowView_Tests-acknowledgements.plist │ │ ├── Pods-TIMFlowView_Tests-dummy.m │ │ ├── Pods-TIMFlowView_Tests-umbrella.h │ │ ├── Pods-TIMFlowView_Tests.debug.xcconfig │ │ ├── Pods-TIMFlowView_Tests.modulemap │ │ └── Pods-TIMFlowView_Tests.release.xcconfig │ │ └── TIMFlowView │ │ ├── TIMFlowView-Info.plist │ │ ├── TIMFlowView-dummy.m │ │ ├── TIMFlowView-prefix.pch │ │ ├── TIMFlowView-umbrella.h │ │ ├── TIMFlowView.modulemap │ │ └── TIMFlowView.xcconfig ├── TIMFlowView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── TIMFlowView-Example.xcscheme ├── TIMFlowView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── TIMFlowView │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── CollectionDemoController.swift │ ├── DefaultDemoController.swift │ ├── DemoHeaderView.swift │ ├── DemoSectionHeaderFooterView.swift │ ├── DemoViewController.swift │ ├── FlowDemoItem.swift │ ├── FlowModel.swift │ ├── HeaderDemoController.swift │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── banner │ │ │ ├── Contents.json │ │ │ ├── banner01.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── banner01.png │ │ │ ├── banner02.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── banner02.png │ │ │ └── banner03.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── banner03.png │ │ └── placeholder.imageset │ │ │ ├── Contents.json │ │ │ ├── placeholder@2x.png │ │ │ └── placeholder@3x.png │ ├── Info.plist │ ├── ZCycleView │ │ ├── ZCycleLayout.swift │ │ ├── ZCycleView.swift │ │ ├── ZCycleViewCell.swift │ │ └── ZPageControl.swift │ └── images.plist └── Tests │ ├── Info.plist │ └── Tests.swift ├── ExampleImages ├── 全部功能开启.gif ├── 带有header的瀑布流.gif └── 普通瀑布流.gif ├── LICENSE ├── README.md ├── TIMFlowView.podspec ├── TIMFlowView ├── TIMFlowHeaderFooterView.swift ├── TIMFlowView.swift ├── TIMFlowViewCellMarginType.swift ├── TIMFlowViewDataSource.swift ├── TIMFlowViewDelegate.swift ├── TIMFlowViewItem.swift └── TIMFlowViewUtils.swift └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | Pods/ 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/TIMFlowView.xcworkspace -scheme TIMFlowView-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | source 'https://github.com/CocoaPods/Specs.git' 4 | 5 | target 'TIMFlowView_Example' do 6 | use_frameworks! 7 | inhibit_all_warnings! 8 | 9 | pod 'TIMFlowView', :path => '../' 10 | pod 'KakaJSON' 11 | pod 'Kingfisher' 12 | 13 | target 'TIMFlowView_Tests' do 14 | inherit! :search_paths 15 | 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - KakaJSON (1.1.1) 3 | - Kingfisher (4.10.1) 4 | - TIMFlowView (1.0.0) 5 | 6 | DEPENDENCIES: 7 | - KakaJSON 8 | - Kingfisher 9 | - TIMFlowView (from `../`) 10 | 11 | SPEC REPOS: 12 | https://github.com/CocoaPods/Specs.git: 13 | - KakaJSON 14 | - Kingfisher 15 | 16 | EXTERNAL SOURCES: 17 | TIMFlowView: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | KakaJSON: 18a9fbd9e8381e52751eb6da8536bda8d2944fa1 22 | Kingfisher: c148cd7b47ebde9989f6bc7c27dcaa79d81279a0 23 | TIMFlowView: e632a277bf4db70d141038198cf50294127527a6 24 | 25 | PODFILE CHECKSUM: b65379eca84b0452fbabfe1cf82034b6d7bb402d 26 | 27 | COCOAPODS: 1.8.0 28 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/TIMFlowView.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TIMFlowView", 3 | "version": "1.0.0", 4 | "summary": "Swift 模仿 UITableView 写的一个瀑布流视图", 5 | "description": "TODO: Add long description of the pod here.", 6 | "homepage": "https://github.com/Tim/TIMFlowView", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Tim": "TOPshuaiyeai@163.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/Tim/TIMFlowView.git", 16 | "tag": "1.0.0" 17 | }, 18 | "platforms": { 19 | "ios": "9.0" 20 | }, 21 | "swift_versions": "5.0", 22 | "source_files": "TIMFlowView/*.Swift", 23 | "swift_version": "5.0" 24 | } 25 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - KakaJSON (1.1.1) 3 | - Kingfisher (4.10.1) 4 | - TIMFlowView (1.0.0) 5 | 6 | DEPENDENCIES: 7 | - KakaJSON 8 | - Kingfisher 9 | - TIMFlowView (from `../`) 10 | 11 | SPEC REPOS: 12 | https://github.com/CocoaPods/Specs.git: 13 | - KakaJSON 14 | - Kingfisher 15 | 16 | EXTERNAL SOURCES: 17 | TIMFlowView: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | KakaJSON: 18a9fbd9e8381e52751eb6da8536bda8d2944fa1 22 | Kingfisher: c148cd7b47ebde9989f6bc7c27dcaa79d81279a0 23 | TIMFlowView: e632a277bf4db70d141038198cf50294127527a6 24 | 25 | PODFILE CHECKSUM: b65379eca84b0452fbabfe1cf82034b6d7bb402d 26 | 27 | COCOAPODS: 1.8.0 28 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## KakaJSON 5 | 6 | MIT License 7 | 8 | Copyright (c) 2019 KakaOpenSource 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | ## Kingfisher 30 | 31 | The MIT License (MIT) 32 | 33 | Copyright (c) 2018 Wei Wang 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy 36 | of this software and associated documentation files (the "Software"), to deal 37 | in the Software without restriction, including without limitation the rights 38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | copies of the Software, and to permit persons to whom the Software is 40 | furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all 43 | copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | 53 | 54 | 55 | ## TIMFlowView 56 | 57 | <<<<<<< HEAD 58 | Copyright (c) 2020 Tim 59 | ======= 60 | MIT License 61 | 62 | Copyright (c) 2020 Latte 63 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 64 | 65 | Permission is hereby granted, free of charge, to any person obtaining a copy 66 | of this software and associated documentation files (the "Software"), to deal 67 | in the Software without restriction, including without limitation the rights 68 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 69 | copies of the Software, and to permit persons to whom the Software is 70 | furnished to do so, subject to the following conditions: 71 | 72 | <<<<<<< HEAD 73 | The above copyright notice and this permission notice shall be included in 74 | all copies or substantial portions of the Software. 75 | ======= 76 | The above copyright notice and this permission notice shall be included in all 77 | copies or substantial portions of the Software. 78 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 79 | 80 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 81 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 82 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 83 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 84 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 85 | <<<<<<< HEAD 86 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 87 | THE SOFTWARE. 88 | ======= 89 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 90 | SOFTWARE. 91 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 92 | 93 | Generated by CocoaPods - https://cocoapods.org 94 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2019 KakaOpenSource 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | KakaJSON 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | The MIT License (MIT) 49 | 50 | Copyright (c) 2018 Wei Wang 51 | 52 | Permission is hereby granted, free of charge, to any person obtaining a copy 53 | of this software and associated documentation files (the "Software"), to deal 54 | in the Software without restriction, including without limitation the rights 55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | copies of the Software, and to permit persons to whom the Software is 57 | furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in all 60 | copies or substantial portions of the Software. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 68 | SOFTWARE. 69 | 70 | 71 | License 72 | MIT 73 | Title 74 | Kingfisher 75 | Type 76 | PSGroupSpecifier 77 | 78 | 79 | FooterText 80 | <<<<<<< HEAD 81 | Copyright (c) 2020 Tim <TOPshuaiyeai@163.com> 82 | ======= 83 | MIT License 84 | 85 | Copyright (c) 2020 Latte 86 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 87 | 88 | Permission is hereby granted, free of charge, to any person obtaining a copy 89 | of this software and associated documentation files (the "Software"), to deal 90 | in the Software without restriction, including without limitation the rights 91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 92 | copies of the Software, and to permit persons to whom the Software is 93 | furnished to do so, subject to the following conditions: 94 | 95 | <<<<<<< HEAD 96 | The above copyright notice and this permission notice shall be included in 97 | all copies or substantial portions of the Software. 98 | ======= 99 | The above copyright notice and this permission notice shall be included in all 100 | copies or substantial portions of the Software. 101 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 102 | 103 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 104 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 105 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 106 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 107 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 108 | <<<<<<< HEAD 109 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 110 | THE SOFTWARE. 111 | ======= 112 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 113 | SOFTWARE. 114 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 115 | 116 | License 117 | MIT 118 | Title 119 | TIMFlowView 120 | Type 121 | PSGroupSpecifier 122 | 123 | 124 | FooterText 125 | Generated by CocoaPods - https://cocoapods.org 126 | Title 127 | 128 | Type 129 | PSGroupSpecifier 130 | 131 | 132 | StringsTable 133 | Acknowledgements 134 | Title 135 | Acknowledgements 136 | 137 | 138 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_TIMFlowView_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_TIMFlowView_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/KakaJSON/KakaJSON.framework" 165 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 166 | install_framework "${BUILT_PRODUCTS_DIR}/TIMFlowView/TIMFlowView.framework" 167 | fi 168 | if [[ "$CONFIGURATION" == "Release" ]]; then 169 | install_framework "${BUILT_PRODUCTS_DIR}/KakaJSON/KakaJSON.framework" 170 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 171 | install_framework "${BUILT_PRODUCTS_DIR}/TIMFlowView/TIMFlowView.framework" 172 | fi 173 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 174 | wait 175 | fi 176 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_TIMFlowView_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_TIMFlowView_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON/KakaJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView/TIMFlowView.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON/KakaJSON.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView/TIMFlowView.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView" 7 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "KakaJSON" -framework "Kingfisher" -framework "TIMFlowView" 8 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 9 | PODS_BUILD_DIR = ${BUILD_DIR} 10 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 12 | PODS_ROOT = ${SRCROOT}/Pods 13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 14 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_TIMFlowView_Example { 2 | umbrella header "Pods-TIMFlowView_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON/KakaJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView/TIMFlowView.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON/KakaJSON.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView/TIMFlowView.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView" 7 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "KakaJSON" -framework "Kingfisher" -framework "TIMFlowView" 8 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 9 | PODS_BUILD_DIR = ${BUILD_DIR} 10 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 12 | PODS_ROOT = ${SRCROOT}/Pods 13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 14 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_TIMFlowView_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_TIMFlowView_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_TIMFlowView_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_TIMFlowView_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON/KakaJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView/TIMFlowView.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "KakaJSON" -framework "Kingfisher" -framework "TIMFlowView" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_TIMFlowView_Tests { 2 | umbrella header "Pods-TIMFlowView_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KakaJSON/KakaJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView/TIMFlowView.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "KakaJSON" -framework "Kingfisher" -framework "TIMFlowView" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/TIMFlowView/TIMFlowView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/TIMFlowView/TIMFlowView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_TIMFlowView : NSObject 3 | @end 4 | @implementation PodsDummy_TIMFlowView 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/TIMFlowView/TIMFlowView-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/TIMFlowView/TIMFlowView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double TIMFlowViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char TIMFlowViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/TIMFlowView/TIMFlowView.modulemap: -------------------------------------------------------------------------------- 1 | framework module TIMFlowView { 2 | umbrella header "TIMFlowView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/TIMFlowView/TIMFlowView.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/TIMFlowView 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/TIMFlowView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2246D1D623E86366005B7485 /* ZCycleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2246D1D223E86366005B7485 /* ZCycleView.swift */; }; 11 | 2246D1D723E86366005B7485 /* ZCycleViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2246D1D323E86366005B7485 /* ZCycleViewCell.swift */; }; 12 | 2246D1D823E86366005B7485 /* ZCycleLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2246D1D423E86366005B7485 /* ZCycleLayout.swift */; }; 13 | 2246D1D923E86366005B7485 /* ZPageControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2246D1D523E86366005B7485 /* ZPageControl.swift */; }; 14 | 2246D1DD23EE8AEC005B7485 /* DemoSectionHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2246D1DC23EE8AEC005B7485 /* DemoSectionHeaderFooterView.swift */; }; 15 | 22801B7323D5FDC5003496EF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7223D5FDC5003496EF /* AppDelegate.swift */; }; 16 | 22801B7523D5FDD5003496EF /* images.plist in Resources */ = {isa = PBXBuildFile; fileRef = 22801B7423D5FDD5003496EF /* images.plist */; }; 17 | 22801B7723D5FDDD003496EF /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7623D5FDDD003496EF /* DemoViewController.swift */; }; 18 | 22801B7E23D5FDEB003496EF /* HeaderDemoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7823D5FDEA003496EF /* HeaderDemoController.swift */; }; 19 | 22801B7F23D5FDEB003496EF /* CollectionDemoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7923D5FDEA003496EF /* CollectionDemoController.swift */; }; 20 | 22801B8023D5FDEB003496EF /* FlowDemoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7A23D5FDEB003496EF /* FlowDemoItem.swift */; }; 21 | 22801B8123D5FDEB003496EF /* DefaultDemoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7B23D5FDEB003496EF /* DefaultDemoController.swift */; }; 22 | 22801B8223D5FDEB003496EF /* DemoHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7C23D5FDEB003496EF /* DemoHeaderView.swift */; }; 23 | 22801B8323D5FDEB003496EF /* FlowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B7D23D5FDEB003496EF /* FlowModel.swift */; }; 24 | 22801B8723D5FDF9003496EF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 22801B8623D5FDF9003496EF /* Images.xcassets */; }; 25 | 22801B8C23D5FE31003496EF /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 22801B8823D5FE31003496EF /* LaunchScreen.xib */; }; 26 | 22801B8D23D5FE31003496EF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22801B8A23D5FE31003496EF /* Main.storyboard */; }; 27 | 34B034631849EF40BA2F8BF9 /* Pods_TIMFlowView_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 593447037EBEAD900A345CE0 /* Pods_TIMFlowView_Tests.framework */; }; 28 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 29 | 768A18FB686CC98AEAB02611 /* Pods_TIMFlowView_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2AEF27D42F091029E4E861C /* Pods_TIMFlowView_Example.framework */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXContainerItemProxy section */ 33 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 38 | remoteInfo = TIMFlowView; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 2246D1D223E86366005B7485 /* ZCycleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZCycleView.swift; sourceTree = ""; }; 44 | 2246D1D323E86366005B7485 /* ZCycleViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZCycleViewCell.swift; sourceTree = ""; }; 45 | 2246D1D423E86366005B7485 /* ZCycleLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZCycleLayout.swift; sourceTree = ""; }; 46 | 2246D1D523E86366005B7485 /* ZPageControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZPageControl.swift; sourceTree = ""; }; 47 | 2246D1DC23EE8AEC005B7485 /* DemoSectionHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoSectionHeaderFooterView.swift; sourceTree = ""; }; 48 | 22801B7223D5FDC5003496EF /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 49 | 22801B7423D5FDD5003496EF /* images.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = images.plist; sourceTree = ""; }; 50 | 22801B7623D5FDDD003496EF /* DemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoViewController.swift; sourceTree = ""; }; 51 | 22801B7823D5FDEA003496EF /* HeaderDemoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderDemoController.swift; sourceTree = ""; }; 52 | 22801B7923D5FDEA003496EF /* CollectionDemoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionDemoController.swift; sourceTree = ""; }; 53 | 22801B7A23D5FDEB003496EF /* FlowDemoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowDemoItem.swift; sourceTree = ""; }; 54 | 22801B7B23D5FDEB003496EF /* DefaultDemoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultDemoController.swift; sourceTree = ""; }; 55 | 22801B7C23D5FDEB003496EF /* DemoHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoHeaderView.swift; sourceTree = ""; }; 56 | 22801B7D23D5FDEB003496EF /* FlowModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowModel.swift; sourceTree = ""; }; 57 | 22801B8423D5FDF3003496EF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 22801B8623D5FDF9003496EF /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 59 | 22801B8923D5FE31003496EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 60 | 22801B8B23D5FE31003496EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 61 | 40C102F404E7EEE182597882 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 62 | 593447037EBEAD900A345CE0 /* Pods_TIMFlowView_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TIMFlowView_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 607FACD01AFB9204008FA782 /* TIMFlowView_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TIMFlowView_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 607FACE51AFB9204008FA782 /* TIMFlowView_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TIMFlowView_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 68 | 7E53C08A02624840672354EB /* Pods-TIMFlowView_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIMFlowView_Example.debug.xcconfig"; path = "Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example.debug.xcconfig"; sourceTree = ""; }; 69 | 81742060854BA88BAEC27A93 /* Pods-TIMFlowView_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIMFlowView_Example.release.xcconfig"; path = "Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example.release.xcconfig"; sourceTree = ""; }; 70 | 8573A328E722B6E6D467AE37 /* Pods-TIMFlowView_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIMFlowView_Tests.release.xcconfig"; path = "Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests.release.xcconfig"; sourceTree = ""; }; 71 | A4C3E4550E8EE6793484AE1F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 72 | B67393DD332AFA409F780ABE /* TIMFlowView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = TIMFlowView.podspec; path = ../TIMFlowView.podspec; sourceTree = ""; }; 73 | C2AEF27D42F091029E4E861C /* Pods_TIMFlowView_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TIMFlowView_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | F06F49F954380907D447AE53 /* Pods-TIMFlowView_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIMFlowView_Tests.debug.xcconfig"; path = "Target Support Files/Pods-TIMFlowView_Tests/Pods-TIMFlowView_Tests.debug.xcconfig"; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 768A18FB686CC98AEAB02611 /* Pods_TIMFlowView_Example.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | 34B034631849EF40BA2F8BF9 /* Pods_TIMFlowView_Tests.framework in Frameworks */, 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 183F35541676D76D99D41782 /* Frameworks */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | C2AEF27D42F091029E4E861C /* Pods_TIMFlowView_Example.framework */, 101 | 593447037EBEAD900A345CE0 /* Pods_TIMFlowView_Tests.framework */, 102 | ); 103 | name = Frameworks; 104 | sourceTree = ""; 105 | }; 106 | 21B7F13E60EB0F80888C652E /* Pods */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 7E53C08A02624840672354EB /* Pods-TIMFlowView_Example.debug.xcconfig */, 110 | 81742060854BA88BAEC27A93 /* Pods-TIMFlowView_Example.release.xcconfig */, 111 | F06F49F954380907D447AE53 /* Pods-TIMFlowView_Tests.debug.xcconfig */, 112 | 8573A328E722B6E6D467AE37 /* Pods-TIMFlowView_Tests.release.xcconfig */, 113 | ); 114 | path = Pods; 115 | sourceTree = ""; 116 | }; 117 | 2246D1D123E86366005B7485 /* ZCycleView */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 2246D1D223E86366005B7485 /* ZCycleView.swift */, 121 | 2246D1D323E86366005B7485 /* ZCycleViewCell.swift */, 122 | 2246D1D423E86366005B7485 /* ZCycleLayout.swift */, 123 | 2246D1D523E86366005B7485 /* ZPageControl.swift */, 124 | ); 125 | path = ZCycleView; 126 | sourceTree = ""; 127 | }; 128 | 607FACC71AFB9204008FA782 = { 129 | isa = PBXGroup; 130 | children = ( 131 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 132 | 607FACD21AFB9204008FA782 /* Example for TIMFlowView */, 133 | 607FACE81AFB9204008FA782 /* Tests */, 134 | 607FACD11AFB9204008FA782 /* Products */, 135 | 21B7F13E60EB0F80888C652E /* Pods */, 136 | 183F35541676D76D99D41782 /* Frameworks */, 137 | ); 138 | sourceTree = ""; 139 | }; 140 | 607FACD11AFB9204008FA782 /* Products */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 607FACD01AFB9204008FA782 /* TIMFlowView_Example.app */, 144 | 607FACE51AFB9204008FA782 /* TIMFlowView_Tests.xctest */, 145 | ); 146 | name = Products; 147 | sourceTree = ""; 148 | }; 149 | 607FACD21AFB9204008FA782 /* Example for TIMFlowView */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 2246D1D123E86366005B7485 /* ZCycleView */, 153 | 22801B7223D5FDC5003496EF /* AppDelegate.swift */, 154 | 22801B7923D5FDEA003496EF /* CollectionDemoController.swift */, 155 | 22801B7B23D5FDEB003496EF /* DefaultDemoController.swift */, 156 | 22801B7C23D5FDEB003496EF /* DemoHeaderView.swift */, 157 | 2246D1DC23EE8AEC005B7485 /* DemoSectionHeaderFooterView.swift */, 158 | 22801B7A23D5FDEB003496EF /* FlowDemoItem.swift */, 159 | 22801B7D23D5FDEB003496EF /* FlowModel.swift */, 160 | 22801B7823D5FDEA003496EF /* HeaderDemoController.swift */, 161 | 22801B7623D5FDDD003496EF /* DemoViewController.swift */, 162 | 22801B7423D5FDD5003496EF /* images.plist */, 163 | 22801B8423D5FDF3003496EF /* Info.plist */, 164 | 22801B8823D5FE31003496EF /* LaunchScreen.xib */, 165 | 22801B8A23D5FE31003496EF /* Main.storyboard */, 166 | 22801B8623D5FDF9003496EF /* Images.xcassets */, 167 | 607FACD31AFB9204008FA782 /* Supporting Files */, 168 | ); 169 | name = "Example for TIMFlowView"; 170 | path = TIMFlowView; 171 | sourceTree = ""; 172 | }; 173 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 607FACD41AFB9204008FA782 /* Info.plist */, 177 | ); 178 | name = "Supporting Files"; 179 | sourceTree = ""; 180 | }; 181 | 607FACE81AFB9204008FA782 /* Tests */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 185 | 607FACE91AFB9204008FA782 /* Supporting Files */, 186 | ); 187 | path = Tests; 188 | sourceTree = ""; 189 | }; 190 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 607FACEA1AFB9204008FA782 /* Info.plist */, 194 | ); 195 | name = "Supporting Files"; 196 | sourceTree = ""; 197 | }; 198 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | B67393DD332AFA409F780ABE /* TIMFlowView.podspec */, 202 | 40C102F404E7EEE182597882 /* README.md */, 203 | A4C3E4550E8EE6793484AE1F /* LICENSE */, 204 | ); 205 | name = "Podspec Metadata"; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXGroup section */ 209 | 210 | /* Begin PBXNativeTarget section */ 211 | 607FACCF1AFB9204008FA782 /* TIMFlowView_Example */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "TIMFlowView_Example" */; 214 | buildPhases = ( 215 | B352CAE739C4BED136C07E15 /* [CP] Check Pods Manifest.lock */, 216 | 607FACCC1AFB9204008FA782 /* Sources */, 217 | 607FACCD1AFB9204008FA782 /* Frameworks */, 218 | 607FACCE1AFB9204008FA782 /* Resources */, 219 | 057C9F0563E8F915DE0B5E00 /* [CP] Embed Pods Frameworks */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | ); 225 | name = TIMFlowView_Example; 226 | productName = TIMFlowView; 227 | productReference = 607FACD01AFB9204008FA782 /* TIMFlowView_Example.app */; 228 | productType = "com.apple.product-type.application"; 229 | }; 230 | 607FACE41AFB9204008FA782 /* TIMFlowView_Tests */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "TIMFlowView_Tests" */; 233 | buildPhases = ( 234 | 763D5D2D4A3566945FB47B40 /* [CP] Check Pods Manifest.lock */, 235 | 607FACE11AFB9204008FA782 /* Sources */, 236 | 607FACE21AFB9204008FA782 /* Frameworks */, 237 | 607FACE31AFB9204008FA782 /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 243 | ); 244 | name = TIMFlowView_Tests; 245 | productName = Tests; 246 | productReference = 607FACE51AFB9204008FA782 /* TIMFlowView_Tests.xctest */; 247 | productType = "com.apple.product-type.bundle.unit-test"; 248 | }; 249 | /* End PBXNativeTarget section */ 250 | 251 | /* Begin PBXProject section */ 252 | 607FACC81AFB9204008FA782 /* Project object */ = { 253 | isa = PBXProject; 254 | attributes = { 255 | LastSwiftUpdateCheck = 0830; 256 | LastUpgradeCheck = 1130; 257 | ORGANIZATIONNAME = CocoaPods; 258 | TargetAttributes = { 259 | 607FACCF1AFB9204008FA782 = { 260 | CreatedOnToolsVersion = 6.3.1; 261 | LastSwiftMigration = 1130; 262 | }; 263 | 607FACE41AFB9204008FA782 = { 264 | CreatedOnToolsVersion = 6.3.1; 265 | LastSwiftMigration = 1130; 266 | TestTargetID = 607FACCF1AFB9204008FA782; 267 | }; 268 | }; 269 | }; 270 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "TIMFlowView" */; 271 | compatibilityVersion = "Xcode 3.2"; 272 | developmentRegion = en; 273 | hasScannedForEncodings = 0; 274 | knownRegions = ( 275 | en, 276 | Base, 277 | ); 278 | mainGroup = 607FACC71AFB9204008FA782; 279 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 280 | projectDirPath = ""; 281 | projectRoot = ""; 282 | targets = ( 283 | 607FACCF1AFB9204008FA782 /* TIMFlowView_Example */, 284 | 607FACE41AFB9204008FA782 /* TIMFlowView_Tests */, 285 | ); 286 | }; 287 | /* End PBXProject section */ 288 | 289 | /* Begin PBXResourcesBuildPhase section */ 290 | 607FACCE1AFB9204008FA782 /* Resources */ = { 291 | isa = PBXResourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 22801B8C23D5FE31003496EF /* LaunchScreen.xib in Resources */, 295 | 22801B8D23D5FE31003496EF /* Main.storyboard in Resources */, 296 | 22801B8723D5FDF9003496EF /* Images.xcassets in Resources */, 297 | 22801B7523D5FDD5003496EF /* images.plist in Resources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | 607FACE31AFB9204008FA782 /* Resources */ = { 302 | isa = PBXResourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | /* End PBXResourcesBuildPhase section */ 309 | 310 | /* Begin PBXShellScriptBuildPhase section */ 311 | 057C9F0563E8F915DE0B5E00 /* [CP] Embed Pods Frameworks */ = { 312 | isa = PBXShellScriptBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | inputPaths = ( 317 | "${PODS_ROOT}/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-frameworks.sh", 318 | "${BUILT_PRODUCTS_DIR}/KakaJSON/KakaJSON.framework", 319 | "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework", 320 | "${BUILT_PRODUCTS_DIR}/TIMFlowView/TIMFlowView.framework", 321 | ); 322 | name = "[CP] Embed Pods Frameworks"; 323 | outputPaths = ( 324 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KakaJSON.framework", 325 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework", 326 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TIMFlowView.framework", 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | shellPath = /bin/sh; 330 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TIMFlowView_Example/Pods-TIMFlowView_Example-frameworks.sh\"\n"; 331 | showEnvVarsInLog = 0; 332 | }; 333 | 763D5D2D4A3566945FB47B40 /* [CP] Check Pods Manifest.lock */ = { 334 | isa = PBXShellScriptBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | ); 338 | inputFileListPaths = ( 339 | ); 340 | inputPaths = ( 341 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 342 | "${PODS_ROOT}/Manifest.lock", 343 | ); 344 | name = "[CP] Check Pods Manifest.lock"; 345 | outputFileListPaths = ( 346 | ); 347 | outputPaths = ( 348 | "$(DERIVED_FILE_DIR)/Pods-TIMFlowView_Tests-checkManifestLockResult.txt", 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | shellPath = /bin/sh; 352 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 353 | showEnvVarsInLog = 0; 354 | }; 355 | B352CAE739C4BED136C07E15 /* [CP] Check Pods Manifest.lock */ = { 356 | isa = PBXShellScriptBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | ); 360 | inputFileListPaths = ( 361 | ); 362 | inputPaths = ( 363 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 364 | "${PODS_ROOT}/Manifest.lock", 365 | ); 366 | name = "[CP] Check Pods Manifest.lock"; 367 | outputFileListPaths = ( 368 | ); 369 | outputPaths = ( 370 | "$(DERIVED_FILE_DIR)/Pods-TIMFlowView_Example-checkManifestLockResult.txt", 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | shellPath = /bin/sh; 374 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 375 | showEnvVarsInLog = 0; 376 | }; 377 | /* End PBXShellScriptBuildPhase section */ 378 | 379 | /* Begin PBXSourcesBuildPhase section */ 380 | 607FACCC1AFB9204008FA782 /* Sources */ = { 381 | isa = PBXSourcesBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | 2246D1D723E86366005B7485 /* ZCycleViewCell.swift in Sources */, 385 | 2246D1D923E86366005B7485 /* ZPageControl.swift in Sources */, 386 | 22801B7323D5FDC5003496EF /* AppDelegate.swift in Sources */, 387 | 22801B8023D5FDEB003496EF /* FlowDemoItem.swift in Sources */, 388 | 22801B7723D5FDDD003496EF /* DemoViewController.swift in Sources */, 389 | 22801B8323D5FDEB003496EF /* FlowModel.swift in Sources */, 390 | 2246D1D823E86366005B7485 /* ZCycleLayout.swift in Sources */, 391 | 22801B8223D5FDEB003496EF /* DemoHeaderView.swift in Sources */, 392 | 2246D1DD23EE8AEC005B7485 /* DemoSectionHeaderFooterView.swift in Sources */, 393 | 22801B7F23D5FDEB003496EF /* CollectionDemoController.swift in Sources */, 394 | 22801B8123D5FDEB003496EF /* DefaultDemoController.swift in Sources */, 395 | 22801B7E23D5FDEB003496EF /* HeaderDemoController.swift in Sources */, 396 | 2246D1D623E86366005B7485 /* ZCycleView.swift in Sources */, 397 | ); 398 | runOnlyForDeploymentPostprocessing = 0; 399 | }; 400 | 607FACE11AFB9204008FA782 /* Sources */ = { 401 | isa = PBXSourcesBuildPhase; 402 | buildActionMask = 2147483647; 403 | files = ( 404 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 405 | ); 406 | runOnlyForDeploymentPostprocessing = 0; 407 | }; 408 | /* End PBXSourcesBuildPhase section */ 409 | 410 | /* Begin PBXTargetDependency section */ 411 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 412 | isa = PBXTargetDependency; 413 | target = 607FACCF1AFB9204008FA782 /* TIMFlowView_Example */; 414 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 415 | }; 416 | /* End PBXTargetDependency section */ 417 | 418 | /* Begin PBXVariantGroup section */ 419 | 22801B8823D5FE31003496EF /* LaunchScreen.xib */ = { 420 | isa = PBXVariantGroup; 421 | children = ( 422 | 22801B8923D5FE31003496EF /* Base */, 423 | ); 424 | name = LaunchScreen.xib; 425 | sourceTree = ""; 426 | }; 427 | 22801B8A23D5FE31003496EF /* Main.storyboard */ = { 428 | isa = PBXVariantGroup; 429 | children = ( 430 | 22801B8B23D5FE31003496EF /* Base */, 431 | ); 432 | name = Main.storyboard; 433 | sourceTree = ""; 434 | }; 435 | /* End PBXVariantGroup section */ 436 | 437 | /* Begin XCBuildConfiguration section */ 438 | 607FACED1AFB9204008FA782 /* Debug */ = { 439 | isa = XCBuildConfiguration; 440 | buildSettings = { 441 | ALWAYS_SEARCH_USER_PATHS = NO; 442 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 444 | CLANG_CXX_LIBRARY = "libc++"; 445 | CLANG_ENABLE_MODULES = YES; 446 | CLANG_ENABLE_OBJC_ARC = YES; 447 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 448 | CLANG_WARN_BOOL_CONVERSION = YES; 449 | CLANG_WARN_COMMA = YES; 450 | CLANG_WARN_CONSTANT_CONVERSION = YES; 451 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 452 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 453 | CLANG_WARN_EMPTY_BODY = YES; 454 | CLANG_WARN_ENUM_CONVERSION = YES; 455 | CLANG_WARN_INFINITE_RECURSION = YES; 456 | CLANG_WARN_INT_CONVERSION = YES; 457 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 459 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 462 | CLANG_WARN_STRICT_PROTOTYPES = YES; 463 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 464 | CLANG_WARN_UNREACHABLE_CODE = YES; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 467 | COPY_PHASE_STRIP = NO; 468 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 469 | ENABLE_STRICT_OBJC_MSGSEND = YES; 470 | ENABLE_TESTABILITY = YES; 471 | GCC_C_LANGUAGE_STANDARD = gnu99; 472 | GCC_DYNAMIC_NO_PIC = NO; 473 | GCC_NO_COMMON_BLOCKS = YES; 474 | GCC_OPTIMIZATION_LEVEL = 0; 475 | GCC_PREPROCESSOR_DEFINITIONS = ( 476 | "DEBUG=1", 477 | "$(inherited)", 478 | ); 479 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 480 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 481 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 482 | GCC_WARN_UNDECLARED_SELECTOR = YES; 483 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 484 | GCC_WARN_UNUSED_FUNCTION = YES; 485 | GCC_WARN_UNUSED_VARIABLE = YES; 486 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 487 | MTL_ENABLE_DEBUG_INFO = YES; 488 | ONLY_ACTIVE_ARCH = YES; 489 | SDKROOT = iphoneos; 490 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 491 | }; 492 | name = Debug; 493 | }; 494 | 607FACEE1AFB9204008FA782 /* Release */ = { 495 | isa = XCBuildConfiguration; 496 | buildSettings = { 497 | ALWAYS_SEARCH_USER_PATHS = NO; 498 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 499 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 500 | CLANG_CXX_LIBRARY = "libc++"; 501 | CLANG_ENABLE_MODULES = YES; 502 | CLANG_ENABLE_OBJC_ARC = YES; 503 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 504 | CLANG_WARN_BOOL_CONVERSION = YES; 505 | CLANG_WARN_COMMA = YES; 506 | CLANG_WARN_CONSTANT_CONVERSION = YES; 507 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 508 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 509 | CLANG_WARN_EMPTY_BODY = YES; 510 | CLANG_WARN_ENUM_CONVERSION = YES; 511 | CLANG_WARN_INFINITE_RECURSION = YES; 512 | CLANG_WARN_INT_CONVERSION = YES; 513 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 514 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 515 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 516 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 517 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 518 | CLANG_WARN_STRICT_PROTOTYPES = YES; 519 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 520 | CLANG_WARN_UNREACHABLE_CODE = YES; 521 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 522 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 523 | COPY_PHASE_STRIP = NO; 524 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 525 | ENABLE_NS_ASSERTIONS = NO; 526 | ENABLE_STRICT_OBJC_MSGSEND = YES; 527 | GCC_C_LANGUAGE_STANDARD = gnu99; 528 | GCC_NO_COMMON_BLOCKS = YES; 529 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 530 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 531 | GCC_WARN_UNDECLARED_SELECTOR = YES; 532 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 533 | GCC_WARN_UNUSED_FUNCTION = YES; 534 | GCC_WARN_UNUSED_VARIABLE = YES; 535 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 536 | MTL_ENABLE_DEBUG_INFO = NO; 537 | SDKROOT = iphoneos; 538 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 539 | VALIDATE_PRODUCT = YES; 540 | }; 541 | name = Release; 542 | }; 543 | 607FACF01AFB9204008FA782 /* Debug */ = { 544 | isa = XCBuildConfiguration; 545 | baseConfigurationReference = 7E53C08A02624840672354EB /* Pods-TIMFlowView_Example.debug.xcconfig */; 546 | buildSettings = { 547 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 548 | CLANG_ENABLE_MODULES = YES; 549 | INFOPLIST_FILE = TIMFlowView/Info.plist; 550 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 551 | MODULE_NAME = ExampleApp; 552 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SWIFT_OBJC_BRIDGING_HEADER = ""; 555 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 556 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 557 | SWIFT_VERSION = 5.0; 558 | }; 559 | name = Debug; 560 | }; 561 | 607FACF11AFB9204008FA782 /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | baseConfigurationReference = 81742060854BA88BAEC27A93 /* Pods-TIMFlowView_Example.release.xcconfig */; 564 | buildSettings = { 565 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 566 | CLANG_ENABLE_MODULES = YES; 567 | INFOPLIST_FILE = TIMFlowView/Info.plist; 568 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 569 | MODULE_NAME = ExampleApp; 570 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 571 | PRODUCT_NAME = "$(TARGET_NAME)"; 572 | SWIFT_OBJC_BRIDGING_HEADER = ""; 573 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 574 | SWIFT_VERSION = 5.0; 575 | }; 576 | name = Release; 577 | }; 578 | 607FACF31AFB9204008FA782 /* Debug */ = { 579 | isa = XCBuildConfiguration; 580 | baseConfigurationReference = F06F49F954380907D447AE53 /* Pods-TIMFlowView_Tests.debug.xcconfig */; 581 | buildSettings = { 582 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 583 | FRAMEWORK_SEARCH_PATHS = ( 584 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 585 | "$(inherited)", 586 | ); 587 | GCC_PREPROCESSOR_DEFINITIONS = ( 588 | "DEBUG=1", 589 | "$(inherited)", 590 | ); 591 | INFOPLIST_FILE = Tests/Info.plist; 592 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 593 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 594 | PRODUCT_NAME = "$(TARGET_NAME)"; 595 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 596 | SWIFT_VERSION = 5.0; 597 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TIMFlowView_Example.app/TIMFlowView_Example"; 598 | }; 599 | name = Debug; 600 | }; 601 | 607FACF41AFB9204008FA782 /* Release */ = { 602 | isa = XCBuildConfiguration; 603 | baseConfigurationReference = 8573A328E722B6E6D467AE37 /* Pods-TIMFlowView_Tests.release.xcconfig */; 604 | buildSettings = { 605 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 606 | FRAMEWORK_SEARCH_PATHS = ( 607 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 608 | "$(inherited)", 609 | ); 610 | INFOPLIST_FILE = Tests/Info.plist; 611 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 612 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 613 | PRODUCT_NAME = "$(TARGET_NAME)"; 614 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 615 | SWIFT_VERSION = 5.0; 616 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TIMFlowView_Example.app/TIMFlowView_Example"; 617 | }; 618 | name = Release; 619 | }; 620 | /* End XCBuildConfiguration section */ 621 | 622 | /* Begin XCConfigurationList section */ 623 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "TIMFlowView" */ = { 624 | isa = XCConfigurationList; 625 | buildConfigurations = ( 626 | 607FACED1AFB9204008FA782 /* Debug */, 627 | 607FACEE1AFB9204008FA782 /* Release */, 628 | ); 629 | defaultConfigurationIsVisible = 0; 630 | defaultConfigurationName = Release; 631 | }; 632 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "TIMFlowView_Example" */ = { 633 | isa = XCConfigurationList; 634 | buildConfigurations = ( 635 | 607FACF01AFB9204008FA782 /* Debug */, 636 | 607FACF11AFB9204008FA782 /* Release */, 637 | ); 638 | defaultConfigurationIsVisible = 0; 639 | defaultConfigurationName = Release; 640 | }; 641 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "TIMFlowView_Tests" */ = { 642 | isa = XCConfigurationList; 643 | buildConfigurations = ( 644 | 607FACF31AFB9204008FA782 /* Debug */, 645 | 607FACF41AFB9204008FA782 /* Release */, 646 | ); 647 | defaultConfigurationIsVisible = 0; 648 | defaultConfigurationName = Release; 649 | }; 650 | /* End XCConfigurationList section */ 651 | }; 652 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 653 | } 654 | -------------------------------------------------------------------------------- /Example/TIMFlowView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/TIMFlowView.xcodeproj/xcshareddata/xcschemes/TIMFlowView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/TIMFlowView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/TIMFlowView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/TIMFlowView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim on 01/18/2020. 6 | // Copyright (c) 2020 Tim. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/TIMFlowView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/TIMFlowView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/TIMFlowView/CollectionDemoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionDemoController.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/20. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import TIMFlowView 11 | 12 | class CollectionDemoController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | title = "模仿 UIColletionView " 17 | view.backgroundColor = UIColor.orange 18 | setupFlowView() 19 | setupData() 20 | } 21 | 22 | private func setupFlowView() { 23 | let fView = TIMFlowView() 24 | let flowVY: CGFloat = isIphoneX ? 88.0 : 64.0 25 | let height = kScreenHeight - flowVY 26 | fView.frame = CGRect(x: 0, y: flowVY, width: view.bounds.width, height: height) 27 | fView.flowDataSource = self 28 | fView.flowDelegate = self 29 | fView.floatingHeaderEnable = true 30 | fView.backgroundColor = UIColor.orange 31 | 32 | // 添加 banner 视图 33 | let headerView = DemoHeaderView.headerView(with: kScreenWidth * 0.56) { (index) in 34 | print("点击了第\(index)个banner") 35 | } 36 | fView.flowHeaderView = headerView 37 | 38 | let footerLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 120.0)) 39 | footerLabel.text = "这是一个尾部视图的大title" 40 | footerLabel.backgroundColor = #colorLiteral(red: 0.7308493557, green: 0.141261917, blue: 1, alpha: 1) 41 | footerLabel.textColor = UIColor.white 42 | footerLabel.font = UIFont.systemFont(ofSize: 25.0) 43 | fView.flowFooterView = footerLabel 44 | 45 | 46 | view.addSubview(fView) 47 | flowView = fView 48 | } 49 | 50 | private func setupData() { 51 | DispatchQueue.global().async { 52 | guard let plistPath = Bundle.main.path(forResource: "images.plist", ofType: nil), 53 | let models = NSArray(contentsOfFile: plistPath) as? [[String: Any]] else { 54 | print("未获取到模型文件") 55 | return 56 | } 57 | 58 | for model in models { 59 | self.flowModels.append(model.kj.model(FlowModel.self)) 60 | } 61 | 62 | DispatchQueue.main.async { 63 | self.flowView.reloadData() 64 | } 65 | } 66 | } 67 | 68 | private weak var flowView: TIMFlowView! 69 | private lazy var flowModels: [FlowModel] = [] 70 | } 71 | 72 | 73 | extension CollectionDemoController: TIMFlowViewDataSource { 74 | func numberOfColumns(in flowView: TIMFlowView, at section: Int) -> Int { 75 | switch section { 76 | case 0: 77 | return 3 78 | case 1: 79 | return 2 80 | default: 81 | return 3 82 | } 83 | } 84 | 85 | func numberOfSections(in flowView: TIMFlowView) -> Int { 20 } 86 | func numberOfItems(in flowView: TIMFlowView, at section: Int) -> Int { 87 | switch section { 88 | case 0: 89 | return 9 90 | case 1: 91 | return flowModels.count 92 | default: 93 | return 9 94 | } 95 | } 96 | 97 | func flowViewItem(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> TIMFlowViewItem? { 98 | let section = indexPath.section 99 | switch section { 100 | case 0: 101 | let collectionItemID = "collectionItemID" 102 | var item: TIMFlowViewItem? 103 | guard let dequeueItem = flowView.dequeueReuseableItem(with: collectionItemID) else { 104 | item = TIMFlowViewItem(with: collectionItemID) 105 | item?.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) 106 | return item 107 | } 108 | item = dequeueItem 109 | return item 110 | case 1: 111 | let item = FlowDemoItem.item(with: flowView) 112 | item?.flowModel = flowModels[indexPath.item] 113 | return item 114 | default: 115 | let collectionItemID = "collectionItemID" 116 | var item: TIMFlowViewItem? 117 | guard let dequeueItem = flowView.dequeueReuseableItem(with: collectionItemID) else { 118 | item = TIMFlowViewItem(with: collectionItemID) 119 | item?.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) 120 | return item 121 | } 122 | item = dequeueItem 123 | return item 124 | } 125 | } 126 | } 127 | 128 | extension CollectionDemoController: TIMFlowViewDelegate { 129 | func itemHeight(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> CGFloat { 130 | let section = indexPath.section 131 | switch section { 132 | case 0: 133 | return 80.0 134 | case 1: 135 | let model = flowModels[indexPath.item] 136 | return model.height * (flowView.itemWidhth(in: indexPath.section) / model.width) 137 | default: 138 | return 80.0 139 | } 140 | } 141 | 142 | func viewForSectionHeader(in flowView: TIMFlowView, at section: Int) -> TIMFlowHeaderFooterView? { 143 | let headerView = DemoSectionHeaderFooterView.headerFooterView(with: flowView) 144 | headerView?.sectionIndex = section 145 | return headerView 146 | } 147 | 148 | func viewForSectionFooter(in flowView: TIMFlowView, at section: Int) -> TIMFlowHeaderFooterView? { 149 | let footer = DemoSectionHeaderFooterView.headerFooterView(with: flowView) 150 | footer?.isHeader = false 151 | footer?.sectionIndex = section 152 | return footer 153 | } 154 | 155 | func didSelected(in flowView: TIMFlowView, at indexPath: TIMIndexPath) { 156 | print("点击了第\(indexPath.section)个分区的第\(indexPath.item)个item") 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Example/TIMFlowView/DefaultDemoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultDemoController.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/20. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import TIMFlowView 11 | import KakaJSON 12 | 13 | class DefaultDemoController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | title = "最普通的瀑布流" 18 | setupFlowView() 19 | setupData() 20 | } 21 | 22 | private func setupFlowView() { 23 | let fView = TIMFlowView() 24 | let flowVY: CGFloat = isIphoneX ? 88.0 : 64.0 25 | let height = kScreenHeight - flowVY 26 | fView.frame = CGRect(x: 0, y: flowVY, width: view.bounds.width, height: height) 27 | fView.flowDataSource = self 28 | fView.flowDelegate = self 29 | fView.backgroundColor = UIColor.orange 30 | view.addSubview(fView) 31 | flowView = fView 32 | } 33 | 34 | private func setupData() { 35 | DispatchQueue.global().async { 36 | guard let plistPath = Bundle.main.path(forResource: "images.plist", ofType: nil), 37 | let models = NSArray(contentsOfFile: plistPath) as? [[String: Any]] else { 38 | print("未获取到模型文件") 39 | return 40 | } 41 | 42 | for model in models { 43 | self.flowModels.append(model.kj.model(FlowModel.self)) 44 | } 45 | 46 | DispatchQueue.main.async { 47 | self.flowView.reloadData() 48 | } 49 | } 50 | } 51 | 52 | private weak var flowView: TIMFlowView! 53 | private lazy var flowModels: [FlowModel] = [] 54 | 55 | } 56 | 57 | extension DefaultDemoController: TIMFlowViewDataSource { 58 | func numberOfColumns(in flowView: TIMFlowView, at section: Int) -> Int { 59 | 2 60 | } 61 | func numberOfItems(in flowView: TIMFlowView, at section: Int) -> Int { 62 | flowModels.count 63 | } 64 | 65 | func flowViewItem(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> TIMFlowViewItem? { 66 | let item = FlowDemoItem.item(with: flowView) 67 | item?.flowModel = flowModels[indexPath.item] 68 | return item 69 | } 70 | } 71 | 72 | extension DefaultDemoController: TIMFlowViewDelegate { 73 | func itemHeight(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> CGFloat { 74 | let model = flowModels[indexPath.item] 75 | return model.height * (flowView.itemWidhth(in: indexPath.section) / model.width) 76 | } 77 | 78 | func didSelected(in flowView: TIMFlowView, at indexPath: TIMIndexPath) { 79 | print("点击了第\(indexPath.section)个分区的第\(indexPath.item)个item") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Example/TIMFlowView/DemoHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoHeaderView.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/20. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import TIMFlowView 10 | 11 | class DemoHeaderView: UIView { 12 | 13 | 14 | class func headerView(with height: CGFloat, tapAction: @escaping (_ index: Int) -> Void) -> DemoHeaderView { 15 | let headerView = self.init() 16 | headerView.frame = CGRect(x: 0, y: 0, width: 0, height: height) 17 | headerView.tapAction = tapAction 18 | return headerView 19 | } 20 | 21 | override init(frame: CGRect) { 22 | super.init(frame: frame) 23 | setupBannerView() 24 | } 25 | 26 | required init?(coder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | 31 | override func layoutSubviews() { 32 | super.layoutSubviews() 33 | cycleView.frame = CGRect(x: 8.0, y: 8.0, width: frame.width - 2 * 8.0, height: frame.height - 2 * 8.0) 34 | } 35 | 36 | private func setupBannerView() { 37 | 38 | let cycleView = ZCycleView() 39 | cycleView.delegate = self 40 | cycleView.isInfinite = true 41 | cycleView.isAutomatic = true 42 | cycleView.timeInterval = 3 43 | cycleView.setImagesGroup([#imageLiteral(resourceName: "banner03"), #imageLiteral(resourceName: "banner01"), #imageLiteral(resourceName: "banner02")]) 44 | cycleView.itemSpacing = 0 45 | cycleView.itemSize = CGSize(width: kScreenWidth - 2 * 8.0, height: kScreenWidth * 0.56 - 2 * 8.0) 46 | cycleView.layer.cornerRadius = 8.0 47 | addSubview(cycleView) 48 | self.cycleView = cycleView 49 | 50 | } 51 | 52 | private var cycleView: ZCycleView! 53 | private var tapAction: ((Int) -> ())? 54 | } 55 | 56 | extension DemoHeaderView: ZCycleViewProtocol { 57 | func cycleViewConfigureDefaultCellImage(_ cycleView: ZCycleView, imageView: UIImageView, image: UIImage?, index: Int) { 58 | imageView.contentMode = .scaleAspectFill 59 | imageView.layer.cornerRadius = 8.0 60 | imageView.layer.masksToBounds = true 61 | imageView.image = image 62 | } 63 | 64 | func cycleViewDidSelectedIndex(_ cycleView: ZCycleView, index: Int) { 65 | tapAction!(index) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Example/TIMFlowView/DemoSectionHeaderFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoSectionHeaderFooterView.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim on 2020/2/8. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import TIMFlowView 10 | 11 | class DemoSectionHeaderFooterView: TIMFlowHeaderFooterView { 12 | var sectionIndex: Int? { 13 | willSet { 14 | titleLabel.text = "当前第\(String(describing: newValue))个分区\(isHeader ? "头部视图" : "尾部视图")" 15 | } 16 | } 17 | 18 | var isHeader: Bool { 19 | willSet { 20 | if newValue == true { 21 | backgroundColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1) 22 | } else { 23 | backgroundColor = #colorLiteral(red: 0.02465939626, green: 0.8039215803, blue: 0.6574816938, alpha: 1) 24 | } 25 | } 26 | } 27 | 28 | class func headerFooterView(with flowView: TIMFlowView) -> DemoSectionHeaderFooterView? { 29 | let headerFooterID = "headerFooterID" 30 | guard let headerFooterView = flowView.dequeueReuseableSectionHeaderView(with: headerFooterID) else { 31 | let hfView = DemoSectionHeaderFooterView(with: headerFooterID) 32 | hfView.backgroundColor = #colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1) 33 | hfView.frame = CGRect(x: 0, y: 0, width: 0, height: 44.0) 34 | return hfView 35 | } 36 | 37 | return headerFooterView as? DemoSectionHeaderFooterView 38 | } 39 | 40 | override init(frame: CGRect) { 41 | isHeader = true 42 | super.init(frame: frame) 43 | setupUI() 44 | } 45 | 46 | required init?(coder: NSCoder) { 47 | fatalError("init(coder:) has not been implemented") 48 | } 49 | 50 | private func setupUI() { 51 | let title = UILabel() 52 | title.font = UIFont.systemFont(ofSize: 14.0) 53 | title.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) 54 | addSubview(title) 55 | titleLabel = title 56 | } 57 | 58 | 59 | override func layoutSubviews() { 60 | super.layoutSubviews() 61 | titleLabel.frame = CGRect(x: 15.0, y: 0, width: bounds.width - 2 * 15.0, height: bounds.height) 62 | } 63 | 64 | 65 | private weak var titleLabel: UILabel! 66 | } 67 | -------------------------------------------------------------------------------- /Example/TIMFlowView/DemoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoViewController.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/20. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let CellID = "DemoCell" 12 | 13 | class DemoViewController: UITableViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: CellID) 18 | } 19 | 20 | // MARK: - Table view data source 21 | 22 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 23 | return 3 24 | } 25 | 26 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 27 | let cell = tableView.dequeueReusableCell(withIdentifier: CellID, for: indexPath) 28 | switch indexPath.row { 29 | case 0: 30 | cell.textLabel?.text = "普通瀑布流" 31 | case 1: 32 | cell.textLabel?.text = "带有 header 的瀑布流" 33 | case 2: 34 | cell.textLabel?.text = "模仿 UICollectionView" 35 | default: 36 | break 37 | } 38 | 39 | 40 | return cell 41 | } 42 | 43 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 44 | var destination: UIViewController! = nil 45 | 46 | switch indexPath.row { 47 | case 0: 48 | destination = DefaultDemoController() 49 | case 1: 50 | destination = HeaderDemoController() 51 | 52 | case 2: 53 | destination = CollectionDemoController() 54 | default: 55 | break 56 | } 57 | 58 | navigationController?.pushViewController(destination, animated: true) 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Example/TIMFlowView/FlowDemoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowDemoItem.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/19. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import TIMFlowView 10 | import Kingfisher 11 | 12 | class FlowDemoItem: TIMFlowViewItem { 13 | 14 | /// 模型 15 | var flowModel: FlowModel? { 16 | willSet { 17 | nameLabel.text = newValue?.name 18 | let imageURL = URL(string: newValue?.url ?? "") 19 | imageView.kf.setImage(with: imageURL, placeholder: UIImage(named: "placeholder"), options: nil, progressBlock: nil, completionHandler: nil) 20 | } 21 | } 22 | 23 | class func item(with flowView: TIMFlowView) -> FlowDemoItem? { 24 | let flowItemId = "CellID" 25 | guard let item = flowView.dequeueReuseableItem(with: flowItemId) else { 26 | return FlowDemoItem(with: flowItemId) 27 | } 28 | 29 | return item as? FlowDemoItem 30 | } 31 | 32 | override init(frame: CGRect) { 33 | super.init(frame: frame) 34 | setupUI() 35 | } 36 | 37 | 38 | private func setupUI() { 39 | let content = UIView() 40 | 41 | addSubview(content) 42 | contentView = content 43 | 44 | let image = UIImageView() 45 | image.contentMode = .center 46 | content.addSubview(image) 47 | imageView = image 48 | 49 | let name = UILabel() 50 | name.backgroundColor = UIColor(white: 0, alpha: 0.8) 51 | name.textColor = UIColor(white: 0.6, alpha: 1) 52 | name.font = UIFont.systemFont(ofSize: 11.0) 53 | name.textAlignment = .left 54 | content.addSubview(name) 55 | nameLabel = name 56 | } 57 | 58 | // 请务必在此方法中设置 subview 的 frame 59 | // 否则会因获取不到 self 的frame 造成效果显示不正常 60 | // 方法还在寻求解决方案 61 | // 如果有大神有解决方案还请来信告知 62 | // guoyong19890907@gmail.com 63 | override func layoutSubviews() { 64 | super.layoutSubviews() 65 | contentView.frame = bounds 66 | contentView.backgroundColor = UIColor.white 67 | let shapPath = UIBezierPath(roundedRect: bounds, cornerRadius: 5.0) 68 | let shapLayer = CAShapeLayer() 69 | shapLayer.path = shapPath.cgPath 70 | shapLayer.frame = contentView.bounds 71 | contentView.layer.mask = shapLayer 72 | 73 | imageView.frame = bounds 74 | imageView.contentMode = .scaleAspectFit 75 | nameLabel.frame = CGRect(x: 0, y: bounds.height - 30.0, width: bounds.width, height: 30.0) 76 | } 77 | 78 | required init?(coder: NSCoder) { 79 | fatalError("init(coder:) has not been implemented") 80 | } 81 | 82 | 83 | private weak var contentView: UIView! 84 | private weak var imageView: UIImageView! 85 | private weak var nameLabel: UILabel! 86 | } 87 | -------------------------------------------------------------------------------- /Example/TIMFlowView/FlowModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowModel.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/19. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import KakaJSON 11 | 12 | struct FlowModel: Convertible { 13 | var name: String = "" 14 | var url: String = "" 15 | var width: CGFloat = 0 16 | var height: CGFloat = 0 17 | } 18 | -------------------------------------------------------------------------------- /Example/TIMFlowView/HeaderDemoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderDemoController.swift 3 | // TIMFlowView_Example 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/20. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import KakaJSON 11 | import TIMFlowView 12 | 13 | class HeaderDemoController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | title = "带有 Header 的瀑布流" 18 | setupFlowView() 19 | setupData() 20 | } 21 | 22 | private func setupFlowView() { 23 | let fView = TIMFlowView() 24 | let flowVY: CGFloat = isIphoneX ? 88.0 : 64.0 25 | let height = kScreenHeight - flowVY 26 | fView.frame = CGRect(x: 0, y: flowVY, width: view.bounds.width, height: height) 27 | fView.flowDataSource = self 28 | fView.flowDelegate = self 29 | fView.backgroundColor = UIColor.orange 30 | 31 | // 添加 banner 视图 32 | let headerView = DemoHeaderView.headerView(with: kScreenWidth * 0.56) { (index) in 33 | print("点击了第\(index)个banner") 34 | } 35 | fView.flowHeaderView = headerView 36 | 37 | view.addSubview(fView) 38 | flowView = fView 39 | } 40 | 41 | private func setupData() { 42 | DispatchQueue.global().async { 43 | guard let plistPath = Bundle.main.path(forResource: "images.plist", ofType: nil), 44 | let models = NSArray(contentsOfFile: plistPath) as? [[String: Any]] else { 45 | print("未获取到模型文件") 46 | return 47 | } 48 | 49 | for model in models { 50 | self.flowModels.append(model.kj.model(FlowModel.self)) 51 | } 52 | 53 | DispatchQueue.main.async { 54 | self.flowView.reloadData() 55 | } 56 | } 57 | } 58 | 59 | private weak var flowView: TIMFlowView! 60 | private lazy var flowModels: [FlowModel] = [] 61 | } 62 | 63 | extension HeaderDemoController: TIMFlowViewDataSource { 64 | func numberOfColumns(in flowView: TIMFlowView, at section: Int) -> Int { 2 } 65 | func numberOfItems(in flowView: TIMFlowView, at section: Int) -> Int { flowModels.count } 66 | 67 | func flowViewItem(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> TIMFlowViewItem? { 68 | let item = FlowDemoItem.item(with: flowView) 69 | item?.flowModel = flowModels[indexPath.item] 70 | return item 71 | } 72 | } 73 | 74 | extension HeaderDemoController: TIMFlowViewDelegate { 75 | func itemHeight(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> CGFloat { 76 | let model = flowModels[indexPath.item] 77 | return model.height * (flowView.itemWidhth(in: indexPath.section) / model.width) 78 | } 79 | 80 | func didSelected(in flowView: TIMFlowView, at indexPath: TIMIndexPath) { 81 | print("点击了第\(indexPath.section)个分区的第\(indexPath.item)个item") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/banner01.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "banner01.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/banner01.imageset/banner01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/Example/TIMFlowView/Images.xcassets/banner/banner01.imageset/banner01.png -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/banner02.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "banner02.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/banner02.imageset/banner02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/Example/TIMFlowView/Images.xcassets/banner/banner02.imageset/banner02.png -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/banner03.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "banner03.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/banner/banner03.imageset/banner03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/Example/TIMFlowView/Images.xcassets/banner/banner03.imageset/banner03.png -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/placeholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "placeholder@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "placeholder@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/placeholder.imageset/placeholder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/Example/TIMFlowView/Images.xcassets/placeholder.imageset/placeholder@2x.png -------------------------------------------------------------------------------- /Example/TIMFlowView/Images.xcassets/placeholder.imageset/placeholder@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/Example/TIMFlowView/Images.xcassets/placeholder.imageset/placeholder@3x.png -------------------------------------------------------------------------------- /Example/TIMFlowView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/TIMFlowView/ZCycleView/ZCycleLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZCycleLayout.swift 3 | // ZCycleView 4 | // 5 | // Created by mengqingzheng on 2017/11/24. 6 | // Copyright © 2017年 MQZHot. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ZCycleLayout: UICollectionViewFlowLayout { 12 | 13 | var scale: CGFloat = 1 { 14 | didSet { if scale >= 1 { invalidateLayout() } } 15 | } 16 | 17 | override func prepare() { 18 | super.prepare() 19 | if let collectionView = collectionView { 20 | if scrollDirection == .horizontal { 21 | let offset = (collectionView.frame.size.width-itemSize.width)/2 22 | sectionInset = UIEdgeInsets.init(top: 0, left: offset, bottom: 0, right: 0) 23 | } else { 24 | let offset = (collectionView.frame.size.height-itemSize.height)/2 25 | sectionInset = UIEdgeInsets.init(top: offset, left: 0, bottom: 0, right: 0) 26 | } 27 | } 28 | } 29 | 30 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 31 | return true 32 | } 33 | 34 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 35 | if let attributes = super.layoutAttributesForElements(in: rect), 36 | let collectionView = collectionView { 37 | let attris = NSArray(array: attributes, copyItems:true) as! [UICollectionViewLayoutAttributes] 38 | for attri in attris { 39 | var scale: CGFloat = 1 40 | var absOffset: CGFloat = 0 41 | let centerX = collectionView.bounds.size.width*0.5 + collectionView.contentOffset.x 42 | let centerY = collectionView.bounds.size.height*0.5 + collectionView.contentOffset.y 43 | if scrollDirection == .horizontal { 44 | absOffset = abs(attri.center.x-centerX) 45 | let distance = itemSize.width+minimumLineSpacing 46 | if absOffset < distance {///当前index 47 | scale = (1-absOffset/distance)*(self.scale-1) + 1 48 | } 49 | } else { 50 | absOffset = abs(attri.center.y-centerY) 51 | let distance = itemSize.height+minimumLineSpacing 52 | if absOffset < distance { 53 | scale = (1-absOffset/distance)*(self.scale-1) + 1 54 | } 55 | } 56 | 57 | attri.zIndex = Int(scale * 1000) 58 | attri.transform = CGAffineTransform(scaleX:scale,y: scale) 59 | } 60 | return attris 61 | } 62 | return nil 63 | } 64 | 65 | override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { 66 | var minSpace = CGFloat.greatestFiniteMagnitude 67 | var offset = proposedContentOffset 68 | if let collectionView = collectionView { 69 | let centerX = offset.x + collectionView.bounds.size.width/2 70 | let centerY = offset.y + collectionView.bounds.size.height/2 71 | var visibleRect: CGRect 72 | if scrollDirection == .horizontal { 73 | visibleRect = CGRect(origin: CGPoint(x: offset.x, y: 0), size: collectionView.bounds.size) 74 | } else { 75 | visibleRect = CGRect(origin: CGPoint(x: 0, y: offset.y), size: collectionView.bounds.size) 76 | } 77 | if let attris = layoutAttributesForElements(in: visibleRect) { 78 | for attri in attris { 79 | if scrollDirection == .horizontal { 80 | if abs(minSpace) > abs(attri.center.x-centerX) { 81 | minSpace = attri.center.x-centerX 82 | } 83 | } else { 84 | if abs(minSpace) > abs(attri.center.y-centerY) { 85 | minSpace = attri.center.y-centerY 86 | } 87 | } 88 | } 89 | } 90 | if scrollDirection == .horizontal { 91 | offset.x += minSpace 92 | } else { 93 | offset.y += minSpace 94 | } 95 | } 96 | return offset 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/TIMFlowView/ZCycleView/ZCycleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZCycleView.swift 3 | // ZCycleView 4 | // 5 | // Created by mengqingzheng on 2017/11/17. 6 | // Copyright © 2017年 MQZHot. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc public protocol ZCycleViewProtocol: class { 12 | /// 配置默认的cell, 13 | /// 如果使用默认的cell显示图片,必须实现`cycleViewConfigureDefaultCellImage`或`cycleViewConfigureDefaultCellImageUrl` 14 | @objc optional func cycleViewConfigureDefaultCellImage(_ cycleView: ZCycleView, imageView: UIImageView, image: UIImage?, index: Int) 15 | @objc optional func cycleViewConfigureDefaultCellImageUrl(_ cycleView: ZCycleView, imageView: UIImageView, imageUrl: String?, index: Int) 16 | @objc optional func cycleViewConfigureDefaultCellText(_ cycleView: ZCycleView, titleLabel: UILabel, index: Int) 17 | /// 滚动到第index个cell 18 | @objc optional func cycleViewDidScrollToIndex(_ cycleView: ZCycleView, index: Int) 19 | /// 点击了第index个cell 20 | @objc optional func cycleViewDidSelectedIndex(_ cycleView: ZCycleView, index: Int) 21 | /// pageControl设置 22 | @objc optional func cycleViewConfigurePageControl(_ cycleView: ZCycleView, pageControl: ZPageControl) 23 | /// 自定义cell 24 | @objc optional func cycleViewCustomCellIdentifier() -> String 25 | @objc optional func cycleViewCustomCellClass() -> AnyClass 26 | @objc optional func cycleViewCustomCellClassNib() -> UINib 27 | @objc optional func cycleViewCustomCellSetup(_ cycleView: ZCycleView, cell: UICollectionViewCell, for index: Int) 28 | } 29 | 30 | public class ZCycleView: UIView { 31 | /// 设置本地图片 32 | /// - Parameter imagesGroup: image数组 33 | /// - Parameter titlesGroup: 标题数组 34 | public func setImagesGroup(_ imagesGroup: Array, 35 | titlesGroup: Array? = nil) { 36 | if imagesGroup.count == 0 { return } 37 | realDataCount = imagesGroup.count 38 | self.imagesGroup = imagesGroup 39 | self.titlesGroup = titlesGroup ?? [] 40 | self.imageUrlsGroup = [] 41 | reload() 42 | } 43 | 44 | /// 设置网络图片 45 | /// - Parameter urlsGroup: url数组 46 | /// - Parameter titlesGroup: 标题数组 47 | public func setUrlsGroup(_ urlsGroup: Array, 48 | titlesGroup: Array? = nil) { 49 | if urlsGroup.count == 0 { return } 50 | realDataCount = urlsGroup.count 51 | self.imageUrlsGroup = urlsGroup 52 | self.titlesGroup = titlesGroup ?? [] 53 | self.imagesGroup = [] 54 | reload() 55 | } 56 | 57 | /// 设置文字 58 | /// - Parameter titlesGroup: 文字数组 59 | public func setTitlesGroup(_ titlesGroup: Array) { 60 | if titlesGroup.count == 0 { return } 61 | realDataCount = titlesGroup.count 62 | self.imagesGroup = [] 63 | self.imageUrlsGroup = [] 64 | self.titlesGroup = titlesGroup 65 | reload() 66 | } 67 | /// cell identifier 68 | /// add by LeeYZ 69 | private var reuseIdentifier: String = "ZCycleViewCell" 70 | 71 | /// 是否自动滚动 72 | public var isAutomatic: Bool = true 73 | /// 是否无限轮播 74 | public var isInfinite: Bool = true { 75 | didSet { 76 | if isInfinite == false { 77 | itemsCount = realDataCount <= 1 || !isInfinite ? realDataCount : realDataCount*200 78 | collectionView.reloadData() 79 | collectionView.setContentOffset(.zero, animated: false) 80 | dealFirstPage() 81 | } 82 | } 83 | } 84 | /// 滚动时间间隔,默认2s 85 | public var timeInterval: Int = 2 86 | /// 滚动方向 87 | public var scrollDirection: UICollectionView.ScrollDirection = .horizontal { 88 | didSet { flowLayout.scrollDirection = scrollDirection } 89 | } 90 | /// 占位图 91 | public var placeholderImage: UIImage? = nil { 92 | didSet { placeholderImgView.image = placeholderImage } 93 | } 94 | /// item大小,默认ZCycleView大小 95 | public var itemSize: CGSize? { 96 | didSet { 97 | guard let itemSize = itemSize else { return } 98 | let width = min(bounds.size.width, itemSize.width) 99 | let height = min(bounds.size.height, itemSize.height) 100 | flowLayout.itemSize = CGSize(width: width, height: height) 101 | } 102 | } 103 | /// 中间item的放大比例, >=1 104 | public var itemZoomScale: CGFloat = 1 { 105 | didSet { 106 | collectionView.isPagingEnabled = itemZoomScale == 1 107 | flowLayout.scale = itemZoomScale 108 | } 109 | } 110 | /// item 间距 111 | public var itemSpacing: CGFloat = 0 { 112 | didSet { flowLayout.minimumLineSpacing = itemSpacing } 113 | } 114 | 115 | /// delegate 116 | public weak var delegate: ZCycleViewProtocol? { 117 | didSet { 118 | if delegate != nil { registerCell() } 119 | } 120 | } 121 | 122 | //============================================= 123 | private var flowLayout: ZCycleLayout! 124 | private var collectionView: UICollectionView! 125 | private var placeholderImgView: UIImageView! 126 | private var pageControl: ZPageControl! 127 | private var imagesGroup: Array = [] 128 | private var imageUrlsGroup: Array = [] 129 | private var titlesGroup: Array = [] 130 | private var timer: Timer? 131 | private var itemsCount: Int = 0 132 | private var realDataCount: Int = 0 133 | 134 | override public init(frame: CGRect) { 135 | super.init(frame: frame) 136 | addPlaceholderImgView() 137 | addCollectionView() 138 | addPageControl() 139 | } 140 | 141 | required public init?(coder aDecoder: NSCoder) { 142 | super.init(coder: aDecoder) 143 | addPlaceholderImgView() 144 | addCollectionView() 145 | addPageControl() 146 | } 147 | 148 | override public func willMove(toWindow newWindow: UIWindow?) { 149 | super.willMove(toWindow: newWindow) 150 | if newWindow != nil { 151 | self.startTimer() 152 | } else { 153 | self.cancelTimer() 154 | } 155 | } 156 | 157 | public override func layoutSubviews() { 158 | super.layoutSubviews() 159 | flowLayout.itemSize = itemSize != nil ? itemSize! : bounds.size 160 | collectionView.frame = bounds 161 | pageControl.frame = CGRect(x: 0, y: frame.size.height-25, width: frame.size.width, height: 25) 162 | collectionView.setContentOffset(.zero, animated: false) 163 | dealFirstPage() 164 | delegate?.cycleViewConfigurePageControl?(self, pageControl: pageControl) 165 | } 166 | } 167 | 168 | // MARK: - setupUI 169 | extension ZCycleView { 170 | private func addPlaceholderImgView() { 171 | placeholderImgView = UIImageView(frame: CGRect.zero) 172 | placeholderImgView.image = placeholderImage 173 | addSubview(placeholderImgView) 174 | placeholderImgView.translatesAutoresizingMaskIntoConstraints = false 175 | let hCons = NSLayoutConstraint.constraints(withVisualFormat: "H:|[placeholderImgView]|", 176 | options: NSLayoutConstraint.FormatOptions(), 177 | metrics: nil, 178 | views: ["placeholderImgView": placeholderImgView!]) 179 | let vCons = NSLayoutConstraint.constraints(withVisualFormat: "V:|[placeholderImgView]|", 180 | options: NSLayoutConstraint.FormatOptions(), 181 | metrics: nil, 182 | views: ["placeholderImgView": placeholderImgView!]) 183 | addConstraints(hCons) 184 | addConstraints(vCons) 185 | } 186 | 187 | private func addCollectionView() { 188 | flowLayout = ZCycleLayout() 189 | flowLayout.itemSize = itemSize != nil ? itemSize! : bounds.size 190 | flowLayout.minimumInteritemSpacing = 10000 191 | flowLayout.minimumLineSpacing = itemSpacing 192 | flowLayout.scrollDirection = scrollDirection 193 | 194 | collectionView = UICollectionView(frame: bounds, collectionViewLayout: flowLayout) 195 | collectionView.backgroundColor = UIColor.clear 196 | collectionView.bounces = false 197 | collectionView.showsVerticalScrollIndicator = false 198 | collectionView.showsHorizontalScrollIndicator = false 199 | collectionView.delegate = self 200 | collectionView.dataSource = self 201 | collectionView.scrollsToTop = false 202 | collectionView.decelerationRate = UIScrollView.DecelerationRate(rawValue: 0.0) 203 | registerCell() 204 | addSubview(collectionView) 205 | } 206 | 207 | /// add by LeeYZ 208 | private func registerCell() { 209 | if let customReuseIdentifier = delegate?.cycleViewCustomCellIdentifier?() { 210 | self.reuseIdentifier = customReuseIdentifier 211 | } 212 | if let customClass = delegate?.cycleViewCustomCellClass?() { 213 | collectionView.register(customClass, forCellWithReuseIdentifier: reuseIdentifier) 214 | } else if let customNib = delegate?.cycleViewCustomCellClassNib?() { 215 | collectionView.register(customNib, forCellWithReuseIdentifier: reuseIdentifier) 216 | } else { 217 | collectionView.register(ZCycleViewCell.self, forCellWithReuseIdentifier: reuseIdentifier) 218 | } 219 | } 220 | 221 | private func addPageControl() { 222 | pageControl = ZPageControl() 223 | addSubview(pageControl) 224 | } 225 | 226 | private func reload() { 227 | placeholderImgView.isHidden = true 228 | itemsCount = realDataCount <= 1 || !isInfinite ? realDataCount : realDataCount*200 229 | collectionView.reloadData() 230 | collectionView.setContentOffset(.zero, animated: false) 231 | dealFirstPage() 232 | if isAutomatic { startTimer() } 233 | if pageControl.isHidden { return } 234 | pageControl.numberOfPages = realDataCount 235 | pageControl.isHidden = realDataCount == 1 || (imagesGroup.count == 0 && imageUrlsGroup.count == 0) 236 | pageControl.currentPage = currentIndex() % realDataCount 237 | } 238 | } 239 | 240 | // MARK: - UICollectionViewDataSource / UICollectionViewDelegate 241 | extension ZCycleView: UICollectionViewDelegate, UICollectionViewDataSource { 242 | 243 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 244 | return itemsCount 245 | } 246 | 247 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 248 | let cycleCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) 249 | let index = indexPath.item % realDataCount 250 | 251 | if self.delegate?.cycleViewCustomCellSetup?(self, cell: cycleCell, for: index) != nil { 252 | return cycleCell 253 | } 254 | /// 使用默认的cell 255 | guard let cell = cycleCell as? ZCycleViewCell else { return cycleCell } 256 | cell.onlyText = imagesGroup.count == 0 && imageUrlsGroup.count == 0 257 | let title = index < titlesGroup.count ? titlesGroup[index] : nil 258 | cell.titleLabel.text = title 259 | if imagesGroup.count != 0 { 260 | let image = index < imagesGroup.count ? imagesGroup[index] : nil 261 | delegate?.cycleViewConfigureDefaultCellImage?(self, imageView: cell.imageView, image: image, index: index) 262 | if delegate?.cycleViewConfigureDefaultCellImage?(self, imageView: cell.imageView, image: image, index: index) == nil { 263 | NSException(name: .init(rawValue: "ZCycleViewError"), reason: "cycleViewConfigureDefaultCellImage方法未实现", userInfo: nil).raise() 264 | } 265 | } 266 | if imageUrlsGroup.count != 0 { 267 | let imageUrl = index < imageUrlsGroup.count ? imageUrlsGroup[index] : nil 268 | delegate?.cycleViewConfigureDefaultCellImageUrl?(self, imageView: cell.imageView, imageUrl: imageUrl, index: index) 269 | if delegate?.cycleViewConfigureDefaultCellImageUrl?(self, imageView: cell.imageView, imageUrl: imageUrl, index: index) == nil { 270 | NSException(name: .init(rawValue: "ZCycleViewError"), reason: "cycleViewConfigureDefaultCellImageUrl方法未实现", userInfo: nil).raise() 271 | } 272 | } 273 | delegate?.cycleViewConfigureDefaultCellText?(self, titleLabel: cell.titleLabel, index: index) 274 | 275 | return cell 276 | } 277 | 278 | public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 279 | let centerViewPoint = convert(collectionView.center, to: collectionView) 280 | guard let centerIndex = collectionView.indexPathForItem(at: centerViewPoint) else { return } 281 | if indexPath.item == centerIndex.item { 282 | let index = indexPath.item % realDataCount 283 | if let delegate = delegate { delegate.cycleViewDidSelectedIndex?(self, index: index) } 284 | } else { 285 | collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) 286 | } 287 | } 288 | } 289 | 290 | // MARK: - UIScrollViewDelegate 291 | extension ZCycleView { 292 | public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 293 | if isAutomatic { cancelTimer() } 294 | dealLastPage() 295 | dealFirstPage() 296 | } 297 | 298 | public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 299 | if isAutomatic { startTimer() } 300 | } 301 | 302 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 303 | scrollViewDidEndScrollingAnimation(scrollView) 304 | } 305 | 306 | public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 307 | guard let delegate = delegate else { return } 308 | let index = currentIndex() % realDataCount 309 | pageControl?.currentPage = index 310 | delegate.cycleViewDidScrollToIndex?(self, index: index) 311 | } 312 | 313 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 314 | pageControl?.currentPage = currentIndex() % realDataCount 315 | } 316 | } 317 | 318 | // MARK: - 处理第一帧和最后一帧 319 | extension ZCycleView { 320 | private func dealFirstPage() { 321 | if currentIndex() == 0 && itemsCount > 1 && isInfinite { 322 | let targetIndex = itemsCount / 2 323 | let scrollPosition: UICollectionView.ScrollPosition = scrollDirection == .horizontal ? .centeredHorizontally : .centeredVertically 324 | collectionView.scrollToItem(at: IndexPath(item: targetIndex, section: 0), at: scrollPosition, animated: false) 325 | } 326 | } 327 | private func dealLastPage() { 328 | if currentIndex() == itemsCount-1 && itemsCount > 1 && isInfinite { 329 | let targetIndex = itemsCount / 2 - 1 330 | let scrollPosition: UICollectionView.ScrollPosition = scrollDirection == .horizontal ? .centeredHorizontally : .centeredVertically 331 | collectionView.scrollToItem(at: IndexPath(item: targetIndex, section: 0), at: scrollPosition, animated: false) 332 | } 333 | } 334 | } 335 | 336 | // MARK: - 定时器操作 337 | extension ZCycleView { 338 | private func startTimer() { 339 | if !isAutomatic { return } 340 | if itemsCount <= 1 { return } 341 | cancelTimer() 342 | timer = Timer.init(timeInterval: Double(timeInterval), target: self, selector: #selector(timeRepeat), userInfo: nil, repeats: true) 343 | RunLoop.main.add(timer!, forMode: RunLoop.Mode.common) 344 | } 345 | 346 | private func cancelTimer() { 347 | if timer != nil { 348 | timer?.invalidate() 349 | timer = nil 350 | } 351 | } 352 | 353 | @objc private func timeRepeat() { 354 | let current = currentIndex() 355 | var targetIndex = current + 1 356 | if (current == itemsCount - 1) { 357 | if isInfinite == false {return} 358 | dealLastPage() 359 | targetIndex = itemsCount / 2 360 | } 361 | let scrollPosition: UICollectionView.ScrollPosition = scrollDirection == .horizontal ? .centeredHorizontally : .centeredVertically 362 | collectionView.scrollToItem(at: IndexPath(item: targetIndex, section: 0), at: scrollPosition, animated: true) 363 | } 364 | 365 | private func currentIndex() -> Int { 366 | let itemWH = scrollDirection == .horizontal ? flowLayout.itemSize.width+itemSpacing : flowLayout.itemSize.height+itemSpacing 367 | let offsetXY = scrollDirection == .horizontal ? collectionView.contentOffset.x : collectionView.contentOffset.y 368 | if itemWH == 0 { return 0 } 369 | let index = round(offsetXY / itemWH) 370 | return Int(index) 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /Example/TIMFlowView/ZCycleView/ZCycleViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZCycleViewCell.swift 3 | // ZCycleView 4 | // 5 | // Created by mengqingzheng on 2017/11/17. 6 | // Copyright © 2017年 MQZHot. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ZCycleViewCell: UICollectionViewCell { 12 | 13 | var imageView: UIImageView! 14 | var titleLabel: UILabel! 15 | var onlyText: Bool = false { 16 | didSet { 17 | if onlyText { 18 | titleLabel.frame = contentView.bounds 19 | } else { 20 | titleLabel.frame = CGRect(x: 0, y: contentView.bounds.size.height-25, width: contentView.bounds.size.width, height: 25) 21 | } 22 | } 23 | } 24 | 25 | override init(frame: CGRect) { 26 | super.init(frame: frame) 27 | addImageView() 28 | addTitleLabel() 29 | } 30 | 31 | private func addImageView() { 32 | imageView = UIImageView(frame: contentView.bounds) 33 | imageView.clipsToBounds = true 34 | contentView.addSubview(imageView) 35 | } 36 | 37 | private func addTitleLabel() { 38 | titleLabel = UILabel() 39 | titleLabel.textColor = .gray 40 | titleLabel.font = UIFont.systemFont(ofSize: 15) 41 | titleLabel.clipsToBounds = true 42 | contentView.addSubview(titleLabel) 43 | } 44 | 45 | required init?(coder aDecoder: NSCoder) { 46 | fatalError("init(coder:) has not been implemented") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Example/TIMFlowView/ZCycleView/ZPageControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZPageControl.swift 3 | // ZPageControl 4 | // 5 | // Created by mengqingzheng on 2017/11/18. 6 | // Copyright © 2017年 MQZHot. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum ZPageControlAlignment { 12 | case center 13 | case left 14 | case right 15 | } 16 | 17 | public class ZPageControl: UIControl { 18 | /// page数量 19 | public var numberOfPages: Int = 0 20 | /// 圆点间距 21 | public var spacing: CGFloat = 8 22 | /// 圆点大小 23 | public var dotSize: CGSize = CGSize(width: 8, height: 8) 24 | /// 当前圆点大小 25 | public var currentDotSize: CGSize? 26 | /// 圆点对齐方式 27 | public var alignment: ZPageControlAlignment = .center 28 | /// 圆点圆角 29 | public var dotRadius: CGFloat? 30 | /// 当前圆点圆角 31 | public var currentDotRadius: CGFloat? 32 | /// 当前位置 33 | public var currentPage: Int = 0 { didSet { updateFrame() } } 34 | /// 当前颜色 35 | public var currentPageIndicatorTintColor: UIColor = UIColor.white 36 | /// 圆点颜色 37 | public var pageIndicatorTintColor: UIColor = UIColor.gray 38 | /// 圆点图片 39 | public var dotImage: UIImage? 40 | /// 当前圆点图片 41 | public var currentDotImage: UIImage? 42 | 43 | private var items = [UIImageView]() 44 | 45 | override public func layoutSubviews() { 46 | super.layoutSubviews() 47 | setupItems() 48 | } 49 | 50 | private func updateFrame() { 51 | for (index, item) in items.enumerated() { 52 | let frame = getFrame(index: index) 53 | item.frame = frame 54 | } 55 | setImageOrTintColor() 56 | } 57 | 58 | private func setupItems() { 59 | items.forEach { $0.removeFromSuperview() } 60 | items.removeAll() 61 | for i in 0.. CGRect { 90 | let itemW = dotSize.width + spacing 91 | let currentSize = currentDotSize == nil ? dotSize : currentDotSize! 92 | let currentItemW = currentSize.width + spacing 93 | let totalWidth = itemW*CGFloat(numberOfPages-1)+currentItemW+spacing 94 | var orignX: CGFloat = 0 95 | switch alignment { 96 | case .center: 97 | orignX = (frame.size.width-totalWidth)/2+spacing 98 | case .left: 99 | orignX = spacing 100 | case .right: 101 | orignX = frame.size.width-totalWidth+spacing 102 | } 103 | var x: CGFloat = 0 104 | if index <= currentPage { 105 | x = orignX + CGFloat(index)*itemW 106 | } else { 107 | x = orignX + CGFloat(index-1)*itemW + currentItemW 108 | } 109 | let width = index == currentPage ? currentSize.width : dotSize.width 110 | let height = index == currentPage ? currentSize.height : dotSize.height 111 | let y = (frame.size.height-height)/2 112 | return CGRect(x: x, y: y, width: width, height: height) 113 | } 114 | 115 | override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 116 | let hitView = super.hitTest(point, with: event) 117 | if hitView == self { return nil } 118 | return hitView 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Example/TIMFlowView/images.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | url 7 | https://i.ibb.co/6mVw478/GOT.png 8 | name 9 | GOT 10 | width 11 | 1382 12 | height 13 | 2048 14 | 15 | 16 | url 17 | https://i.ibb.co/K2vqKWs/H.png 18 | name 19 | H 20 | width 21 | 528 22 | height 23 | 755 24 | 25 | 26 | url 27 | https://i.ibb.co/s109nWV/expanse.png 28 | name 29 | Expanse 30 | width 31 | 509 32 | height 33 | 755 34 | 35 | 36 | url 37 | https://i.ibb.co/wBX2kXf/hunt.png 38 | name 39 | Hunt 40 | width 41 | 660 42 | height 43 | 966 44 | 45 | 46 | url 47 | https://i.ibb.co/Kjq5mhn/iron-man.png 48 | name 49 | Iron Man 50 | width 51 | 900 52 | height 53 | 1350 54 | 55 | 56 | url 57 | https://i.ibb.co/KVkQpJQ/moon.png 58 | name 59 | Moon 60 | width 61 | 700 62 | height 63 | 874 64 | 65 | 66 | url 67 | https://i.ibb.co/6NMp6Dt/shaun-of-the-dead.png 68 | name 69 | Shaun of the dead 70 | width 71 | 720 72 | height 73 | 1018 74 | 75 | 76 | url 77 | https://i.ibb.co/gm4xBnN/the-big-blue.png 78 | name 79 | The big blue 80 | width 81 | 658 82 | height 83 | 1000 84 | 85 | 86 | url 87 | https://i.ibb.co/yqpZBtZ/the-great-wall.png 88 | name 89 | The great wall 90 | width 91 | 1000 92 | height 93 | 1583 94 | 95 | 96 | url 97 | https://i.ibb.co/wKmRKdf/wind.png 98 | name 99 | Wind River 100 | width 101 | 509 102 | height 103 | 755 104 | 105 | 106 | url 107 | https://i.ibb.co/2SSCQKc/wonderstruck.png 108 | name 109 | Wonderstruck 110 | width 111 | 720 112 | height 113 | 1067 114 | 115 | 116 | url 117 | https://i.ibb.co/9HvR4Vr/image.png 118 | name 119 | 一出好戏 120 | width 121 | 1000 122 | height 123 | 1400 124 | 125 | 126 | url 127 | https://i.ibb.co/rF7c0BP/image.png 128 | name 129 | 三城记 130 | width 131 | 900 132 | height 133 | 1260 134 | 135 | 136 | url 137 | https://i.ibb.co/hXCv4qy/image.png 138 | name 139 | 东游 140 | width 141 | 900 142 | height 143 | 1600 144 | 145 | 146 | url 147 | https://i.ibb.co/nzKctNK/image.png 148 | name 149 | 中国女排 150 | width 151 | 540 152 | height 153 | 754 154 | 155 | 156 | url 157 | https://i.ibb.co/TTgqDqP/image.png 158 | name 159 | 亲密 160 | width 161 | 701 162 | height 163 | 1024 164 | 165 | 166 | url 167 | https://i.ibb.co/vQ4hRVS/image.png 168 | name 169 | 人海同游 170 | width 171 | 466 172 | height 173 | 763 174 | 175 | 176 | url 177 | https://i.ibb.co/jR7RZ3Z/image.png 178 | name 179 | 你好,疯子 180 | width 181 | 1000 182 | height 183 | 1476 184 | 185 | 186 | url 187 | https://i.ibb.co/8jNVcDd/image.png 188 | name 189 | Your name 190 | width 191 | 1000 192 | height 193 | 1425 194 | 195 | 196 | url 197 | https://i.ibb.co/McMVm8K/image.png 198 | name 199 | 冰雪奇缘2 200 | width 201 | 320 202 | height 203 | 3978 204 | 205 | 206 | url 207 | https://i.ibb.co/z6dfPGV/image.png 208 | name 209 | 决胜时刻 210 | width 211 | 1000 212 | height 213 | 1400 214 | 215 | 216 | url 217 | https://i.ibb.co/gMVS6B2/image.png 218 | name 219 | 剑士柳白猿 220 | width 221 | 550 222 | height 223 | 770 224 | 225 | 226 | url 227 | https://i.ibb.co/ZTp7nwr/image.png 228 | name 229 | 功夫瑜伽 230 | width 231 | 320 232 | height 233 | 3568 234 | 235 | 236 | url 237 | https://i.ibb.co/b665MYj/image.png 238 | name 239 | 助力回血 240 | width 241 | 1000 242 | height 243 | 1775 244 | 245 | 246 | url 247 | https://i.ibb.co/1qhBCty/image.png 248 | name 249 | 勇士之门 250 | width 251 | 1142 252 | height 253 | 1599 254 | 255 | 256 | url 257 | https://i.ibb.co/dP9Fz7F/image.png 258 | name 259 | 吃瓜群众潮这看 260 | width 261 | 500 262 | height 263 | 891 264 | 265 | 266 | url 267 | https://i.ibb.co/Sf7Zy5H/image.png 268 | name 269 | 后会无期 270 | width 271 | 900 272 | height 273 | 1259 274 | 275 | 276 | url 277 | https://i.ibb.co/30zQKtY/image.png 278 | name 279 | 告别 280 | width 281 | 1080 282 | height 283 | 1484 284 | 285 | 286 | url 287 | https://i.ibb.co/Jn6pkjb/image.png 288 | name 289 | 哪吒之魔童降世 290 | width 291 | 1000 292 | height 293 | 1400 294 | 295 | 296 | url 297 | https://i.ibb.co/Y2Yzq5h/image.png 298 | name 299 | 四个春天 300 | width 301 | 440 302 | height 303 | 600 304 | 305 | 306 | url 307 | https://i.ibb.co/GJdMRX9/image.png 308 | name 309 | 囧妈 310 | width 311 | 900 312 | height 313 | 1359 314 | 315 | 316 | url 317 | https://i.ibb.co/bWX8nSS/image.png 318 | name 319 | 地久天长 320 | width 321 | 900 322 | height 323 | 1336 324 | 325 | 326 | url 327 | https://i.ibb.co/25tHW97/image.png 328 | name 329 | 大圣归来 330 | width 331 | 800 332 | height 333 | 1420 334 | 335 | 336 | url 337 | https://i.ibb.co/WG6gYdG/image.png 338 | name 339 | 天涯明月刀 340 | width 341 | 320 342 | height 343 | 1478 344 | 345 | 346 | url 347 | https://i.ibb.co/XJmM8mS/image.png 348 | name 349 | 天龙新阳派 350 | width 351 | 800 352 | height 353 | 1425 354 | 355 | 356 | url 357 | https://i.ibb.co/DCHwzkm/image.png 358 | name 359 | 如懿传 360 | width 361 | 1000 362 | height 363 | 1397 364 | 365 | 366 | url 367 | https://i.ibb.co/RPjDpXD/image.png 368 | name 369 | 媚者无疆 370 | width 371 | 320 372 | height 373 | 1637 374 | 375 | 376 | url 377 | https://i.ibb.co/9wFFr5Y/image.png 378 | name 379 | 宝行星 380 | width 381 | 1000 382 | height 383 | 1482 384 | 385 | 386 | url 387 | https://i.ibb.co/bXpyX0K/image.png 388 | name 389 | 宵夜 390 | width 391 | 640 392 | height 393 | 853 394 | 395 | 396 | url 397 | https://i.ibb.co/7ychznS/image.png 398 | name 399 | 寻龙诀 400 | width 401 | 440 402 | height 403 | 782 404 | 405 | 406 | url 407 | https://i.ibb.co/QJZMj4j/image.png 408 | name 409 | 少年巴比伦 410 | width 411 | 1000 412 | height 413 | 1438 414 | 415 | 416 | url 417 | https://i.ibb.co/pJdWCL5/image.png 418 | name 419 | 山海秘闻录 420 | width 421 | 320 422 | height 423 | 3432 424 | 425 | 426 | url 427 | https://i.ibb.co/TYtGWjy/image.png 428 | name 429 | 带我去远方 430 | width 431 | 933 432 | height 433 | 1279 434 | 435 | 436 | url 437 | https://i.ibb.co/6wYvZ8K/image.png 438 | name 439 | 440 | width 441 | 1000 442 | height 443 | 1474 444 | 445 | 446 | url 447 | https://i.ibb.co/Jj19Tvz/2.png 448 | name 449 | 450 | width 451 | 1000 452 | height 453 | 1474 454 | 455 | 456 | url 457 | https://i.ibb.co/bLJDxn6/image.png 458 | name 459 | 忘忧镇 460 | width 461 | 720 462 | height 463 | 1291 464 | 465 | 466 | url 467 | https://i.ibb.co/pdHx2KJ/image.png 468 | name 469 | 念念 470 | width 471 | 900 472 | height 473 | 1259 474 | 475 | 476 | url 477 | https://i.ibb.co/bv03Nv9/image.png 478 | name 479 | 悟空传 480 | width 481 | 679 482 | height 483 | 1000 484 | 485 | 486 | url 487 | https://i.ibb.co/L9n5y7p/image.png 488 | name 489 | 惊蛰 490 | width 491 | 540 492 | height 493 | 805 494 | 495 | 496 | url 497 | https://i.ibb.co/7jj5FK5/image.png 498 | name 499 | 我在故宫修文物 500 | width 501 | 1000 502 | height 503 | 1500 504 | 505 | 506 | url 507 | https://i.ibb.co/R6XLmGD/image.png 508 | name 509 | 战神无敌 510 | width 511 | 320 512 | height 513 | 4460 514 | 515 | 516 | url 517 | https://i.ibb.co/K7hRmTB/image.png 518 | name 519 | 新年好 520 | width 521 | 1080 522 | height 523 | 1543 524 | 525 | 526 | url 527 | https://i.ibb.co/547FnSr/2.png 528 | name 529 | 新年好 530 | width 531 | 1080 532 | height 533 | 1543 534 | 535 | 536 | url 537 | https://i.ibb.co/VBSzVtX/image.png 538 | name 539 | 无双 540 | width 541 | 714 542 | height 543 | 1000 544 | 545 | 546 | url 547 | https://i.ibb.co/3vwvJRY/image.png 548 | name 549 | 无问西东 550 | width 551 | 595 552 | height 553 | 842 554 | 555 | 556 | url 557 | https://i.ibb.co/M6BGDWJ/image.png 558 | name 559 | 明天会更好 560 | width 561 | 900 562 | height 563 | 1350 564 | 565 | 566 | url 567 | https://i.ibb.co/4Vyp0Wy/image.png 568 | name 569 | 末代帝国 570 | width 571 | 1000 572 | height 573 | 1512 574 | 575 | 576 | url 577 | https://i.ibb.co/9nSKnJn/image.png 578 | name 579 | 机器之血 580 | width 581 | 320 582 | height 583 | 1610 584 | 585 | 586 | url 587 | https://i.ibb.co/LJbZLV5/image.png 588 | name 589 | 极盗车神 590 | width 591 | 720 592 | height 593 | 1018 594 | 595 | 596 | url 597 | https://i.ibb.co/p4M6DwP/image.png 598 | name 599 | 梦境 600 | width 601 | 800 602 | height 603 | 1200 604 | 605 | 606 | url 607 | https://i.ibb.co/s20rydC/image.png 608 | name 609 | 武动乾坤 610 | width 611 | 320 612 | height 613 | 3395 614 | 615 | 616 | url 617 | https://i.ibb.co/r7CKhrN/image.png 618 | name 619 | 死侍 620 | width 621 | 692 622 | height 623 | 1024 624 | 625 | 626 | url 627 | https://i.ibb.co/g67JbXp/image.png 628 | name 629 | 毒战 630 | width 631 | 1024 632 | height 633 | 606 634 | 635 | 636 | url 637 | https://i.ibb.co/gTc5GQv/image.png 638 | name 639 | 毒液 640 | width 641 | 1000 642 | height 643 | 1400 644 | 645 | 646 | url 647 | https://i.ibb.co/fDDPwKw/2.png 648 | name 649 | 毒液 650 | width 651 | 1000 652 | height 653 | 1400 654 | 655 | 656 | url 657 | https://i.ibb.co/GsvZgdv/image.png 658 | name 659 | 水母 660 | width 661 | 960 662 | height 663 | 1400 664 | 665 | 666 | url 667 | https://i.ibb.co/bB3J686/image.png 668 | name 669 | 江湖儿女 670 | width 671 | 573 672 | height 673 | 800 674 | 675 | 676 | url 677 | https://i.ibb.co/6Y4MYsn/image.png 678 | name 679 | 江湖儿女 680 | width 681 | 556 682 | height 683 | 800 684 | 685 | 686 | url 687 | https://i.ibb.co/Jk0z5Wg/image.png 688 | name 689 | 河妖 690 | width 691 | 900 692 | height 693 | 1747 694 | 695 | 696 | url 697 | https://i.ibb.co/F4t2YZF/2.png 698 | name 699 | 河妖 700 | width 701 | 900 702 | height 703 | 1747 704 | 705 | 706 | url 707 | https://i.ibb.co/L8yfwpj/image.png 708 | name 709 | 泡沫之夏 710 | width 711 | 690 712 | height 713 | 1047 714 | 715 | 716 | url 717 | https://i.ibb.co/xfBnNHN/image.png 718 | name 719 | 海上牧云记 720 | width 721 | 320 722 | height 723 | 2337 724 | 725 | 726 | url 727 | https://i.ibb.co/3F663z4/image.png 728 | name 729 | 温暖的弦 730 | width 731 | 534 732 | height 733 | 800 734 | 735 | 736 | url 737 | https://i.ibb.co/SJcGDxT/image.png 738 | name 739 | 爱丽丝梦游仙境 740 | width 741 | 1000 742 | height 743 | 1478 744 | 745 | 746 | url 747 | https://i.ibb.co/Qp03XMx/image.png 748 | name 749 | 神奇女侠 750 | width 751 | 1000 752 | height 753 | 1481 754 | 755 | 756 | url 757 | https://i.ibb.co/NCthJyF/image.png 758 | name 759 | 绣春刀 760 | width 761 | 900 762 | height 763 | 1257 764 | 765 | 766 | url 767 | https://i.ibb.co/DGwsVng/image.png 768 | name 769 | 罗生门 770 | width 771 | 705 772 | height 773 | 1000 774 | 775 | 776 | url 777 | https://i.ibb.co/QvnHz0Z/image.png 778 | name 779 | 美女与野兽 780 | width 781 | 320 782 | height 783 | 1985 784 | 785 | 786 | url 787 | https://i.ibb.co/y66qyfT/image.png 788 | name 789 | 老炮 790 | width 791 | 662 792 | height 793 | 1000 794 | 795 | 796 | url 797 | https://i.ibb.co/qdRFZtv/image.png 798 | name 799 | 芳华 800 | width 801 | 1000 802 | height 803 | 1400 804 | 805 | 806 | url 807 | https://i.ibb.co/xSMFB7s/image.png 808 | name 809 | Spider Man 810 | width 811 | 600 812 | height 813 | 890 814 | 815 | 816 | url 817 | https://i.ibb.co/CwyfGXZ/image.png 818 | name 819 | Bat Man 820 | width 821 | 533 822 | height 823 | 822 824 | 825 | 826 | url 827 | https://i.ibb.co/R7nt3xM/image.png 828 | name 829 | 触不可及 830 | width 831 | 1024 832 | height 833 | 1434 834 | 835 | 836 | url 837 | https://i.ibb.co/q0PKr8h/image.png 838 | name 839 | 诛仙 840 | width 841 | 1080 842 | height 843 | 1619 844 | 845 | 846 | url 847 | https://i.ibb.co/JvH7ry2/image.png 848 | name 849 | 这一座城 850 | width 851 | 1156 852 | height 853 | 2041 854 | 855 | 856 | url 857 | https://i.ibb.co/Ntwr7nz/image.png 858 | name 859 | 迷失地铁 860 | width 861 | 540 862 | height 863 | 959 864 | 865 | 866 | url 867 | https://i.ibb.co/F406mbB/image.png 868 | name 869 | 道士下山 870 | width 871 | 700 872 | height 873 | 1031 874 | 875 | 876 | url 877 | https://i.ibb.co/GWQ38RG/image.png 878 | name 879 | 那日上午 880 | width 881 | 700 882 | height 883 | 990 884 | 885 | 886 | url 887 | https://i.ibb.co/LR89kRv/image.png 888 | name 889 | 邪不压正 890 | width 891 | 960 892 | height 893 | 1344 894 | 895 | 896 | url 897 | https://i.ibb.co/tcd7PKn/image.png 898 | name 899 | 都挺好 900 | width 901 | 633 902 | height 903 | 950 904 | 905 | 906 | url 907 | https://i.ibb.co/g4RHYxD/image.png 908 | name 909 | 铁道飞虎 910 | width 911 | 810 912 | height 913 | 1200 914 | 915 | 916 | url 917 | https://i.ibb.co/HCJLCJt/image.png 918 | name 919 | 锦鲤 920 | width 921 | 900 922 | height 923 | 1288 924 | 925 | 926 | url 927 | https://i.ibb.co/6YLhX9d/image.png 928 | name 929 | 长城 930 | width 931 | 1000 932 | height 933 | 562 934 | 935 | 936 | url 937 | https://i.ibb.co/hMxt14g/image.png 938 | name 939 | 阿拉丁神灯 940 | width 941 | 800 942 | height 943 | 1132 944 | 945 | 946 | url 947 | https://i.ibb.co/Bj6Vpbm/2.png 948 | name 949 | 阿拉丁神灯 950 | width 951 | 1080 952 | height 953 | 1542 954 | 955 | 956 | url 957 | https://i.ibb.co/qJhsPQ2/image.png 958 | name 959 | 霍比特人 960 | width 961 | 403 962 | height 963 | 600 964 | 965 | 966 | url 967 | https://i.ibb.co/10NC6G1/image.png 968 | name 969 | 香蜜沉沉烬如霜 970 | width 971 | 320 972 | height 973 | 2880 974 | 975 | 976 | url 977 | https://i.ibb.co/ZHGkwQ3/image.png 978 | name 979 | 鹤唳华亭 980 | width 981 | 900 982 | height 983 | 1260 984 | 985 | 986 | url 987 | https://i.ibb.co/HtfB0r1/2.png 988 | name 989 | 鹤唳华亭 990 | width 991 | 690 992 | height 993 | 966 994 | 995 | 996 | url 997 | https://i.ibb.co/yP0yD4y/image.png 998 | name 999 | 黄金时代 1000 | width 1001 | 580 1002 | height 1003 | 854 1004 | 1005 | 1006 | 1007 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import TIMFlowView 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ExampleImages/全部功能开启.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/ExampleImages/全部功能开启.gif -------------------------------------------------------------------------------- /ExampleImages/带有header的瀑布流.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/ExampleImages/带有header的瀑布流.gif -------------------------------------------------------------------------------- /ExampleImages/普通瀑布流.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BearLatte/TIMFlowView/60c0ff58369bfd5517537062f2981dc8873c14b3/ExampleImages/普通瀑布流.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | Copyright (c) 2020 Tim 3 | ======= 4 | MIT License 5 | 6 | Copyright (c) 2020 Latte 7 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | <<<<<<< HEAD 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | ======= 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | <<<<<<< HEAD 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | ======= 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | >>>>>>> 7a5f154ca2fcdba77cda0f8e35b39705dce286ee 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TIMFlowView 2 | [![CI Status](https://img.shields.io/travis/Tim/TIMFlowView.svg?style=flat)](https://travis-ci.org/Tim/TIMFlowView) 3 | [![Version](https://img.shields.io/cocoapods/v/TIMFlowView.svg?style=flat)](https://cocoapods.org/pods/TIMFlowView) 4 | [![License](https://img.shields.io/cocoapods/l/TIMFlowView.svg?style=flat)](https://cocoapods.org/pods/TIMFlowView) 5 | [![Platform](https://img.shields.io/cocoapods/p/TIMFlowView.svg?style=flat)](https://cocoapods.org/pods/TIMFlowView) 6 | 7 | ## 写在前面 8 | 9 | 写这个视图的初衷在于公司的产品对于`UITableView`悬停的 `SectionHeader`有近乎执着的爱,并且给出的设计瀑布流居多,多次沟通无果,决定自己动手解决,本项目的思路来源于`UITableView`,并且采用了模仿`UITableView`的数据源和代理协议实现,只要你会用`UITableView`那就一定会使用本项目。以下为本项目支持的操作: 10 | 11 | - [x] 分区显示 12 | - [x] 分区头和分区尾部视图 13 | - [x] 分区头部滑动悬停 14 | - [x] 头视图和尾视图 15 | - [x] 九宫格视图(参考 UICollectionView) 16 | - [x] 瀑布流视图(参考网易云音乐广场功能) 17 | 18 | 19 | ## Example 20 | 21 | 具体的代码操作烦请阅读 demo 中的代码或者项目源代码解决,运行 demo 可以得到如下的结果: 22 | 23 | ![普通瀑布流视图](/Users/tim/Development/TIMFlowView/ExampleImages/普通瀑布流.gif)![带有header的瀑布流](/Users/tim/Development/TIMFlowView/ExampleImages/带有header的瀑布流.gif)![全部功能开启](/Users/tim/Development/TIMFlowView/ExampleImages/全部功能开启.gif) 24 | 25 | ## Requirements 26 | 27 | - Xcode 10.0 ~> 28 | - Swift 4.0 ~> 29 | 30 | ## Installation 31 | 32 | 本项目推荐使用 [Cocoapods](https://cocoapods.org) 引入,引入方法: 33 | 34 | ```ruby 35 | # 如果是 Cocoapods 1.8.0 版本或以上的用户请添加 CocoaPods 源在你的 Podfile 文件最顶部添加 36 | source 'https://github.com/CocoaPods/Specs.git' # 添加这一句,不然会报 cdn 错误 37 | 38 | target 'xxx' do 39 | pod 'TIMFlowView' 40 | end 41 | ``` 42 | 43 | 之后打开命令行工具执行 `pod install` 或者 `pod update`。 44 | 45 | 除此之外还可以通过源代码的方式进行导入: 46 | 47 | - 将项目通过 zip 的形式下载下来并解包; 48 | - 将 `TIMFlowView` 文件夹拖入你项目中的某个路径下(不要忘记勾选 copy item if needed 复选框)。 49 | 50 | ## Useg 51 | 52 | - 具体使用方式见 Demo 或者项目源代码中的注释。 53 | 54 | ## Author 55 | 56 | 有任何问题欢迎大家提 issue,或者联系我本人,以下是邮箱和微信: 57 | 58 | - Email: guoyong19890907@gmail.com 59 | - Wechat: 2625991041 60 | 61 | ## License 62 | 63 | TIMFlowView is available under the MIT license. See the LICENSE file for more info. 64 | -------------------------------------------------------------------------------- /TIMFlowView.podspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pod::Spec.new do |s| 4 | s.name = 'TIMFlowView' 5 | s.version = '1.0.0' 6 | s.summary = 'Swift 模仿 UITableView 写的一个瀑布流视图' 7 | 8 | s.description = <<-DESC 9 | 完全模仿 UITableView 实现的瀑布流视图,支持分区显示、添加分区头尾视图,添加视图头尾视图,九宫格视图、瀑布流视图,分区头滑动悬停等操作。 10 | DESC 11 | 12 | s.homepage = 'https://github.com/BearLatte/TIMFlowView' 13 | s.license = { :type => 'MIT', :file => 'LICENSE' } 14 | s.author = { 'Tim' => 'guoyong19890907@gmail.com'} 15 | s.source = { :git => 'https://github.com/BearLatte/TIMFlowView.git', :tag => s.version.to_s } 16 | s.ios.deployment_target = '9.0' 17 | s.swift_version = '5.0' 18 | s.source_files = 'TIMFlowView/*.Swift' 19 | end 20 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowHeaderFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowHeaderFooterView.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim on 2020/2/7. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class TIMFlowHeaderFooterView: UIView { 12 | 13 | /// 重用标识符 14 | internal var reuseIdentifier: String 15 | 16 | public convenience init(with reuseIdentifier: String) { 17 | self.init() 18 | self.reuseIdentifier = reuseIdentifier 19 | } 20 | 21 | public override init(frame: CGRect) { 22 | reuseIdentifier = "" 23 | super.init(frame: frame) 24 | } 25 | 26 | required public init?(coder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowView.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/16. 6 | // Copyright © 2020 Tim. All rights reserved. 7 | // API 主视图 8 | 9 | import UIKit 10 | 11 | open class TIMFlowView: UIScrollView { 12 | // MARK: - Public Property 13 | /// 瀑布流数据源协议 14 | open weak var flowDataSource: TIMFlowViewDataSource? = nil 15 | 16 | /// 瀑布流代理协议 17 | open weak var flowDelegate: TIMFlowViewDelegate? 18 | 19 | /// 是否让分区头视图悬停 20 | public var floatingHeaderEnable: Bool = false 21 | 22 | public override init(frame: CGRect) { 23 | super.init(frame: frame) 24 | if #available(iOS 11.0, *) { 25 | contentInsetAdjustmentBehavior = .never 26 | } 27 | delegate = self 28 | } 29 | 30 | required public init?(coder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | 34 | /// 头视图 35 | /// 默认与 flowView 同宽,高度根据传进来高度自动计算 36 | public var flowHeaderView: UIView? { 37 | willSet { 38 | guard let header = newValue else { 39 | return 40 | } 41 | header.layoutIfNeeded() 42 | var headerFrame = header.frame 43 | headerFrame = CGRect(x: 0, y: 0, width: bounds.width, height: headerFrame.height) 44 | header.frame = headerFrame 45 | insertSubview(header, at: 0) 46 | floatingBeginY = header.frame.maxY 47 | } 48 | } 49 | 50 | /// 尾部视图 51 | /// 默认与 flowView 同宽,高度根据传进来高度自动计算 52 | public var flowFooterView: UIView? 53 | 54 | 55 | // MARK: - Private Property 56 | 57 | /// 所有 cell 的位置信息, key: section, value: section 对应的 cell 的frame 58 | private lazy var itemFrames: [Int: [CGRect]] = [:] 59 | 60 | /// 当前正在显示的所有 cell 61 | private lazy var displayingItems: [TIMIndexPath: TIMFlowViewItem] = [:] 62 | 63 | /// cell 缓存池,存放已经滑出屏幕 frame 的 cell, key: section 索引, value: section对应的缓存池 64 | private lazy var reuseableItems: [Int: Set] = [:] 65 | 66 | /// 所有分区头视图的位置信息 67 | private lazy var sectionHeaderFrames: [CGRect?] = [] 68 | 69 | /// 当前显示的分区头视图 70 | private lazy var displayingSectionHeader: [Int: TIMFlowHeaderFooterView] = [:] 71 | 72 | /// 分区头缓存池, 存放已经划出屏幕 frame 的 分区头视图 73 | private lazy var reuseableSectionHeader: Set = [] 74 | 75 | /// 所有分区尾视图的位置信息 76 | private lazy var sectionFooterFrames: [CGRect?] = [] 77 | 78 | /// 当前显示的分区尾视图 79 | private lazy var displayingSectionFooter: [Int: TIMFlowHeaderFooterView] = [:] 80 | 81 | /// 分区尾缓存池,存放已经划出屏幕 frame 的 分区尾视图 82 | private lazy var reuseableSectionFooter: Set = [] 83 | 84 | /// 需要悬停的header 85 | private var needFloatingHeader: [Int: TIMFlowHeaderFooterView] = [:] 86 | 87 | /// 记录当前偏移量 88 | private var offsetY: CGFloat = 0 89 | 90 | /// 记录初始 Y 值,也就是说在哪个位置开始悬停 91 | private var floatingBeginY: CGFloat = 0 92 | 93 | /// 记录当前悬停的视图索引 94 | private var currentFloatingIndex = 0 95 | 96 | /// 记录当前悬停的视图 97 | private var currentFloatingHeaderView: TIMFlowHeaderFooterView? 98 | 99 | /// 记录临界点 100 | private var criticalY: CGFloat = 0 101 | } 102 | 103 | // MARK: - Life Cycle 104 | extension TIMFlowView { 105 | public override func willMove(toSuperview newSuperview: UIView?) { 106 | reloadData() 107 | } 108 | 109 | override public func layoutSubviews() { 110 | super.layoutSubviews() 111 | // 获取 cell 的数量,根据数量遍历后拿到对应的 cell 112 | // 给 cell 设置 frame 添加到滚动视图上 113 | // 如果数据量非常大的情况下,此处会造成滑动时 CPU 占有率很高的性能问题 114 | // 待研究更理想的算法 115 | 116 | // 获取分区数 117 | let numberOfSection: Int! = flowDataSource?.numberOfSections(in: self) 118 | 119 | for sectionIndex in 0 ..< numberOfSection { 120 | // 布局分区头视图 121 | if floatingHeaderEnable { 122 | guard let headerView = needFloatingHeader[sectionIndex] else { 123 | return 124 | } 125 | 126 | if isInScreen(aFrame: headerView.frame) { 127 | addSubview(headerView) 128 | } 129 | 130 | } else { 131 | if let sectionHeaderFrame = sectionHeaderFrames[sectionIndex] { 132 | var sectionHeader = displayingSectionHeader[sectionIndex] 133 | if isInScreen(aFrame: sectionHeaderFrame) { 134 | if sectionHeader == nil { 135 | sectionHeader = flowDelegate?.viewForSectionHeader(in: self, at: sectionIndex) 136 | sectionHeader?.frame = sectionHeaderFrame 137 | addSubview(sectionHeader!) 138 | displayingSectionHeader[sectionIndex] = sectionHeader 139 | } 140 | } else { 141 | if sectionHeader != nil { 142 | sectionHeader?.removeFromSuperview() 143 | displayingSectionHeader.removeValue(forKey: sectionIndex) 144 | reuseableSectionHeader.insert(sectionHeader!) 145 | } 146 | } 147 | } 148 | } 149 | 150 | // 布局每个 item 151 | guard let currentSectionItemFrames = itemFrames[sectionIndex] else { 152 | return 153 | } 154 | 155 | // 获取 item 数量 156 | let numberOfItems = currentSectionItemFrames.count 157 | for i in 0 ..< numberOfItems { 158 | let itemFrame = currentSectionItemFrames[i] 159 | 160 | // 从字典中取出item 161 | let indexPath = TIMIndexPath(sectionIndex, i) 162 | var item = displayingItems[indexPath] 163 | 164 | if isInScreen(aFrame: itemFrame) { 165 | if item == nil { 166 | item = flowDataSource?.flowViewItem(in: self, at: indexPath) 167 | item?.frame = itemFrame 168 | addSubview(item!) 169 | displayingItems[indexPath] = item 170 | } 171 | } else { 172 | if item != nil { 173 | item?.removeFromSuperview() 174 | displayingItems.removeValue(forKey: indexPath) 175 | reuseableItems[sectionIndex]?.insert(item!) 176 | } 177 | } 178 | } 179 | 180 | // 布局分区尾部视图 181 | if let sectionFooterFrame = sectionFooterFrames[sectionIndex] { 182 | var sectionFooter = displayingSectionFooter[sectionIndex] 183 | if isInScreen(aFrame: sectionFooterFrame) { 184 | if sectionFooter == nil { 185 | sectionFooter = flowDelegate?.viewForSectionFooter(in: self, at: sectionIndex) 186 | sectionFooter?.frame = sectionFooterFrame 187 | addSubview(sectionFooter!) 188 | displayingSectionFooter[sectionIndex] = sectionFooter 189 | } 190 | } else { 191 | if sectionFooter != nil { 192 | sectionFooter?.removeFromSuperview() 193 | displayingSectionFooter.removeValue(forKey: sectionIndex) 194 | reuseableSectionFooter.insert(sectionFooter!) 195 | } 196 | } 197 | } 198 | } 199 | } 200 | } 201 | 202 | // MARK: - Public API 203 | public extension TIMFlowView { 204 | /// 根据 self 的宽度计算 cell 的宽度 205 | func itemWidhth(in section: Int) -> CGFloat { 206 | guard let numberOfSections = flowDataSource?.numberOfSections(in: self) else { 207 | return 0 208 | } 209 | 210 | // 越界异常抛出 211 | if section >= numberOfSections { 212 | fatalError("索引越界,当前操作的分区不能大于总分区数量") 213 | } 214 | 215 | // 获取列数 216 | guard let numberOfColumns = flowDataSource?.numberOfColumns(in: self, at: section), 217 | let leftMargin = flowDelegate?.margin(in: self, at: section, for: .left), 218 | let rightMrgin = flowDelegate?.margin(in: self, at: section, for: .right), 219 | let columnMargin = flowDelegate?.margin(in: self, at: section, for: .column) else { 220 | return 0 221 | } 222 | 223 | return (frame.width - leftMargin - rightMrgin - CGFloat(numberOfColumns - 1) * columnMargin) / CGFloat(numberOfColumns) 224 | } 225 | 226 | /// 刷新当前数据 227 | func reloadData() { 228 | // 删除所有的分区头视图 229 | displayingSectionHeader.forEach { (_, value) in 230 | value.removeFromSuperview() 231 | } 232 | 233 | displayingSectionHeader.removeAll() 234 | sectionHeaderFrames.removeAll() 235 | reuseableSectionHeader.removeAll() 236 | 237 | // 将当前显示的所有 cell 从父视图移除 238 | displayingItems.forEach { (_, value) in 239 | value.removeFromSuperview() 240 | } 241 | 242 | // 删除所有正在显示的cell 243 | displayingItems.removeAll() 244 | 245 | 246 | // 删除所有 frame 的缓存 247 | itemFrames.removeAll() 248 | 249 | // 清空缓存池 250 | reuseableItems.removeAll() 251 | 252 | // 删除分区尾视图 253 | displayingSectionFooter.forEach { (_, value) in 254 | value.removeFromSuperview() 255 | } 256 | displayingSectionFooter.removeAll() 257 | sectionFooterFrames.removeAll() 258 | reuseableSectionFooter.removeAll() 259 | 260 | // 删除保留的悬停数据 261 | needFloatingHeader.forEach { (_, value) in 262 | value.removeFromSuperview() 263 | } 264 | needFloatingHeader.removeAll() 265 | 266 | // 获取分区数 267 | guard let numberOfSections: Int = flowDataSource?.numberOfSections(in: self) else { 268 | return 269 | } 270 | 271 | for _ in 0 ..< numberOfSections { 272 | sectionHeaderFrames.append(nil) 273 | sectionFooterFrames.append(nil) 274 | } 275 | 276 | // 保留一个记录最大 Y 值的对象 277 | var maxY = flowHeaderView == nil ? 0.0 : flowHeaderView?.frame.height ?? 0.0 278 | 279 | // 根据分区计算每个 header、footer、cell 的 frame 280 | for sectionIndex in 0 ..< numberOfSections { 281 | // 保存 item 的宽度 282 | let itemW = self.itemWidhth(in: sectionIndex) 283 | 284 | // 获取列数 285 | let columns = self.numberOfColumns(section: sectionIndex) 286 | 287 | // 获取当前分区的 cell 的数量 288 | let numberOfItems = (self.flowDataSource?.numberOfItems(in: self, at: sectionIndex))! 289 | 290 | // 获取当前分区的间距设置 291 | let topMargin = self.margin(at: sectionIndex, for: .top) 292 | let leftMargin = self.margin(at: sectionIndex, for: .left) 293 | let bottomMargin = self.margin(at: sectionIndex, for: .bottom) 294 | let columnMargin = self.margin(at: sectionIndex, for: .column) 295 | let rowMargin = self.margin(at: sectionIndex, for: .row) 296 | 297 | // 获取当前分区的头视图 298 | if let sectionHeaederView = self.flowDelegate?.viewForSectionHeader(in: self, at: sectionIndex) { 299 | let sectionFrame = CGRect(x: 0, y: maxY, width: self.bounds.width, height: sectionHeaederView.frame.height) 300 | self.sectionHeaderFrames[sectionIndex] = sectionFrame 301 | if floatingHeaderEnable == true { 302 | sectionHeaederView.frame = sectionFrame 303 | needFloatingHeader[sectionIndex] = sectionHeaederView 304 | } 305 | maxY = sectionFrame.maxY 306 | } 307 | 308 | // 计算某一列的最大Y值 309 | var maxYOfColumns: [CGFloat] = [] 310 | for _ in 0 ..< columns { 311 | maxYOfColumns.append(maxY) 312 | } 313 | 314 | // 计算每个 item 的 frame 315 | var itemFrames: [CGRect] = [] 316 | for itemIndex in 0 ..< numberOfItems { 317 | let itemH = self.height(at: TIMIndexPath(sectionIndex, itemIndex)) // 高度 318 | var itemColumn = 0 // 当前列索引 319 | var maxYOfItemColumn = maxYOfColumns[itemColumn] // 取出第一列的Y值 320 | 321 | for i in 0 ..< columns { 322 | if maxYOfColumns[i] < maxYOfItemColumn { 323 | itemColumn = i 324 | maxYOfItemColumn = maxYOfColumns[i] 325 | } 326 | } 327 | 328 | // 计算 item 的 x 坐标 329 | let itemX: CGFloat = leftMargin + CGFloat(itemColumn) * (columnMargin + itemW) 330 | var itemY: CGFloat = 0 331 | 332 | if maxYOfItemColumn == maxY { 333 | itemY = maxYOfItemColumn + topMargin 334 | } else { 335 | itemY = maxYOfItemColumn + rowMargin 336 | } 337 | 338 | let frame = CGRect(x: itemX, y: itemY, width: itemW, height: itemH) 339 | itemFrames.append(frame) 340 | maxYOfColumns[itemColumn] = frame.maxY 341 | } 342 | 343 | self.itemFrames[sectionIndex] = itemFrames 344 | 345 | // 计算当前最大Y值 346 | maxY = maxYOfColumns[0] 347 | for i in 0 ..< maxYOfColumns.count { 348 | if maxY < maxYOfColumns[i] { 349 | maxY = maxYOfColumns[i] 350 | } 351 | } 352 | 353 | maxY = maxY + bottomMargin 354 | 355 | if let sectionFooterView = self.flowDelegate?.viewForSectionFooter(in: self, at: sectionIndex) { 356 | let footerFrame = CGRect(x: 0, y: maxY, width: self.bounds.width, height: sectionFooterView.frame.height) 357 | self.sectionFooterFrames[sectionIndex] = footerFrame 358 | maxY = footerFrame.maxY 359 | } 360 | } 361 | 362 | guard let footer = flowFooterView else { 363 | contentSize = CGSize(width: 0, height: maxY) 364 | setNeedsLayout() 365 | return 366 | } 367 | 368 | var footerFrame = footer.frame 369 | footerFrame = CGRect(x: 0, y: maxY, width: bounds.width, height: footerFrame.size.height) 370 | footer.frame = footerFrame 371 | addSubview(footer) 372 | contentSize = CGSize(width: 0, height: footerFrame.maxY) 373 | setNeedsLayout() 374 | } 375 | 376 | // 从缓存池中获取 item 377 | func dequeueReuseableItem(with identifier: String) -> TIMFlowViewItem? { 378 | var reuseItem: TIMFlowViewItem? 379 | for (_, var value) in reuseableItems { 380 | for (_, item) in value.enumerated() { 381 | if item.reuseIdentifier == identifier { 382 | reuseItem = item 383 | break 384 | } 385 | } 386 | 387 | if reuseItem != nil { 388 | value.remove(reuseItem!) 389 | break 390 | } 391 | } 392 | 393 | return reuseItem 394 | } 395 | 396 | // 从缓存池中获取分区头视图 397 | func dequeueReuseableSectionHeaderView(with identifier: String) -> Header? { 398 | var reuseHeader: Header? 399 | for (_, header) in reuseableSectionHeader.enumerated() { 400 | if header.reuseIdentifier == identifier { 401 | reuseHeader = header as? Header 402 | break 403 | } 404 | } 405 | 406 | if reuseHeader != nil { 407 | reuseableSectionHeader.remove(reuseHeader!) 408 | } 409 | 410 | return reuseHeader 411 | } 412 | 413 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 414 | if flowDelegate == nil { 415 | return 416 | } 417 | 418 | guard let touch = (touches as NSSet).anyObject() as? UITouch else { 419 | return 420 | } 421 | 422 | let point = touch.location(in: self) 423 | var selectedIndexPath: TIMIndexPath? = nil 424 | for (key, value) in displayingItems { 425 | if value.frame.contains(point) { 426 | selectedIndexPath = key 427 | break 428 | } 429 | } 430 | 431 | if selectedIndexPath != nil { 432 | flowDelegate?.didSelected(in: self, at: selectedIndexPath!) 433 | } 434 | } 435 | } 436 | 437 | // MARK: - Private API 438 | extension TIMFlowView { 439 | private func isInScreen(aFrame: CGRect) -> Bool { 440 | (aFrame.maxY > contentOffset.y) && (aFrame.minY < (contentOffset.y + bounds.height)) 441 | } 442 | 443 | 444 | private func margin(at section: Int, for type: TIMFlowViewCellMarginType) -> CGFloat { 445 | if let margin = flowDelegate?.margin(in: self, at: section, for: type) { 446 | return margin 447 | } 448 | 449 | return DEFAULT_CELL_MARGIN 450 | } 451 | 452 | 453 | 454 | private func numberOfColumns(section: Int) -> Int { 455 | if let columns = flowDataSource?.numberOfColumns(in: self, at: section){ 456 | return columns 457 | } 458 | 459 | return DEFAULT_COLUMN_COUNT 460 | } 461 | 462 | private func height(at indexPath: TIMIndexPath) -> CGFloat { 463 | 464 | if let height = flowDelegate?.itemHeight(in: self, at: indexPath) { 465 | return height 466 | } 467 | 468 | return DEFAULT_CELL_HEIGHT 469 | } 470 | } 471 | 472 | 473 | extension TIMFlowView: UIScrollViewDelegate { 474 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 475 | if floatingHeaderEnable { 476 | 477 | if currentFloatingIndex >= needFloatingHeader.count || currentFloatingIndex < 0 { 478 | return 479 | } 480 | 481 | offsetY = contentOffset.y 482 | 483 | if offsetY <= 0 { 484 | return 485 | } 486 | 487 | if offsetY >= floatingBeginY { 488 | guard let floatingHeader = needFloatingHeader[currentFloatingIndex] else { 489 | return 490 | } 491 | 492 | floatingHeader.frame = CGRect(x: 0, y: frame.minY, width: floatingHeader.bounds.width, height: floatingHeader.bounds.height) 493 | 494 | superview?.addSubview(floatingHeader) 495 | 496 | // 取出当前索引分区内的item的frames,并获取当前分区的最大Y值 497 | var sectionMaxY: CGFloat! = 0 498 | if sectionFooterFrames[currentFloatingIndex] != nil { 499 | sectionMaxY = sectionFooterFrames[currentFloatingIndex]?.maxY 500 | } else { 501 | if let itemFrames = itemFrames[currentFloatingIndex] { 502 | for i in 0 ..< itemFrames.count { 503 | sectionMaxY < itemFrames[i].maxY ? (sectionMaxY = itemFrames[i].maxY) : (sectionMaxY = sectionMaxY) 504 | } 505 | 506 | sectionMaxY += flowDelegate?.margin(in: self, at: currentFloatingIndex, for: .bottom) ?? 0 507 | } 508 | } 509 | 510 | if offsetY >= (sectionMaxY - floatingHeader.bounds.height) && offsetY < sectionMaxY { 511 | let animationY = currentFloatingHeaderView!.frame.minY - (offsetY - (sectionMaxY - floatingHeader.bounds.height)) 512 | currentFloatingHeaderView?.frame = CGRect(x: 0, y:animationY , width: currentFloatingHeaderView!.bounds.width, height: currentFloatingHeaderView!.bounds.height) 513 | } 514 | 515 | if offsetY > sectionMaxY { 516 | 517 | currentFloatingHeaderView?.frame = (sectionHeaderFrames[currentFloatingIndex])! 518 | addSubview(currentFloatingHeaderView!) 519 | 520 | currentFloatingIndex += 1 521 | floatingBeginY = sectionMaxY 522 | } 523 | 524 | currentFloatingHeaderView = floatingHeader 525 | } else { 526 | // 获取当前索引分区的最小 Y 值 527 | guard let minY = sectionHeaderFrames[currentFloatingIndex]?.minY else { 528 | return 529 | } 530 | 531 | 532 | if offsetY <= minY && currentFloatingIndex != 0 { 533 | currentFloatingHeaderView?.frame = sectionHeaderFrames[currentFloatingIndex]! 534 | addSubview(currentFloatingHeaderView!) 535 | 536 | currentFloatingIndex -= 1 537 | floatingBeginY = (sectionHeaderFrames[currentFloatingIndex]?.minY)! 538 | } 539 | 540 | guard let floatingHeader = needFloatingHeader[currentFloatingIndex] else { 541 | return 542 | } 543 | 544 | floatingHeader.frame = CGRect(x: 0, y: frame.minY, width: floatingHeader.bounds.width, height: floatingHeader.bounds.height) 545 | superview?.addSubview(floatingHeader) 546 | currentFloatingHeaderView = floatingHeader 547 | 548 | if currentFloatingIndex == 0 && currentFloatingHeaderView != nil { 549 | currentFloatingHeaderView?.frame = sectionHeaderFrames[currentFloatingIndex]! 550 | addSubview(currentFloatingHeaderView!) 551 | } 552 | 553 | } 554 | } 555 | } 556 | } 557 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowViewCellMarginType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowViewCellMarginType.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/16. 6 | // Copyright © 2020 Tim. All rights reserved. 7 | // 可根据 cell 的属性分别设置每项之间的间距 8 | 9 | import Foundation 10 | 11 | public enum TIMFlowViewCellMarginType { 12 | case top // 上 13 | case left // 左 14 | case bottom // 下 15 | case right // 右 16 | case column // 每一列 17 | case row // 每一行 18 | } 19 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowViewDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowViewDataSource.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/16. 6 | // Copyright © 2020 Tim. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct TIMIndexPath: Hashable { 12 | 13 | /// 分区索引 14 | public var section: Int 15 | 16 | /// item 索引 17 | public var item: Int 18 | 19 | init(_ section: Int, _ itme: Int) { 20 | self.section = section 21 | self.item = itme 22 | } 23 | } 24 | 25 | public protocol TIMFlowViewDataSource: NSObjectProtocol { 26 | 27 | /// 返回分组数量,默认为 1 28 | /// - Parameter flowView: 提供视图给外面设置 29 | func numberOfSections(in flowView: TIMFlowView) -> Int 30 | 31 | 32 | /// 返回每个分区的 cell 数量 33 | /// - Parameters: 34 | /// - flowView: 提供视图给外面设置 35 | /// - section: 提供分区的索引 36 | func numberOfItems(in flowView: TIMFlowView, at section: Int) -> Int 37 | 38 | /// 返回列数,默认为两列 39 | /// - Parameter flowView: 提供视图给外面设置 40 | func numberOfColumns(in flowView: TIMFlowView, at section: Int) -> Int 41 | 42 | /// 返回 index 对应索引的 cell 43 | /// - Parameters: 44 | /// - flowView: 提供视图给外面设置 45 | /// - index: 提供索引给外面使用 46 | func flowViewItem(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> TIMFlowViewItem? 47 | } 48 | 49 | public extension TIMFlowViewDataSource { 50 | func numberOfSections(in flowView: TIMFlowView) -> Int { 1 } 51 | func numberOfColumns(in flowView: TIMFlowView, at section: Int) -> Int { DEFAULT_COLUMN_COUNT } 52 | } 53 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowViewDelegate.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/16. 6 | // Copyright © 2020 Tim. All rights reserved. 7 | // 8 | 9 | public protocol TIMFlowViewDelegate: UIScrollViewDelegate { 10 | /// 返回 cell 的高度 11 | /// - Parameters: 12 | /// - flowView: 提供瀑布流视图给外部使用 13 | /// - index: cell 的索引 14 | func itemHeight(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> CGFloat 15 | 16 | /// 返回 cell 的间距,可根据类型分别返回,默认为 8 个点 17 | /// - Parameters: 18 | /// - flowView: 提供瀑布流视图给外部使用 19 | /// - marginType: 间距的类型,分别有:上,左,下,右,每一行,每一列 20 | func margin(in flowView: TIMFlowView, at section: Int, for marginType: TIMFlowViewCellMarginType) -> CGFloat 21 | 22 | /// 点击 cell 时的回调 23 | /// - Parameters: 24 | /// - flowView: 提供瀑布流视图给外部使用 25 | /// - index: cell 的索引 26 | func didSelected(in flowView: TIMFlowView, at indexPath: TIMIndexPath) 27 | 28 | /// 分区头视图 29 | /// - Parameters: 30 | /// - flowView: 提供瀑布流视图给外部使用 31 | /// - sectionIndex: 分区索引 32 | func viewForSectionHeader(in flowView: TIMFlowView, at section: Int) -> TIMFlowHeaderFooterView? 33 | 34 | /// 分区尾视图 35 | /// - Parameters: 36 | /// - flowView: 提供瀑布流视图给外部使用 37 | /// - sectionIndex: 分区索引 38 | func viewForSectionFooter(in flowView: TIMFlowView, at section: Int) -> TIMFlowHeaderFooterView? 39 | } 40 | 41 | public extension TIMFlowViewDelegate { 42 | func itemHeight(in flowView: TIMFlowView, at indexPath: TIMIndexPath) -> CGFloat { DEFAULT_CELL_HEIGHT } 43 | func margin(in flowView: TIMFlowView, at section: Int, for marginType: TIMFlowViewCellMarginType) -> CGFloat { DEFAULT_CELL_MARGIN } 44 | func didSelected(in flowView: TIMFlowView, at indexPath: TIMIndexPath) { } 45 | func viewForSectionHeader(in flowView: TIMFlowView, at section: Int) -> TIMFlowHeaderFooterView? { nil } 46 | func viewForSectionFooter(in flowView: TIMFlowView, at section: Int) -> TIMFlowHeaderFooterView? { nil } 47 | } 48 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowViewItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowViewCell.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/16. 6 | // Copyright © 2020 Tim. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class TIMFlowViewItem: UIView { 12 | /// 重用标识符 13 | internal var reuseIdentifier: String 14 | 15 | public convenience init(with reuseIdentifier: String) { 16 | self.init() 17 | self.reuseIdentifier = reuseIdentifier 18 | } 19 | 20 | public override init(frame: CGRect) { 21 | reuseIdentifier = "" 22 | super.init(frame: frame) 23 | } 24 | 25 | required public init?(coder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /TIMFlowView/TIMFlowViewUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TIMFlowViewUtils.swift 3 | // TIMFlowView 4 | // 5 | // Created by Tim's Mac Book Pro on 2020/1/16. 6 | // Copyright © 2020 Tim. All rights reserved. 7 | // 一些常量值 8 | 9 | /// 瀑布流默认列数 10 | internal let DEFAULT_COLUMN_COUNT = 2 11 | 12 | /// cell 的默认高度 13 | internal let DEFAULT_CELL_HEIGHT: CGFloat = 70.0 14 | 15 | /// 默认间距 16 | internal let DEFAULT_CELL_MARGIN: CGFloat = 8.0 17 | 18 | /// 当前设备是否为 iPhone X 系列 19 | public let isIphoneX = kScreenHeight >= 812 20 | 21 | /// 状态栏高度 22 | public let kStatusBarHeight : CGFloat = isIphoneX ? 44.0 : 20.0 23 | 24 | /// 导航栏高度 25 | public let kNavigationBarHeight : CGFloat = (kStatusBarHeight + 44.0) 26 | 27 | /// tabBar 高度 28 | public let kTabBarHeight : CGFloat = (kStatusBarHeight == 20.0) ? 49.0 : 83.0 29 | 30 | /// 顶部安全距离 31 | public let kTopSafeArea = (kStatusBarHeight - 20.0) 32 | 33 | /// 底部安全距离 34 | public let kBottomSafeArea = kTabBarHeight - 49.0 35 | 36 | /// 快速获取屏幕的Bounds属性 37 | public let kScreenBounds = UIScreen.main.bounds 38 | 39 | /// 屏幕宽度 40 | public let kScreenWidth = kScreenBounds.width 41 | 42 | /// 屏幕高度 43 | public let kScreenHeight = kScreenBounds.height 44 | 45 | /// 屏幕宽高 46 | public let kScreenSize = kScreenBounds.size 47 | 48 | /// 分辨率缩放比例 49 | public let kScreenScale = UIScreen.main.scale 50 | 51 | /// 客服QQ 52 | public let kCustomerServiceQQ = "800184955" 53 | 54 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------