Skip to content

Commit

Permalink
Cleanup supplementary views, part 1, close #56 (#84)
Browse files Browse the repository at this point in the history
This still needs all kinds of work to correctly model how collection view supplementary views work, but this is a start.
We're artificially (and implicitly) restricted to headers and footers. We'll probably have to keep this limitation for 1.0.
This aims to make a more general transition easier.

- Make `kind` a property of `SupplementaryViewInfo`
- Make `ViewRegistrationMethod` a proper type on its own (which we can use for cell registration in #18)
  • Loading branch information
jessesquires authored Jan 26, 2018
1 parent cfaa9ae commit 6a92a4d
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 31 deletions.
1 change: 1 addition & 0 deletions Example/CollectionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ extension CollectionViewController {
viewInfo: SupplementaryViewInfo(
registrationMethod: .nib(name: "CollectionViewHeaderView", bundle: nil),
reuseIdentifier: "CollectionViewHeaderView",
kind: .header,
accessibilityFormat: "CollectionViewHeaderView"
)
)
Expand Down
4 changes: 4 additions & 0 deletions ReactiveLists.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
258E31D41F0D8F3100D6F324 /* SupplementaryViewInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 258E31D21F0D8F3100D6F324 /* SupplementaryViewInfo.swift */; };
25B1B0B920195F1C0036545F /* CollectionViewDriverDiffingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25B1B0B720195F160036545F /* CollectionViewDriverDiffingTests.swift */; };
25B1B0BA201A53CF0036545F /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32124A712019312200EE12FC /* Typealiases.swift */; };
32E7A1F8201BADE800B90EBC /* ViewRegistrationMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7A1F7201BADE800B90EBC /* ViewRegistrationMethod.swift */; };
351B0BB920168E2E0034569D /* CollectionViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 351B0BB420168D2E0034569D /* CollectionViewCells.swift */; };
351B0BBA20168E2E0034569D /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 351B0BB320168D2E0034569D /* CollectionViewController.swift */; };
357B96DA201934C50000443F /* CollectionToolCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 357B96D9201934C50000443F /* CollectionToolCell.xib */; };
Expand Down Expand Up @@ -114,6 +115,7 @@
276442FEC917A7E0F32CE5B4 /* Pods-ReactiveLists-ReactiveListsExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactiveLists-ReactiveListsExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ReactiveLists-ReactiveListsExample/Pods-ReactiveLists-ReactiveListsExample.debug.xcconfig"; sourceTree = "<group>"; };
2F35530D29268B112F99A187 /* Pods_ReactiveLists_ReactiveListsExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ReactiveLists_ReactiveListsExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
32124A712019312200EE12FC /* Typealiases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typealiases.swift; sourceTree = "<group>"; };
32E7A1F7201BADE800B90EBC /* ViewRegistrationMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewRegistrationMethod.swift; sourceTree = "<group>"; };
351B0BB320168D2E0034569D /* CollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewController.swift; sourceTree = "<group>"; };
351B0BB420168D2E0034569D /* CollectionViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCells.swift; sourceTree = "<group>"; };
357B96D9201934C50000443F /* CollectionToolCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CollectionToolCell.xib; sourceTree = "<group>"; };
Expand Down Expand Up @@ -262,6 +264,7 @@
258E31AE1F0D8D9C00D6F324 /* TableViewDriver.swift */,
258E31AF1F0D8D9C00D6F324 /* TableViewModel.swift */,
32124A712019312200EE12FC /* Typealiases.swift */,
32E7A1F7201BADE800B90EBC /* ViewRegistrationMethod.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -616,6 +619,7 @@
files = (
2541B73D1F29A13B002C3090 /* Diffing.swift in Sources */,
258E31B31F0D8D9C00D6F324 /* TableViewDriver.swift in Sources */,
32E7A1F8201BADE800B90EBC /* ViewRegistrationMethod.swift in Sources */,
258E31B11F0D8D9C00D6F324 /* CollectionViewDriver.swift in Sources */,
258E31B41F0D8D9C00D6F324 /* TableViewModel.swift in Sources */,
258E31D31F0D8F3100D6F324 /* AccessibilityFormats.swift in Sources */,
Expand Down
8 changes: 4 additions & 4 deletions Sources/CollectionViewDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,17 @@ public class CollectionViewDriver: NSObject, UICollectionViewDataSource, UIColle
if let header = $0.headerViewModel?.viewInfo {
switch header.registrationMethod {
case let .nib(name, bundle):
collectionView.register(UINib(nibName: name, bundle: bundle), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: header.reuseIdentifier)
collectionView.register(UINib(nibName: name, bundle: bundle), forSupplementaryViewOfKind: header.kind.collectionElementKind, withReuseIdentifier: header.reuseIdentifier)
case let .viewClass(viewClass):
collectionView.register(viewClass, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: header.reuseIdentifier)
collectionView.register(viewClass, forSupplementaryViewOfKind: header.kind.collectionElementKind, withReuseIdentifier: header.reuseIdentifier)
}
}
if let footer = $0.footerViewModel?.viewInfo {
switch footer.registrationMethod {
case let .nib(name, bundle):
collectionView.register(UINib(nibName: name, bundle: bundle), forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: footer.reuseIdentifier)
collectionView.register(UINib(nibName: name, bundle: bundle), forSupplementaryViewOfKind: footer.kind.collectionElementKind, withReuseIdentifier: footer.reuseIdentifier)
case let .viewClass(viewClass):
collectionView.register(viewClass, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: footer.reuseIdentifier)
collectionView.register(viewClass, forSupplementaryViewOfKind: footer.kind.collectionElementKind, withReuseIdentifier: footer.reuseIdentifier)
}
}
}
Expand Down
47 changes: 20 additions & 27 deletions Sources/SupplementaryViewInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,24 @@ import Foundation
import UIKit

