diff --git a/BlueprintLists/Sources/Deprecations.swift b/BlueprintLists/Sources/Deprecations.swift new file mode 100644 index 000000000..839d5a2e0 --- /dev/null +++ b/BlueprintLists/Sources/Deprecations.swift @@ -0,0 +1,58 @@ +// +// Deprecations.swift +// BlueprintLists +// +// Created by Kyle Van Essen on 5/29/20. +// + +import BlueprintUI + + +/// +/// This file contains deprecations which have occurred in BlueprintLists, for which there are reasonable +/// forward-moving defaults (eg, renames), to ease transitions for consumers when they update their library version. +/// +/// To add new deprecations and changes: +/// ------------------------------------ +/// 1) Add a new `MARK: Deprecated ` section for the deprecations you are adding. +/// +/// 2) Add deprecation annotations like so: +/// ``` +/// @available(*, deprecated, renamed: "ItemContent") +/// public typealias ItemElement = ItemContent +/// ``` +/// +/// Or, when deprecating properties, add a passthrough like so: +/// ``` +/// public extension Item { +/// @available(*, deprecated, renamed: "content") +/// var element : Content { +/// self.content +/// } +/// } +/// ``` +/// +/// 3) After 1-2 months has passed, mark the previously `deprecated` items as `unavailable`: +/// ``` +/// @available(*, unavailable, renamed: "ItemContent") +/// ``` +/// +/// 4) After another 1-2 months have passed, feel free to remove the `MARK: Deprecated` section you added. +/// + +// +// MARK: Deprecated May 29, 2019 +// + +@available(*, deprecated, renamed: "BlueprintItemContent") +public typealias BlueprintItemElement = BlueprintItemContent + +@available(*, deprecated, renamed: "BlueprintHeaderFooterContent") +public typealias BlueprintHeaderFooterElement = BlueprintHeaderFooterContent + +public extension BlueprintHeaderFooterContent { + @available(*, deprecated, renamed: "elementRepresentation") + var element : Element { + self.elementRepresentation + } +} diff --git a/BlueprintLists/Sources/HeaderFooter.swift b/BlueprintLists/Sources/HeaderFooter.swift index 190ffe36d..a189cea9a 100644 --- a/BlueprintLists/Sources/HeaderFooter.swift +++ b/BlueprintLists/Sources/HeaderFooter.swift @@ -6,33 +6,28 @@ // import BlueprintUI - import Listable -// -// MARK: Blueprint Elements -// -public protocol BlueprintHeaderFooterElement : HeaderFooterElement where ContentView == BlueprintView +public protocol BlueprintHeaderFooterContent : HeaderFooterContent where ContentView == BlueprintView { // // MARK: Creating Blueprint Element Representations // - var element : BlueprintUI.Element { get } + var elementRepresentation : Element { get } } -// -// MARK: Applying Blueprint Elements -// - - -public extension BlueprintHeaderFooterElement +public extension BlueprintHeaderFooterContent { + // + // MARK: HeaderFooterContent + // + func apply(to view: ContentView, reason: ApplyReason) { - view.element = self.element + view.element = self.elementRepresentation } static func createReusableHeaderFooterView(frame: CGRect) -> ContentView diff --git a/BlueprintLists/Sources/Item.swift b/BlueprintLists/Sources/Item.swift index d48fda037..d07f49778 100644 --- a/BlueprintLists/Sources/Item.swift +++ b/BlueprintLists/Sources/Item.swift @@ -15,11 +15,11 @@ import Listable // /// -/// An `ItemElement` specialized for use with Blueprint. Instead of providing +/// An `ItemContent` specialized for use with Blueprint. Instead of providing /// a custom view from `createReusableContentView`, and then updating it in `apply(to:)`, /// you instead provide Blueprint element trees, and `Listable` handles mapping this to an underlying `BlueprintView`. /// -public protocol BlueprintItemElement : ItemElement +public protocol BlueprintItemContent : ItemContent where ContentView == BlueprintView, BackgroundView == BlueprintView, @@ -29,75 +29,75 @@ public protocol BlueprintItemElement : ItemElement // MARK: Creating Blueprint Element Representations // - /// Required. Create and return the element used to represent the content of the element. + /// Required. Create and return the Blueprint element used to represent the content. /// - /// You can use the provided `ApplyItemElementInfo` to vary the appearance of your content - /// based on the current state of the element. + /// You can use the provided `ApplyItemContentInfo` to vary the appearance of the element + /// based on the current state of the item. /// - func element(with info : ApplyItemElementInfo) -> BlueprintUI.Element + func element(with info : ApplyItemContentInfo) -> Element - /// Optional. Create and return the element used to represent the background of the element. - /// You usually provide this method alongside `selectedBackgroundElement`, if your element + /// Optional. Create and return the Blueprint element used to represent the background of the content. + /// You usually provide this method alongside `selectedBackgroundElement`, if your content /// supports selection or highlighting. /// - /// You can use the provided `ApplyItemElementInfo` to vary the appearance of your content - /// based on the current state of the element. + /// You can use the provided `ApplyItemContentInfo` to vary the appearance of the element + /// based on the current state of the item. /// /// Note /// ---- /// The default implementation of this method returns nil, and provides no background. /// - func backgroundElement(with info : ApplyItemElementInfo) -> BlueprintUI.Element? + func backgroundElement(with info : ApplyItemContentInfo) -> Element? - /// Optional. Create and return the element used to represent the background of the element when it is selected or highlighted. - /// You usually provide this method alongside `backgroundElement`, if your element supports selection or highlighting. + /// Optional. Create and return the Blueprint element used to represent the background of the content when it is selected or highlighted. + /// You usually provide this method alongside `backgroundElement`, if your content supports selection or highlighting. /// - /// You can use the provided `ApplyItemElementInfo` to vary the appearance of your content - /// based on the current state of the element. + /// You can use the provided `ApplyItemContentInfo` to vary the appearance of the element + /// based on the current state of the item. /// /// Note /// ---- /// The default implementation of this method returns nil, and provides no selected background. /// - func selectedBackgroundElement(with info : ApplyItemElementInfo) -> BlueprintUI.Element? + func selectedBackgroundElement(with info : ApplyItemContentInfo) -> Element? } -public extension BlueprintItemElement +public extension BlueprintItemContent { // // MARK: Default Implementations // - /// By default, elements have no background. - func backgroundElement(with info : ApplyItemElementInfo) -> BlueprintUI.Element? + /// By default, content has no background. + func backgroundElement(with info : ApplyItemContentInfo) -> Element? { nil } - /// By default, elements have no selected background. - func selectedBackgroundElement(with info : ApplyItemElementInfo) -> BlueprintUI.Element? + /// By default, content has no selected background. + func selectedBackgroundElement(with info : ApplyItemContentInfo) -> Element? { nil } } -public extension BlueprintItemElement +public extension BlueprintItemContent { // - // MARK: ItemElement + // MARK: ItemContent // - /// Maps the `BlueprintItemElement` methods into the underlying `BlueprintView`s used to render the element. - func apply(to views : ItemElementViews, for reason: ApplyReason, with info : ApplyItemElementInfo) + /// Maps the `BlueprintItemContent` methods into the underlying `BlueprintView`s used to render the element. + func apply(to views : ItemContentViews, for reason: ApplyReason, with info : ApplyItemContentInfo) { views.content.element = self.element(with: info) views.background.element = self.backgroundElement(with: info) views.selectedBackground.element = self.selectedBackgroundElement(with: info) } - /// Creates the `BlueprintView` used to render the content of the element. + /// Creates the `BlueprintView` used to render the content of the item. static func createReusableContentView(frame: CGRect) -> ContentView { let view = BlueprintView(frame: frame) @@ -106,7 +106,7 @@ public extension BlueprintItemElement return view } - /// Creates the `BlueprintView` used to render the background of the element. + /// Creates the `BlueprintView` used to render the background of the item. static func createReusableBackgroundView(frame: CGRect) -> BackgroundView { let view = BlueprintView(frame: frame) diff --git a/BlueprintLists/Sources/List.swift b/BlueprintLists/Sources/List.swift index cb611e9eb..9441eaac2 100644 --- a/BlueprintLists/Sources/List.swift +++ b/BlueprintLists/Sources/List.swift @@ -10,7 +10,7 @@ import BlueprintUI import Listable -public struct List : BlueprintUI.Element +public struct List : Element { public var listDescription : ListDescription @@ -34,7 +34,7 @@ public struct List : BlueprintUI.Element } // - // MARK: BlueprintUI.Element + // MARK: Element // public var content : ElementContent { diff --git a/BlueprintLists/Sources/Section.swift b/BlueprintLists/Sources/Section.swift deleted file mode 100644 index 0b41928f6..000000000 --- a/BlueprintLists/Sources/Section.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Section.swift -// BlueprintLists -// -// Created by Kyle Van Essen on 11/20/19. -// - -import Listable - - -// -// MARK: Building Content -// - - -public extension Section -{ - // - // MARK: Adding Items - // - - static func += (lhs : inout Section, rhs : Element) - { - lhs += Item(rhs) - } - - static func += (lhs : inout Section, rhs : [Element]) - { - lhs.items += rhs.map { Item($0) } - } -} diff --git a/Demo/Sources/Demos/Demo Screens/AutoScrollingViewController.swift b/Demo/Sources/Demos/Demo Screens/AutoScrollingViewController.swift index f5a89bf48..716424923 100644 --- a/Demo/Sources/Demos/Demo Screens/AutoScrollingViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/AutoScrollingViewController.swift @@ -87,7 +87,7 @@ final class AutoScrollingViewController : UIViewController } -struct BottomPinnedItem : BlueprintItemElement, Equatable +struct BottomPinnedItem : BlueprintItemContent, Equatable { var text : String @@ -95,7 +95,7 @@ struct BottomPinnedItem : BlueprintItemElement, Equatable return .init(self.text) } - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { var box = Box( backgroundColor: .white, diff --git a/Demo/Sources/Demos/Demo Screens/BlueprintListDemoViewController.swift b/Demo/Sources/Demos/Demo Screens/BlueprintListDemoViewController.swift index d68f0fcfb..c23cb86b9 100644 --- a/Demo/Sources/Demos/Demo Screens/BlueprintListDemoViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/BlueprintListDemoViewController.swift @@ -65,7 +65,7 @@ final class BlueprintListDemoViewController : UIViewController } } -struct PodcastRow : BlueprintItemElement, Equatable +struct PodcastRow : BlueprintItemContent, Equatable { var podcast : Podcast @@ -73,7 +73,7 @@ struct PodcastRow : BlueprintItemElement, Equatable return .init(self.podcast.name) } - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { Row { row in diff --git a/Demo/Sources/Demos/Demo Screens/CollectionViewAppearance.swift b/Demo/Sources/Demos/Demo Screens/CollectionViewAppearance.swift index 6fea08fe3..fc04d6828 100644 --- a/Demo/Sources/Demos/Demo Screens/CollectionViewAppearance.swift +++ b/Demo/Sources/Demos/Demo Screens/CollectionViewAppearance.swift @@ -39,11 +39,11 @@ extension UIColor } -struct DemoHeader : BlueprintHeaderFooterElement, Equatable +struct DemoHeader : BlueprintHeaderFooterContent, Equatable { var title : String - var element: Element { + var elementRepresentation: Element { Label(text: self.title) { $0.font = .systemFont(ofSize: 20.0, weight: .bold) } @@ -56,11 +56,11 @@ struct DemoHeader : BlueprintHeaderFooterElement, Equatable } } -struct DemoHeader2 : BlueprintHeaderFooterElement, Equatable +struct DemoHeader2 : BlueprintHeaderFooterContent, Equatable { var title : String - var element: Element { + var elementRepresentation: Element { Label(text: self.title) { $0.font = .systemFont(ofSize: 20.0, weight: .bold) } @@ -74,7 +74,7 @@ struct DemoHeader2 : BlueprintHeaderFooterElement, Equatable } -struct DemoItem : BlueprintItemElement, Equatable +struct DemoItem : BlueprintItemContent, Equatable { var text : String @@ -84,7 +84,7 @@ struct DemoItem : BlueprintItemElement, Equatable typealias SwipeActionsView = DefaultSwipeActionsView - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { Label(text: self.text) { $0.font = .systemFont(ofSize: 16.0, weight: .medium) @@ -93,7 +93,7 @@ struct DemoItem : BlueprintItemElement, Equatable .inset(horizontal: 15.0, vertical: 10.0) } - func backgroundElement(with info: ApplyItemElementInfo) -> Element? + func backgroundElement(with info: ApplyItemContentInfo) -> Element? { Box( backgroundColor: .white, @@ -102,7 +102,7 @@ struct DemoItem : BlueprintItemElement, Equatable ) } - func selectedBackgroundElement(with info: ApplyItemElementInfo) -> Element? + func selectedBackgroundElement(with info: ApplyItemContentInfo) -> Element? { Box( backgroundColor: info.state.isSelected ? .white(0.9) : .white(0.95), @@ -113,11 +113,11 @@ struct DemoItem : BlueprintItemElement, Equatable } -struct DemoFooter : BlueprintHeaderFooterElement, Equatable +struct DemoFooter : BlueprintHeaderFooterContent, Equatable { var text : String - var element: Element { + var elementRepresentation: Element { return Centered(Label(text: self.text)) } } diff --git a/Demo/Sources/Demos/Demo Screens/CollectionViewDictionaryDemoViewController.swift b/Demo/Sources/Demos/Demo Screens/CollectionViewDictionaryDemoViewController.swift index 484f68015..29cb9e432 100644 --- a/Demo/Sources/Demos/Demo Screens/CollectionViewDictionaryDemoViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/CollectionViewDictionaryDemoViewController.swift @@ -133,7 +133,7 @@ final public class CollectionViewDictionaryDemoViewController : UIViewController } } -fileprivate struct SearchBarElement : ItemElement +fileprivate struct SearchBarElement : ItemContent { var text : String @@ -145,7 +145,7 @@ fileprivate struct SearchBarElement : ItemElement return .init("search") } - func apply(to views : ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) + func apply(to views : ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) { views.content.onStateChanged = self.onChange views.content.text = self.text @@ -162,16 +162,16 @@ fileprivate struct SearchBarElement : ItemElement return SearchBar(frame: frame) } - func apply(to views : ItemElementViews, with info: ApplyItemElementInfo) {} + func apply(to views : ItemContentViews, with info: ApplyItemContentInfo) {} } -fileprivate struct SectionHeader : BlueprintHeaderFooterElement, Equatable +fileprivate struct SectionHeader : BlueprintHeaderFooterContent, Equatable { var title : String // MARK: BlueprintItemElement - var element: Element { + var elementRepresentation: Element { return Box( backgroundColor: UIColor(white: 0.85, alpha: 1.0), cornerStyle: .rounded(radius: 10.0), @@ -192,14 +192,14 @@ fileprivate struct SectionHeader : BlueprintHeaderFooterElement, Equatable } -fileprivate struct WordRow : BlueprintItemElement, Equatable +fileprivate struct WordRow : BlueprintItemContent, Equatable { var title : String var detail : String // MARK: BlueprintItemElement - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { return Box( backgroundColor: .init(white: 0.96, alpha: 1.0), diff --git a/Demo/Sources/Demos/Demo Screens/CoordinatorViewController.swift b/Demo/Sources/Demos/Demo Screens/CoordinatorViewController.swift index a400a6b76..fedc5130d 100644 --- a/Demo/Sources/Demos/Demo Screens/CoordinatorViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/CoordinatorViewController.swift @@ -32,7 +32,7 @@ final class CoordinatorViewController : UIViewController } -fileprivate struct PodcastElement : BlueprintItemElement, Equatable +fileprivate struct PodcastElement : BlueprintItemContent, Equatable { var podcast : Podcast @@ -42,7 +42,7 @@ fileprivate struct PodcastElement : BlueprintItemElement, Equatable .init(podcast.name) } - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { Column { col in col.horizontalAlignment = .fill @@ -101,7 +101,7 @@ fileprivate struct PodcastElement : BlueprintItemElement, Equatable } } - func selectedBackgroundElement(with info: ApplyItemElementInfo) -> Element? { + func selectedBackgroundElement(with info: ApplyItemContentInfo) -> Element? { Box(backgroundColor: .init(white: 0.9, alpha: 1.0)) } @@ -110,9 +110,9 @@ fileprivate struct PodcastElement : BlueprintItemElement, Equatable Coordinator(actions: actions, info: info) } - final class Coordinator : ItemElementCoordinator + final class Coordinator : ItemContentCoordinator { - typealias ItemElementType = PodcastElement + typealias ItemContentType = PodcastElement let actions: CoordinatorActions let info: CoordinatorInfo @@ -127,7 +127,7 @@ fileprivate struct PodcastElement : BlueprintItemElement, Equatable func wasSelected() { self.actions.update(animated: true) { - $0.element.showBottomBar = true + $0.content.showBottomBar = true } Timer.scheduledTimer(withTimeInterval: TimeInterval.random(in: 0.3...0.5), repeats: true) { [weak self] timer in @@ -137,17 +137,17 @@ fileprivate struct PodcastElement : BlueprintItemElement, Equatable } self.actions.update { - switch $0.element.podcast.downloadState { + switch $0.content.podcast.downloadState { case .notDownloaded: - $0.element.podcast.downloadState = .downloading(0.0) + $0.content.podcast.downloadState = .downloading(0.0) case .downloading(let progress): let newProgress = progress + Double.random(in: 0...0.05) if newProgress >= 1.0 { timer.invalidate() - $0.element.podcast.downloadState = .downloaded + $0.content.podcast.downloadState = .downloaded } else { - $0.element.podcast.downloadState = .downloading(newProgress) + $0.content.podcast.downloadState = .downloading(newProgress) } case .downloaded: break case .error: break @@ -158,7 +158,7 @@ fileprivate struct PodcastElement : BlueprintItemElement, Equatable func wasDeselected() { self.actions.update(animated: true) { - $0.element.showBottomBar = false + $0.content.showBottomBar = false } } } diff --git a/Demo/Sources/Demos/Demo Screens/HorizontalLayoutViewController.swift b/Demo/Sources/Demos/Demo Screens/HorizontalLayoutViewController.swift index 0addce994..cfa1e85ec 100644 --- a/Demo/Sources/Demos/Demo Screens/HorizontalLayoutViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/HorizontalLayoutViewController.swift @@ -112,12 +112,12 @@ final class HorizontalLayoutViewController : UIViewController } -fileprivate struct HorizontalHeader : BlueprintHeaderFooterElement, Equatable +fileprivate struct HorizontalHeader : BlueprintHeaderFooterContent, Equatable { var title : String var color : UIColor - var element: Element { + var elementRepresentation: Element { return Box( backgroundColor: self.color, cornerStyle: .rounded(radius: 15.0), @@ -128,13 +128,13 @@ fileprivate struct HorizontalHeader : BlueprintHeaderFooterElement, Equatable } } -fileprivate struct CardElement : BlueprintItemElement, Equatable +fileprivate struct CardElement : BlueprintItemContent, Equatable { var title : String var detail : String var color : UIColor - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { return Box( backgroundColor: self.color, diff --git a/Demo/Sources/Demos/Demo Screens/InvoicesPaymentScheduleDemoViewController.swift b/Demo/Sources/Demos/Demo Screens/InvoicesPaymentScheduleDemoViewController.swift index 913f4b7c9..2f1d472f9 100644 --- a/Demo/Sources/Demos/Demo Screens/InvoicesPaymentScheduleDemoViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/InvoicesPaymentScheduleDemoViewController.swift @@ -255,7 +255,7 @@ fileprivate struct ViewData : Equatable } -fileprivate struct ToggleRow : BlueprintItemElement +fileprivate struct ToggleRow : BlueprintItemContent { var content : Content var onToggle : (Bool) -> () @@ -268,7 +268,7 @@ fileprivate struct ToggleRow : BlueprintItemElement // MARK: BlueprintItemElement - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { return Inset(top: 10.0, bottom: 10.0, left: 0.0, right: 0.0, wrapping: Row { row in @@ -289,29 +289,29 @@ fileprivate struct ToggleRow : BlueprintItemElement } } -fileprivate struct SectionHeader : BlueprintHeaderFooterElement, Equatable +fileprivate struct SectionHeader : BlueprintHeaderFooterContent, Equatable { var text : String // MARK: BlueprintHeaderFooterElement - var element: Element { + var elementRepresentation: Element { return Label(text: self.text) } } -fileprivate struct SectionFooter : BlueprintHeaderFooterElement, Equatable +fileprivate struct SectionFooter : BlueprintHeaderFooterContent, Equatable { var text : String // MARK: BlueprintHeaderFooterElement - var element: Element { + var elementRepresentation: Element { return Label(text: self.text) } } -fileprivate struct SegmentedControlRow : BlueprintItemElement +fileprivate struct SegmentedControlRow : BlueprintItemContent { var id : String var control : SegmentedControl @@ -326,7 +326,7 @@ fileprivate struct SegmentedControlRow : BlueprintItemElement // MARK: BlueprintItemElement - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { return self.control } @@ -341,7 +341,7 @@ fileprivate struct SegmentedControlRow : BlueprintItemElement } } -fileprivate struct AmountRow : BlueprintItemElement +fileprivate struct AmountRow : BlueprintItemContent { var content : Content @@ -360,7 +360,7 @@ fileprivate struct AmountRow : BlueprintItemElement // MARK: BlueprintItemElement - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { var box = Box(wrapping: Row { row in @@ -407,12 +407,12 @@ fileprivate struct AmountRow : BlueprintItemElement } -fileprivate struct ButtonRow : BlueprintItemElement +fileprivate struct ButtonRow : BlueprintItemContent { var text : String var onTap : () -> () - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { fatalError() } diff --git a/Demo/Sources/Demos/Demo Screens/ItemizationEditorViewController.swift b/Demo/Sources/Demos/Demo Screens/ItemizationEditorViewController.swift index 6065f2266..e19351c90 100644 --- a/Demo/Sources/Demos/Demo Screens/ItemizationEditorViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/ItemizationEditorViewController.swift @@ -173,22 +173,22 @@ final class ItemizationEditorViewController : UIViewController } } -struct Header : BlueprintHeaderFooterElement, Equatable +struct Header : BlueprintHeaderFooterContent, Equatable { var title : String - var element : Element { + var elementRepresentation : Element { return Inset(insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 10.0, right: 0.0), wrapping: Label(text: self.title) { label in label.font = .systemFont(ofSize: 30.0, weight: .bold) }) } } -struct Footer : BlueprintHeaderFooterElement, Equatable +struct Footer : BlueprintHeaderFooterContent, Equatable { var text : String - var element : Element { + var elementRepresentation : Element { return Label(text: self.text) { label in label.font = .systemFont(ofSize: 14.0, weight: .regular) label.alignment = .center @@ -196,14 +196,14 @@ struct Footer : BlueprintHeaderFooterElement, Equatable } } -struct ChoiceItem : BlueprintItemElement, Equatable +struct ChoiceItem : BlueprintItemContent, Equatable { var title : String var detail : String // MARK: BlueprintItemElement - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { var box = Box( cornerStyle: .rounded(radius: 8.0), @@ -238,7 +238,7 @@ struct ChoiceItem : BlueprintItemElement, Equatable } } -struct ToggleItem : BlueprintItemElement +struct ToggleItem : BlueprintItemContent { var content : Content @@ -261,7 +261,7 @@ struct ToggleItem : BlueprintItemElement return .init(self.content.title) } - func element(with info: ApplyItemElementInfo) -> Element + func element(with info: ApplyItemContentInfo) -> Element { var box = Box( cornerStyle: .rounded(radius: 8.0), diff --git a/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift b/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift index cce39ee0a..7a75d6073 100644 --- a/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift @@ -55,7 +55,7 @@ final class KeyboardTestingViewController : UIViewController } } -struct TextFieldElement : BlueprintItemElement, Equatable +struct TextFieldElement : BlueprintItemContent, Equatable { var content : String @@ -65,7 +65,7 @@ struct TextFieldElement : BlueprintItemElement, Equatable return .init(self.content) } - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { let textField = TextField(text: self.content) diff --git a/Demo/Sources/Demos/Demo Screens/ReorderingViewController.swift b/Demo/Sources/Demos/Demo Screens/ReorderingViewController.swift index c4ce4ec93..c7ce4fc8f 100644 --- a/Demo/Sources/Demos/Demo Screens/ReorderingViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/ReorderingViewController.swift @@ -78,7 +78,7 @@ final class ReorderingViewController : UIViewController } -struct ReorderItem : BlueprintItemElement, Equatable +struct ReorderItem : BlueprintItemContent, Equatable { var text : String @@ -86,7 +86,7 @@ struct ReorderItem : BlueprintItemElement, Equatable return .init(self.text) } - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { var box = Box( backgroundColor: .white, diff --git a/Demo/Sources/Demos/Demo Screens/SwipeActionsViewController.swift b/Demo/Sources/Demos/Demo Screens/SwipeActionsViewController.swift index 77cf63d02..c55aa427c 100644 --- a/Demo/Sources/Demos/Demo Screens/SwipeActionsViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/SwipeActionsViewController.swift @@ -124,14 +124,14 @@ final class SwipeActionsViewController: UIViewController { } - struct SwipeActionsDemoItem: BlueprintItemElement, Equatable { + struct SwipeActionsDemoItem: BlueprintItemContent, Equatable { var item: SwipeActionItem var identifier: Identifier { return .init(item.identifier) } - func element(with info : ApplyItemElementInfo) -> Element { + func element(with info : ApplyItemContentInfo) -> Element { return Column { column in column.horizontalAlignment = .fill diff --git a/Demo/Sources/Demos/Demo Screens/WidthCustomizationViewController.swift b/Demo/Sources/Demos/Demo Screens/WidthCustomizationViewController.swift index 34088db44..636e6e5b3 100644 --- a/Demo/Sources/Demos/Demo Screens/WidthCustomizationViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/WidthCustomizationViewController.swift @@ -105,7 +105,7 @@ final class WidthCustomizationViewController : UIViewController } -fileprivate struct CardElement : BlueprintItemElement, Equatable +fileprivate struct CardElement : BlueprintItemContent, Equatable { var title : String var color : UIColor @@ -118,7 +118,7 @@ fileprivate struct CardElement : BlueprintItemElement, Equatable return .init(self.title) } - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { return Box( backgroundColor: self.color, diff --git a/Demo/Sources/Demos/DemosRootViewController.swift b/Demo/Sources/Demos/DemosRootViewController.swift index c326889d4..5317cd8d8 100644 --- a/Demo/Sources/Demos/DemosRootViewController.swift +++ b/Demo/Sources/Demos/DemosRootViewController.swift @@ -126,7 +126,7 @@ public final class DemosRootViewController : UIViewController }) section += Item( - DemoItem(text: "Item Element Coordinator"), + DemoItem(text: "Item Content Coordinator"), selectionStyle: .tappable, onSelect : { _ in self.push(CoordinatorViewController()) diff --git a/Listable/Sources/Content.swift b/Listable/Sources/Content.swift index 98a54e3c0..2484ba8c3 100644 --- a/Listable/Sources/Content.swift +++ b/Listable/Sources/Content.swift @@ -1,5 +1,5 @@ // -// Elements.swift +// Content.swift // Listable // // Created by Kyle Van Essen on 6/21/19. diff --git a/Listable/Sources/Deprecations.swift b/Listable/Sources/Deprecations.swift new file mode 100644 index 000000000..10937475e --- /dev/null +++ b/Listable/Sources/Deprecations.swift @@ -0,0 +1,73 @@ +// +// Deprecations.swift +// Listable +// +// Created by Kyle Van Essen on 5/29/20. +// + +import Foundation + +/// +/// This file contains deprecations which have occurred in Listable, for which there are reasonable +/// forward-moving defaults (eg, renames), to ease transitions for consumers when they update their library version. +/// +/// To add new deprecations and changes: +/// ------------------------------------ +/// 1) Add a new `MARK: Deprecated ` section for the deprecations you are adding. +/// +/// 2) Add deprecation annotations like so: +/// ``` +/// @available(*, deprecated, renamed: "ItemContent") +/// public typealias ItemElement = ItemContent +/// ``` +/// +/// Or, when deprecating properties, add a passthrough like so: +/// ``` +/// public extension Item { +/// @available(*, deprecated, renamed: "content") +/// var element : Content { +/// self.content +/// } +/// } +/// ``` +/// +/// 3) After 1-2 months has passed, mark the previously `deprecated` items as `unavailable`: +/// ``` +/// @available(*, unavailable, renamed: "ItemContent") +/// ``` +/// +/// 4) After another 1-2 months have passed, feel free to remove the `MARK: Deprecated` section you added. +/// + +// +// MARK: Deprecated May 29, 2019 +// + +@available(*, deprecated, renamed: "ItemContent") +public typealias ItemElement = ItemContent + +@available(*, deprecated, renamed: "ApplyItemContentInfo") +public typealias ApplyItemElementInfo = ApplyItemContentInfo + +@available(*, deprecated, renamed: "ItemContentViews") +public typealias ItemElementViews = ItemContentViews + +@available(*, deprecated, renamed: "ItemContentSwipeActionsView") +public typealias ItemElementSwipeActionsView = ItemContentSwipeActionsView + +public extension Item { + @available(*, deprecated, renamed: "content") + var element : Content { + self.content + } +} + +@available(*, deprecated, renamed: "HeaderFooterContent") +public typealias HeaderFooterElement = HeaderFooterContent + +public extension HeaderFooter { + @available(*, deprecated, renamed: "content") + var element : Content { + self.content + } +} diff --git a/Listable/Sources/ListItemElement.swift b/Listable/Sources/EmbeddedList.swift similarity index 58% rename from Listable/Sources/ListItemElement.swift rename to Listable/Sources/EmbeddedList.swift index c860dd7dd..f69e4106c 100644 --- a/Listable/Sources/ListItemElement.swift +++ b/Listable/Sources/EmbeddedList.swift @@ -1,17 +1,21 @@ // -// ListItemElement.swift +// EmbeddedList.swift // Listable // // Created by Kyle Van Essen on 11/10/19. // -public extension Item where Element == ListItemElement +public extension Item where Content == EmbeddedList { - static func list(identifier : Identifier, sizing : ListItemSizing, build : ListDescription.Build) -> Item + static func list( + identifier : Identifier, + sizing : EmbeddedList.Sizing, + build : ListDescription.Build + ) -> Item { return Item( - ListItemElement(identifier: identifier, build: build), + EmbeddedList(identifier: identifier, build: build), sizing: sizing.toStandardSizing, layout: ItemLayout(width: .fill) ) @@ -19,21 +23,7 @@ public extension Item where Element == ListItemElement } -public enum ListItemSizing : Equatable -{ - case `default` - case fixed(width: CGFloat = 0.0, height : CGFloat = 0.0) - - var toStandardSizing : Sizing { - switch self { - case .default: return .default - case .fixed(let w, let h): return .fixed(width: w, height: h) - } - } -} - - -public struct ListItemElement : ItemElement +public struct EmbeddedList : ItemContent { // // MARK: Public Properties @@ -66,21 +56,21 @@ public struct ListItemElement : ItemElement } // - // MARK: ItemElement + // MARK: ItemContent // public typealias ContentView = ListView - public var identifier: Identifier { + public var identifier: Identifier { return .init(self.contentIdentifier) } - public func apply(to views : ItemElementViews, for reason: ApplyReason, with info : ApplyItemElementInfo) + public func apply(to views : ItemContentViews, for reason: ApplyReason, with info : ApplyItemContentInfo) { views.content.setProperties(with: self.listDescription) } - public func isEquivalent(to other: ListItemElement) -> Bool + public func isEquivalent(to other: EmbeddedList) -> Bool { return false } @@ -90,3 +80,19 @@ public struct ListItemElement : ItemElement ListView(frame: frame) } } + +public extension EmbeddedList +{ + enum Sizing : Equatable + { + case `default` + case fixed(width: CGFloat = 0.0, height : CGFloat = 0.0) + + var toStandardSizing : Listable.Sizing { + switch self { + case .default: return .default + case .fixed(let w, let h): return .fixed(width: w, height: h) + } + } + } +} diff --git a/Listable/Sources/HeaderFooter.swift b/Listable/Sources/HeaderFooter/HeaderFooter.swift similarity index 76% rename from Listable/Sources/HeaderFooter.swift rename to Listable/Sources/HeaderFooter/HeaderFooter.swift index 2a2a9720e..3b5a721c9 100644 --- a/Listable/Sources/HeaderFooter.swift +++ b/Listable/Sources/HeaderFooter/HeaderFooter.swift @@ -22,16 +22,16 @@ public protocol AnyHeaderFooter_Internal } -public struct HeaderFooter : AnyHeaderFooter +public struct HeaderFooter : AnyHeaderFooter { - public var element : Element + public var content : Content public var sizing : Sizing public var layout : HeaderFooterLayout public var debuggingIdentifier : String? = nil - internal let reuseIdentifier : ReuseIdentifier + internal let reuseIdentifier : ReuseIdentifier // // MARK: Initialization @@ -40,45 +40,45 @@ public struct HeaderFooter : AnyHeaderFooter public typealias Build = (inout HeaderFooter) -> () public init( - _ element : Element, + _ content : Content, build : Build ) { - self.init(element) + self.init(content) build(&self) } public init( - _ element : Element, + _ content : Content, sizing : Sizing = .thatFitsWith(.init(.atLeast(.default))), layout : HeaderFooterLayout = HeaderFooterLayout() ) { - self.element = element + self.content = content self.sizing = sizing self.layout = layout - self.reuseIdentifier = ReuseIdentifier.identifier(for: Element.self) + self.reuseIdentifier = ReuseIdentifier.identifier(for: Content.self) } // MARK: AnyHeaderFooter_Internal public func apply(to anyView : UIView, reason: ApplyReason) { - let view = anyView as! Element.ContentView + let view = anyView as! Content.ContentView - self.element.apply(to: view, reason: reason) + self.content.apply(to: view, reason: reason) } public func anyIsEquivalent(to other : AnyHeaderFooter) -> Bool { - guard let other = other as? HeaderFooter else { + guard let other = other as? HeaderFooter else { return false } - return self.element.isEquivalent(to: other.element) + return self.content.isEquivalent(to: other.content) } public func newPresentationHeaderFooterState() -> Any diff --git a/Listable/Sources/HeaderFooterElement.swift b/Listable/Sources/HeaderFooter/HeaderFooterContent.swift similarity index 78% rename from Listable/Sources/HeaderFooterElement.swift rename to Listable/Sources/HeaderFooter/HeaderFooterContent.swift index 6d2ec7a68..e8f8561e8 100644 --- a/Listable/Sources/HeaderFooterElement.swift +++ b/Listable/Sources/HeaderFooter/HeaderFooterContent.swift @@ -1,12 +1,12 @@ // -// HeaderFooterElement.swift +// HeaderFooterContent.swift // Listable // // Created by Kyle Van Essen on 8/10/19. // -public protocol HeaderFooterElement +public protocol HeaderFooterContent { // // MARK: Applying To Displayed View @@ -31,10 +31,10 @@ public protocol HeaderFooterElement /// -/// If your `HeaderFooterElement` is `Equatable`, you do not need to provide an `isEquivalent` method. +/// If your `HeaderFooterContent` is `Equatable`, you do not need to provide an `isEquivalent` method. /// This default implementation will be provided for you. /// -public extension HeaderFooterElement where Self:Equatable +public extension HeaderFooterContent where Self:Equatable { func isEquivalent(to other : Self) -> Bool { diff --git a/Listable/Sources/Identifier.swift b/Listable/Sources/Identifier.swift index b4aaf5e3c..4b79283d4 100644 --- a/Listable/Sources/Identifier.swift +++ b/Listable/Sources/Identifier.swift @@ -14,7 +14,7 @@ public final class AnyIdentifier : Hashable private let hash : Int - public init(_ value : Identifier) + public init(_ value : Identifier) { self.value = AnyHashable(value) @@ -39,15 +39,15 @@ public final class AnyIdentifier : Hashable } -public final class Identifier : Hashable +public final class Identifier : Hashable { private let type : ObjectIdentifier private let value : AnyHashable? private let hash : Int - /// Identifier which identifies by the type of `Element` only. - /// If you have multiple of `Element` within a list, it is recommended that + /// Identifier which identifies by the type of `Represented` only. + /// If you have multiple of `Represented` within a list, it is recommended that /// you use `init(_ value:)` to provide a unique inner value. public convenience init() { @@ -57,7 +57,7 @@ public final class Identifier : Hashable public init(_ value : Value) { self.value = AnyHashable(value) - self.type = ObjectIdentifier(Element.self) + self.type = ObjectIdentifier(Represented.self) var hasher = Hasher() hasher.combine(self.type) @@ -71,7 +71,7 @@ public final class Identifier : Hashable // MARK: Equatable - public static func == (lhs: Identifier, rhs: Identifier) -> Bool + public static func == (lhs: Identifier, rhs: Identifier) -> Bool { return lhs.hash == rhs.hash && lhs.type == rhs.type && lhs.value == rhs.value } diff --git a/Listable/Sources/Internal/DefaultSwipeView.swift b/Listable/Sources/Internal/DefaultSwipeView.swift index 3b2607934..2ca5cfe75 100644 --- a/Listable/Sources/Internal/DefaultSwipeView.swift +++ b/Listable/Sources/Internal/DefaultSwipeView.swift @@ -9,7 +9,7 @@ import UIKit private let haptics = UIImpactFeedbackGenerator(style: .light) -public final class DefaultSwipeActionsView: UIView, ItemElementSwipeActionsView { +public final class DefaultSwipeActionsView: UIView, ItemContentSwipeActionsView { private var actionButtons: [DefaultSwipeActionButton] = [] private var calculatedNaturalWidth: CGFloat = 0 diff --git a/Listable/Sources/Internal/ItemElementCell.ContentViewContainer.swift b/Listable/Sources/Internal/ItemCell.ContentViewContainer.swift similarity index 96% rename from Listable/Sources/Internal/ItemElementCell.ContentViewContainer.swift rename to Listable/Sources/Internal/ItemCell.ContentViewContainer.swift index b2f11770c..6c5b7a685 100644 --- a/Listable/Sources/Internal/ItemElementCell.ContentViewContainer.swift +++ b/Listable/Sources/Internal/ItemCell.ContentViewContainer.swift @@ -1,5 +1,5 @@ // -// ItemCellView.swift +// ItemCell.ContentContainerView.swift // Listable // // Created by Kyle Van Essen on 3/23/20. @@ -8,11 +8,11 @@ import UIKit -extension ItemElementCell { +extension ItemCell { final class ContentContainerView : UIView { - let contentView : Element.ContentView + let contentView : Content.ContentView private var swipeConfiguration: SwipeConfiguration? private var swipeState: SwipeActionState = .closed { @@ -26,7 +26,7 @@ extension ItemElementCell { override init(frame : CGRect) { let bounds = CGRect(origin: .zero, size: frame.size) - self.contentView = Element.createReusableContentView(frame: bounds) + self.contentView = Content.createReusableContentView(frame: bounds) super.init(frame: frame) @@ -108,7 +108,7 @@ extension ItemElementCell { public func registerSwipeActionsIfNeeded(actions: SwipeActionsConfiguration, reason: ApplyReason) { if swipeConfiguration == nil { - let swipeView = Element.SwipeActionsView(didPerformAction: self.didPerformAction) + let swipeView = Content.SwipeActionsView(didPerformAction: self.didPerformAction) insertSubview(swipeView, belowSubview: contentView) swipeView.clipsToBounds = true @@ -234,7 +234,7 @@ extension ItemElementCell { struct SwipeConfiguration { let panGestureRecognizer: UIPanGestureRecognizer - let swipeView: Element.SwipeActionsView + let swipeView: Content.SwipeActionsView var numberOfActions: Int } } diff --git a/Listable/Sources/Internal/ItemElementCell.swift b/Listable/Sources/Internal/ItemCell.swift similarity index 91% rename from Listable/Sources/Internal/ItemElementCell.swift rename to Listable/Sources/Internal/ItemCell.swift index 49dbb77fc..958cb13c0 100644 --- a/Listable/Sources/Internal/ItemElementCell.swift +++ b/Listable/Sources/Internal/ItemCell.swift @@ -1,5 +1,5 @@ // -// ItemElementCell.swift +// ItemCell.swift // Listable // // Created by Kyle Van Essen on 9/22/19. @@ -14,12 +14,12 @@ import UIKit /// Information on how cell selection appearance customization works: /// https://developer.apple.com/documentation/uikit/uicollectionviewdelegate/changing_the_appearance_of_selected_and_highlighted_cells /// -final class ItemElementCell : UICollectionViewCell +final class ItemCell : UICollectionViewCell { let contentContainer : ContentContainerView - let background : Element.BackgroundView - let selectedBackground : Element.SelectedBackgroundView + let background : Content.BackgroundView + let selectedBackground : Content.SelectedBackgroundView override init(frame: CGRect) { @@ -27,8 +27,8 @@ final class ItemElementCell : UICollectionViewCell self.contentContainer = ContentContainerView(frame: bounds) - self.background = Element.createReusableBackgroundView(frame: bounds) - self.selectedBackground = Element.createReusableSelectedBackgroundView(frame: bounds) + self.background = Content.createReusableBackgroundView(frame: bounds) + self.selectedBackground = Content.createReusableSelectedBackgroundView(frame: bounds) super.init(frame: frame) diff --git a/Listable/Sources/Internal/Presentation State/PresentationState.HeaderFooterState.swift b/Listable/Sources/Internal/Presentation State/PresentationState.HeaderFooterState.swift index 385a87154..f5a0910ec 100644 --- a/Listable/Sources/Internal/Presentation State/PresentationState.HeaderFooterState.swift +++ b/Listable/Sources/Internal/Presentation State/PresentationState.HeaderFooterState.swift @@ -64,11 +64,11 @@ extension PresentationState } } - final class HeaderFooterState : AnyPresentationHeaderFooterState + final class HeaderFooterState : AnyPresentationHeaderFooterState { - var model : HeaderFooter + var model : HeaderFooter - init(_ model : HeaderFooter) + init(_ model : HeaderFooter) { self.model = model } @@ -82,7 +82,7 @@ extension PresentationState func dequeueAndPrepareReusableHeaderFooterView(in cache : ReusableViewCache, frame : CGRect) -> UIView { let view = cache.pop(with: self.model.reuseIdentifier) { - return Element.createReusableHeaderFooterView(frame: frame) + return Content.createReusableHeaderFooterView(frame: frame) } self.applyTo(view: view, reason: .willDisplay) @@ -97,21 +97,21 @@ extension PresentationState func createReusableHeaderFooterView(frame : CGRect) -> UIView { - return Element.createReusableHeaderFooterView(frame: frame) + return Content.createReusableHeaderFooterView(frame: frame) } func applyTo(view : UIView, reason : ApplyReason) { - let view = view as! Element.ContentView + let view = view as! Content.ContentView - self.model.element.apply(to: view, reason: reason) + self.model.content.apply(to: view, reason: reason) } func setNew(headerFooter anyHeaderFooter: AnyHeaderFooter) { let oldModel = self.model - self.model = anyHeaderFooter as! HeaderFooter + self.model = anyHeaderFooter as! HeaderFooter let isEquivalent = self.model.anyIsEquivalent(to: oldModel) @@ -148,9 +148,9 @@ extension PresentationState let size : CGSize = measurementCache.use( with: self.model.reuseIdentifier, create: { - return Element.createReusableHeaderFooterView(frame: .zero) + return Content.createReusableHeaderFooterView(frame: .zero) }, { view in - self.model.element.apply(to: view, reason: .willDisplay) + self.model.content.apply(to: view, reason: .willDisplay) return self.model.sizing.measure(with: view, in: sizeConstraint, layoutDirection: layoutDirection, defaultSize: defaultSize) }) diff --git a/Listable/Sources/Internal/Presentation State/PresentationState.ItemState.swift b/Listable/Sources/Internal/Presentation State/PresentationState.ItemState.swift index f585821c2..85d9af08b 100644 --- a/Listable/Sources/Internal/Presentation State/PresentationState.ItemState.swift +++ b/Listable/Sources/Internal/Presentation State/PresentationState.ItemState.swift @@ -43,7 +43,7 @@ protocol AnyPresentationItemState : AnyObject } -protocol ItemElementCoordinatorDelegate : AnyObject +protocol ItemContentCoordinatorDelegate : AnyObject { func coordinatorUpdated(for item : AnyItem, animated : Bool) } @@ -52,7 +52,7 @@ protocol ItemElementCoordinatorDelegate : AnyObject public struct ItemStateDependencies { internal var reorderingDelegate : ReorderingActionsDelegate - internal var coordinatorDelegate : ItemElementCoordinatorDelegate + internal var coordinatorDelegate : ItemContentCoordinatorDelegate } @@ -66,19 +66,19 @@ extension PresentationState case noChange } - final class ItemState : AnyPresentationItemState + final class ItemState : AnyPresentationItemState { - var model : Item { + var model : Item { self.storage.model } private(set) var coordination : Coordination struct Coordination { - var coordinator : Element.Coordinator + var coordinator : Content.Coordinator - let actions : ItemElementCoordinatorActions - let info : ItemElementCoordinatorInfo + let actions : ItemContentCoordinatorActions + let info : ItemContentCoordinatorInfo } let reorderingActions: ReorderingActions @@ -87,17 +87,17 @@ extension PresentationState let storage : Storage - init(with model : Item, dependencies : ItemStateDependencies) + init(with model : Item, dependencies : ItemStateDependencies) { self.reorderingActions = ReorderingActions() self.itemPosition = .single - self.cellRegistrationInfo = (ItemElementCell.self, model.reuseIdentifier.stringValue) + self.cellRegistrationInfo = (ItemCell.self, model.reuseIdentifier.stringValue) let storage = Storage(model) self.storage = storage - let actions = ItemElementCoordinatorActions( + let actions = ItemContentCoordinatorActions( current: { storage.model }, update: { new, _ in @@ -108,12 +108,12 @@ extension PresentationState } ) - let info = ItemElementCoordinatorInfo( + let info = ItemContentCoordinatorInfo( original: storage.model, current: { storage.model } ) - let coordinator = model.element.makeCoordinator(actions: actions, info: info) + let coordinator = model.content.makeCoordinator(actions: actions, info: info) self.coordination = Coordination( coordinator: coordinator, @@ -161,9 +161,9 @@ extension PresentationState self.isDisplayed = isDisplayed if self.isDisplayed { - self.model.onDisplay?(self.model.element) + self.model.onDisplay?(self.model.content) } else { - self.model.onEndDisplay?(self.model.element) + self.model.onEndDisplay?(self.model.content) } } @@ -177,7 +177,7 @@ extension PresentationState { let anyCell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellRegistrationInfo.reuseIdentifier, for: indexPath) - let cell = anyCell as! ItemElementCell + let cell = anyCell as! ItemCell // Theme cell & apply content. @@ -194,9 +194,9 @@ extension PresentationState func applyTo(cell anyCell : UICollectionViewCell, itemState : Listable.ItemState, reason : ApplyReason) { - let cell = anyCell as! ItemElementCell + let cell = anyCell as! ItemCell - let applyInfo = ApplyItemElementInfo( + let applyInfo = ApplyItemContentInfo( state: itemState, position: self.itemPosition, reordering: self.reorderingActions @@ -204,8 +204,8 @@ extension PresentationState // Apply Model State - self.model.element.apply( - to: ItemElementViews(content: cell.contentContainer.contentView, background: cell.background, selectedBackground: cell.selectedBackground), + self.model.content.apply( + to: ItemContentViews(content: cell.contentContainer.contentView, background: cell.background, selectedBackground: cell.selectedBackground), for: reason, with: applyInfo ) @@ -234,7 +234,7 @@ extension PresentationState func setNew(item anyItem: AnyItem, reason: ItemUpdateReason) { let old = self.model - let new = anyItem as! Item + let new = anyItem as! Item self.storage.model = new @@ -254,7 +254,7 @@ extension PresentationState func willDisplay(cell anyCell : UICollectionViewCell, in collectionView : UICollectionView, for indexPath : IndexPath) { - let cell = (anyCell as! ItemElementCell) + let cell = (anyCell as! ItemCell) self.storage.state.visibleCell = cell } @@ -278,9 +278,9 @@ extension PresentationState self.storage.state.isSelected = isSelected if isSelected { - self.model.onSelect?(self.model.element) + self.model.onSelect?(self.model.content) } else { - self.model.onDeselect?(self.model.element) + self.model.onDeselect?(self.model.content) } self.applyToVisibleCell() @@ -335,12 +335,12 @@ extension PresentationState if let size = self.cachedSizes[key] { return size } else { - SignpostLogger.log(.begin, log: .updateContent, name: "Measure ItemElement", for: self.model) + SignpostLogger.log(.begin, log: .updateContent, name: "Measure ItemContent", for: self.model) let size : CGSize = measurementCache.use( with: self.model.reuseIdentifier, create: { - return ItemElementCell() + return ItemCell() }, { cell in let itemState = Listable.ItemState(isSelected: false, isHighlighted: false) @@ -351,7 +351,7 @@ extension PresentationState self.cachedSizes[key] = size - SignpostLogger.log(.end, log: .updateContent, name: "Measure ItemElement", for: self.model) + SignpostLogger.log(.end, log: .updateContent, name: "Measure ItemContent", for: self.model) return size } @@ -371,7 +371,7 @@ extension PresentationState.ItemState var didSetState : (State, State) -> () = { _, _ in } - var model : Item { + var model : Item { willSet { guard self.model.identifier == newValue.identifier else { fatalError("Cannot change the identifier of an item while updating it. Changed from '\(self.model.identifier)' to '\(newValue.identifier)'.") @@ -389,7 +389,7 @@ extension PresentationState.ItemState } } - init(_ model : Item) + init(_ model : Item) { self.model = model @@ -400,6 +400,6 @@ extension PresentationState.ItemState internal struct State : Equatable { var isSelected : Bool - var visibleCell : ItemElementCell? + var visibleCell : ItemCell? } } diff --git a/Listable/Sources/Internal/SupplementaryContainerView.swift b/Listable/Sources/Internal/SupplementaryContainerView.swift index dda9f7dc5..ef6a6edfa 100644 --- a/Listable/Sources/Internal/SupplementaryContainerView.swift +++ b/Listable/Sources/Internal/SupplementaryContainerView.swift @@ -126,7 +126,7 @@ final class SupplementaryContainerView : UICollectionReusableView override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { - // Note – Please keep this comment in sync with the comment in ItemElementCell. + // Note – Please keep this comment in sync with the comment in ItemCell. /** Listable already properly sizes each cell. We do not use self-sizing cells. diff --git a/Listable/Sources/Item.swift b/Listable/Sources/Item/Item.swift similarity index 85% rename from Listable/Sources/Item.swift rename to Listable/Sources/Item/Item.swift index 9731637a5..78b069e85 100644 --- a/Listable/Sources/Item.swift +++ b/Listable/Sources/Item/Item.swift @@ -38,11 +38,11 @@ public protocol AnyItem_Internal } -public struct Item : AnyItem +public struct Item : AnyItem { public var identifier : AnyIdentifier - public var element : Element + public var content : Content public var sizing : Sizing public var layout : ItemLayout @@ -53,19 +53,19 @@ public struct Item : AnyItem public var reordering : Reordering? - public typealias OnSelect = (Element) -> () + public typealias OnSelect = (Content) -> () public var onSelect : OnSelect? - public typealias OnDeselect = (Element) -> () + public typealias OnDeselect = (Content) -> () public var onDeselect : OnDeselect? - public typealias OnDisplay = (Element) -> () + public typealias OnDisplay = (Content) -> () public var onDisplay : OnDisplay? - public typealias OnEndDisplay = (Element) -> () + public typealias OnEndDisplay = (Content) -> () public var onEndDisplay : OnEndDisplay? - internal let reuseIdentifier : ReuseIdentifier + internal let reuseIdentifier : ReuseIdentifier public var debuggingIdentifier : String? = nil @@ -76,17 +76,17 @@ public struct Item : AnyItem public typealias Build = (inout Item) -> () public init( - _ element : Element, + _ content : Content, build : Build ) { - self.init(element) + self.init(content) build(&self) } public init( - _ element : Element, + _ content : Content, sizing : Sizing? = nil, layout : ItemLayout? = nil, selectionStyle : ItemSelectionStyle? = nil, @@ -98,11 +98,11 @@ public struct Item : AnyItem onDeselect : OnDeselect? = nil ) { - self.element = element + self.content = content if let sizing = sizing { self.sizing = sizing - } else if let sizing = element.defaultItemProperties.sizing { + } else if let sizing = content.defaultItemProperties.sizing { self.sizing = sizing } else { self.sizing = .thatFitsWith(.init(.atLeast(.default))) @@ -110,7 +110,7 @@ public struct Item : AnyItem if let layout = layout { self.layout = layout - } else if let layout = element.defaultItemProperties.layout { + } else if let layout = content.defaultItemProperties.layout { self.layout = layout } else { self.layout = ItemLayout() @@ -118,7 +118,7 @@ public struct Item : AnyItem if let selectionStyle = selectionStyle { self.selectionStyle = selectionStyle - } else if let selectionStyle = element.defaultItemProperties.selectionStyle { + } else if let selectionStyle = content.defaultItemProperties.selectionStyle { self.selectionStyle = selectionStyle } else { self.selectionStyle = .notSelectable @@ -126,7 +126,7 @@ public struct Item : AnyItem if let swipeActions = swipeActions { self.swipeActions = swipeActions - } else if let swipeActions = element.defaultItemProperties.swipeActions { + } else if let swipeActions = content.defaultItemProperties.swipeActions { self.swipeActions = swipeActions } else { self.swipeActions = nil @@ -140,29 +140,29 @@ public struct Item : AnyItem self.onSelect = onSelect self.onDeselect = onDeselect - self.reuseIdentifier = ReuseIdentifier.identifier(for: Element.self) + self.reuseIdentifier = ReuseIdentifier.identifier(for: Content.self) - self.identifier = self.element.identifier.toAny + self.identifier = self.content.identifier.toAny } // MARK: AnyItem_Internal public func anyIsEquivalent(to other : AnyItem) -> Bool { - guard let other = other as? Item else { + guard let other = other as? Item else { return false } - return self.element.isEquivalent(to: other.element) + return self.content.isEquivalent(to: other.content) } public func anyWasMoved(comparedTo other : AnyItem) -> Bool { - guard let other = other as? Item else { + guard let other = other as? Item else { return true } - return self.element.wasMoved(comparedTo: other.element) + return self.content.wasMoved(comparedTo: other.content) } public func newPresentationItemState(with dependencies : ItemStateDependencies) -> Any @@ -179,10 +179,10 @@ public struct Item : AnyItem /// /// The order of precedence used when assigning values is: /// 1) The value passed to the initializer. -/// 2) The value from `ItemProperties` on the contained `ItemElement`, if non-nil. +/// 2) The value from `defaultItemProperties` on the contained `ItemContent`, if non-nil. /// 3) A standard, default value. /// -public struct DefaultItemProperties +public struct DefaultItemProperties { public var sizing : Sizing? public var layout : ItemLayout? diff --git a/Listable/Sources/ItemElement.swift b/Listable/Sources/Item/ItemContent.swift similarity index 67% rename from Listable/Sources/ItemElement.swift rename to Listable/Sources/Item/ItemContent.swift index 6e0b97e72..fc67c89fa 100644 --- a/Listable/Sources/ItemElement.swift +++ b/Listable/Sources/Item/ItemContent.swift @@ -1,24 +1,24 @@ // -// ItemElement.swift +// ItemContent.swift // Listable // // Created by Kyle Van Essen on 8/10/19. // -public protocol ItemElement where Coordinator.ItemElementType == Self +public protocol ItemContent where Coordinator.ItemContentType == Self { // // MARK: Identification // - /// Identifies the element across updates to the list. This value must remain the same, - /// otherwise the element will be considered a new item, and the old one removed from the list. + /// Identifies the content across updates to the list. This value must remain the same, + /// otherwise the content will be considered a new item, and the old one removed from the list. /// - /// Does not have to be globally unique – the list will make a "best guess" if there are multiple elements + /// Does not have to be globally unique – the list will make a "best guess" if there are multiple items /// with the same identifier. However, diffing of changes will be more correct with a unique identifier. /// - /// If you're backing your element with some sort of client or server-provided data, consider using its + /// If you're backing your content with some sort of client or server-provided data, consider using its /// server or client UUID here, or some other unique identifier from the underlying data model. var identifier : Identifier { get } @@ -27,7 +27,7 @@ public protocol ItemElement where Coordinator.ItemElementType == Self // /// Default values to assign to various properties on the `Item` which wraps - /// this `ItemElement`, if those values are not passed to the `Item` initializer. + /// this `ItemContent`, if those values are not passed to the `Item` initializer. var defaultItemProperties : DefaultItemProperties { get } // @@ -35,15 +35,15 @@ public protocol ItemElement where Coordinator.ItemElementType == Self // /** - Called when rendering the element. This is where you should push data from your - element into the passed in views. + Called when rendering the content. This is where you should push data from your + content into the passed in views. Do not retain a reference to the passed in views – they are reused by the list. */ func apply( - to views : ItemElementViews, + to views : ItemContentViews, for reason: ApplyReason, - with info : ApplyItemElementInfo + with info : ApplyItemContentInfo ) // @@ -51,7 +51,7 @@ public protocol ItemElement where Coordinator.ItemElementType == Self // /** - Return true if the element's sort changed based on the old value passed into the function. + Return true if the content's sort changed based on the old value passed into the function. The list view uses the value of this method to be more intelligent about what has moved within the list. @@ -62,13 +62,13 @@ public protocol ItemElement where Coordinator.ItemElementType == Self func wasMoved(comparedTo other : Self) -> Bool /** - Return false if the element' changed based on the old value passed into the function. + Return false if the content' changed based on the old value passed into the function. - If this method returns false, the row representing the element is reloaded. + If this method returns false, the row representing the content is reloaded. Note ---- - There is a default implementation of this method when `ItemElement ` conforms to `Equatable` + There is a default implementation of this method when `ItemContent ` conforms to `Equatable` which returns `self == other`. */ func isEquivalent(to other : Self) -> Bool @@ -77,40 +77,40 @@ public protocol ItemElement where Coordinator.ItemElementType == Self // MARK: Creating & Providing Swipe Action Views // - /// The view type to use to render swipe actions (delete, etc) for this item element. + /// The view type to use to render swipe actions (delete, etc) for this content. /// A default implementation, which matches `UITableView`, is provided. - associatedtype SwipeActionsView: ItemElementSwipeActionsView = DefaultSwipeActionsView + associatedtype SwipeActionsView: ItemContentSwipeActionsView = DefaultSwipeActionsView // // MARK: Creating & Providing Content Views // - /// The content view used to draw the element. + /// The content view used to draw the content. /// The content view is drawn at the top of the view hierarchy, above the background views. associatedtype ContentView:UIView - /// Create and return a new content view used to render the element. + /// Create and return a new content view used to render the content. /// /// Note /// ---- /// Do not do configuration in this method that will be changed by your view's theme or appearance – instead - /// do that work in `apply(to:)`, so the appearance will be updated if the appearance of elements changes. + /// do that work in `apply(to:)`, so the appearance will be updated if the appearance of content changes. static func createReusableContentView(frame : CGRect) -> ContentView // // MARK: Content Coordination // - /// The coordinator type to use to manage the live state of the `Item` and `ItemElement`, + /// The coordinator type to use to manage the live state of the `Item` and `ItemContent`, /// if you need to update content based on signals such as notifications, view state, appearance state, /// etc. - associatedtype Coordinator : ItemElementCoordinator = DefaultItemElementCoordinator + associatedtype Coordinator : ItemContentCoordinator = DefaultItemContentCoordinator /// The actions passed to the coordinator. - typealias CoordinatorActions = ItemElementCoordinatorActions + typealias CoordinatorActions = ItemContentCoordinatorActions /// The info passed to the coordinator. - typealias CoordinatorInfo = ItemElementCoordinatorInfo + typealias CoordinatorInfo = ItemContentCoordinatorInfo /// Creates a new coordinator with the provided actions and info. func makeCoordinator(actions : CoordinatorActions, info : CoordinatorInfo) -> Coordinator @@ -119,7 +119,7 @@ public protocol ItemElement where Coordinator.ItemElementType == Self // MARK: Creating & Providing Background Views // - /// The background view used to draw the background of the element. + /// The background view used to draw the background of the content. /// The background view is drawn below the content view. /// /// Note @@ -130,15 +130,15 @@ public protocol ItemElement where Coordinator.ItemElementType == Self /// associatedtype BackgroundView:UIView = UIView - /// Create and return a new background view used to render the element's background. + /// Create and return a new background view used to render the content's background. /// /// Note /// ---- /// Do not do configuration in this method that will be changed by your view's theme or appearance – instead - /// do that work in `apply(to:)`, so the appearance will be updated if the appearance of elements changes. + /// do that work in `apply(to:)`, so the appearance will be updated if the appearance of content changes. static func createReusableBackgroundView(frame : CGRect) -> BackgroundView - /// The selected background view used to draw the background of the element when it is selected or highlighted. + /// The selected background view used to draw the background of the content when it is selected or highlighted. /// The selected background view is drawn below the content view. /// /// Note @@ -150,9 +150,9 @@ public protocol ItemElement where Coordinator.ItemElementType == Self associatedtype SelectedBackgroundView:UIView = BackgroundView - /// Create and return a new background view used to render the element's selected background. + /// Create and return a new background view used to render the content's selected background. /// - /// This view is displayed when the element is highlighted or selected. + /// This view is displayed when the content is highlighted or selected. /// /// If your `BackgroundView` and `SelectedBackgroundView` are the same type, this method /// is provided automatically by calling `createReusableBackgroundView`. @@ -160,47 +160,47 @@ public protocol ItemElement where Coordinator.ItemElementType == Self /// Note /// ---- /// Do not do configuration in this method that will be changed by your view's theme or appearance – instead - /// do that work in `apply(to:)`, so the appearance will be updated if the appearance of elements changes. + /// do that work in `apply(to:)`, so the appearance will be updated if the appearance of content changes. static func createReusableSelectedBackgroundView(frame : CGRect) -> SelectedBackgroundView } -/// The views owned by the item element, passed to the `apply(to:) method to theme and provide content.` -public struct ItemElementViews +/// The views owned by the item content, passed to the `apply(to:) method to theme and provide content.` +public struct ItemContentViews { - /// The content view of the element. - public var content : Element.ContentView + /// The content view of the content. + public var content : Content.ContentView - /// The background view of the element. - public var background : Element.BackgroundView + /// The background view of the content. + public var background : Content.BackgroundView - /// The selected background view of the element. - /// Displayed when the element is highlighted or selected. - public var selectedBackground : Element.SelectedBackgroundView + /// The selected background view of the content. + /// Displayed when the content is highlighted or selected. + public var selectedBackground : Content.SelectedBackgroundView } -/// Information about the current state of the element, which is passed to `apply(to:)` +/// Information about the current state of the content, which is passed to `apply(to:)` /// during configuration and preparation for display. /// -/// You can use this information to alter the display of your element, such as changing +/// You can use this information to alter the display of your content, such as changing /// the background color for highlights and selections, providing different corner styles /// for different item positions, etc. -public struct ApplyItemElementInfo +public struct ApplyItemContentInfo { - /// The state of the `Item` currently displaying the element. Is it highlighted, selected, etc. + /// The state of the `Item` currently displaying the content. Is it highlighted, selected, etc. public var state : ItemState /// The position of the item within its section. public var position : ItemPosition - /// Provides access to actions to handle re-ordering the element within the list. + /// Provides access to actions to handle re-ordering the content within the list. public var reordering : ReorderingActions } -/// Provide a default implementation of `isEquivalent(to:)` if the `ItemElement` is `Equatable`. -public extension ItemElement where Self:Equatable +/// Provide a default implementation of `isEquivalent(to:)` if the `ItemContent` is `Equatable`. +public extension ItemContent where Self:Equatable { func isEquivalent(to other : Self) -> Bool { @@ -209,8 +209,18 @@ public extension ItemElement where Self:Equatable } +/// Provides a default implementation of `identifier` when self conforms to Swift's `Identifiable` protocol. +@available(iOS 13.0, *) +public extension ItemContent where Self:Identifiable +{ + var identifier : Identifier { + .init(self.id) + } +} + + /// Implement `wasMoved` in terms of `isEquivalent(to:)` by default. -public extension ItemElement +public extension ItemContent { func wasMoved(comparedTo other : Self) -> Bool { @@ -221,7 +231,7 @@ public extension ItemElement /// Provide a default implementation of `defaultItemProperties` which returns an /// empty instance that does not provide any defaults. -public extension ItemElement +public extension ItemContent { var defaultItemProperties : DefaultItemProperties { .init() @@ -230,17 +240,17 @@ public extension ItemElement /// Provides a default coordinator for items without a specified coordinator. -public extension ItemElement where Coordinator == DefaultItemElementCoordinator +public extension ItemContent where Coordinator == DefaultItemContentCoordinator { - func makeCoordinator(actions : ItemElementCoordinatorActions, info : ItemElementCoordinatorInfo) -> Coordinator + func makeCoordinator(actions : ItemContentCoordinatorActions, info : ItemContentCoordinatorInfo) -> Coordinator { - DefaultItemElementCoordinator(actions: actions, info: info, view: nil) + DefaultItemContentCoordinator(actions: actions, info: info, view: nil) } } /// Provide a UIView when no special background view is specified. -public extension ItemElement where BackgroundView == UIView +public extension ItemContent where BackgroundView == UIView { static func createReusableBackgroundView(frame : CGRect) -> BackgroundView { @@ -250,7 +260,7 @@ public extension ItemElement where BackgroundView == UIView /// Provide a UIView when no special selected background view is specified. -public extension ItemElement where BackgroundView == SelectedBackgroundView +public extension ItemContent where BackgroundView == SelectedBackgroundView { static func createReusableSelectedBackgroundView(frame : CGRect) -> BackgroundView { @@ -263,7 +273,7 @@ public extension ItemElement where BackgroundView == SelectedBackgroundView /// /// If you do so, you're completely responsible for creating and laying out the actions, /// as well as updating the layout based on the swipe state. -public protocol ItemElementSwipeActionsView: UIView { +public protocol ItemContentSwipeActionsView: UIView { var swipeActionsWidth: CGFloat { get } diff --git a/Listable/Sources/ItemElementCoordinator.swift b/Listable/Sources/Item/ItemContentCoordinator.swift similarity index 68% rename from Listable/Sources/ItemElementCoordinator.swift rename to Listable/Sources/Item/ItemContentCoordinator.swift index 47563d3c9..69f7a6809 100644 --- a/Listable/Sources/ItemElementCoordinator.swift +++ b/Listable/Sources/Item/ItemContentCoordinator.swift @@ -1,5 +1,5 @@ // -// ItemElementCoordinator.swift +// ItemContentCoordinator.swift // Listable // // Created by Kyle Van Essen on 5/19/20. @@ -7,29 +7,29 @@ /// -/// A type which lets you interactively manage the contents of an `Item` or `ItemElement` +/// A type which lets you interactively manage the contents of an `Item` or `ItemContent` /// within a list. /// -/// Eg, you might create a `ItemElementCoordinator` which listens to a -/// notification, and then updates a field on the `Item` or `ItemElement` in response +/// Eg, you might create a `ItemContentCoordinator` which listens to a +/// notification, and then updates a field on the `Item` or `ItemContent` in response /// to this notification. /// -/// `ItemElementCoordinator` is created when an item is being prepared to be presented +/// `ItemContentCoordinator` is created when an item is being prepared to be presented /// on screen for the first time, and lives for as long as the item is present in the list. If you need /// to pull in any changes to the item due to time passing, you can update the item within the /// `wasCreated`callback. /// -/// There are default implementations of all `ItemElementCoordinator` methods. You only +/// There are default implementations of all `ItemContentCoordinator` methods. You only /// need to provide implementations for the methods relevant to you. /// /// Example /// ------- -/// A simple `ItemElementCoordinator` might look like this: +/// A simple `ItemContentCoordinator` might look like this: /// /// ``` -/// final class MyCoordinator : ItemElementCoordinator +/// final class MyCoordinator : ItemContentCoordinator /// { -/// typealias ItemElementType = MyElementType +/// typealias ItemContentType = MyContentType /// /// let actions: CoordinatorActions /// let info: CoordinatorInfo @@ -46,24 +46,24 @@ /// @objc func downloadUpdated(notification : Notification) /// { /// self.actions.update { -/// $0.element.downloadProgress = notification.userInfo["download_progress"] as! CGFloat +/// $0.content.downloadProgress = notification.userInfo["download_progress"] as! CGFloat /// } /// } /// } /// ``` /// -public protocol ItemElementCoordinator : AnyObject +public protocol ItemContentCoordinator : AnyObject { - /// The type of `ItemElement` associated with this coordinator. - associatedtype ItemElementType : ItemElement + /// The type of `ItemContent` associated with this coordinator. + associatedtype ItemContentType : ItemContent // MARK: Actions & Info /// The available actions you can perform on the coordinated `Item`. Eg, updating it to a new value. - var actions : ItemElementType.CoordinatorActions { get } + var actions : ItemContentType.CoordinatorActions { get } /// Info about the coordinated `Item`, such as its original and current value. - var info : ItemElementType.CoordinatorInfo { get } + var info : ItemContentType.CoordinatorInfo { get } // MARK: Instance Lifecycle @@ -73,7 +73,7 @@ public protocol ItemElementCoordinator : AnyObject /// Invoked on the coordinator when an external update is pushed onto the owned `Item`. /// This happens when the developer updates the content of the list, and the item is /// reported as changed via its `isEquivalent(to:)` method. - func wasUpdated(old : Item, new : Item) + func wasUpdated(old : Item, new : Item) /// Invoked on the coordinator when its owned item is removed from the list due to /// the item, or its entire section, being removed from the list. @@ -83,7 +83,7 @@ public protocol ItemElementCoordinator : AnyObject // MARK: Visibility & View Lifecycle /// The view type associated with the item. - typealias View = ItemElementType.ContentView + typealias View = ItemContentType.ContentView /// The view, if any, currently used to display the item. var view : View? { get set } @@ -104,12 +104,12 @@ public protocol ItemElementCoordinator : AnyObject } -public extension ItemElementCoordinator +public extension ItemContentCoordinator { // MARK: Instance Lifecycle func wasCreated() {} - func wasUpdated(old : Item, new : Item) {} + func wasUpdated(old : Item, new : Item) {} func wasRemoved() {} // MARK: Visibility Lifecycle @@ -127,25 +127,25 @@ public extension ItemElementCoordinator /// The available actions you can perform as a coordinator, which are reported back to the list to manage the item. -public final class ItemElementCoordinatorActions +public final class ItemContentCoordinatorActions { - private let currentProvider : () -> Item - var updateCallback : (Item, Bool) -> () + private let currentProvider : () -> Item + var updateCallback : (Item, Bool) -> () - init(current : @escaping () -> Item, update : @escaping (Item, Bool) -> ()) + init(current : @escaping () -> Item, update : @escaping (Item, Bool) -> ()) { self.currentProvider = current self.updateCallback = update } /// Updates the item to the provided item. - public func update(animated: Bool = false, _ new : Item) + public func update(animated: Bool = false, _ new : Item) { self.updateCallback(new, animated) } /// Allows you to update the item passed into the update closure. - public func update(animated: Bool = false, _ update : (inout Item) -> ()) + public func update(animated: Bool = false, _ update : (inout Item) -> ()) { var new = self.currentProvider() @@ -157,22 +157,22 @@ public final class ItemElementCoordinatorActions /// Information about the current and original state of the item. -public final class ItemElementCoordinatorInfo +public final class ItemContentCoordinatorInfo { /// The original state of the item, as passed to the list. /// This is property is updated when the list is updated, and the /// `isEquivalent(to:)` reports a change to the item. - public internal(set) var original : Item + public internal(set) var original : Item /// The current value of the item, including changes made /// by the coordinator itself. - public var current : Item { + public var current : Item { self.currentProvider() } - private let currentProvider : () -> Item + private let currentProvider : () -> Item - init(original : Item, current : @escaping () -> Item) + init(original : Item, current : @escaping () -> Item) { self.original = original @@ -181,18 +181,18 @@ public final class ItemElementCoordinatorInfo } -/// The default `ItemElementCoordinator`, which performs no actions. -public final class DefaultItemElementCoordinator : ItemElementCoordinator +/// The default `ItemContentCoordinator`, which performs no actions. +public final class DefaultItemContentCoordinator : ItemContentCoordinator { - public let actions : Element.CoordinatorActions - public let info : Element.CoordinatorInfo + public let actions : Content.CoordinatorActions + public let info : Content.CoordinatorInfo - public var view : Element.ContentView? + public var view : Content.ContentView? internal init( - actions: Element.CoordinatorActions, - info: Element.CoordinatorInfo, - view: DefaultItemElementCoordinator.View? + actions: Content.CoordinatorActions, + info: Content.CoordinatorInfo, + view: DefaultItemContentCoordinator.View? ) { self.actions = actions self.info = info diff --git a/Listable/Sources/ListView/ListView.swift b/Listable/Sources/ListView/ListView.swift index e31158ce8..978dcff63 100644 --- a/Listable/Sources/ListView/ListView.swift +++ b/Listable/Sources/ListView/ListView.swift @@ -279,7 +279,7 @@ public final class ListView : UIView /// If the item is contained in the list, true is returned. If it is not, false is returned. /// @discardableResult - public func scrollTo(item : Identifier, position : ItemScrollPosition, animated : Bool = false) -> Bool + public func scrollTo(item : Identifier, position : ItemScrollPosition, animated : Bool = false) -> Bool { return self.scrollTo(item: item.toAny, position: position, animated: animated) } @@ -807,7 +807,7 @@ public final class ListView : UIView } -extension ListView : ItemElementCoordinatorDelegate +extension ListView : ItemContentCoordinatorDelegate { func coordinatorUpdated(for : AnyItem, animated : Bool) { diff --git a/Listable/Sources/Section.swift b/Listable/Sources/Section.swift index 3148a5f86..1dcd7db0a 100644 --- a/Listable/Sources/Section.swift +++ b/Listable/Sources/Section.swift @@ -105,11 +105,16 @@ public struct Section lhs.add(rhs) } - public static func += (lhs : inout Section, rhs : Item) + public static func += (lhs : inout Section, rhs : Item) { lhs.add(rhs) } + public static func += (lhs : inout Section, rhs : Content) + { + lhs += Item(rhs) + } + // // MARK: Adding & Removing Multiple Items // @@ -119,11 +124,16 @@ public struct Section lhs.items += rhs } - public static func += (lhs : inout Section, rhs : [Item]) + public static func += (lhs : inout Section, rhs : [Item]) { lhs.items += rhs } + public static func += (lhs : inout Section, rhs : [Content]) + { + lhs.items += rhs.map { Item($0) } + } + // // MARK: Slicing // diff --git a/Listable/Tests/HeaderFooterTests.swift b/Listable/Tests/HeaderFooter/HeaderFooterTests.swift similarity index 100% rename from Listable/Tests/HeaderFooterTests.swift rename to Listable/Tests/HeaderFooter/HeaderFooterTests.swift diff --git a/Listable/Tests/Internal/ItemElementCellTests.swift b/Listable/Tests/Internal/ItemCellTests.swift similarity index 65% rename from Listable/Tests/Internal/ItemElementCellTests.swift rename to Listable/Tests/Internal/ItemCellTests.swift index 4b319b57e..bc9b70be2 100644 --- a/Listable/Tests/Internal/ItemElementCellTests.swift +++ b/Listable/Tests/Internal/ItemCellTests.swift @@ -1,5 +1,5 @@ // -// ItemElementCellTests.swift +// ItemCellTests.swift // Listable-Unit-Tests // // Created by Kyle Van Essen on 11/22/19. @@ -14,7 +14,7 @@ class ItemElementCellTests : XCTestCase { func test_init() { - let cell = ItemElementCell(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0))) + let cell = ItemCell(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0))) XCTAssertEqual(cell.backgroundColor, .clear) XCTAssertEqual(cell.layer.masksToBounds, false) @@ -30,23 +30,23 @@ class ItemElementCellTests : XCTestCase // The default implementation of size that fits on UIView returns the existing size of the view. // Make sure that value is returned from the cell. - let cell1 = ItemElementCell(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0))) + let cell1 = ItemCell(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0))) XCTAssertEqual(cell1.sizeThatFits(.zero), CGSize(width: 100.0, height: 100.0)) - let cell2 = ItemElementCell(frame: CGRect(origin: .zero, size: CGSize(width: 150.0, height: 150.0))) + let cell2 = ItemCell(frame: CGRect(origin: .zero, size: CGSize(width: 150.0, height: 150.0))) XCTAssertEqual(cell2.sizeThatFits(.zero), CGSize(width: 150.0, height: 150.0)) } } -fileprivate struct TestItemElement : ItemElement, Equatable +fileprivate struct TestItemContent : ItemContent, Equatable { // MARK: ItemElement - var identifier: Identifier { + var identifier: Identifier { return .init("Test") } - func apply(to views: ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) {} + func apply(to views: ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) {} typealias ContentView = UIView diff --git a/Listable/Tests/Internal/Presentation State/PresentationState.ItemStateTests.swift b/Listable/Tests/Internal/Presentation State/PresentationState.ItemStateTests.swift index cce8feb37..8ba4c9389 100644 --- a/Listable/Tests/Internal/Presentation State/PresentationState.ItemStateTests.swift +++ b/Listable/Tests/Internal/Presentation State/PresentationState.ItemStateTests.swift @@ -13,14 +13,14 @@ class PresentationState_ItemStateTests : XCTestCase { func test_init() { - let coordinatorDelegate = ItemElementCoordinatorDelegateMock() + let coordinatorDelegate = ItemContentCoordinatorDelegateMock() let dependencies = ItemStateDependencies( reorderingDelegate: ReorderingActionsDelegateMock(), coordinatorDelegate: coordinatorDelegate ) - let initial = Item(TestElement(value: "initial")) + let initial = Item(TestContent(value: "initial")) let state = PresentationState.ItemState(with: initial, dependencies: dependencies) @@ -30,7 +30,7 @@ class PresentationState_ItemStateTests : XCTestCase XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.coordination.coordinator.wasCreated_calls.count, 1) - XCTAssertEqual(state.model.element.updates, [ + XCTAssertEqual(state.model.content.updates, [ "update within coordinator init" ]) @@ -42,7 +42,7 @@ class PresentationState_ItemStateTests : XCTestCase XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.coordination.coordinator.wasCreated_calls.count, 1) - XCTAssertEqual(state.model.element.updates, [ + XCTAssertEqual(state.model.content.updates, [ "update within coordinator init", "first update" ]) @@ -53,7 +53,7 @@ class PresentationState_ItemStateTests : XCTestCase XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.coordination.coordinator.wasCreated_calls.count, 1) - XCTAssertEqual(state.model.element.updates, [ + XCTAssertEqual(state.model.content.updates, [ "update within coordinator init", "first update", "second update" @@ -65,11 +65,11 @@ class PresentationState_ItemStateTests : XCTestCase self.testcase("Only update isSelected if the selectionStyle changes") { let dependencies = ItemStateDependencies( reorderingDelegate: ReorderingActionsDelegateMock(), - coordinatorDelegate: ItemElementCoordinatorDelegateMock() + coordinatorDelegate: ItemContentCoordinatorDelegateMock() ) let initial = Item( - TestElement(value: "initial"), + TestContent(value: "initial"), selectionStyle: .selectable(isSelected: false) ) @@ -92,7 +92,7 @@ class PresentationState_ItemStateTests : XCTestCase state.storage.state.isSelected = false let updated = Item( - TestElement(value: "updated"), + TestContent(value: "updated"), selectionStyle: .selectable(isSelected: true) ) @@ -104,16 +104,16 @@ class PresentationState_ItemStateTests : XCTestCase self.testcase("Testing Different ItemUpdateReasons") { let dependencies = ItemStateDependencies( reorderingDelegate: ReorderingActionsDelegateMock(), - coordinatorDelegate: ItemElementCoordinatorDelegateMock() + coordinatorDelegate: ItemContentCoordinatorDelegateMock() ) let initial = Item( - TestElement(value: "initial"), + TestContent(value: "initial"), selectionStyle: .selectable(isSelected: false) ) let updated = Item( - TestElement(value: "updated"), + TestContent(value: "updated"), selectionStyle: .selectable(isSelected: true) ) @@ -122,16 +122,16 @@ class PresentationState_ItemStateTests : XCTestCase case .move: let state = PresentationState.ItemState(with: initial, dependencies: dependencies) - XCTAssertEqual(state.model.element.value, "initial") - XCTAssertEqual(state.coordination.info.original.element.value, "initial") + XCTAssertEqual(state.model.content.value, "initial") + XCTAssertEqual(state.coordination.info.original.content.value, "initial") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.storage.state.isSelected, false) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 0) state.setNew(item: updated, reason: .move) - XCTAssertEqual(state.model.element.value, "updated") - XCTAssertEqual(state.coordination.info.original.element.value, "updated") + XCTAssertEqual(state.model.content.value, "updated") + XCTAssertEqual(state.coordination.info.original.content.value, "updated") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 1) XCTAssertEqual(state.storage.state.isSelected, true) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 1) @@ -139,16 +139,16 @@ class PresentationState_ItemStateTests : XCTestCase case .updateFromList: let state = PresentationState.ItemState(with: initial, dependencies: dependencies) - XCTAssertEqual(state.model.element.value, "initial") - XCTAssertEqual(state.coordination.info.original.element.value, "initial") + XCTAssertEqual(state.model.content.value, "initial") + XCTAssertEqual(state.coordination.info.original.content.value, "initial") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.storage.state.isSelected, false) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 0) state.setNew(item: updated, reason: .updateFromList) - XCTAssertEqual(state.model.element.value, "updated") - XCTAssertEqual(state.coordination.info.original.element.value, "updated") + XCTAssertEqual(state.model.content.value, "updated") + XCTAssertEqual(state.coordination.info.original.content.value, "updated") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 1) XCTAssertEqual(state.storage.state.isSelected, true) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 1) @@ -156,16 +156,16 @@ class PresentationState_ItemStateTests : XCTestCase case .updateFromItemCoordinator: let state = PresentationState.ItemState(with: initial, dependencies: dependencies) - XCTAssertEqual(state.model.element.value, "initial") - XCTAssertEqual(state.coordination.info.original.element.value, "initial") + XCTAssertEqual(state.model.content.value, "initial") + XCTAssertEqual(state.coordination.info.original.content.value, "initial") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.storage.state.isSelected, false) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 0) state.setNew(item: updated, reason: .updateFromItemCoordinator) - XCTAssertEqual(state.model.element.value, "updated") - XCTAssertEqual(state.coordination.info.original.element.value, "initial") + XCTAssertEqual(state.model.content.value, "updated") + XCTAssertEqual(state.coordination.info.original.content.value, "initial") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.storage.state.isSelected, true) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 1) @@ -173,16 +173,16 @@ class PresentationState_ItemStateTests : XCTestCase case .noChange: let state = PresentationState.ItemState(with: initial, dependencies: dependencies) - XCTAssertEqual(state.model.element.value, "initial") - XCTAssertEqual(state.coordination.info.original.element.value, "initial") + XCTAssertEqual(state.model.content.value, "initial") + XCTAssertEqual(state.coordination.info.original.content.value, "initial") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.storage.state.isSelected, false) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 0) state.setNew(item: updated, reason: .noChange) - XCTAssertEqual(state.model.element.value, "updated") - XCTAssertEqual(state.coordination.info.original.element.value, "initial") + XCTAssertEqual(state.model.content.value, "updated") + XCTAssertEqual(state.coordination.info.original.content.value, "initial") XCTAssertEqual(state.coordination.coordinator.wasUpdated_calls.count, 0) XCTAssertEqual(state.storage.state.isSelected, true) XCTAssertEqual(state.coordination.coordinator.wasSelected_calls.count, 1) @@ -195,10 +195,10 @@ class PresentationState_ItemStateTests : XCTestCase { let dependencies = ItemStateDependencies( reorderingDelegate: ReorderingActionsDelegateMock(), - coordinatorDelegate: ItemElementCoordinatorDelegateMock() + coordinatorDelegate: ItemContentCoordinatorDelegateMock() ) - let item = Item(TestElement(value: "initial")) + let item = Item(TestContent(value: "initial")) let state = PresentationState.ItemState(with: item, dependencies: dependencies) @@ -247,7 +247,7 @@ class PresentationState_ItemStateTests : XCTestCase visibleCell: nil ), new: .init( isSelected: false, - visibleCell: ItemElementCell() + visibleCell: ItemCell() ) ) @@ -259,7 +259,7 @@ class PresentationState_ItemStateTests : XCTestCase state.updateCoordinatorWithStateChange( old: .init( isSelected: false, - visibleCell: ItemElementCell() + visibleCell: ItemCell() ), new: .init( isSelected: false, visibleCell: nil @@ -278,7 +278,7 @@ class PresentationState_ItemState_StorageTests : XCTestCase { func test_init() { - let item = Item(TestElement(value: "initial"), selectionStyle: .selectable(isSelected: true)) + let item = Item(TestContent(value: "initial"), selectionStyle: .selectable(isSelected: true)) let storage = PresentationState.ItemState.Storage(item) @@ -288,16 +288,16 @@ class PresentationState_ItemState_StorageTests : XCTestCase } -fileprivate struct TestElement : ItemElement, Equatable +fileprivate struct TestContent : ItemContent, Equatable { typealias ContentView = UIView var value : String var updates : [String] = [] - var identifier: Identifier = .init("") + var identifier: Identifier = .init("") - func apply(to views: ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) {} + func apply(to views: ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) {} static func createReusableContentView(frame: CGRect) -> UIView { UIView(frame: frame) @@ -308,9 +308,9 @@ fileprivate struct TestElement : ItemElement, Equatable Coordinator(actions: actions, info: info) } - final class Coordinator : ItemElementCoordinator + final class Coordinator : ItemContentCoordinator { - init(actions : TestElement.CoordinatorActions, info : TestElement.CoordinatorInfo) + init(actions : TestContent.CoordinatorActions, info : TestContent.CoordinatorInfo) { self.actions = actions self.info = info @@ -321,13 +321,13 @@ fileprivate struct TestElement : ItemElement, Equatable func triggerUpdate(with newContent : String) { self.actions.update { - $0.element.updates.append(newContent) + $0.content.updates.append(newContent) } } // MARK: ItemElementCoordinator - typealias ItemElementType = TestElement + typealias ItemContentType = TestContent var actions: CoordinatorActions var info: CoordinatorInfo @@ -341,9 +341,9 @@ fileprivate struct TestElement : ItemElement, Equatable self.wasCreated_calls.append(()) } - var wasUpdated_calls = [(old : Item, new : Item)]() + var wasUpdated_calls = [(old : Item, new : Item)]() - func wasUpdated(old : Item, new : Item) + func wasUpdated(old : Item, new : Item) { self.wasUpdated_calls.append((old, new)) } @@ -357,7 +357,7 @@ fileprivate struct TestElement : ItemElement, Equatable // MARK: ItemElementCoordinator - Visibility & View Lifecycle - typealias View = ItemElementType.ContentView + typealias View = ItemContentType.ContentView var view_didSet_calls = [View?]() @@ -401,7 +401,7 @@ fileprivate struct TestElement : ItemElement, Equatable } -fileprivate class ItemElementCoordinatorDelegateMock : ItemElementCoordinatorDelegate +fileprivate class ItemContentCoordinatorDelegateMock : ItemContentCoordinatorDelegate { var coordinatorUpdated_calls = [AnyItem]() diff --git a/Listable/Tests/Internal/SupplementaryItemViewTests.swift b/Listable/Tests/Internal/SupplementaryItemViewTests.swift index 41f2f3b91..0099c35db 100644 --- a/Listable/Tests/Internal/SupplementaryItemViewTests.swift +++ b/Listable/Tests/Internal/SupplementaryItemViewTests.swift @@ -14,7 +14,7 @@ class SupplementaryContainerViewTests: XCTestCase { func newHeaderFooter() -> AnyPresentationHeaderFooterState { - let headerFooter = HeaderFooter(TestHeaderFooterElement()) + let headerFooter = HeaderFooter(TestHeaderFooterContent()) return PresentationState.HeaderFooterState(headerFooter) } @@ -58,7 +58,7 @@ class SupplementaryContainerViewTests: XCTestCase let content = view.content! - XCTAssertTrue(type(of: content) === TestHeaderFooterElement.View.self) + XCTAssertTrue(type(of: content) === TestHeaderFooterContent.View.self) XCTAssertEqual(view.frame.size, CGSize(width: 100, height: 100)) // Unset the header footer, make sure the view is pushed back into the cache. @@ -67,13 +67,13 @@ class SupplementaryContainerViewTests: XCTestCase XCTAssertNil(view.content) - XCTAssertEqual(cache.count(for: ReuseIdentifier.identifier(for: TestHeaderFooterElement.self)), 1) + XCTAssertEqual(cache.count(for: ReuseIdentifier.identifier(for: TestHeaderFooterContent.self)), 1) // And now, let's set the header one more time to make sure it pulls from the cache. view.headerFooter = self.newHeaderFooter() - XCTAssertEqual(cache.count(for: ReuseIdentifier.identifier(for: TestHeaderFooterElement.self)), 0) + XCTAssertEqual(cache.count(for: ReuseIdentifier.identifier(for: TestHeaderFooterContent.self)), 0) } func test_prepareForReuse() @@ -90,9 +90,9 @@ class SupplementaryContainerViewTests: XCTestCase } } -fileprivate struct TestHeaderFooterElement : HeaderFooterElement, Equatable +fileprivate struct TestHeaderFooterContent : HeaderFooterContent, Equatable { - // MARK: HeaderFooterElement + // MARK: HeaderFooterContent func apply(to view: View, reason: ApplyReason) {} diff --git a/Listable/Tests/ItemElementCoordinatorTests.swift b/Listable/Tests/Item/ItemElementCoordinatorTests.swift similarity index 58% rename from Listable/Tests/ItemElementCoordinatorTests.swift rename to Listable/Tests/Item/ItemElementCoordinatorTests.swift index 3493891ec..3602df6d8 100644 --- a/Listable/Tests/ItemElementCoordinatorTests.swift +++ b/Listable/Tests/Item/ItemElementCoordinatorTests.swift @@ -14,11 +14,11 @@ class ItemElementCoordinatorActionsTests : XCTestCase { func test_update() { - var item = Item(TestElement(value: "first")) + var item = Item(TestContent(value: "first")) var callbackCount = 0 - let actions = ItemElementCoordinatorActions(current: { item }, update: { new, animated in + let actions = ItemContentCoordinatorActions(current: { item }, update: { new, animated in item = new callbackCount += 1 }) @@ -26,20 +26,20 @@ class ItemElementCoordinatorActionsTests : XCTestCase self.testcase("Setter based update") { var updated = item - updated.element.value = "update1" + updated.content.value = "update1" actions.update(updated) - XCTAssertEqual(item.element.value, "update1") + XCTAssertEqual(item.content.value, "update1") XCTAssertEqual(callbackCount, 1) } self.testcase("Closure based update") { actions.update { - $0.element.value = "update2" + $0.content.value = "update2" } - XCTAssertEqual(item.element.value, "update2") + XCTAssertEqual(item.content.value, "update2") XCTAssertEqual(callbackCount, 2) } } @@ -50,30 +50,30 @@ class ItemElementCoordinatorInfoTests : XCTestCase { func test() { - let original = Item(TestElement(value: "original")) + let original = Item(TestContent(value: "original")) var current = original - let info = ItemElementCoordinatorInfo(original: original, current: { current }) + let info = ItemContentCoordinatorInfo(original: original, current: { current }) - current.element.value = "current" + current.content.value = "current" - XCTAssertEqual(info.original.element.value, "original") - XCTAssertEqual(info.current.element.value, "current") + XCTAssertEqual(info.original.content.value, "original") + XCTAssertEqual(info.current.content.value, "current") } } -fileprivate struct TestElement : ItemElement, Equatable +fileprivate struct TestContent : ItemContent, Equatable { var value : String typealias ContentView = UIView - var identifier: Identifier { + var identifier: Identifier { .init(self.value) } - func apply(to views: ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) {} + func apply(to views: ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) {} static func createReusableContentView(frame: CGRect) -> UIView { UIView(frame: frame) diff --git a/Listable/Tests/ItemElementTests.swift b/Listable/Tests/Item/ItemElementTests.swift similarity index 100% rename from Listable/Tests/ItemElementTests.swift rename to Listable/Tests/Item/ItemElementTests.swift diff --git a/Listable/Tests/ItemTests.swift b/Listable/Tests/Item/ItemTests.swift similarity index 100% rename from Listable/Tests/ItemTests.swift rename to Listable/Tests/Item/ItemTests.swift diff --git a/Listable/Tests/Layout/List/DefaultListLayoutTests.swift b/Listable/Tests/Layout/List/DefaultListLayoutTests.swift index e7ee5adbc..55c4619c5 100644 --- a/Listable/Tests/Layout/List/DefaultListLayoutTests.swift +++ b/Listable/Tests/Layout/List/DefaultListLayoutTests.swift @@ -81,20 +81,20 @@ class DefaultListLayoutTests : XCTestCase list.appearance.direction = .vertical - list.content.header = HeaderFooter(TestingHeaderFooterElement(color: .blue), sizing: .fixed(height: 50.0)) - list.content.footer = HeaderFooter(TestingHeaderFooterElement(color: .blue), sizing: .fixed(height: 70.0)) + list.content.header = HeaderFooter(TestingHeaderFooterContent(color: .blue), sizing: .fixed(height: 50.0)) + list.content.footer = HeaderFooter(TestingHeaderFooterContent(color: .blue), sizing: .fixed(height: 70.0)) list += Section(identifier: "first") { section in - section.header = HeaderFooter(TestingHeaderFooterElement(color: .green), sizing: .fixed(height: 55.0)) - section.footer = HeaderFooter(TestingHeaderFooterElement(color: .green), sizing: .fixed(height: 45.0)) + section.header = HeaderFooter(TestingHeaderFooterContent(color: .green), sizing: .fixed(height: 55.0)) + section.footer = HeaderFooter(TestingHeaderFooterContent(color: .green), sizing: .fixed(height: 45.0)) - section += Item(TestingItemElement(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 20.0)) + section += Item(TestingItemContent(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 20.0)) } list += Section(identifier: "second") { section in - section += Item(TestingItemElement(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 40.0)) - section += Item(TestingItemElement(color: .init(white: 0.0, alpha: 0.2)), sizing: .fixed(height: 60.0)) + section += Item(TestingItemContent(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 40.0)) + section += Item(TestingItemContent(color: .init(white: 0.0, alpha: 0.2)), sizing: .fixed(height: 60.0)) } } @@ -144,20 +144,20 @@ class DefaultListLayoutTests : XCTestCase list.appearance.direction = .horizontal - list.content.header = HeaderFooter(TestingHeaderFooterElement(color: .blue), sizing: .fixed(height: 50.0)) - list.content.footer = HeaderFooter(TestingHeaderFooterElement(color: .blue), sizing: .fixed(height: 70.0)) + list.content.header = HeaderFooter(TestingHeaderFooterContent(color: .blue), sizing: .fixed(height: 50.0)) + list.content.footer = HeaderFooter(TestingHeaderFooterContent(color: .blue), sizing: .fixed(height: 70.0)) list += Section(identifier: "first") { section in - section.header = HeaderFooter(TestingHeaderFooterElement(color: .green), sizing: .fixed(height: 55.0)) - section.footer = HeaderFooter(TestingHeaderFooterElement(color: .green), sizing: .fixed(height: 45.0)) + section.header = HeaderFooter(TestingHeaderFooterContent(color: .green), sizing: .fixed(height: 55.0)) + section.footer = HeaderFooter(TestingHeaderFooterContent(color: .green), sizing: .fixed(height: 45.0)) - section += Item(TestingItemElement(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 20.0)) + section += Item(TestingItemContent(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 20.0)) } list += Section(identifier: "second") { section in - section += Item(TestingItemElement(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 40.0)) - section += Item(TestingItemElement(color: .init(white: 0.0, alpha: 0.2)), sizing: .fixed(height: 60.0)) + section += Item(TestingItemContent(color: .init(white: 0.0, alpha: 0.1)), sizing: .fixed(height: 40.0)) + section += Item(TestingItemContent(color: .init(white: 0.0, alpha: 0.2)), sizing: .fixed(height: 60.0)) } } @@ -197,7 +197,7 @@ class DefaultListLayoutTests : XCTestCase } -fileprivate struct TestingHeaderFooterElement : HeaderFooterElement { +fileprivate struct TestingHeaderFooterContent : HeaderFooterContent { var color : UIColor @@ -205,7 +205,7 @@ fileprivate struct TestingHeaderFooterElement : HeaderFooterElement { view.backgroundColor = self.color } - func isEquivalent(to other: TestingHeaderFooterElement) -> Bool { + func isEquivalent(to other: TestingHeaderFooterContent) -> Bool { false } @@ -217,20 +217,20 @@ fileprivate struct TestingHeaderFooterElement : HeaderFooterElement { } -fileprivate struct TestingItemElement : ItemElement { +fileprivate struct TestingItemContent : ItemContent { var color : UIColor - var identifier: Identifier { + var identifier: Identifier { .init("testing") } - func apply(to views: ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) + func apply(to views: ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) { views.content.backgroundColor = self.color } - func isEquivalent(to other: TestingItemElement) -> Bool { + func isEquivalent(to other: TestingItemContent) -> Bool { false } diff --git a/Listable/Tests/ListView/ListView.VisibleContentTests.swift b/Listable/Tests/ListView/ListView.VisibleContentTests.swift index 0602ab842..a735924e3 100644 --- a/Listable/Tests/ListView/ListView.VisibleContentTests.swift +++ b/Listable/Tests/ListView/ListView.VisibleContentTests.swift @@ -47,12 +47,12 @@ class ListView_VisibleContentTests : XCTestCase ) section += Item( - TestElement(color: .init(white: 1.0, alpha: 1)), + TestContent(color: .init(white: 1.0, alpha: 1)), sizing: .fixed(height: 100.0) ) section += Item( - TestElement(color: .init(white: 0.9, alpha: 1)), + TestContent(color: .init(white: 0.9, alpha: 1)), sizing: .fixed(height: 100.0) ) } @@ -71,7 +71,7 @@ class ListView_VisibleContentTests : XCTestCase .init(kind: .sectionHeader, indexPath: IndexPath(item: 0, section: 0)), ], items: [ - .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 0, section: 0)) + .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 0, section: 0)) ] ) ) @@ -89,7 +89,7 @@ class ListView_VisibleContentTests : XCTestCase .init(kind: .sectionHeader, indexPath: IndexPath(item: 0, section: 0)), ], items: [ - .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 0, section: 0)) + .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 0, section: 0)) ] ) ) @@ -106,8 +106,8 @@ class ListView_VisibleContentTests : XCTestCase ListView.VisibleContent.Info( headerFooters: [], items: [ - .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 0, section: 0)), - .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 1, section: 0)) + .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 0, section: 0)), + .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 1, section: 0)) ] ) ) @@ -126,7 +126,7 @@ class ListView_VisibleContentTests : XCTestCase .init(kind: .sectionFooter, indexPath: IndexPath(item: 0, section: 0)), ], items: [ - .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 1, section: 0)) + .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 1, section: 0)) ] ) ) @@ -145,7 +145,7 @@ class ListView_VisibleContentTests : XCTestCase .init(kind: .listFooter, indexPath: IndexPath(item: 0, section: 0)), ], items: [ - .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 1, section: 0)) + .init(identifier: Identifier().toAny, indexPath: IndexPath(item: 1, section: 0)) ] ) ) @@ -170,17 +170,17 @@ class ListView_VisibleContentTests : XCTestCase } } -fileprivate struct TestElement : ItemElement, Equatable +fileprivate struct TestContent : ItemContent, Equatable { var color : UIColor typealias ContentView = UIView - var identifier: Identifier { + var identifier: Identifier { .init() } - func apply(to views: ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) + func apply(to views: ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) { views.content.backgroundColor = self.color } @@ -190,7 +190,7 @@ fileprivate struct TestElement : ItemElement, Equatable } } -fileprivate struct TestHeaderFooter : HeaderFooterElement, Equatable +fileprivate struct TestHeaderFooter : HeaderFooterContent, Equatable { var color : UIColor diff --git a/Listable/Tests/ListView/ListViewTests.swift b/Listable/Tests/ListView/ListViewTests.swift index 0cff9de2f..e2c78aa5a 100644 --- a/Listable/Tests/ListView/ListViewTests.swift +++ b/Listable/Tests/ListView/ListViewTests.swift @@ -55,15 +55,15 @@ class ListViewTests: XCTestCase } -fileprivate struct TestElement : ItemElement, Equatable +fileprivate struct TestContent : ItemContent, Equatable { var title : String - var identifier: Identifier { + var identifier: Identifier { return .init(self.title) } - func apply(to views: ItemElementViews, for reason: ApplyReason, with info: ApplyItemElementInfo) {} + func apply(to views: ItemContentViews, for reason: ApplyReason, with info: ApplyItemContentInfo) {} typealias ContentView = UIView @@ -74,7 +74,7 @@ fileprivate struct TestElement : ItemElement, Equatable } -fileprivate struct TestSupplementary : HeaderFooterElement, Equatable +fileprivate struct TestSupplementary : HeaderFooterContent, Equatable { func apply(to view: UIView, reason: ApplyReason) {} diff --git a/README.md b/README.md index e03118ee2..47be3abfd 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ In this example, we see how to declare a `List` within a Blueprint element hiera ```swift var elementRepresentation : Element { List { list in - list += Section(identifier: "section") { section in + list += Section(identifier: "podcasts") { section in section += self.podcasts.map { PodcastRow(podcast: $0) @@ -206,22 +206,22 @@ var elementRepresentation : Element { } ``` -And in this example, we see how to create a simple `BlueprintItemElement` that uses Blueprint to render its content. +And in this example, we see how to create a simple `BlueprintItemContent` that uses Blueprint to render its content. ```swift -struct DemoItem : BlueprintItemElement, Equatable +struct DemoItem : BlueprintItemContent, Equatable { var text : String - // ItemElement + // ItemContent var identifier: Identifier { return .init(self.text) } - // BlueprintItemElement + // BlueprintItemContent - func element(with info : ApplyItemElementInfo) -> Element + func element(with info : ApplyItemContentInfo) -> Element { var box = Box( backgroundColor: .white, @@ -283,16 +283,16 @@ This allows you to configure the list view however needed within the `setContent ### Item -You can think of `Item` as the wrapper for the content _you_ provide to the list – similar to how a `UITableViewCell` wraps a content view and provides other configuration options. +You can think of `Item` as the wrapper for the content _you_ provide to the list – similar to how a `UITableViewCell` or `UICollectionViewCell` wraps a content view and provides other configuration options. -An `Item` is what you add to a section to represent a row in a list. It contains your provided content (`ItemElement`), alongside things like sizing, layout customization, selection behavior, reordering behavior, and callbacks which are performed when an item is selected, displayed, etc. +An `Item` is what you add to a section to represent a row in a list. It contains your provided content (`ItemContent`), alongside things like sizing, layout customization, selection behavior, reordering behavior, and callbacks which are performed when an item is selected, displayed, etc. ```swift -public struct Item : AnyItem +public struct Item : AnyItem { public var identifier : AnyIdentifier - public var element : Element + public var content : Content public var sizing : Sizing public var layout : ItemLayout @@ -303,16 +303,16 @@ public struct Item : AnyItem public var reordering : Reordering? - public typealias OnSelect = (Element) -> () + public typealias OnSelect = (Content) -> () public var onSelect : OnSelect? - public typealias OnDeselect = (Element) -> () + public typealias OnDeselect = (Content) -> () public var onDeselect : OnDeselect? - public typealias OnDisplay = (Element) -> () + public typealias OnDisplay = (Content) -> () public var onDisplay : OnDisplay? - public typealias OnEndDisplay = (Element) -> () + public typealias OnEndDisplay = (Content) -> () public var onEndDisplay : OnEndDisplay? } ``` @@ -320,38 +320,38 @@ You can add an item to a section via either the `add` function, or via the `+=` ```swift section += Item( - AnElement(title: "Hello, World!"), + YourContent(title: "Hello, World!"), sizing: .default, selection: .notSelectable ) ``` -However, if you want to use all default values from the `Item` initializer, you can skip a step and simply add your `ItemElement` to the section directly. +However, if you want to use all default values from the `Item` initializer, you can skip a step and simply add your `ItemContent` to the section directly. ```swift -section += AnElement(title: "Hello, World!") +section += YourContent(title: "Hello, World!") ``` -### ItemElement +### ItemContent The core value type which represents an item's content. -This view model describes the content of a given row / item, via the `identifier`, plua the `wasMoved` and `isEquivalent` methods. +This view model describes the content of a given row / item, via the `identifier`, plus the `wasMoved` and `isEquivalent` methods. -To convert an `ItemElement` into views for display, the `createReusableContentView(:)` method is called to create a reusable view to use when displaying the element (the same happens for background views as well). +To convert an `ItemContent` into views for display, the `createReusableContentView(:)` method is called to create a reusable view to use when displaying the content (the same happens for background views as well). -To prepare the views for display, the `apply(to:for:with:)` method is called, which is where you push the content from your `ItemElement` onto the provided views. +To prepare the views for display, the `apply(to:for:with:)` method is called, which is where you push the content from your `ItemContent` onto the provided views. ```swift -public protocol ItemElement +public protocol ItemContent { var identifier : Identifier { get } func apply( - to views : ItemElementViews, + to views : ItemContentViews, for reason: ApplyReason, - with info : ApplyItemElementInfo + with info : ApplyItemContentInfo ) func wasMoved(comparedTo other : Self) -> Bool @@ -368,10 +368,10 @@ public protocol ItemElement } ``` -Note however, you usually do not need to implement all these methods! For example, if your `ItemElement` is `Equatable`, you get `isEquivalent` for free – and by default, `wasMoved` is the same was `isEquivalent(other:) == false`. +Note however, you usually do not need to implement all these methods! For example, if your `ItemContent` is `Equatable`, you get `isEquivalent` for free – and by default, `wasMoved` is the same was `isEquivalent(other:) == false`. ```swift -public extension ItemElement +public extension ItemContent { func wasMoved(comparedTo other : Self) -> Bool { @@ -380,7 +380,7 @@ public extension ItemElement } -public extension ItemElement where Self:Equatable +public extension ItemContent where Self:Equatable { func isEquivalent(to other : Self) -> Bool { @@ -392,7 +392,7 @@ public extension ItemElement where Self:Equatable The `BackgroundView` and `SelectedBackgroundView` views also default to a plain `UIView` which do not display any content of their own. You only need to provide these background views if you wish to support customization of the appearance of the item during highlighting and selection. ```swift -public extension ItemElement where BackgroundView == UIView +public extension ItemContent where BackgroundView == UIView { static func createReusableBackgroundView(frame : CGRect) -> BackgroundView { @@ -404,7 +404,7 @@ public extension ItemElement where BackgroundView == UIView The `SelectedBackgroundView` also defaults to the type of `BackgroundView` unless you explicitly want two different view types. ```swift -public extension ItemElement where BackgroundView == SelectedBackgroundView +public extension ItemContent where BackgroundView == SelectedBackgroundView { static func createReusableSelectedBackgroundView(frame : CGRect) -> BackgroundView { @@ -414,17 +414,17 @@ public extension ItemElement where BackgroundView == SelectedBackgroundView ``` -This is all a bit abstract, so consider the following example: An `ItemElement` which provides a title and detail label. +This is all a bit abstract, so consider the following example: An `ItemContent` which provides a title and detail label. ```swift -struct SubtitleItem : ItemElement, Equatable +struct SubtitleItem : ItemContent, Equatable { var title : String var detail : String - // ItemElement + // ItemContent - func apply(to views : ItemElementViews, for reason: ApplyReason, with info : ApplyItemElementInfo) + func apply(to views : ItemContentViews, for reason: ApplyReason, with info : ApplyItemContentInfo) { views.content.titleLabel.text = self.title views.content.detailLabel.text = self.detail @@ -448,12 +448,12 @@ struct SubtitleItem : ItemElement, Equatable ``` ### HeaderFooter -How to describe a header or footer within a list. Very similar API to `ItemElement`, but with less stuff, as headers and footers are display-only. +How to describe a header or footer within a list. Very similar API to `Item`, but with less stuff, as headers and footers are display-only. ```swift -public struct HeaderFooter : AnyHeaderFooter +public struct HeaderFooter : AnyHeaderFooter { - public var element : Element + public var content : Content public var sizing : Sizing public var layout : HeaderFooterLayout @@ -471,11 +471,11 @@ self.listView.setContent { list in } ``` -#### HeaderFooterElement -Again, a similar API to `ItemElement`, but with a reduced surface area, given the reduced concerns of header and footers. +#### HeaderFooterContent +Again, a similar API to `ItemContent`, but with a reduced surface area, given the reduced concerns of header and footers. ```swift -public protocol HeaderFooterElement +public protocol HeaderFooterContent { func apply(to view : Appearance.ContentView, reason : ApplyReason) @@ -486,10 +486,10 @@ public protocol HeaderFooterElement } ``` -As usual, if your `HeaderFooterElement` is `Equatable`, you get `isEquivalent` for free. +As is with `Item`, if your `HeaderFooterContent` is `Equatable`, you get `isEquivalent` for free. ```swift -public extension HeaderFooterElement where Self:Equatable +public extension HeaderFooterContent where Self:Equatable { func isEquivalent(to other : Self) -> Bool { @@ -501,7 +501,7 @@ public extension HeaderFooterElement where Self:Equatable A standard implementation may look like this: ```swift -struct Header : HeaderFooterElement, Equatable +struct Header : HeaderFooterContent, Equatable { var title : String @@ -574,32 +574,32 @@ var elementRepresentation : Element { } ```` -### BlueprintItemElement -`BlueprintItemElement` simplifies the `ItemElement` creation process, asking you for an `Element` description, instead of view types and view instances. +### BlueprintItemContent +`BlueprintItemContent` simplifies the `ItemContent` creation process, asking you for a Blueprint `Element` description, instead of view types and view instances. -Unless you are supporting highlighting and selection of your `ItemElement`, you do not need to provide implementations of `backgroundElement(:)` and `selectedBackgroundElement(:)` – they default to returning nil. Similar to `ItemElement`, `wasMoved(:)` and `isEquivalent(:)` are also provided based on `Equatable` conformance. +Unless you are supporting highlighting and selection of your `ItemContent`, you do not need to provide implementations of `backgroundElement(:)` and `selectedBackgroundElement(:)` – they default to returning nil. Similar to `ItemContent`, `wasMoved(:)` and `isEquivalent(:)` are also provided based on `Equatable` conformance. ```swift -public protocol BlueprintItemElement : ItemElement +public protocol BlueprintItemContent : ItemContent { var identifier : Identifier { get } func wasMoved(comparedTo other : Self) -> Bool func isEquivalent(to other : Self) -> Bool - func element(with info : ApplyItemElementInfo) -> BlueprintUI.Element + func element(with info : ApplyItemContentInfo) -> Element - func backgroundElement(with info : ApplyItemElementInfo) -> BlueprintUI.Element? + func backgroundElement(with info : ApplyItemContentInfo) -> Element? - func selectedBackgroundElement(with info : ApplyItemElementInfo) -> BlueprintUI.Element? + func selectedBackgroundElement(with info : ApplyItemContentInfo) -> Element? } ``` -A standard `BlueprintItemElement` may look something like this: +A standard `BlueprintItemContent` may look something like this: ```swift -struct PersonElement : BlueprintItemElement, Equatable +struct MyPerson : BlueprintItemContent, Equatable { var name : String var phoneNumber : String @@ -608,7 +608,7 @@ struct PersonElement : BlueprintItemElement, Equatable .init(name) } - func element(with info : ApplyItemElementInfo) -> BlueprintUI.Element { + func element(with info : ApplyItemContentInfo) -> Element { Row { $0.add(child: Label(text: name)) $0.add(child: Spacer()) @@ -619,28 +619,28 @@ struct PersonElement : BlueprintItemElement, Equatable } ``` -### BlueprintHeaderFooterElement -Similarly, `BlueprintHeaderFooterElement` makes creating a header or footer easy – just implement `element`, which provides the content element for your header or footer. +### BlueprintHeaderFooterContent +Similarly, `BlueprintHeaderFooterContent` makes creating a header or footer easy – just implement `elementRepresentation`, which provides the content element for your header or footer. As usual, `isEquivalent(to:)` is provided if your type is `Equatable`. ```swift -public protocol BlueprintHeaderFooterElement +public protocol BlueprintHeaderFooterContent : HeaderFooterElement { func isEquivalent(to other : Self) -> Bool - var element : BlueprintUI.Element { get } + var elementRepresentation : Element { get } } ``` -A standard `BlueprintHeaderFooterElement` may look something like this: +A standard `BlueprintHeaderFooterContent` may look something like this: ```swift -struct HeaderElement : BlueprintItemElement, Equatable +struct MyHeader : BlueprintHeaderFooterContent, Equatable { var name : String var itemCount : String - var element : BlueprintUI.Element { + var elementRepresentation : Element { Row { $0.add(child: Label(text: name)) $0.add(child: Spacer())