Skip to content

Commit

Permalink
Merge pull request #193 from kyleve/kve/zindex-ordered-by-global-index
Browse files Browse the repository at this point in the history
Support tappable headers
  • Loading branch information
kyleve authored Aug 13, 2020
2 parents 0b5fc16 + 387c62e commit 53b9c51
Show file tree
Hide file tree
Showing 28 changed files with 600 additions and 78 deletions.
62 changes: 58 additions & 4 deletions BlueprintLists/Sources/HeaderFooter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,82 @@ public typealias BlueprintHeaderContent = BlueprintHeaderFooterContent
public typealias BlueprintFooterContent = BlueprintHeaderFooterContent


public protocol BlueprintHeaderFooterContent : HeaderFooterContent where ContentView == BlueprintView
public protocol BlueprintHeaderFooterContent : HeaderFooterContent
where
ContentView == BlueprintView,
BackgroundView == BlueprintView,
PressedBackgroundView == BlueprintView
{
//
// MARK: Creating Blueprint Element Representations
//

/// Required. Create and return the Blueprint element used to represent the content.
var elementRepresentation : Element { get }

/// Optional. Create and return the Blueprint element used to represent the background of the content.
/// You usually provide this method alongside `pressedBackground`, if your content
/// supports an `onTap` handler.
///
/// Note
/// ----
/// The default implementation of this method returns nil, and provides no background.
///
var background : Element? { get }

/// Optional. Create and return the Blueprint element used to represent the background of the content when it is pressed.
/// You usually provide this method alongside `background`, if your content supports an `onTap` handler.
///
/// Note
/// ----
/// The default implementation of this method returns nil, and provides no selected background.
///
var pressedBackground : Element? { get }
}


public extension BlueprintHeaderFooterContent
{
//
// MARK: BlueprintHeaderFooterContent
//

var background : Element? {
nil
}

var pressedBackground : Element? {
nil
}

//
// MARK: HeaderFooterContent
//

func apply(to view: ContentView, reason: ApplyReason)
func apply(to views: HeaderFooterContentViews<Self>, reason: ApplyReason)
{
views.content.element = self.elementRepresentation
views.background.element = self.background
views.pressed.element = self.pressedBackground
}

static func createReusableContentView(frame: CGRect) -> ContentView
{
view.element = self.elementRepresentation
let view = BlueprintView(frame: frame)
view.backgroundColor = .clear

return view
}

static func createReusableBackgroundView(frame: CGRect) -> BackgroundView
{
let view = BlueprintView(frame: frame)
view.backgroundColor = .clear

return view
}

static func createReusableHeaderFooterView(frame: CGRect) -> ContentView
static func createReusablePressedBackgroundView(frame: CGRect) -> PressedBackgroundView
{
let view = BlueprintView(frame: frame)
view.backgroundColor = .clear
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

- [Simplify `Sizing` now that enums support default associated values.](https://github.com/kyleve/Listable/pull/189). Now instead of separate `.thatFits` and `.thatFitsWith(Constraint)` enums, there is a single `.thatFits(Constraint = .noConstraint)` case (the same applies for `autolayout`).

- Changed [how `zIndexes` are assigned to header and items, and support tapping headers / footers](https://github.com/kyleve/Listable/pull/193). This allows registering an `onTap` handler for any HeaderFooter, and providing a background to display while the tap's press is active.

### Misc

# Past Releases
Expand Down
4 changes: 4 additions & 0 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
0A07119324BA798400CDF65D /* ListStateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A07119224BA798400CDF65D /* ListStateViewController.swift */; };
0A0E070423870A5700DDD27D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 0A0E070323870A5700DDD27D /* README.md */; };
0A49210424E5E11300D17038 /* AccordionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A49210324E5E11300D17038 /* AccordionViewController.swift */; };
0A793B5824E4B53500850139 /* ManualSelectionManagementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A793B5724E4B53500850139 /* ManualSelectionManagementViewController.swift */; };
0A87BA652463567B0047C3B5 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 0A87BA642463567B0047C3B5 /* CHANGELOG.md */; };
0AA4D9B9248064A300CF95A5 /* CustomLayoutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA4D9A8248064A200CF95A5 /* CustomLayoutsViewController.swift */; };
Expand Down Expand Up @@ -45,6 +46,7 @@
06908B38E59ACD7502B5332F /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = "<group>"; };
0A07119224BA798400CDF65D /* ListStateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListStateViewController.swift; sourceTree = "<group>"; };
0A0E070323870A5700DDD27D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
0A49210324E5E11300D17038 /* AccordionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccordionViewController.swift; sourceTree = "<group>"; };
0A793B5724E4B53500850139 /* ManualSelectionManagementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualSelectionManagementViewController.swift; sourceTree = "<group>"; };
0A87BA642463567B0047C3B5 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = "<group>"; };
0AA4D9A8248064A200CF95A5 /* CustomLayoutsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomLayoutsViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -117,6 +119,7 @@
0AA4D9CA248064AE00CF95A5 /* Demo Screens */ = {
isa = PBXGroup;
children = (
0A49210324E5E11300D17038 /* AccordionViewController.swift */,
0AA4D9B4248064A300CF95A5 /* AutoScrollingViewController.swift */,
0AA4D9AC248064A300CF95A5 /* BlueprintListDemoViewController.swift */,
0AA4D9B2248064A300CF95A5 /* CollectionViewAppearance.swift */,
Expand Down Expand Up @@ -429,6 +432,7 @@
0AA4D9C9248064A300CF95A5 /* CollectionViewBasicDemoViewController.swift in Sources */,
0AA4D9BC248064A300CF95A5 /* KeyboardTestingViewController.swift in Sources */,
0AA4D9C1248064A300CF95A5 /* CoordinatorViewController.swift in Sources */,
0A49210424E5E11300D17038 /* AccordionViewController.swift in Sources */,
0AA4D9B9248064A300CF95A5 /* CustomLayoutsViewController.swift in Sources */,
0AA4D9C3248064A300CF95A5 /* CollectionViewAppearance.swift in Sources */,
2B8804652490844A003BB351 /* SpacingCustomizationViewController.swift in Sources */,
Expand Down
106 changes: 106 additions & 0 deletions Demo/Sources/Demos/Demo Screens/AccordionViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// AccordionViewController.swift
// Demo
//
// Created by Kyle Van Essen on 8/10/20.
// Copyright © 2020 Kyle Van Essen. All rights reserved.
//


import BlueprintLists
import BlueprintUICommonControls


final class AccordionViewController : ListViewController
{
var expandedSectionIndex : Int = 1

override func configure(list: inout ListProperties) {

list += (1...10).map { sectionIndex in

Section(sectionIndex) { section in

section.header = HeaderFooter(
AccordionHeader(text: "Section Header #\(sectionIndex)"),
onTap: { _ in
self.expandedSectionIndex = sectionIndex
self.reload(animated: true)
}
)

if expandedSectionIndex == sectionIndex {
section += (1...sectionIndex).map { itemIndex in
Item(AccordionRow(text: "Row #\(sectionIndex), \(itemIndex)")) {
$0.insertAndRemoveAnimations = .fade
}
}
}
}
}
}
}

fileprivate struct AccordionHeader : BlueprintHeaderFooterContent, Equatable
{
var text : String

var elementRepresentation: Element {
Label(text: self.text) {
$0.alignment = .left
$0.font = .systemFont(ofSize: 18.0, weight: .semibold)
}
.inset(horizontal: 20.0, vertical: 10.0)
.constrainedTo(height: .atLeast(80.0))
}

var background: Element? {
self.background(with: .white)
}

var pressedBackground: Element? {
self.background(with: .white(0.9))
}

private func background(with color : UIColor) -> Element {
Overlay(
elements: [
Box(backgroundColor: color),

Box(backgroundColor: .init(white: 0.85, alpha: 1.0))
.constrainedTo(height: .absolute(1.0))
.aligned(vertically: .bottom, horizontally: .fill)
]
)
}
}


fileprivate struct AccordionRow : BlueprintItemContent, Equatable
{
var text : String

var identifier: Identifier<AccordionRow> {
.init(text)
}

func element(with info: ApplyItemContentInfo) -> Element {
Label(text: self.text) {
$0.alignment = .left
$0.font = .systemFont(ofSize: 16.0, weight: .regular)
}
.inset(horizontal: 20.0, vertical: 10.0)
.constrainedTo(height: .atLeast(60.0))
}

func backgroundElement(with info: ApplyItemContentInfo) -> Element? {
Overlay(
elements: [
Box(backgroundColor: .white),
Box(backgroundColor: .init(white: 0.90, alpha: 1.0))
.constrainedTo(height: .absolute(1.0))
.aligned(vertically: .bottom, horizontally: .fill)
]
)
}
}
9 changes: 8 additions & 1 deletion Demo/Sources/Demos/DemosRootViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,21 @@ public final class DemosRootViewController : ListViewController
onSelect: { _ in
self.push(ItemInsertAndRemoveAnimationsViewController())
})

section += Item(
DemoItem(text: "Manual Selection Management"),
selectionStyle: .selectable(),
onSelect: { _ in
self.push(ManualSelectionManagementViewController())
}
)

section += Item(
DemoItem(text: "Accordion View"),
selectionStyle: .tappable,
onSelect: { _ in
self.push(AccordionViewController())
})
}