public struct SupplementaryViewInfo {
/// The method for registering the supplementary view
public enum RegistrationMethod: Equatable {
/// Class-based views
case viewClass(AnyClass)
/// Nib-based views
case nib(name: String, bundle: Bundle?)

public static func == (lhs: RegistrationMethod, rhs: RegistrationMethod) -> Bool {
switch (lhs, rhs) {
case let (.viewClass(lhsClass), .viewClass(rhsClass)):
return lhsClass == rhsClass
case let (.nib(lhsName, lhsBundle), .nib(rhsName, rhsBundle)):
return lhsName == rhsName && lhsBundle == rhsBundle
default:
return false
}
}
}
public let registrationMethod: ViewRegistrationMethod

public let reuseIdentifier: String

public let kind: SupplementaryViewKind

let registrationMethod: RegistrationMethod
let reuseIdentifier: String
/// `TableViewDataSource` and `CollectionViewDataSource` will automatically apply an `accessibilityIdentifier` to the supplementary view based on this format
let accessibilityFormat: SupplementaryAccessibilityFormat
/// `TableViewDataSource` and `CollectionViewDataSource` will automatically apply
/// an `accessibilityIdentifier` to the supplementary view based on this format.
public let accessibilityFormat: SupplementaryAccessibilityFormat

public init(
registrationMethod: RegistrationMethod,
reuseIdentifier: String,
accessibilityFormat: SupplementaryAccessibilityFormat
) {
public init(registrationMethod: ViewRegistrationMethod,
reuseIdentifier: String,
kind: SupplementaryViewKind,
accessibilityFormat: SupplementaryAccessibilityFormat) {
self.registrationMethod = registrationMethod
self.reuseIdentifier = reuseIdentifier
self.kind = kind
self.accessibilityFormat = accessibilityFormat
}
}
Expand All @@ -57,7 +44,6 @@ public enum SupplementaryViewKind {
case header
case footer

/// Initialize with `UICollectionElementKindSectionHeader` or `UICollectionElementKindSectionFooter`
init?(collectionElementKindString: String) {
switch collectionElementKindString {
case UICollectionElementKindSectionHeader:
Expand All @@ -68,4 +54,11 @@ public enum SupplementaryViewKind {
return nil
}
}

var collectionElementKind: String {
switch self {
case .header: return UICollectionElementKindSectionHeader
case .footer: return UICollectionElementKindSectionFooter
}
}
}
39 changes: 39 additions & 0 deletions Sources/ViewRegistrationMethod.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// PlanGrid
// https://www.plangrid.com
// https://medium.com/plangrid-technology
//
// Documentation
// https://plangrid.github.io/ReactiveLists
//
// GitHub
// https://github.com/plangrid/ReactiveLists
//
// License
// Copyright © 2018-present PlanGrid, Inc.
// Released under an MIT license: https://opensource.org/licenses/MIT
//

import Foundation
import UIKit

/// The method for registering cells and supplementary views
public enum ViewRegistrationMethod {
/// Class-based views
case viewClass(AnyClass)
/// Nib-based views
case nib(name: String, bundle: Bundle?)
}

extension ViewRegistrationMethod: Equatable {
public static func == (lhs: ViewRegistrationMethod, rhs: ViewRegistrationMethod) -> Bool {
switch (lhs, rhs) {
case let (.viewClass(lhsClass), .viewClass(rhsClass)):
return lhsClass == rhsClass
case let (.nib(lhsName, lhsBundle), .nib(rhsName, rhsBundle)):
return lhsName == rhsName && lhsBundle == rhsBundle
default:
return false
}
}
}
1 change: 1 addition & 0 deletions Tests/Fixtures/TestCollectionViewModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct TestCollectionViewSupplementaryViewModel: CollectionViewSupplementaryView
self.viewInfo = SupplementaryViewInfo(
registrationMethod: .viewClass(viewKind == .header ? HeaderView.self : FooterView.self),
reuseIdentifier: "reuse_\(kindString)+\(sectionLabel)", // e.g. reuse_header+A
kind: viewKind,
accessibilityFormat: SupplementaryAccessibilityFormat("access_\(kindString)+%{section}")) // e.g. access_header+%{section}
}

Expand Down
1 change: 1 addition & 0 deletions Tests/Fixtures/TestTableViewModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct TestHeaderFooterViewModel: TableViewSectionHeaderFooterViewModel {
self.viewInfo = SupplementaryViewInfo(
registrationMethod: .viewClass(viewKind == .header ? HeaderView.self : FooterView.self),
reuseIdentifier: "reuse_\(kindString)+\(label)", // e.g. reuse_header_3
kind: viewKind,
accessibilityFormat: SupplementaryAccessibilityFormat("access_\(kindString)+%{section}")) // e.g. access_header+%{section}
}

Expand Down

0 comments on commit 6a92a4d

Please sign in to comment.