Skip to content

Commit

Permalink
Code review and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kyleve committed Dec 11, 2020
1 parent 60fd845 commit 527e920
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 52 deletions.
4 changes: 4 additions & 0 deletions BlueprintUILists/Sources/BlueprintItemContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,8 @@ public extension BlueprintItemContent

return view
}

static func createReusableSelectedBackgroundView(frame: CGRect) -> SelectedBackgroundView {
self.createReusableBackgroundView(frame: frame)
}
}
6 changes: 3 additions & 3 deletions ListableUI/Sources/HeaderFooter/HeaderFooterContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public protocol HeaderFooterContent
/// You do not need to provide this `typealias` unless you would like
/// to draw a selected background view.
///
associatedtype PressedBackgroundView:UIView = BackgroundView
associatedtype PressedBackgroundView:UIView = UIView

/// Create and return a new background view used to render the content's pressed background.
///
Expand Down Expand Up @@ -171,10 +171,10 @@ public extension HeaderFooterContent where Self.BackgroundView == UIView
}
}

public extension HeaderFooterContent where Self.PressedBackgroundView == BackgroundView
public extension HeaderFooterContent where Self.PressedBackgroundView == UIView
{
static func createReusablePressedBackgroundView(frame : CGRect) -> PressedBackgroundView
{
self.createReusableBackgroundView(frame: frame)
PressedBackgroundView(frame: frame)
}
}
16 changes: 16 additions & 0 deletions ListableUI/Sources/Internal/HeaderFooterContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ final class HeaderFooterContentView<Content:HeaderFooterContent> : UIView
self.content.sizeThatFits(size)
}

override func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize {
self.content.systemLayoutSizeFitting(targetSize)
}

override func systemLayoutSizeFitting(
_ targetSize: CGSize,
withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority,
verticalFittingPriority: UILayoutPriority
) -> CGSize {
self.content.systemLayoutSizeFitting(
targetSize,
withHorizontalFittingPriority: horizontalFittingPriority,
verticalFittingPriority: verticalFittingPriority
)
}

override func layoutSubviews() {
super.layoutSubviews()

Expand Down
36 changes: 17 additions & 19 deletions ListableUI/Sources/Internal/ItemCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import UIKit
protocol AnyItemCell : UICollectionViewCell
{
func closeSwipeActions()

func wasDequeued(with liveCells : LiveCells)
}

///
Expand Down Expand Up @@ -122,13 +124,25 @@ final class ItemCell<Content:ItemContent> : UICollectionViewCell, AnyItemCell
func closeSwipeActions() {
self.contentContainer.performAnimatedClose()
}

private var hasBeenDequeued = false

func wasDequeued(with liveCells : LiveCells) {
guard hasBeenDequeued == false else {
return
}

self.hasBeenDequeued = true

liveCells.add(self)
}
}


final class LiveCells {

func add(_ cell : AnyItemCell) {
self.cells.insert(.init(cell))
self.cells.append(.init(cell: cell))

self.cells = self.cells.filter { $0.cell != nil }
}
Expand All @@ -141,25 +155,9 @@ final class LiveCells {
}
}

private var cells : Set<LiveCell> = []
private(set) var cells : [LiveCell] = []

private struct LiveCell : Hashable {

private let identifier : ObjectIdentifier

struct LiveCell {
weak var cell : AnyItemCell?

init(_ cell : AnyItemCell) {
self.identifier = ObjectIdentifier(cell)
self.cell = cell
}

func hash(into hasher: inout Hasher) {
hasher.combine(self.identifier)
}

static func == (lhs : LiveCell, rhs : LiveCell) -> Bool {
lhs.identifier == rhs.identifier
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UIView+Additions.swift
// UIView.swift
// ListableUI
//
// Created by Kyle Van Essen on 10/28/20.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UIViewPropertyAnimator+System.swift
// UIViewPropertyAnimator.swift
// ListableUI
//
// Created by Kyle Bashour on 4/17/20.
Expand Down
8 changes: 4 additions & 4 deletions ListableUI/Sources/Item/ItemContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public protocol ItemContent where Coordinator.ItemContentType == Self
/// You do not need to provide this `typealias` unless you would like
/// to draw a selected background view.
///
associatedtype SelectedBackgroundView:UIView = BackgroundView
associatedtype SelectedBackgroundView:UIView = UIView


/// Create and return a new background view used to render the content's selected background.
Expand Down Expand Up @@ -295,11 +295,11 @@ public extension ItemContent where BackgroundView == UIView


/// Provide a UIView when no special selected background view is specified.
public extension ItemContent where BackgroundView == SelectedBackgroundView
public extension ItemContent where BackgroundView == UIView
{
static func createReusableSelectedBackgroundView(frame : CGRect) -> BackgroundView
static func createReusableSelectedBackgroundView(frame : CGRect) -> SelectedBackgroundView
{
self.createReusableBackgroundView(frame: frame)
SelectedBackgroundView(frame: frame)
}
}

Expand Down
2 changes: 1 addition & 1 deletion ListableUI/Sources/ListView/ListView.DataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal extension ListView
environment: environment
)

self.liveCells.add(cell)
cell.wasDequeued(with: self.liveCells)

return cell
}
Expand Down
93 changes: 78 additions & 15 deletions ListableUI/Tests/Internal/ItemCellTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,29 @@ class ItemElementCellTests : XCTestCase

func test_sizeThatFits()
{
// 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 = ItemCell<TestItemContent>(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 = ItemCell<TestItemContent>(frame: CGRect(origin: .zero, size: CGSize(width: 150.0, height: 150.0)))
XCTAssertEqual(cell2.sizeThatFits(.zero), CGSize(width: 150.0, height: 150.0))
let cell = ItemCell<TestItemContent>(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0)))
XCTAssertEqual(cell.sizeThatFits(.zero), CGSize(width: 40.0, height: 50.0))
}

