Skip to content

Commit

Permalink
Add support for setting default HeaderFooter properties via HeaderFoo…
Browse files Browse the repository at this point in the history
…terContent's defaultHeaderFooterProperties.
  • Loading branch information
kyleve committed Jun 30, 2021
1 parent dba45f4 commit 5edb19d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 50 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- [Introduce `defaultHeaderFooterProperties` on `HeaderFooterContent`](https://github.com/kyleve/Listable/pull/304), to allow specifying default values for a `HeaderFooter` when values are not passed to the initializer.

### Removed

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct AutoLayoutContent : ItemContent, Equatable {
header + detail
}

var defaultItemProperties: DefaultItemProperties<Self> {
var defaultItemProperties: DefaultProperties {
.init(
sizing: .autolayout()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// DefaultHeaderFooterProperties.swift
// ListableUI
//
// Created by Kyle Van Essen on 6/27/21.
//

import Foundation


/// Allows specifying default properties to apply to a header / footer when it is initialized,
/// if those values are not provided to the initializer.
/// Only non-nil values are used – if you do not want to provide a default value,
/// simply leave the property nil.
///
/// The order of precedence used when assigning values is:
/// 1) The value passed to the initializer.
/// 2) The value from `defaultHeaderFooterProperties` on the contained `HeaderFooterContent`, if non-nil.
/// 3) A standard, default value.
public struct DefaultHeaderFooterProperties<Content:HeaderFooterContent>
{
public var sizing : Sizing?
public var layouts : HeaderFooterLayouts?

public init(
sizing : Sizing? = nil,
layouts : HeaderFooterLayouts? = nil
) {
self.sizing = sizing
self.layouts = layouts
}
}
26 changes: 22 additions & 4 deletions ListableUI/Sources/HeaderFooter/HeaderFooter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,18 @@ public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter

public init(
_ content : Content,
sizing : Sizing = .thatFits(.init(.atLeast(.default))),
layouts : HeaderFooterLayouts = .init(),
sizing : Sizing? = nil,
layouts : HeaderFooterLayouts? = nil,
onTap : OnTap? = nil
) {
assertIsValueType(Content.self)

self.content = content

self.sizing = sizing
self.layouts = layouts
let defaults = self.content.defaultHeaderFooterProperties

self.sizing = finalValue(from: sizing, defaults.sizing, .thatFits(.init(.atLeast(.default))))
self.layouts = finalValue(from: layouts, defaults.layouts, .init())

self.onTap = onTap

Expand Down Expand Up @@ -110,3 +112,19 @@ extension HeaderFooter : SignpostLoggable
)
}
}


private func finalValue<Value>(
from provided : Value?,
_ contentDefault : Value?,
_ default : @autoclosure () -> Value
) -> Value
{
if let value = provided {
return value
} else if let value = contentDefault {
return value
} else {
return `default`()
}
}
35 changes: 28 additions & 7 deletions ListableUI/Sources/HeaderFooter/HeaderFooterContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,23 @@ public typealias FooterContent = HeaderFooterContent
/// z-Index 1) `BackgroundView`
///
public protocol HeaderFooterContent
{
{
//
// MARK: Tracking Changes
//

func isEquivalent(to other : Self) -> Bool

//
// MARK: Default Properties
//

typealias DefaultProperties = DefaultHeaderFooterProperties<Self>

/// Default values to assign to various properties on the `HeaderFooter` which wraps
/// this `HeaderFooterContent`, if those values are not passed to the `HeaderFooter` initializer.
var defaultHeaderFooterProperties : DefaultProperties { get }

//
// MARK: Applying To Displayed View
//
Expand All @@ -54,12 +70,6 @@ public protocol HeaderFooterContent
with info : ApplyHeaderFooterContentInfo
)

//
// MARK: Tracking Changes
//

func isEquivalent(to other : Self) -> Bool

//
// MARK: Creating & Providing Content Views
//
Expand Down Expand Up @@ -173,10 +183,21 @@ public extension HeaderFooterContent where Self.BackgroundView == UIView
}
}


public extension HeaderFooterContent where Self.PressedBackgroundView == UIView
{
static func createReusablePressedBackgroundView(frame : CGRect) -> PressedBackgroundView
{
PressedBackgroundView(frame: frame)
}
}


/// Provide a default implementation of `defaultHeaderFooterProperties` which returns an
/// empty instance that does not provide any defaults.
public extension HeaderFooterContent
{
var defaultHeaderFooterProperties : DefaultProperties {
.init()
}
}
58 changes: 22 additions & 36 deletions ListableUI/Sources/Item/Item.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,44 +77,14 @@ public struct Item<Content:ItemContent> : AnyItem
assertIsValueType(Content.self)

self.content = content

if let sizing = sizing {
self.sizing = sizing
} else if let sizing = content.defaultItemProperties.sizing {
self.sizing = sizing
} else {
self.sizing = .thatFits(.init(.atLeast(.default)))
}

if let layouts = layouts {
self.layouts = layouts
} else if let layouts = content.defaultItemProperties.layouts {
self.layouts = layouts
} else {
self.layouts = ItemLayouts()
}
let defaults = self.content.defaultItemProperties

if let selectionStyle = selectionStyle {
self.selectionStyle = selectionStyle
} else if let selectionStyle = content.defaultItemProperties.selectionStyle {
self.selectionStyle = selectionStyle
} else {
self.selectionStyle = .notSelectable
}

if let insertAndRemoveAnimations = insertAndRemoveAnimations {
self.insertAndRemoveAnimations = insertAndRemoveAnimations
} else if let insertAndRemoveAnimations = content.defaultItemProperties.insertAndRemoveAnimations {
self.insertAndRemoveAnimations = insertAndRemoveAnimations
}

if let swipeActions = swipeActions {
self.swipeActions = swipeActions
} else if let swipeActions = content.defaultItemProperties.swipeActions {
self.swipeActions = swipeActions
} else {
self.swipeActions = nil
}
self.sizing = finalValue(from: sizing, defaults.sizing, .thatFits(.init(.atLeast(.default))))
self.layouts = finalValue(from: layouts, defaults.layouts, .init())
self.selectionStyle = finalValue(from: selectionStyle, defaults.selectionStyle, .notSelectable)
self.insertAndRemoveAnimations = finalValue(from: insertAndRemoveAnimations, defaults.insertAndRemoveAnimations, nil)
self.swipeActions = finalValue(from: swipeActions, defaults.swipeActions, nil)

self.reordering = reordering
self.onWasReordered = onWasReordered
Expand Down Expand Up @@ -203,3 +173,19 @@ extension Item : SignpostLoggable
)
}
}


private func finalValue<Value>(
from provided : Value?,
_ contentDefault : Value?,
_ default : @autoclosure () -> Value
) -> Value
{
if let value = provided {
return value
} else if let value = contentDefault {
return value
} else {
return `default`()
}
}
7 changes: 5 additions & 2 deletions ListableUI/Sources/Item/ItemContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,12 @@ public protocol ItemContent where Coordinator.ItemContentType == Self
// MARK: Default Item Properties
//

typealias DefaultProperties = DefaultItemProperties<Self>


/// Default values to assign to various properties on the `Item` which wraps
/// this `ItemContent`, if those values are not passed to the `Item` initializer.
var defaultItemProperties : DefaultItemProperties<Self> { get }
var defaultItemProperties : DefaultProperties { get }

//
// MARK: Applying To Displayed View
Expand Down Expand Up @@ -539,7 +542,7 @@ public extension ItemContent
/// empty instance that does not provide any defaults.
public extension ItemContent
{
var defaultItemProperties : DefaultItemProperties<Self> {
var defaultItemProperties : DefaultProperties {
.init()
}
}
Expand Down

0 comments on commit 5edb19d

Please sign in to comment.