├── .swift-version
├── Cartfile.private
├── Cartfile.resolved
├── AKPFlowLayout.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ └── AKPFlowLayout.xcscheme
└── project.pbxproj
├── carthage.sh
├── AKPFlowLayout
├── AKPFlowLayoutError.swift
├── AKPFlowLayout.h
├── Info.plist
├── AKPFlowLayoutAttributes.swift
├── AKPLayoutConfigOptions.swift
└── AKPFlowLayout.swift
├── AKPFlowLayout.xcworkspace
└── contents.xcworkspacedata
├── AKPFlowLayout.podspec
├── AKPFlowLayoutTests
├── Info.plist
└── AKPFlowLayoutTests.swift
├── License
├── .gitignore
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/Cartfile.private:
--------------------------------------------------------------------------------
1 | github "Quick/Quick"
2 | github "Quick/Nimble"
3 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Quick/Nimble" "v5.0.0"
2 | github "Quick/Quick" "v0.10.0"
3 |
--------------------------------------------------------------------------------
/AKPFlowLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/carthage.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | if ! command -v carthage > /dev/null; then
4 | printf 'Carthage is not installed.\n'
5 | printf 'See https://github.com/Carthage/Carthage for install instructions.\n'
6 | exit 1
7 | fi
8 |
9 | carthage update --platform iOS --use-submodules --no-use-binaries
10 |
--------------------------------------------------------------------------------
/AKPFlowLayout/AKPFlowLayoutError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AKPFlowLayoutError.swift
3 | // SwiftNetworkImages
4 | //
5 | // Created by Arseniy on 11/6/16.
6 | // Copyright © 2016 Arseniy Kuznetsov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// AKPFlowLayout Errors
12 |
13 | public enum AKPFlowLayoutError: Error {
14 | case sectionHeadersPinToVisibleBoundsSettingError
15 | }
16 |
--------------------------------------------------------------------------------
/AKPFlowLayout/AKPFlowLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // AKPFlowLayout.h
3 | // AKPFlowLayout
4 | //
5 | // Created by Arseniy on 12/6/16.
6 | // Copyright © 2016 Arseniy Kuznetsov. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | //! Project version number for AKPFlowLayout.
13 | FOUNDATION_EXPORT double AKPFlowLayoutVersionNumber;
14 |
15 | //! Project version string for AKPFlowLayout.
16 | FOUNDATION_EXPORT const unsigned char AKPFlowLayoutVersionString[];
17 |
18 |
--------------------------------------------------------------------------------
/AKPFlowLayout.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
10 |
12 |
13 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/AKPFlowLayout.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = "AKPFlowLayout"
3 | spec.version = "0.1.2"
4 | spec.summary = "A custom UICollectionView layout with configurable global header and pinnable, stretchable section headers"
5 | spec.homepage = "https://github.com/akpw/AKPFlowLayout"
6 | spec.license = { type: 'MIT', file: 'LICENSE' }
7 | spec.authors = { "Arseniy Kuznetsov" => 'k.arseniy@gmail.com' }
8 |
9 | spec.platform = :ios, "8.0"
10 | spec.requires_arc = true
11 | spec.source = { git: "https://github.com/akpw/AKPFlowLayout.git", tag: "v#{spec.version}", submodules: true }
12 | spec.source_files = "AKPFlowLayout/**/*.{h,swift}"
13 | end
14 |
--------------------------------------------------------------------------------
/AKPFlowLayoutTests/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 |
--------------------------------------------------------------------------------
/AKPFlowLayout/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
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Arseniy Kuznetsov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/AKPFlowLayout/AKPFlowLayoutAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AKPFlowLayoutAttributes.swift
3 | // AKPFlowLayout
4 | //
5 | // Created by Arseniy on 14/6/16.
6 | // Copyright © 2016 Arseniy Kuznetsov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// Layout Attributes class for AKPFlowLayout
12 |
13 | open class AKPFlowLayoutAttributes: UICollectionViewLayoutAttributes {
14 |
15 | /// Set by AKPFlowLayout when managing section headers stretching
16 | /// Can be used further for e.g. reporting amount of stretch back to the collection view items
17 | open var stretchFactor: CGFloat = 0
18 |
19 | override open func copy(with zone: NSZone?) -> Any {
20 | let aCopy = super.copy(with: zone) as! AKPFlowLayoutAttributes
21 | aCopy.stretchFactor = stretchFactor
22 | return aCopy
23 | }
24 |
25 | override open func isEqual(_ object: Any?) -> Bool {
26 | if let attributes = object as? AKPFlowLayoutAttributes {
27 | if attributes.stretchFactor == stretchFactor {
28 | return super.isEqual(object)
29 | }
30 | }
31 | return false
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 | *.pyc
10 |
11 | # caches
12 | #########
13 | __pycache__/
14 |
15 | # setup / build
16 | ###############
17 | *.egg-info
18 | *.pot
19 | *.py[co]
20 | dist/
21 | build/
22 |
23 | # Packages #
24 | ############
25 | # it's better to unpack these files and commit the raw source
26 | # git has its own built in compression methods
27 | *.7z
28 | *.dmg
29 | *.gz
30 | *.iso
31 | *.jar
32 | *.rar
33 | *.tar
34 | *.zip
35 |
36 | # Logs and databases #
37 | ######################
38 | *.log
39 | *.sql
40 | *.sqlite
41 |
42 | # OS generated files #
43 | ######################
44 | .DS_Store
45 | .DS_Store?
46 | ._*
47 | .Spotlight-V100
48 | .Trashes
49 | ehthumbs.db
50 | Thumbs.db
51 |
52 | # Xcode #
53 | #########
54 | */build/*
55 | *.pbxuser
56 | !default.pbxuser
57 | *.mode1v3
58 | !default.mode1v3
59 | *.mode2v3
60 | !default.mode2v3
61 | *.perspectivev3
62 | !default.perspectivev3
63 | xcuserdata
64 |
65 | #############
66 | *.moved-aside
67 | DerivedData
68 | .idea/
69 | *.iml
70 | *.hmap
71 | *.xccheckout
72 | *.xcuserstate
73 | *.xcscmblueprint
74 | *.ipa
75 |
76 | #CocoaPods
77 | Pods
78 | Podfile.lock
79 | .xcworkspace
80 |
81 | # Swift Package Manager
82 | .build/
83 |
84 | # Carthage
85 | Carthage/Build
86 | Carthage/Checkouts
87 | .gitmodules
88 |
89 | # Other source repository archive directories #
90 | ###############################################
91 | .hg
92 | .svn
93 | CVS
94 |
--------------------------------------------------------------------------------
/AKPFlowLayout/AKPLayoutConfigOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AKPLayoutConfigOptions.swift
3 | // SwiftNetworkImages
4 | //
5 | // Created by Arseniy on 9/6/16.
6 | // Copyright © 2016 Arseniy Kuznetsov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// AKPFlowLayout configuration options
12 |
13 | public struct AKPLayoutConfigOptions: OptionSet {
14 | public static let firstSectionIsGlobalHeader =
15 | AKPLayoutConfigOptions(rawValue: 1 << 0)
16 | public static let firstSectionStretchable =
17 | AKPLayoutConfigOptions(rawValue: 1 << 1)
18 | public static let sectionsPinToGlobalHeaderOrVisibleBounds =
19 | AKPLayoutConfigOptions(rawValue: 1 << 2)
20 | public let rawValue: Int
21 | public init(rawValue: Int) {
22 | self.rawValue = rawValue
23 | }
24 | }
25 |
26 | extension AKPLayoutConfigOptions: CustomStringConvertible {
27 | public var descriptions: [String] {
28 | let optionsDescriptions = ["First Section Is Global Header",
29 | "First Section Stretchable",
30 | "Sections Pin To Global Header Or Visible Bounds"]
31 | var memberDescriptions = [String]()
32 | for (shift, description) in optionsDescriptions.enumerated()
33 | where contains( AKPLayoutConfigOptions( rawValue: 1 << shift) ) {
34 | memberDescriptions.append(description)
35 | }
36 | return memberDescriptions
37 | }
38 |
39 | public var description: String {
40 | return descriptions.description
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/AKPFlowLayoutTests/AKPFlowLayoutTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AKPFlowLayoutTests.swift
3 | // AKPFlowLayoutTests
4 | //
5 | // Created by Arseniy on 12/6/16.
6 | // Copyright © 2016 Arseniy Kuznetsov. All rights reserved.
7 | //
8 |
9 | import Quick
10 | import Nimble
11 | import AKPFlowLayout
12 |
13 | class AKPFlowLayoutTests: QuickSpec {
14 | override func spec() {
15 | describe("AKPFlowLayout") {
16 | let akpFlowLayout: AKPFlowLayout = {
17 | $0.firsSectionMaximumStretchHeight = 200
18 | return $0
19 | }( AKPFlowLayout() )
20 |
21 | it("uses AKPFlowLayoutAttributes when creating layout attributes") {
22 | print(AKPFlowLayout.layoutAttributesClass)
23 | expect(AKPFlowLayout.layoutAttributesClass == AKPFlowLayoutAttributes.self).to(beTrue())
24 | }
25 |
26 | it("has all layout config options on by default") {
27 | expect(akpFlowLayout.layoutOptions.contains(.firstSectionIsGlobalHeader)).to(beTrue())
28 | expect(akpFlowLayout.layoutOptions.contains(.firstSectionStretchable)).to(beTrue())
29 | expect(akpFlowLayout.layoutOptions.contains(.sectionsPinToGlobalHeaderOrVisibleBounds)).to(beTrue())
30 | }
31 |
32 | it("when running on iSO9, it disables built-in sectionHeadersPinToVisibleBounds") {
33 | if #available(iOS 9.0, *) {
34 | akpFlowLayout.sectionHeadersPinToVisibleBounds = false
35 | expect(akpFlowLayout.sectionHeadersPinToVisibleBounds).to(beFalse())
36 |
37 | akpFlowLayout.sectionHeadersPinToVisibleBounds = true
38 | expect(akpFlowLayout.sectionHeadersPinToVisibleBounds).to(beFalse())
39 | }
40 | }
41 |
42 | it("has given firsSectionMaximumStretchHeight value") {
43 | expect(akpFlowLayout.firsSectionMaximumStretchHeight == 200).to(beTrue())
44 | }
45 |
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AKPFlowLayout
2 | ============
3 |
4 | 
5 | 
6 |
7 |
8 | AKPFlowLayout is a custom Collection View layout with configurable global header and pinnable, stretchable sections.
9 |
10 |
11 | ## Blogs
12 | * [Custom UICollectionView: Global Headers](https://akpw.github.io//articles/2016/06/16/CollectionView-I.html)
13 |
14 |
15 | ## Sample App
16 | * [SwiftNetworkImages](https://github.com/akpw/SwiftNetworkImages)
17 |
18 | ## Dev Docs
19 | [Initial docs][docsLink], generated with [jazzy](https://github.com/realm/jazzy) and hosted by [GitHub Pages](https://pages.github.com).
20 |
21 | ## Features
22 |
23 | * A custom `UICollectionViewFlowLayout`-based layout with support for:
24 | - Global header
25 | - Sticky section headers
26 | - Pinnable, stretchable sections
27 |
28 | * Fully configurable
29 |
30 | * Built for performace using custom invalidation context
31 |
32 | * Written in Swift 3 and Xcode 8
33 |
34 | ## Requirements
35 | * iOS 8+
36 | * Xcode 8
37 | * Swift 3
38 |
39 | ## Installation
40 |
41 | #### [CocoaPods](http://cocoapods.org) (recommended)
42 |
43 | ````sh
44 | use_frameworks!
45 | pod 'AKPFlowLayout'
46 | ````
47 |
48 | #### [Carthage](https://github.com/Carthage/Carthage)
49 | 1. Add AKPFlowLayout to your [`Cartfile`](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile):
50 | ```
51 | github "akpw/AKPFlowLayout"
52 | ```
53 | 2. Follow the [Carthage instructions on adding frameworks](https://github.com/Carthage/Carthage/blob/master/README.md#adding-frameworks-to-an-application) for further reference
54 |
55 |
56 | ## Building the project
57 |
58 | 1) Clone the repository
59 |
60 | ```bash
61 | $ git clone https://github.com/akpw/AKPFlowLayout
62 | ```
63 |
64 | 2) Run carthage.sh
65 |
66 | ```bash
67 | $ cd AKPFlowLayout
68 | $ ./carthage.sh
69 | ```
70 |
71 | 3) Open the workspace in Xcode
72 |
73 | ```bash
74 | $ open "AKPFlowLayout.xcworkspace"
75 | ```
76 |
77 | 4) Compile and test in Xcode
78 |
79 |
80 |
81 |
82 | [docsLink]:https://akpw.github.io//AKPFlowLayout/index.html
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/AKPFlowLayout.xcodeproj/xcshareddata/xcschemes/AKPFlowLayout.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/AKPFlowLayout/AKPFlowLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AKPFlowLayout.swift
3 | // SwiftNetworkImages
4 | //
5 | // Created by Arseniy on 18/5/16.
6 | // Copyright © 2016 Arseniy Kuznetsov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | Global / Sticky / Stretchy Headers using UICollectionViewFlowLayout.
13 | Works for iOS8 and above.
14 | */
15 |
16 | public final class AKPFlowLayout: UICollectionViewFlowLayout {
17 | /// Layout configuration options
18 | public var layoutOptions: AKPLayoutConfigOptions = [.firstSectionIsGlobalHeader,
19 | .firstSectionStretchable,
20 | .sectionsPinToGlobalHeaderOrVisibleBounds]
21 | /// For stretchy headers, allowis limiting amount of stretch
22 | public var firsSectionMaximumStretchHeight = CGFloat.greatestFiniteMagnitude
23 |
24 | // MARK: - Initialization
25 | override public init() {
26 | super.init()
27 | // For iOS9, needs to ensure the impl does not interfere with `sectionHeadersPinToVisibleBounds`
28 | // Seems to be no reasonable way yet to use Swift property observers with conditional compilation,
29 | // so falling back to KVO
30 | if #available(iOS 9.0, *) {
31 | addObserver(self, forKeyPath: "sectionHeadersPinToVisibleBounds",
32 | options: .new, context: &AKPFlowLayoutKVOContext)
33 | }
34 | }
35 | required public init?(coder aDecoder: NSCoder) {
36 | super.init(coder: aDecoder)
37 | }
38 | deinit {
39 | if #available(iOS 9.0, *) {
40 | removeObserver(self, forKeyPath: "sectionHeadersPinToVisibleBounds", context: &AKPFlowLayoutKVOContext)
41 | }
42 | }
43 |
44 | // MARK: - 📐Custom Layout
45 | /// - returns: AKPFlowLayoutAttributes class for handling layout attributes
46 | override public class var layoutAttributesClass : AnyClass {
47 | return AKPFlowLayoutAttributes.self
48 | }
49 |
50 | /// Returns layout attributes for specified rectangle, with added custom headers
51 | override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
52 | guard shouldDoCustomLayout else { return super.layoutAttributesForElements(in: rect) }
53 |
54 | guard var layoutAttributes = super.layoutAttributesForElements(in: rect) as? [AKPFlowLayoutAttributes],
55 | // calculate custom headers that should be confined in the rect
56 | let customSectionHeadersIdxs = customSectionHeadersIdxs(rect) else { return nil }
57 |
58 | // add the custom headers to the regular UICollectionViewFlowLayout layoutAttributes
59 | for idx in customSectionHeadersIdxs {
60 | let indexPath = IndexPath(item: 0, section: idx)
61 | if let attributes = super.layoutAttributesForSupplementaryView(
62 | ofKind: UICollectionElementKindSectionHeader,
63 | at: indexPath) as? AKPFlowLayoutAttributes {
64 | layoutAttributes.append(attributes)
65 | }
66 | }
67 | // for section headers, need to adjust their attributes
68 | for attributes in layoutAttributes where
69 | attributes.representedElementKind == UICollectionElementKindSectionHeader {
70 | (attributes.frame, attributes.zIndex) = adjustLayoutAttributes(forSectionAttributes: attributes)
71 | }
72 | return layoutAttributes
73 | }
74 |
75 | /// Adjusts layout attributes for the custom section headers
76 | override public func layoutAttributesForSupplementaryView(ofKind elementKind: String,
77 | at indexPath: IndexPath)
78 | -> UICollectionViewLayoutAttributes? {
79 | guard shouldDoCustomLayout else {
80 | return super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) }
81 |
82 | guard let sectionHeaderAttributes = super.layoutAttributesForSupplementaryView(
83 | ofKind: elementKind,
84 | at: indexPath)
85 | as? AKPFlowLayoutAttributes else { return nil }
86 | // Adjust section attributes
87 | (sectionHeaderAttributes.frame, sectionHeaderAttributes.zIndex) =
88 | adjustLayoutAttributes(forSectionAttributes: sectionHeaderAttributes)
89 | return sectionHeaderAttributes
90 | }
91 |
92 | // MARK: - 🎳Invalidation
93 | /// - returns: `true`, unless running on iOS9 with `sectionHeadersPinToVisibleBounds` set to `true`
94 | override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
95 | guard shouldDoCustomLayout else { return super.shouldInvalidateLayout(forBoundsChange: newBounds) }
96 | return true
97 | }
98 |
99 | /// Custom invalidation
100 | override public func invalidationContext(forBoundsChange newBounds: CGRect)
101 | -> UICollectionViewLayoutInvalidationContext {
102 | guard shouldDoCustomLayout,
103 | let invalidationContext = super.invalidationContext(forBoundsChange: newBounds)
104 | as? UICollectionViewFlowLayoutInvalidationContext,
105 | let oldBounds = collectionView?.bounds
106 | else { return super.invalidationContext(forBoundsChange: newBounds) }
107 | // Size changes?
108 | if oldBounds.size != newBounds.size {
109 | // re-query the collection view delegate for metrics such as size information etc.
110 | invalidationContext.invalidateFlowLayoutDelegateMetrics = true
111 | }
112 |
113 | // Origin changes?
114 | if oldBounds.origin != newBounds.origin {
115 | // find and invalidate sections that would fall into the new bounds
116 | guard let sectionIdxPaths = sectionsHeadersIDxs(forRect: newBounds) else {return invalidationContext}
117 |
118 | // then invalidate
119 | let invalidatedIdxPaths = sectionIdxPaths.map { IndexPath(item: 0, section: $0) }
120 | invalidationContext.invalidateSupplementaryElements(
121 | ofKind: UICollectionElementKindSectionHeader, at: invalidatedIdxPaths )
122 | }
123 | return invalidationContext
124 | }
125 | fileprivate var previousStretchFactor = CGFloat(0)
126 | }
127 |
128 | // MARK: - 🕶Private Helpers
129 | extension AKPFlowLayout {
130 | fileprivate var shouldDoCustomLayout: Bool {
131 | var requestForCustomLayout = layoutOptions.contains(.firstSectionIsGlobalHeader) ||
132 | layoutOptions.contains(.firstSectionStretchable) ||
133 | layoutOptions.contains(.sectionsPinToGlobalHeaderOrVisibleBounds)
134 | // iOS9 supports sticky headers natively, so we should not
135 | // interfere with the the built-in functionality
136 | if #available(iOS 9.0, *) {
137 | requestForCustomLayout = requestForCustomLayout && !sectionHeadersPinToVisibleBounds
138 | }
139 | return requestForCustomLayout
140 | }
141 |
142 | fileprivate func zIndexForSection(_ section: Int) -> Int {
143 | return section > 0 ? 128 : 256
144 | }
145 |
146 | // Given a rect, calculates indexes of all confined section headers
147 | // _including_ the custom headers
148 | fileprivate func sectionsHeadersIDxs(forRect rect: CGRect) -> Set? {
149 | guard let layoutAttributes = super.layoutAttributesForElements(in: rect)
150 | as? [AKPFlowLayoutAttributes] else {return nil}
151 | let sectionsShouldPin = layoutOptions.contains(.sectionsPinToGlobalHeaderOrVisibleBounds)
152 |
153 | var headersIdxs = Set()
154 | for attributes in layoutAttributes
155 | where attributes.visibleSectionHeader(sectionsShouldPin) {
156 | headersIdxs.insert((attributes.indexPath as NSIndexPath).section)
157 | }
158 | if layoutOptions.contains(.firstSectionIsGlobalHeader) {
159 | headersIdxs.insert(0)
160 | }
161 | return headersIdxs
162 | }
163 |
164 | // Given a rect, calculates the indexes of confined custom section headers
165 | // _excluding_ the regular headers handled by UICollectionViewFlowLayout
166 | fileprivate func customSectionHeadersIdxs(_ rect: CGRect) -> Set? {
167 | guard let layoutAttributes = super.layoutAttributesForElements(in: rect),
168 | var sectionIdxs = sectionsHeadersIDxs(forRect: rect) else {return nil}
169 |
170 | // remove the sections that should already be taken care of by UICollectionViewFlowLayout
171 | for attributes in layoutAttributes
172 | where attributes.representedElementKind == UICollectionElementKindSectionHeader {
173 | sectionIdxs.remove((attributes.indexPath as NSIndexPath).section)
174 | }
175 | return sectionIdxs
176 | }
177 |
178 | // Adjusts layout attributes of section headers
179 | fileprivate func adjustLayoutAttributes(forSectionAttributes
180 | sectionHeadersLayoutAttributes: AKPFlowLayoutAttributes)
181 | -> (CGRect, Int) {
182 | guard let collectionView = collectionView else { return (CGRect.zero, 0) }
183 | let section = (sectionHeadersLayoutAttributes.indexPath as NSIndexPath).section
184 | var sectionFrame = sectionHeadersLayoutAttributes.frame
185 |
186 | // 1. Establish the section boundaries:
187 | let (minY, maxY) = boundaryMetrics(forSectionAttributes: sectionHeadersLayoutAttributes)
188 |
189 | // 2. Determine the height and insets of the first section,
190 | // in case it's stretchable or serves as a global header
191 | let (firstSectionHeight, firstSectionInsets) = firstSectionMetrics()
192 |
193 | // 3. If within the above boundaries, the section should follow content offset
194 | // (adjusting a few more things along the way)
195 | var offset = collectionView.contentOffset.y + collectionView.contentInset.top
196 | if (section > 0) {
197 | // The global section
198 | if layoutOptions.contains(.sectionsPinToGlobalHeaderOrVisibleBounds) {
199 | if layoutOptions.contains(.firstSectionIsGlobalHeader) {
200 | // A global header adjustment
201 | offset += firstSectionHeight + firstSectionInsets.top
202 | }
203 | sectionFrame.origin.y = min(max(offset, minY), maxY)
204 | }
205 | } else {
206 | if layoutOptions.contains(.firstSectionStretchable) && offset < 0 {
207 | // Stretchy header
208 | if firstSectionHeight - offset < firsSectionMaximumStretchHeight {
209 | sectionFrame.size.height = firstSectionHeight - offset
210 | sectionHeadersLayoutAttributes.stretchFactor = fabs(offset)
211 | previousStretchFactor = sectionHeadersLayoutAttributes.stretchFactor
212 | } else {
213 | // need to limit the stretch
214 | sectionFrame.size.height = firsSectionMaximumStretchHeight
215 | sectionHeadersLayoutAttributes.stretchFactor = previousStretchFactor
216 | }
217 | sectionFrame.origin.y += offset + firstSectionInsets.top
218 | } else if layoutOptions.contains(.firstSectionIsGlobalHeader) {
219 | // Sticky header position needs to be relative to the global header
220 | sectionFrame.origin.y += offset + firstSectionInsets.top
221 | } else {
222 | sectionFrame.origin.y = min(max(offset, minY), maxY)
223 | }
224 | }
225 | return (sectionFrame, zIndexForSection(section))
226 | }
227 |
228 | fileprivate func boundaryMetrics(
229 | forSectionAttributes sectionHeadersLayoutAttributes: UICollectionViewLayoutAttributes)
230 | -> (CGFloat, CGFloat) {
231 | // get attributes for first and last items in section
232 | guard let collectionView = collectionView else { return (0, 0) }
233 | let section = (sectionHeadersLayoutAttributes.indexPath as NSIndexPath).section
234 |
235 | // Trying to use layoutAttributesForItemAtIndexPath for empty section would
236 | // cause EXC_ARITHMETIC in simulator (division by zero items)
237 | let lastInSectionIdx = collectionView.numberOfItems(inSection: section) - 1
238 | if lastInSectionIdx < 0 { return (0, 0) }
239 |
240 | guard let attributesForFirstItemInSection = layoutAttributesForItem(
241 | at: IndexPath(item: 0, section: section)),
242 | let attributesForLastItemInSection = layoutAttributesForItem(
243 | at: IndexPath(item: lastInSectionIdx, section: section))
244 | else {return (0, 0)}
245 | let sectionFrame = sectionHeadersLayoutAttributes.frame
246 |
247 | // Section Boundaries:
248 | // The section should not be higher than the top of its first cell
249 | let minY = attributesForFirstItemInSection.frame.minY - sectionFrame.height
250 | // The section should not be lower than the bottom of its last cell
251 | let maxY = attributesForLastItemInSection.frame.maxY - sectionFrame.height
252 | return (minY, maxY)
253 | }
254 |
255 | fileprivate func firstSectionMetrics() -> (height: CGFloat, insets: UIEdgeInsets) {
256 | guard let collectionView = collectionView else { return (0, UIEdgeInsets.zero) }
257 | // height of the first section
258 | var firstSectionHeight = headerReferenceSize.height
259 | if let delegate = collectionView.delegate as? UICollectionViewDelegateFlowLayout
260 | , firstSectionHeight == 0 {
261 | firstSectionHeight = delegate.collectionView!(collectionView,
262 | layout: self,
263 | referenceSizeForHeaderInSection: 0).height
264 | }
265 | // insets of the first section
266 | var theSectionInset = sectionInset
267 | if let delegate = collectionView.delegate as? UICollectionViewDelegateFlowLayout
268 | , theSectionInset == UIEdgeInsets.zero {
269 | theSectionInset = delegate.collectionView!(collectionView,
270 | layout: self,
271 | insetForSectionAt: 0)
272 | }
273 | return (firstSectionHeight, theSectionInset)
274 | }
275 | }
276 |
277 | // MARK: - KVO check for `sectionHeadersPinToVisibleBounds`
278 | extension AKPFlowLayout {
279 | /// KVO check for `sectionHeadersPinToVisibleBounds`.
280 | /// For iOS9, needs to ensure the impl does not interfere with `sectionHeadersPinToVisibleBounds`
281 | override public func observeValue(forKeyPath keyPath: String?, of object: Any?,
282 | change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
283 | if context == &AKPFlowLayoutKVOContext {
284 | if let newValue = change?[NSKeyValueChangeKey.newKey],
285 | let boolValue = newValue as? Bool , boolValue {
286 | print("AKPFlowLayout supports sticky headers by default, therefore " +
287 | "the built-in functionality via sectionHeadersPinToVisibleBounds has been disabled")
288 | if #available(iOS 9.0, *) { sectionHeadersPinToVisibleBounds = false }
289 | }
290 | } else {
291 | super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
292 | }
293 | }
294 | }
295 |
296 |
297 | private extension AKPFlowLayoutAttributes {
298 | // Determines if element is a section, or is a cell in a section with custom header
299 | func visibleSectionHeader(_ sectionsShouldPin: Bool) -> Bool {
300 | let isHeader = representedElementKind == UICollectionElementKindSectionHeader
301 | let isCellInPinnedSection = sectionsShouldPin && ( representedElementCategory == .cell )
302 | return isCellInPinnedSection || isHeader
303 | }
304 | }
305 |
306 | private var AKPFlowLayoutKVOContext = 0
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
--------------------------------------------------------------------------------
/AKPFlowLayout.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 577338171DB8AD7D0083B350 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 577338151DB8AD7D0083B350 /* Nimble.framework */; };
11 | 577338181DB8AD7D0083B350 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 577338161DB8AD7D0083B350 /* Quick.framework */; };
12 | 57C203C81D103318004AB164 /* AKPFlowLayoutAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C203C71D103318004AB164 /* AKPFlowLayoutAttributes.swift */; };
13 | 57FA3EBA1D0D877E00EFD324 /* AKPFlowLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FA3EB91D0D877E00EFD324 /* AKPFlowLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
14 | 57FA3EC11D0D877E00EFD324 /* AKPFlowLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57FA3EB61D0D877E00EFD324 /* AKPFlowLayout.framework */; };
15 | 57FA3EC61D0D877E00EFD324 /* AKPFlowLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FA3EC51D0D877E00EFD324 /* AKPFlowLayoutTests.swift */; };
16 | 57FA3F0D1D0D8FC300EFD324 /* AKPFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FA3F0A1D0D8FC300EFD324 /* AKPFlowLayout.swift */; };
17 | 57FA3F0E1D0D8FC300EFD324 /* AKPFlowLayoutError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FA3F0B1D0D8FC300EFD324 /* AKPFlowLayoutError.swift */; };
18 | 57FA3F0F1D0D8FC300EFD324 /* AKPLayoutConfigOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FA3F0C1D0D8FC300EFD324 /* AKPLayoutConfigOptions.swift */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXContainerItemProxy section */
22 | 57FA3EC21D0D877E00EFD324 /* PBXContainerItemProxy */ = {
23 | isa = PBXContainerItemProxy;
24 | containerPortal = 57FA3EAD1D0D877E00EFD324 /* Project object */;
25 | proxyType = 1;
26 | remoteGlobalIDString = 57FA3EB51D0D877E00EFD324;
27 | remoteInfo = AKPFlowLayout;
28 | };
29 | /* End PBXContainerItemProxy section */
30 |
31 | /* Begin PBXFileReference section */
32 | 577338151DB8AD7D0083B350 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = "Carthage/Checkouts/Nimble/build/Debug-iphoneos/Nimble.framework"; sourceTree = ""; };
33 | 577338161DB8AD7D0083B350 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = "Carthage/Checkouts/Quick/build/Debug-iphoneos/Quick.framework"; sourceTree = ""; };
34 | 57C203C71D103318004AB164 /* AKPFlowLayoutAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKPFlowLayoutAttributes.swift; sourceTree = ""; };
35 | 57FA3EB61D0D877E00EFD324 /* AKPFlowLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AKPFlowLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 57FA3EB91D0D877E00EFD324 /* AKPFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AKPFlowLayout.h; sourceTree = ""; };
37 | 57FA3EBB1D0D877E00EFD324 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | 57FA3EC01D0D877E00EFD324 /* AKPFlowLayoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AKPFlowLayoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
39 | 57FA3EC51D0D877E00EFD324 /* AKPFlowLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AKPFlowLayoutTests.swift; sourceTree = ""; };
40 | 57FA3EC71D0D877E00EFD324 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
41 | 57FA3F0A1D0D8FC300EFD324 /* AKPFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKPFlowLayout.swift; sourceTree = ""; };
42 | 57FA3F0B1D0D8FC300EFD324 /* AKPFlowLayoutError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKPFlowLayoutError.swift; sourceTree = ""; };
43 | 57FA3F0C1D0D8FC300EFD324 /* AKPLayoutConfigOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKPLayoutConfigOptions.swift; sourceTree = ""; };
44 | /* End PBXFileReference section */
45 |
46 | /* Begin PBXFrameworksBuildPhase section */
47 | 57FA3EB21D0D877E00EFD324 /* Frameworks */ = {
48 | isa = PBXFrameworksBuildPhase;
49 | buildActionMask = 2147483647;
50 | files = (
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | 57FA3EBD1D0D877E00EFD324 /* Frameworks */ = {
55 | isa = PBXFrameworksBuildPhase;
56 | buildActionMask = 2147483647;
57 | files = (
58 | 577338171DB8AD7D0083B350 /* Nimble.framework in Frameworks */,
59 | 577338181DB8AD7D0083B350 /* Quick.framework in Frameworks */,
60 | 57FA3EC11D0D877E00EFD324 /* AKPFlowLayout.framework in Frameworks */,
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | /* End PBXFrameworksBuildPhase section */
65 |
66 | /* Begin PBXGroup section */
67 | 57FA3EAC1D0D877E00EFD324 = {
68 | isa = PBXGroup;
69 | children = (
70 | 57FA3EB81D0D877E00EFD324 /* AKPFlowLayout */,
71 | 57FA3EC41D0D877E00EFD324 /* AKPFlowLayoutTests */,
72 | 57FA3F091D0D8C0100EFD324 /* Frameworks */,
73 | 57FA3EB71D0D877E00EFD324 /* Products */,
74 | );
75 | sourceTree = "";
76 | };
77 | 57FA3EB71D0D877E00EFD324 /* Products */ = {
78 | isa = PBXGroup;
79 | children = (
80 | 57FA3EB61D0D877E00EFD324 /* AKPFlowLayout.framework */,
81 | 57FA3EC01D0D877E00EFD324 /* AKPFlowLayoutTests.xctest */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 57FA3EB81D0D877E00EFD324 /* AKPFlowLayout */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 57FA3EB91D0D877E00EFD324 /* AKPFlowLayout.h */,
90 | 57FA3F0A1D0D8FC300EFD324 /* AKPFlowLayout.swift */,
91 | 57FA3F0C1D0D8FC300EFD324 /* AKPLayoutConfigOptions.swift */,
92 | 57C203C71D103318004AB164 /* AKPFlowLayoutAttributes.swift */,
93 | 57FA3F0B1D0D8FC300EFD324 /* AKPFlowLayoutError.swift */,
94 | 57FA3EBB1D0D877E00EFD324 /* Info.plist */,
95 | );
96 | path = AKPFlowLayout;
97 | sourceTree = "";
98 | };
99 | 57FA3EC41D0D877E00EFD324 /* AKPFlowLayoutTests */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 57FA3EC51D0D877E00EFD324 /* AKPFlowLayoutTests.swift */,
103 | 57FA3EC71D0D877E00EFD324 /* Info.plist */,
104 | );
105 | path = AKPFlowLayoutTests;
106 | sourceTree = "";
107 | };
108 | 57FA3F091D0D8C0100EFD324 /* Frameworks */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 577338151DB8AD7D0083B350 /* Nimble.framework */,
112 | 577338161DB8AD7D0083B350 /* Quick.framework */,
113 | );
114 | name = Frameworks;
115 | sourceTree = "";
116 | };
117 | /* End PBXGroup section */
118 |
119 | /* Begin PBXHeadersBuildPhase section */
120 | 57FA3EB31D0D877E00EFD324 /* Headers */ = {
121 | isa = PBXHeadersBuildPhase;
122 | buildActionMask = 2147483647;
123 | files = (
124 | 57FA3EBA1D0D877E00EFD324 /* AKPFlowLayout.h in Headers */,
125 | );
126 | runOnlyForDeploymentPostprocessing = 0;
127 | };
128 | /* End PBXHeadersBuildPhase section */
129 |
130 | /* Begin PBXNativeTarget section */
131 | 57FA3EB51D0D877E00EFD324 /* AKPFlowLayout */ = {
132 | isa = PBXNativeTarget;
133 | buildConfigurationList = 57FA3ECA1D0D877E00EFD324 /* Build configuration list for PBXNativeTarget "AKPFlowLayout" */;
134 | buildPhases = (
135 | 57FA3EB11D0D877E00EFD324 /* Sources */,
136 | 57FA3EB21D0D877E00EFD324 /* Frameworks */,
137 | 57FA3EB31D0D877E00EFD324 /* Headers */,
138 | 57FA3EB41D0D877E00EFD324 /* Resources */,
139 | );
140 | buildRules = (
141 | );
142 | dependencies = (
143 | );
144 | name = AKPFlowLayout;
145 | productName = AKPFlowLayout;
146 | productReference = 57FA3EB61D0D877E00EFD324 /* AKPFlowLayout.framework */;
147 | productType = "com.apple.product-type.framework";
148 | };
149 | 57FA3EBF1D0D877E00EFD324 /* AKPFlowLayoutTests */ = {
150 | isa = PBXNativeTarget;
151 | buildConfigurationList = 57FA3ECD1D0D877E00EFD324 /* Build configuration list for PBXNativeTarget "AKPFlowLayoutTests" */;
152 | buildPhases = (
153 | 57FA3EBC1D0D877E00EFD324 /* Sources */,
154 | 57FA3EBD1D0D877E00EFD324 /* Frameworks */,
155 | 57FA3EBE1D0D877E00EFD324 /* Resources */,
156 | );
157 | buildRules = (
158 | );
159 | dependencies = (
160 | 57FA3EC31D0D877E00EFD324 /* PBXTargetDependency */,
161 | );
162 | name = AKPFlowLayoutTests;
163 | productName = AKPFlowLayoutTests;
164 | productReference = 57FA3EC01D0D877E00EFD324 /* AKPFlowLayoutTests.xctest */;
165 | productType = "com.apple.product-type.bundle.unit-test";
166 | };
167 | /* End PBXNativeTarget section */
168 |
169 | /* Begin PBXProject section */
170 | 57FA3EAD1D0D877E00EFD324 /* Project object */ = {
171 | isa = PBXProject;
172 | attributes = {
173 | LastSwiftUpdateCheck = 0730;
174 | LastUpgradeCheck = 0800;
175 | ORGANIZATIONNAME = "Arseniy Kuznetsov";
176 | TargetAttributes = {
177 | 57FA3EB51D0D877E00EFD324 = {
178 | CreatedOnToolsVersion = 7.3.1;
179 | DevelopmentTeam = 73TM6WSTY5;
180 | LastSwiftMigration = 0800;
181 | };
182 | 57FA3EBF1D0D877E00EFD324 = {
183 | CreatedOnToolsVersion = 7.3.1;
184 | DevelopmentTeam = 73TM6WSTY5;
185 | LastSwiftMigration = 0800;
186 | };
187 | };
188 | };
189 | buildConfigurationList = 57FA3EB01D0D877E00EFD324 /* Build configuration list for PBXProject "AKPFlowLayout" */;
190 | compatibilityVersion = "Xcode 3.2";
191 | developmentRegion = English;
192 | hasScannedForEncodings = 0;
193 | knownRegions = (
194 | en,
195 | );
196 | mainGroup = 57FA3EAC1D0D877E00EFD324;
197 | productRefGroup = 57FA3EB71D0D877E00EFD324 /* Products */;
198 | projectDirPath = "";
199 | projectRoot = "";
200 | targets = (
201 | 57FA3EB51D0D877E00EFD324 /* AKPFlowLayout */,
202 | 57FA3EBF1D0D877E00EFD324 /* AKPFlowLayoutTests */,
203 | );
204 | };
205 | /* End PBXProject section */
206 |
207 | /* Begin PBXResourcesBuildPhase section */
208 | 57FA3EB41D0D877E00EFD324 /* Resources */ = {
209 | isa = PBXResourcesBuildPhase;
210 | buildActionMask = 2147483647;
211 | files = (
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | };
215 | 57FA3EBE1D0D877E00EFD324 /* Resources */ = {
216 | isa = PBXResourcesBuildPhase;
217 | buildActionMask = 2147483647;
218 | files = (
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXSourcesBuildPhase section */
225 | 57FA3EB11D0D877E00EFD324 /* Sources */ = {
226 | isa = PBXSourcesBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | 57FA3F0E1D0D8FC300EFD324 /* AKPFlowLayoutError.swift in Sources */,
230 | 57C203C81D103318004AB164 /* AKPFlowLayoutAttributes.swift in Sources */,
231 | 57FA3F0D1D0D8FC300EFD324 /* AKPFlowLayout.swift in Sources */,
232 | 57FA3F0F1D0D8FC300EFD324 /* AKPLayoutConfigOptions.swift in Sources */,
233 | );
234 | runOnlyForDeploymentPostprocessing = 0;
235 | };
236 | 57FA3EBC1D0D877E00EFD324 /* Sources */ = {
237 | isa = PBXSourcesBuildPhase;
238 | buildActionMask = 2147483647;
239 | files = (
240 | 57FA3EC61D0D877E00EFD324 /* AKPFlowLayoutTests.swift in Sources */,
241 | );
242 | runOnlyForDeploymentPostprocessing = 0;
243 | };
244 | /* End PBXSourcesBuildPhase section */
245 |
246 | /* Begin PBXTargetDependency section */
247 | 57FA3EC31D0D877E00EFD324 /* PBXTargetDependency */ = {
248 | isa = PBXTargetDependency;
249 | target = 57FA3EB51D0D877E00EFD324 /* AKPFlowLayout */;
250 | targetProxy = 57FA3EC21D0D877E00EFD324 /* PBXContainerItemProxy */;
251 | };
252 | /* End PBXTargetDependency section */
253 |
254 | /* Begin XCBuildConfiguration section */
255 | 57FA3EC81D0D877E00EFD324 /* Debug */ = {
256 | isa = XCBuildConfiguration;
257 | buildSettings = {
258 | ALWAYS_SEARCH_USER_PATHS = NO;
259 | CLANG_ANALYZER_NONNULL = YES;
260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
261 | CLANG_CXX_LIBRARY = "libc++";
262 | CLANG_ENABLE_MODULES = YES;
263 | CLANG_ENABLE_OBJC_ARC = YES;
264 | CLANG_WARN_BOOL_CONVERSION = YES;
265 | CLANG_WARN_CONSTANT_CONVERSION = YES;
266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
267 | CLANG_WARN_EMPTY_BODY = YES;
268 | CLANG_WARN_ENUM_CONVERSION = YES;
269 | CLANG_WARN_INFINITE_RECURSION = YES;
270 | CLANG_WARN_INT_CONVERSION = YES;
271 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
272 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
273 | CLANG_WARN_UNREACHABLE_CODE = YES;
274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
275 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
276 | COPY_PHASE_STRIP = NO;
277 | CURRENT_PROJECT_VERSION = 1;
278 | DEBUG_INFORMATION_FORMAT = dwarf;
279 | ENABLE_STRICT_OBJC_MSGSEND = YES;
280 | ENABLE_TESTABILITY = YES;
281 | GCC_C_LANGUAGE_STANDARD = gnu99;
282 | GCC_DYNAMIC_NO_PIC = NO;
283 | GCC_NO_COMMON_BLOCKS = YES;
284 | GCC_OPTIMIZATION_LEVEL = 0;
285 | GCC_PREPROCESSOR_DEFINITIONS = (
286 | "DEBUG=1",
287 | "$(inherited)",
288 | );
289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
291 | GCC_WARN_UNDECLARED_SELECTOR = YES;
292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
293 | GCC_WARN_UNUSED_FUNCTION = YES;
294 | GCC_WARN_UNUSED_VARIABLE = YES;
295 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
296 | MTL_ENABLE_DEBUG_INFO = YES;
297 | ONLY_ACTIVE_ARCH = YES;
298 | SDKROOT = iphoneos;
299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
300 | TARGETED_DEVICE_FAMILY = "1,2";
301 | VERSIONING_SYSTEM = "apple-generic";
302 | VERSION_INFO_PREFIX = "";
303 | };
304 | name = Debug;
305 | };
306 | 57FA3EC91D0D877E00EFD324 /* Release */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | CLANG_ANALYZER_NONNULL = YES;
311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
312 | CLANG_CXX_LIBRARY = "libc++";
313 | CLANG_ENABLE_MODULES = YES;
314 | CLANG_ENABLE_OBJC_ARC = YES;
315 | CLANG_WARN_BOOL_CONVERSION = YES;
316 | CLANG_WARN_CONSTANT_CONVERSION = YES;
317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
318 | CLANG_WARN_EMPTY_BODY = YES;
319 | CLANG_WARN_ENUM_CONVERSION = YES;
320 | CLANG_WARN_INFINITE_RECURSION = YES;
321 | CLANG_WARN_INT_CONVERSION = YES;
322 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
323 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
324 | CLANG_WARN_UNREACHABLE_CODE = YES;
325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
326 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
327 | COPY_PHASE_STRIP = NO;
328 | CURRENT_PROJECT_VERSION = 1;
329 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
330 | ENABLE_NS_ASSERTIONS = NO;
331 | ENABLE_STRICT_OBJC_MSGSEND = YES;
332 | GCC_C_LANGUAGE_STANDARD = gnu99;
333 | GCC_NO_COMMON_BLOCKS = YES;
334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
336 | GCC_WARN_UNDECLARED_SELECTOR = YES;
337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
338 | GCC_WARN_UNUSED_FUNCTION = YES;
339 | GCC_WARN_UNUSED_VARIABLE = YES;
340 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
341 | MTL_ENABLE_DEBUG_INFO = NO;
342 | SDKROOT = iphoneos;
343 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
344 | TARGETED_DEVICE_FAMILY = "1,2";
345 | VALIDATE_PRODUCT = YES;
346 | VERSIONING_SYSTEM = "apple-generic";
347 | VERSION_INFO_PREFIX = "";
348 | };
349 | name = Release;
350 | };
351 | 57FA3ECB1D0D877E00EFD324 /* Debug */ = {
352 | isa = XCBuildConfiguration;
353 | buildSettings = {
354 | CLANG_ENABLE_MODULES = YES;
355 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
356 | DEFINES_MODULE = YES;
357 | DEVELOPMENT_TEAM = 73TM6WSTY5;
358 | DYLIB_COMPATIBILITY_VERSION = 1;
359 | DYLIB_CURRENT_VERSION = 1;
360 | DYLIB_INSTALL_NAME_BASE = "@rpath";
361 | INFOPLIST_FILE = AKPFlowLayout/Info.plist;
362 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
363 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
364 | OTHER_SWIFT_FLAGS = "";
365 | PRODUCT_BUNDLE_IDENTIFIER = com.akpw.AKPFlowLayout;
366 | PRODUCT_NAME = "$(TARGET_NAME)";
367 | SKIP_INSTALL = YES;
368 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
369 | SWIFT_VERSION = 3.0;
370 | };
371 | name = Debug;
372 | };
373 | 57FA3ECC1D0D877E00EFD324 /* Release */ = {
374 | isa = XCBuildConfiguration;
375 | buildSettings = {
376 | CLANG_ENABLE_MODULES = YES;
377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
378 | DEFINES_MODULE = YES;
379 | DEVELOPMENT_TEAM = 73TM6WSTY5;
380 | DYLIB_COMPATIBILITY_VERSION = 1;
381 | DYLIB_CURRENT_VERSION = 1;
382 | DYLIB_INSTALL_NAME_BASE = "@rpath";
383 | INFOPLIST_FILE = AKPFlowLayout/Info.plist;
384 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
386 | OTHER_SWIFT_FLAGS = "";
387 | PRODUCT_BUNDLE_IDENTIFIER = com.akpw.AKPFlowLayout;
388 | PRODUCT_NAME = "$(TARGET_NAME)";
389 | SKIP_INSTALL = YES;
390 | SWIFT_VERSION = 3.0;
391 | };
392 | name = Release;
393 | };
394 | 57FA3ECE1D0D877E00EFD324 /* Debug */ = {
395 | isa = XCBuildConfiguration;
396 | buildSettings = {
397 | DEVELOPMENT_TEAM = 73TM6WSTY5;
398 | FRAMEWORK_SEARCH_PATHS = (
399 | "$(inherited)",
400 | "$(PROJECT_DIR)/Carthage/Checkouts/Nimble/build/Debug-iphoneos",
401 | "$(PROJECT_DIR)/Carthage/Checkouts/Quick/build/Debug-iphoneos",
402 | );
403 | INFOPLIST_FILE = AKPFlowLayoutTests/Info.plist;
404 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
405 | PRODUCT_BUNDLE_IDENTIFIER = com.akpw.AKPFlowLayoutTests;
406 | PRODUCT_NAME = "$(TARGET_NAME)";
407 | SWIFT_VERSION = 3.0;
408 | };
409 | name = Debug;
410 | };
411 | 57FA3ECF1D0D877E00EFD324 /* Release */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | DEVELOPMENT_TEAM = 73TM6WSTY5;
415 | FRAMEWORK_SEARCH_PATHS = (
416 | "$(inherited)",
417 | "$(PROJECT_DIR)/Carthage/Checkouts/Nimble/build/Debug-iphoneos",
418 | "$(PROJECT_DIR)/Carthage/Checkouts/Quick/build/Debug-iphoneos",
419 | );
420 | INFOPLIST_FILE = AKPFlowLayoutTests/Info.plist;
421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
422 | PRODUCT_BUNDLE_IDENTIFIER = com.akpw.AKPFlowLayoutTests;
423 | PRODUCT_NAME = "$(TARGET_NAME)";
424 | SWIFT_VERSION = 3.0;
425 | };
426 | name = Release;
427 | };
428 | /* End XCBuildConfiguration section */
429 |
430 | /* Begin XCConfigurationList section */
431 | 57FA3EB01D0D877E00EFD324 /* Build configuration list for PBXProject "AKPFlowLayout" */ = {
432 | isa = XCConfigurationList;
433 | buildConfigurations = (
434 | 57FA3EC81D0D877E00EFD324 /* Debug */,
435 | 57FA3EC91D0D877E00EFD324 /* Release */,
436 | );
437 | defaultConfigurationIsVisible = 0;
438 | defaultConfigurationName = Release;
439 | };
440 | 57FA3ECA1D0D877E00EFD324 /* Build configuration list for PBXNativeTarget "AKPFlowLayout" */ = {
441 | isa = XCConfigurationList;
442 | buildConfigurations = (
443 | 57FA3ECB1D0D877E00EFD324 /* Debug */,
444 | 57FA3ECC1D0D877E00EFD324 /* Release */,
445 | );
446 | defaultConfigurationIsVisible = 0;
447 | defaultConfigurationName = Release;
448 | };
449 | 57FA3ECD1D0D877E00EFD324 /* Build configuration list for PBXNativeTarget "AKPFlowLayoutTests" */ = {
450 | isa = XCConfigurationList;
451 | buildConfigurations = (
452 | 57FA3ECE1D0D877E00EFD324 /* Debug */,
453 | 57FA3ECF1D0D877E00EFD324 /* Release */,
454 | );
455 | defaultConfigurationIsVisible = 0;
456 | defaultConfigurationName = Release;
457 | };
458 | /* End XCConfigurationList section */
459 | };
460 | rootObject = 57FA3EAD1D0D877E00EFD324 /* Project object */;
461 | }
462 |
--------------------------------------------------------------------------------