├── 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 | --------------------------------------------------------------------------------