list("layouts") { section in
Expand Down
28 changes: 20 additions & 8 deletions Listable/Sources/HeaderFooter/HeaderFooter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter
public var sizing : Sizing
public var layout : HeaderFooterLayout

public typealias OnTap = (Content) -> ()
public var onTap : OnTap?

public var debuggingIdentifier : String? = nil

internal let reuseIdentifier : ReuseIdentifier<Content>
Expand All @@ -47,8 +50,7 @@ public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter
public init(
_ content : Content,
build : Build
)
{
) {
self.init(content)

build(&self)
Expand All @@ -57,23 +59,32 @@ public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter
public init(
_ content : Content,
sizing : Sizing = .thatFits(.init(.atLeast(.default))),
layout : HeaderFooterLayout = HeaderFooterLayout()
layout : HeaderFooterLayout = HeaderFooterLayout(),
onTap : OnTap? = nil
) {
self.content = content

self.sizing = sizing
self.layout = layout

self.onTap = onTap

self.reuseIdentifier = ReuseIdentifier.identifier(for: Content.self)
}

// MARK: AnyHeaderFooter_Internal

public func apply(to anyView : UIView, reason: ApplyReason)
{
let view = anyView as! Content.ContentView
let view = anyView as! HeaderFooterContentView<Content>

let views = HeaderFooterContentViews<Content>(
content: view.content,
background: view.background,
pressed: view.pressedBackground
)

self.content.apply(to: view, reason: reason)
self.content.apply(to: views, reason: reason)
}

public func anyIsEquivalent(to other : AnyHeaderFooter) -> Bool
Expand Down Expand Up @@ -106,9 +117,10 @@ extension HeaderFooter : SignpostLoggable
public struct HeaderFooterLayout : Equatable
{
public var width : CustomWidth

public init(width : CustomWidth = .default)
{

public init(
width : CustomWidth = .default
) {
self.width = width
}
}
Loading

0 comments on commit 53b9c51

Please sign in to comment.