├── .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 | [](https://travis-ci.org/Tim/TIMFlowView)
3 | [](https://cocoapods.org/pods/TIMFlowView)
4 | [](https://cocoapods.org/pods/TIMFlowView)
5 | [](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 | 
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
--------------------------------------------------------------------------------