├── StickyHeaders.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── StickyHeaders
├── SectionHeader.swift
├── UIColorConvenience.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── AppDelegate.swift
├── SectionHeader.xib
├── StickyHeadersCollectionViewFlowLayout.swift
└── ViewController.swift
├── README.md
└── .gitignore
/StickyHeaders.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/StickyHeaders/SectionHeader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SectionHeader.swift
3 | // StickyHeaders
4 | //
5 | // Created by Bart Jacobs on 01/10/16.
6 | // Copyright © 2016 Cocoacasts. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SectionHeader: UICollectionReusableView {
12 |
13 | // MARK: - Properties
14 |
15 | @IBOutlet var titleLabel: UILabel!
16 |
17 | // MARK: - Initialization
18 |
19 | override func awakeFromNib() {
20 | super.awakeFromNib()
21 |
22 | // Configure Title Label
23 | titleLabel.textColor = .white
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/StickyHeaders/UIColorConvenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorConvenience.swift
3 | // StickyHeaders
4 | //
5 | // Created by Bart Jacobs on 01/10/16.
6 | // Copyright © 2016 Cocoacasts. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIColor {
12 |
13 | static func random() -> UIColor {
14 | let r:CGFloat = CGFloat(arc4random_uniform(255)) / 255.0
15 | let g:CGFloat = CGFloat(arc4random_uniform(255)) / 255.0
16 | let b:CGFloat = CGFloat(arc4random_uniform(255)) / 255.0
17 | return UIColor(red: r, green: g, blue: b, alpha: 1.0)
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/StickyHeaders/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### How to Add Sticky Section Headers to a Collection View
2 |
3 | #### Author: Bart Jacobs
4 |
5 | Even though `UICollectionView` is incredibly flexible and versatile, trivial things are sometimes difficult to accomplish. `UITableView`, on the other hand, has more configuration options, but it is harder to customize. Sticky section headers, for example, are built into table views. Adding them to a collection view requires a bit of extra work.
6 |
7 | If your project targets iOS 9 or higher, then you are in luck. Since iOS 9, the `UICollectionViewFlowLayout` class has two properties, `sectionHeadersPinToVisibleBounds` and `sectionFootersPinToVisibleBounds`, that make it very easy to make section headers and section footers sticky. It is no coincidence that these properties are members of the `UICollectionViewFlowLayout` class. It is `UICollectionViewFlowLayout` we need to subclass to add sticky section headers.
8 |
9 | **Read this article on the [blog](https://cocoacasts.com/how-to-add-sticky-section-headers-to-a-collection-view/)**.
10 |
--------------------------------------------------------------------------------
/StickyHeaders/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 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/StickyHeaders/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 |
--------------------------------------------------------------------------------
/StickyHeaders/Base.lproj/LaunchScreen.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 |
--------------------------------------------------------------------------------
/StickyHeaders/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // StickyHeaders
4 | //
5 | // Created by Bart Jacobs on 01/10/16.
6 | // Copyright © 2016 Cocoacasts. 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: [UIApplicationLaunchOptionsKey: 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 invalidate graphics rendering callbacks. 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 active 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 |
--------------------------------------------------------------------------------
/StickyHeaders/SectionHeader.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #########################
2 | # .gitignore file for Xcode4 / OS X Source projects
3 | #
4 | # NB: if you are storing "built" products, this WILL NOT WORK,
5 | # and you should use a different .gitignore (or none at all)
6 | # This file is for SOURCE projects, where there are many extra
7 | # files that we want to exclude
8 | #
9 | # For updates, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
10 | #########################
11 |
12 | #####
13 | # OS X temporary files that should never be committed
14 |
15 | .DS_Store
16 | *.swp
17 | *.lock
18 | profile
19 |
20 |
21 | ####
22 | # Xcode temporary files that should never be committed
23 | #
24 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this...
25 |
26 | *~.nib
27 |
28 |
29 | ####
30 | # Xcode build files -
31 | #
32 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData"
33 |
34 | DerivedData/
35 |
36 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build"
37 |
38 | build/
39 |
40 |
41 | #####
42 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
43 | #
44 | # This is complicated:
45 | #
46 | # SOMETIMES you need to put this file in version control.
47 | # Apple designed it poorly - if you use "custom executables", they are
48 | # saved in this file.
49 | # 99% of projects do NOT use those, so they do NOT want to version control this file.
50 | # ..but if you're in the 1%, comment out the line "*.pbxuser"
51 |
52 | *.pbxuser
53 | *.mode1v3
54 | *.mode2v3
55 | *.perspectivev3
56 | # NB: also, whitelist the default ones, some projects need to use these
57 | !default.pbxuser
58 | !default.mode1v3
59 | !default.mode2v3
60 | !default.perspectivev3
61 |
62 |
63 | ####
64 | # Xcode 4 - semi-personal settings, often included in workspaces
65 | #
66 | # You can safely ignore the xcuserdata files - but do NOT ignore the files next to them
67 | #
68 |
69 | xcuserdata
70 |
71 | ####
72 | # XCode 4 workspaces - more detailed
73 | #
74 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :)
75 | #
76 | # Workspace layout is quite spammy. For reference:
77 | #
78 | # (root)/
79 | # (project-name).xcodeproj/
80 | # project.pbxproj
81 | # project.xcworkspace/
82 | # contents.xcworkspacedata
83 | # xcuserdata/
84 | # (your name)/xcuserdatad/
85 | # xcuserdata/
86 | # (your name)/xcuserdatad/
87 | #
88 | #
89 | #
90 | # Xcode 4 workspaces - SHARED
91 | #
92 | # This is UNDOCUMENTED (google: "developer.apple.com xcshareddata" - 0 results
93 | # But if you're going to kill personal workspaces, at least keep the shared ones...
94 | #
95 | #
96 | !xcshareddata
97 |
98 | ####
99 | # XCode 4 build-schemes
100 | #
101 | # PRIVATE ones are stored inside xcuserdata
102 | !xcschemes
103 |
104 | ####
105 | # Xcode 4 - Deprecated classes
106 | #
107 | # Allegedly, if you manually "deprecate" your classes, they get moved here.
108 | #
109 | # We're using source-control, so this is a "feature" that we do not want!
110 |
111 | *.moved-aside
112 |
113 | # CocoaPods
114 | /Pods
115 |
--------------------------------------------------------------------------------
/StickyHeaders/StickyHeadersCollectionViewFlowLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StickyHeadersCollectionViewFlowLayout.swift
3 | // StickyHeaders
4 | //
5 | // Created by Bart Jacobs on 01/10/16.
6 | // Copyright © 2016 Cocoacasts. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class StickyHeadersCollectionViewFlowLayout: UICollectionViewFlowLayout {
12 |
13 | // MARK: - Collection View Flow Layout Methods
14 |
15 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
16 | return true
17 | }
18 |
19 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
20 | guard let layoutAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
21 |
22 | // Helpers
23 | let sectionsToAdd = NSMutableIndexSet()
24 | var newLayoutAttributes = [UICollectionViewLayoutAttributes]()
25 |
26 | for layoutAttributesSet in layoutAttributes {
27 | if layoutAttributesSet.representedElementCategory == .cell {
28 | // Add Layout Attributes
29 | newLayoutAttributes.append(layoutAttributesSet)
30 |
31 | // Update Sections to Add
32 | sectionsToAdd.add(layoutAttributesSet.indexPath.section)
33 |
34 | } else if layoutAttributesSet.representedElementCategory == .supplementaryView {
35 | // Update Sections to Add
36 | sectionsToAdd.add(layoutAttributesSet.indexPath.section)
37 | }
38 | }
39 |
40 | for section in sectionsToAdd {
41 | let indexPath = IndexPath(item: 0, section: section)
42 |
43 | if let sectionAttributes = self.layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: indexPath) {
44 | newLayoutAttributes.append(sectionAttributes)
45 | }
46 | }
47 |
48 | return newLayoutAttributes
49 | }
50 |
51 | override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
52 | guard let layoutAttributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) else { return nil }
53 | guard let boundaries = boundaries(forSection: indexPath.section) else { return layoutAttributes }
54 | guard let collectionView = collectionView else { return layoutAttributes }
55 |
56 | // Helpers
57 | let contentOffsetY = collectionView.contentOffset.y
58 | var frameForSupplementaryView = layoutAttributes.frame
59 |
60 | let minimum = boundaries.minimum - frameForSupplementaryView.height
61 | let maximum = boundaries.maximum - frameForSupplementaryView.height
62 |
63 | if contentOffsetY < minimum {
64 | frameForSupplementaryView.origin.y = minimum
65 | } else if contentOffsetY > maximum {
66 | frameForSupplementaryView.origin.y = maximum
67 | } else {
68 | frameForSupplementaryView.origin.y = contentOffsetY
69 | }
70 |
71 | layoutAttributes.frame = frameForSupplementaryView
72 |
73 | return layoutAttributes
74 | }
75 |
76 | // MARK: - Helper Methods
77 |
78 | func boundaries(forSection section: Int) -> (minimum: CGFloat, maximum: CGFloat)? {
79 | // Helpers
80 | var result = (minimum: CGFloat(0.0), maximum: CGFloat(0.0))
81 |
82 | // Exit Early
83 | guard let collectionView = collectionView else { return result }
84 |
85 | // Fetch Number of Items for Section
86 | let numberOfItems = collectionView.numberOfItems(inSection: section)
87 |
88 | // Exit Early
89 | guard numberOfItems > 0 else { return result }
90 |
91 | if let firstItem = layoutAttributesForItem(at: IndexPath(item: 0, section: section)),
92 | let lastItem = layoutAttributesForItem(at: IndexPath(item: (numberOfItems - 1), section: section)) {
93 | result.minimum = firstItem.frame.minY
94 | result.maximum = lastItem.frame.maxY
95 |
96 | // Take Header Size Into Account
97 | result.minimum -= headerReferenceSize.height
98 | result.maximum -= headerReferenceSize.height
99 |
100 | // Take Section Inset Into Account
101 | result.minimum -= sectionInset.top
102 | result.maximum += (sectionInset.top + sectionInset.bottom)
103 | }
104 |
105 | return result
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/StickyHeaders/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // StickyHeaders
4 | //
5 | // Created by Bart Jacobs on 01/10/16.
6 | // Copyright © 2016 Cocoacasts. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | // MARK: - Properties
14 |
15 | fileprivate let CellIdentifier = "Cell"
16 | fileprivate let HeaderIdentifier = "Header"
17 |
18 | // MARK: - View Life Cycle
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | setupCollectionView()
24 | }
25 |
26 | override var preferredStatusBarStyle: UIStatusBarStyle {
27 | return .lightContent
28 | }
29 |
30 | // MARK: - View Methods
31 |
32 | private func setupCollectionView() {
33 | // Initialize Collection View Flow Layout
34 | let layout = StickyHeadersCollectionViewFlowLayout()
35 |
36 | // Initialize Collection View
37 | let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
38 |
39 | // Configure Collection View
40 | collectionView.delegate = self
41 | collectionView.dataSource = self
42 | collectionView.backgroundColor = .white
43 |
44 | // Register Classes for Cell Reuse
45 | collectionView.register(UICollectionViewCell.classForCoder(), forCellWithReuseIdentifier: CellIdentifier)
46 |
47 | // Register XIB for Supplementary View Reuse
48 | let XIB = UINib.init(nibName: "SectionHeader", bundle: Bundle.main)
49 | collectionView.register(XIB, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderIdentifier)
50 |
51 | // Add as Subview
52 | view.addSubview(collectionView)
53 |
54 | // Add Constraints
55 | collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
56 | collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
57 | collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
58 | collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
59 | }
60 |
61 | }
62 |
63 | extension ViewController: UICollectionViewDataSource {
64 |
65 | // MARK: - Collection View Data Source Methods
66 |
67 | func numberOfSections(in collectionView: UICollectionView) -> Int {
68 | return 20
69 | }
70 |
71 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
72 | return Int(arc4random_uniform(10) + 2)
73 | }
74 |
75 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
76 | // Dequeue Reusable Cell
77 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath)
78 |
79 | // Configure Cell
80 | cell.backgroundColor = UIColor(red:0.2, green:0.25, blue:0.3, alpha:1.0)
81 | cell.contentView.backgroundColor = UIColor(red:0.2, green:0.25, blue:0.3, alpha:1.0)
82 |
83 | return cell
84 | }
85 |
86 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
87 | // Dequeue Reusable Supplementary View
88 | if let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderIdentifier, for: indexPath) as? SectionHeader {
89 | // Configure Supplementary View
90 | supplementaryView.backgroundColor = .random()
91 | supplementaryView.titleLabel.text = "Section \(indexPath.section)"
92 |
93 | return supplementaryView
94 | }
95 |
96 | fatalError("Unable to Dequeue Reusable Supplementary View")
97 | }
98 |
99 | }
100 |
101 | extension ViewController: UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
102 |
103 | // MARK: - Collection View Delegate Flow Layout Methods
104 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
105 | return CGSize(width: collectionView.bounds.width, height: 44.0)
106 | }
107 |
108 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
109 | return UIEdgeInsets.zero
110 | }
111 |
112 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
113 | return 2.0
114 | }
115 |
116 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
117 | return 0.0
118 | }
119 |
120 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
121 | return CGSize(width: collectionView.bounds.width, height: 80.0)
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/StickyHeaders.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CC144FDC1D9F8C95004D8E2B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC144FDB1D9F8C95004D8E2B /* AppDelegate.swift */; };
11 | CC144FDE1D9F8C95004D8E2B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC144FDD1D9F8C95004D8E2B /* ViewController.swift */; };
12 | CC144FE11D9F8C95004D8E2B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CC144FDF1D9F8C95004D8E2B /* Main.storyboard */; };
13 | CC144FE31D9F8C95004D8E2B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CC144FE21D9F8C95004D8E2B /* Assets.xcassets */; };
14 | CC144FE61D9F8C95004D8E2B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CC144FE41D9F8C95004D8E2B /* LaunchScreen.storyboard */; };
15 | CC144FEE1D9F8D39004D8E2B /* StickyHeadersCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC144FED1D9F8D39004D8E2B /* StickyHeadersCollectionViewFlowLayout.swift */; };
16 | CC144FF11D9F8EA2004D8E2B /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC144FEF1D9F8EA2004D8E2B /* SectionHeader.swift */; };
17 | CC144FF21D9F8EA2004D8E2B /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = CC144FF01D9F8EA2004D8E2B /* SectionHeader.xib */; };
18 | CC144FF41D9F92C4004D8E2B /* UIColorConvenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC144FF31D9F92C4004D8E2B /* UIColorConvenience.swift */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | CC144FD81D9F8C95004D8E2B /* StickyHeaders.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StickyHeaders.app; sourceTree = BUILT_PRODUCTS_DIR; };
23 | CC144FDB1D9F8C95004D8E2B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
24 | CC144FDD1D9F8C95004D8E2B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
25 | CC144FE01D9F8C95004D8E2B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
26 | CC144FE21D9F8C95004D8E2B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | CC144FE51D9F8C95004D8E2B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
28 | CC144FE71D9F8C95004D8E2B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | CC144FED1D9F8D39004D8E2B /* StickyHeadersCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickyHeadersCollectionViewFlowLayout.swift; sourceTree = ""; };
30 | CC144FEF1D9F8EA2004D8E2B /* SectionHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = ""; };
31 | CC144FF01D9F8EA2004D8E2B /* SectionHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = ""; };
32 | CC144FF31D9F92C4004D8E2B /* UIColorConvenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorConvenience.swift; sourceTree = ""; };
33 | /* End PBXFileReference section */
34 |
35 | /* Begin PBXFrameworksBuildPhase section */
36 | CC144FD51D9F8C95004D8E2B /* Frameworks */ = {
37 | isa = PBXFrameworksBuildPhase;
38 | buildActionMask = 2147483647;
39 | files = (
40 | );
41 | runOnlyForDeploymentPostprocessing = 0;
42 | };
43 | /* End PBXFrameworksBuildPhase section */
44 |
45 | /* Begin PBXGroup section */
46 | CC144FCF1D9F8C95004D8E2B = {
47 | isa = PBXGroup;
48 | children = (
49 | CC144FDA1D9F8C95004D8E2B /* StickyHeaders */,
50 | CC144FD91D9F8C95004D8E2B /* Products */,
51 | );
52 | sourceTree = "";
53 | };
54 | CC144FD91D9F8C95004D8E2B /* Products */ = {
55 | isa = PBXGroup;
56 | children = (
57 | CC144FD81D9F8C95004D8E2B /* StickyHeaders.app */,
58 | );
59 | name = Products;
60 | sourceTree = "";
61 | };
62 | CC144FDA1D9F8C95004D8E2B /* StickyHeaders */ = {
63 | isa = PBXGroup;
64 | children = (
65 | CC144FDB1D9F8C95004D8E2B /* AppDelegate.swift */,
66 | CC144FDD1D9F8C95004D8E2B /* ViewController.swift */,
67 | CC144FEF1D9F8EA2004D8E2B /* SectionHeader.swift */,
68 | CC144FF01D9F8EA2004D8E2B /* SectionHeader.xib */,
69 | CC144FED1D9F8D39004D8E2B /* StickyHeadersCollectionViewFlowLayout.swift */,
70 | CC144FF31D9F92C4004D8E2B /* UIColorConvenience.swift */,
71 | CC144FDF1D9F8C95004D8E2B /* Main.storyboard */,
72 | CC144FE21D9F8C95004D8E2B /* Assets.xcassets */,
73 | CC144FE41D9F8C95004D8E2B /* LaunchScreen.storyboard */,
74 | CC144FE71D9F8C95004D8E2B /* Info.plist */,
75 | );
76 | path = StickyHeaders;
77 | sourceTree = "";
78 | };
79 | /* End PBXGroup section */
80 |
81 | /* Begin PBXNativeTarget section */
82 | CC144FD71D9F8C95004D8E2B /* StickyHeaders */ = {
83 | isa = PBXNativeTarget;
84 | buildConfigurationList = CC144FEA1D9F8C95004D8E2B /* Build configuration list for PBXNativeTarget "StickyHeaders" */;
85 | buildPhases = (
86 | CC144FD41D9F8C95004D8E2B /* Sources */,
87 | CC144FD51D9F8C95004D8E2B /* Frameworks */,
88 | CC144FD61D9F8C95004D8E2B /* Resources */,
89 | );
90 | buildRules = (
91 | );
92 | dependencies = (
93 | );
94 | name = StickyHeaders;
95 | productName = StickyHeaders;
96 | productReference = CC144FD81D9F8C95004D8E2B /* StickyHeaders.app */;
97 | productType = "com.apple.product-type.application";
98 | };
99 | /* End PBXNativeTarget section */
100 |
101 | /* Begin PBXProject section */
102 | CC144FD01D9F8C95004D8E2B /* Project object */ = {
103 | isa = PBXProject;
104 | attributes = {
105 | LastSwiftUpdateCheck = 0800;
106 | LastUpgradeCheck = 0800;
107 | ORGANIZATIONNAME = Cocoacasts;
108 | TargetAttributes = {
109 | CC144FD71D9F8C95004D8E2B = {
110 | CreatedOnToolsVersion = 8.0;
111 | DevelopmentTeam = 2493UGBPKJ;
112 | ProvisioningStyle = Automatic;
113 | };
114 | };
115 | };
116 | buildConfigurationList = CC144FD31D9F8C95004D8E2B /* Build configuration list for PBXProject "StickyHeaders" */;
117 | compatibilityVersion = "Xcode 3.2";
118 | developmentRegion = English;
119 | hasScannedForEncodings = 0;
120 | knownRegions = (
121 | en,
122 | Base,
123 | );
124 | mainGroup = CC144FCF1D9F8C95004D8E2B;
125 | productRefGroup = CC144FD91D9F8C95004D8E2B /* Products */;
126 | projectDirPath = "";
127 | projectRoot = "";
128 | targets = (
129 | CC144FD71D9F8C95004D8E2B /* StickyHeaders */,
130 | );
131 | };
132 | /* End PBXProject section */
133 |
134 | /* Begin PBXResourcesBuildPhase section */
135 | CC144FD61D9F8C95004D8E2B /* Resources */ = {
136 | isa = PBXResourcesBuildPhase;
137 | buildActionMask = 2147483647;
138 | files = (
139 | CC144FF21D9F8EA2004D8E2B /* SectionHeader.xib in Resources */,
140 | CC144FE61D9F8C95004D8E2B /* LaunchScreen.storyboard in Resources */,
141 | CC144FE31D9F8C95004D8E2B /* Assets.xcassets in Resources */,
142 | CC144FE11D9F8C95004D8E2B /* Main.storyboard in Resources */,
143 | );
144 | runOnlyForDeploymentPostprocessing = 0;
145 | };
146 | /* End PBXResourcesBuildPhase section */
147 |
148 | /* Begin PBXSourcesBuildPhase section */
149 | CC144FD41D9F8C95004D8E2B /* Sources */ = {
150 | isa = PBXSourcesBuildPhase;
151 | buildActionMask = 2147483647;
152 | files = (
153 | CC144FF11D9F8EA2004D8E2B /* SectionHeader.swift in Sources */,
154 | CC144FF41D9F92C4004D8E2B /* UIColorConvenience.swift in Sources */,
155 | CC144FDE1D9F8C95004D8E2B /* ViewController.swift in Sources */,
156 | CC144FDC1D9F8C95004D8E2B /* AppDelegate.swift in Sources */,
157 | CC144FEE1D9F8D39004D8E2B /* StickyHeadersCollectionViewFlowLayout.swift in Sources */,
158 | );
159 | runOnlyForDeploymentPostprocessing = 0;
160 | };
161 | /* End PBXSourcesBuildPhase section */
162 |
163 | /* Begin PBXVariantGroup section */
164 | CC144FDF1D9F8C95004D8E2B /* Main.storyboard */ = {
165 | isa = PBXVariantGroup;
166 | children = (
167 | CC144FE01D9F8C95004D8E2B /* Base */,
168 | );
169 | name = Main.storyboard;
170 | sourceTree = "";
171 | };
172 | CC144FE41D9F8C95004D8E2B /* LaunchScreen.storyboard */ = {
173 | isa = PBXVariantGroup;
174 | children = (
175 | CC144FE51D9F8C95004D8E2B /* Base */,
176 | );
177 | name = LaunchScreen.storyboard;
178 | sourceTree = "";
179 | };
180 | /* End PBXVariantGroup section */
181 |
182 | /* Begin XCBuildConfiguration section */
183 | CC144FE81D9F8C95004D8E2B /* Debug */ = {
184 | isa = XCBuildConfiguration;
185 | buildSettings = {
186 | ALWAYS_SEARCH_USER_PATHS = NO;
187 | CLANG_ANALYZER_NONNULL = YES;
188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
189 | CLANG_CXX_LIBRARY = "libc++";
190 | CLANG_ENABLE_MODULES = YES;
191 | CLANG_ENABLE_OBJC_ARC = YES;
192 | CLANG_WARN_BOOL_CONVERSION = YES;
193 | CLANG_WARN_CONSTANT_CONVERSION = YES;
194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
195 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
196 | CLANG_WARN_EMPTY_BODY = YES;
197 | CLANG_WARN_ENUM_CONVERSION = YES;
198 | CLANG_WARN_INFINITE_RECURSION = YES;
199 | CLANG_WARN_INT_CONVERSION = YES;
200 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
201 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
202 | CLANG_WARN_UNREACHABLE_CODE = YES;
203 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
204 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
205 | COPY_PHASE_STRIP = NO;
206 | DEBUG_INFORMATION_FORMAT = dwarf;
207 | ENABLE_STRICT_OBJC_MSGSEND = YES;
208 | ENABLE_TESTABILITY = YES;
209 | GCC_C_LANGUAGE_STANDARD = gnu99;
210 | GCC_DYNAMIC_NO_PIC = NO;
211 | GCC_NO_COMMON_BLOCKS = YES;
212 | GCC_OPTIMIZATION_LEVEL = 0;
213 | GCC_PREPROCESSOR_DEFINITIONS = (
214 | "DEBUG=1",
215 | "$(inherited)",
216 | );
217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
219 | GCC_WARN_UNDECLARED_SELECTOR = YES;
220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
221 | GCC_WARN_UNUSED_FUNCTION = YES;
222 | GCC_WARN_UNUSED_VARIABLE = YES;
223 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
224 | MTL_ENABLE_DEBUG_INFO = YES;
225 | ONLY_ACTIVE_ARCH = YES;
226 | SDKROOT = iphoneos;
227 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
228 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
229 | };
230 | name = Debug;
231 | };
232 | CC144FE91D9F8C95004D8E2B /* Release */ = {
233 | isa = XCBuildConfiguration;
234 | buildSettings = {
235 | ALWAYS_SEARCH_USER_PATHS = NO;
236 | CLANG_ANALYZER_NONNULL = YES;
237 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
238 | CLANG_CXX_LIBRARY = "libc++";
239 | CLANG_ENABLE_MODULES = YES;
240 | CLANG_ENABLE_OBJC_ARC = YES;
241 | CLANG_WARN_BOOL_CONVERSION = YES;
242 | CLANG_WARN_CONSTANT_CONVERSION = YES;
243 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
244 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
245 | CLANG_WARN_EMPTY_BODY = YES;
246 | CLANG_WARN_ENUM_CONVERSION = YES;
247 | CLANG_WARN_INFINITE_RECURSION = YES;
248 | CLANG_WARN_INT_CONVERSION = YES;
249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
250 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
251 | CLANG_WARN_UNREACHABLE_CODE = YES;
252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
253 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
254 | COPY_PHASE_STRIP = NO;
255 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
256 | ENABLE_NS_ASSERTIONS = NO;
257 | ENABLE_STRICT_OBJC_MSGSEND = YES;
258 | GCC_C_LANGUAGE_STANDARD = gnu99;
259 | GCC_NO_COMMON_BLOCKS = YES;
260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
262 | GCC_WARN_UNDECLARED_SELECTOR = YES;
263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
264 | GCC_WARN_UNUSED_FUNCTION = YES;
265 | GCC_WARN_UNUSED_VARIABLE = YES;
266 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
267 | MTL_ENABLE_DEBUG_INFO = NO;
268 | SDKROOT = iphoneos;
269 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
270 | VALIDATE_PRODUCT = YES;
271 | };
272 | name = Release;
273 | };
274 | CC144FEB1D9F8C95004D8E2B /* Debug */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
278 | DEVELOPMENT_TEAM = 2493UGBPKJ;
279 | INFOPLIST_FILE = StickyHeaders/Info.plist;
280 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
281 | PRODUCT_BUNDLE_IDENTIFIER = com.cocoacasts.StickyHeaders;
282 | PRODUCT_NAME = "$(TARGET_NAME)";
283 | SWIFT_VERSION = 3.0;
284 | };
285 | name = Debug;
286 | };
287 | CC144FEC1D9F8C95004D8E2B /* Release */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291 | DEVELOPMENT_TEAM = 2493UGBPKJ;
292 | INFOPLIST_FILE = StickyHeaders/Info.plist;
293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
294 | PRODUCT_BUNDLE_IDENTIFIER = com.cocoacasts.StickyHeaders;
295 | PRODUCT_NAME = "$(TARGET_NAME)";
296 | SWIFT_VERSION = 3.0;
297 | };
298 | name = Release;
299 | };
300 | /* End XCBuildConfiguration section */
301 |
302 | /* Begin XCConfigurationList section */
303 | CC144FD31D9F8C95004D8E2B /* Build configuration list for PBXProject "StickyHeaders" */ = {
304 | isa = XCConfigurationList;
305 | buildConfigurations = (
306 | CC144FE81D9F8C95004D8E2B /* Debug */,
307 | CC144FE91D9F8C95004D8E2B /* Release */,
308 | );
309 | defaultConfigurationIsVisible = 0;
310 | defaultConfigurationName = Release;
311 | };
312 | CC144FEA1D9F8C95004D8E2B /* Build configuration list for PBXNativeTarget "StickyHeaders" */ = {
313 | isa = XCConfigurationList;
314 | buildConfigurations = (
315 | CC144FEB1D9F8C95004D8E2B /* Debug */,
316 | CC144FEC1D9F8C95004D8E2B /* Release */,
317 | );
318 | defaultConfigurationIsVisible = 0;
319 | };
320 | /* End XCConfigurationList section */
321 | };
322 | rootObject = CC144FD01D9F8C95004D8E2B /* Project object */;
323 | }
324 |
--------------------------------------------------------------------------------