func test_systemLayoutSizeFitting() {
XCTFail()
func test_systemLayoutSizeFitting()
{
let cell = ItemCell<TestItemContent>(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0)))
XCTAssertEqual(cell.systemLayoutSizeFitting(.zero), CGSize(width: 41.0, height: 51.0))
}

func test_systemLayoutSizeFitting_withHorizontalFittingPriority_verticalFittingPriority() {
XCTFail()
func test_systemLayoutSizeFitting_withHorizontalFittingPriority_verticalFittingPriority()
{
let cell = ItemCell<TestItemContent>(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0)))

XCTAssertEqual(
cell.systemLayoutSizeFitting(
.zero,
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
),

CGSize(width: 42.0, height: 52.0)
)
}
}

Expand All @@ -62,18 +69,74 @@ fileprivate struct TestItemContent : ItemContent, Equatable
typealias ContentView = UIView

static func createReusableContentView(frame: CGRect) -> UIView {
return UIView(frame: frame)
return View(frame: frame)
}

private final class View : UIView {
override func sizeThatFits(_ size: CGSize) -> CGSize {
CGSize(width: 40, height: 50)
}

override func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize {
CGSize(width: 41, height: 51)
}

override func systemLayoutSizeFitting(
_ targetSize: CGSize,
withHorizontalFittingPriority horizontalFittingPriority:
UILayoutPriority, verticalFittingPriority: UILayoutPriority
) -> CGSize {
CGSize(width: 42, height: 52)
}
}
}


class ItemElementCell_LiveCells_Tests : XCTestCase
{
func test_add() {
let liveCells = LiveCells()

var cell1 : AnyItemCell? = ItemCell<TestContent>(frame: .zero)

liveCells.add(cell1!)

// Should only add the cell once.

XCTAssertEqual(liveCells.cells.count, 1)

// Nil out the first cell

weak var weakCell1 = cell1

cell1 = nil

self.waitFor {
weakCell1 == nil
}

// Register a second cell, should remove the first

let cell2 = ItemCell<TestContent>(frame: .zero)

liveCells.add(cell2)

XCTAssertEqual(liveCells.cells.count, 1)
XCTAssertTrue(liveCells.cells.first?.cell === cell2)
}

func test_perform() {

private struct TestContent : ItemContent, Equatable {

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

static func createReusableContentView(frame: CGRect) -> UIView {
UIView(frame: frame)
}

func apply(to views: ItemContentViews<TestContent>, for reason: ApplyReason, with info: ApplyItemContentInfo) {
// Nothing needed
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SupplementaryContainerViewTests: XCTestCase
func test_sizeThatFits()
{
let cache = ReusableViewCache()
let view = SupplementaryContainerView(frame: CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0)))
let view = SupplementaryContainerView(frame:.zero)

view.reuseCache = cache
view.environment = .empty
Expand All @@ -38,15 +38,43 @@ class SupplementaryContainerViewTests: XCTestCase

view.headerFooter = self.newHeaderFooter()

XCTAssertEqual(view.sizeThatFits(.zero), CGSize(width: 100, height: 100))
XCTAssertEqual(view.sizeThatFits(.zero), CGSize(width: 50, height: 40))
}

func test_systemLayoutSizeFitting() {
XCTFail()
let cache = ReusableViewCache()
let view = SupplementaryContainerView(frame:.zero)

view.reuseCache = cache
view.environment = .empty

XCTAssertEqual(view.sizeThatFits(.zero), .zero)

view.headerFooter = self.newHeaderFooter()

XCTAssertEqual(view.systemLayoutSizeFitting(.zero), CGSize(width: 51, height: 41))
}

func test_systemLayoutSizeFitting_withHorizontalFittingPriority_verticalFittingPriority() {
XCTFail()
let cache = ReusableViewCache()
let view = SupplementaryContainerView(frame:.zero)

view.reuseCache = cache
view.environment = .empty

XCTAssertEqual(view.sizeThatFits(.zero), .zero)

view.headerFooter = self.newHeaderFooter()

XCTAssertEqual(
view.systemLayoutSizeFitting(
.zero,
withHorizontalFittingPriority: .required,
verticalFittingPriority: .defaultLow
),

CGSize(width: 52, height: 42)
)
}

func test_headerFooter()
Expand All @@ -71,7 +99,7 @@ class SupplementaryContainerViewTests: XCTestCase
let content = view.content!

XCTAssertTrue(type(of: content) === HeaderFooterContentView<TestHeaderFooterContent>.self)
XCTAssertEqual(view.frame.size, CGSize(width: 100, height: 100))
XCTAssertEqual(view.frame.size, CGSize(width: 50, height: 40))

// Unset the header footer, make sure the view is pushed back into the cache.

Expand Down Expand Up @@ -125,9 +153,20 @@ fileprivate struct TestHeaderFooterContent : HeaderFooterContent, Equatable

final class View : UIView
{
override func sizeThatFits(_ size: CGSize) -> CGSize
{
return CGSize(width: 100, height: 100)
override func sizeThatFits(_ size: CGSize) -> CGSize {
CGSize(width: 50, height: 40)
}

override func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize {
CGSize(width: 51, height: 41)
}

override func systemLayoutSizeFitting(
_ targetSize: CGSize,
withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority,
verticalFittingPriority: UILayoutPriority
) -> CGSize {
CGSize(width: 52, height: 42)
}
}
}
Loading

0 comments on commit 527e920

Please sign in to comment.