Skip to content

Commit

Permalink
v2.0 (#68)
Browse files Browse the repository at this point in the history
* add ViewProviderComposer

* remove layout restriction

* cleanup CollectionComposer

* basic working

* outside flattening working

* add CollectionHeaderComposer

* support sticky header

* cleanup examples

* remove leftover

* bump version

* update tests

* rename step 1: remove Collection

* rename provider to source

* rename

* WIP

* rename 2

* add Deprecated bridge

* update readme

* remove MCollectionView references

* some renaming

* final collectionView cleanup

* add migration guide

* cleanup migration guide

* rename test and config codecov

* leftover

* cleanup simpleviewprovider

* rename presenter to animator

* update resource

* update readme images

* remove BasicProviderBuilder

* Update README.md

* update migration guide

* update tests

* Update README.md

* update naming
  • Loading branch information
lkzhao authored Jun 23, 2018
1 parent 372c26d commit ad1ed87
Show file tree
Hide file tree
Showing 89 changed files with 2,998 additions and 1,469 deletions.
2 changes: 1 addition & 1 deletion CollectionKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "CollectionKit"
s.version = "1.3.1"
s.version = "2.0.0"
s.summary = "A modern swift framework for building data-driven reusable collection view components."

s.description = <<-DESC
Expand Down
276 changes: 173 additions & 103 deletions CollectionKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 22 additions & 22 deletions CollectionKitTests/CollectionViewSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,17 @@ class CollectionViewSpec: QuickSpec {

override func spec() {
describe("CollectionView") {
var provider: CollectionProvider<Int, UILabel>!
var provider: BasicProvider<Int, UILabel>!
var collectionView: CollectionView!
beforeEach {
provider = CollectionProvider(
data: [1, 2, 3, 4],
viewUpdater: { (label: UILabel, data: Int, index: Int) in
provider = BasicProvider(
dataSource: ArrayDataSource(data: [1, 2, 3, 4]),
viewSource: ClosureViewSource(viewUpdater: { (label: UILabel, data: Int, index: Int) in
label.backgroundColor = .red
label.layer.cornerRadius = 8
label.textAlignment = .center
label.text = "\(data)"
},
sizeProvider: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
}),
sizeSource: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
return CGSize(width: 50, height: 50)
}
)
Expand All @@ -51,7 +50,7 @@ class CollectionViewSpec: QuickSpec {
expect((collectionView.subviews[0] as! UILabel).text) == "1"
expect((collectionView.subviews[0] as! UILabel).textAlignment) == NSTextAlignment.center

expect(provider.layout).to(beAKindOf(FlowLayout<Int>.self))
expect(provider.layout).to(beAKindOf(FlowLayout.self))
expect(collectionView.subviews[0].frame) == CGRect(x: 0, y: 0, width: 50, height: 50)
expect(collectionView.subviews[1].frame) == CGRect(x: 50, y: 0, width: 50, height: 50)
expect(collectionView.subviews[2].frame) == CGRect(x: 100, y: 0, width: 50, height: 50)
Expand Down Expand Up @@ -123,7 +122,7 @@ class CollectionViewSpec: QuickSpec {
expect((collectionView.subviews[1] as! UILabel).text) == "2"
expect(collectionView.contentSize) == CGSize(width: 100, height: 100)

provider.dataProvider = ArrayDataProvider(data:[9, 8, 5, 6, 7])
provider.dataSource = ArrayDataSource(data:[9, 8, 5, 6, 7])
collectionView.layoutIfNeeded()
expect(collectionView.reloadCount) == 2

Expand All @@ -134,7 +133,7 @@ class CollectionViewSpec: QuickSpec {
expect((collectionView.subviews[1] as! UILabel).text) == "8"
expect(collectionView.contentSize) == CGSize(width: 100, height: 150)

provider.dataProvider = ArrayDataProvider(data:[8, 5, 6, 7])
provider.dataSource = ArrayDataSource(data:[8, 5, 6, 7])
collectionView.layoutIfNeeded()
expect(collectionView.reloadCount) == 3

Expand Down Expand Up @@ -183,12 +182,12 @@ class CollectionViewSpec: QuickSpec {
expect((collectionView.cell(at: 0) as! UILabel).text) == "1"
expect((collectionView.cell(at: 1) as! UILabel).text) == "2"

provider = CollectionProvider(
dataProvider: ArrayDataProvider(data: [0,0,0,0], identifierMapper: { _, data in "\(data)" }),
viewUpdater: { (label: UILabel, data: Int, index: Int) in
provider = BasicProvider(
dataSource: ArrayDataSource(data: [0, 0, 0, 0]),
viewSource: ClosureViewSource(viewUpdater: { (label: UILabel, data: Int, index: Int) in
label.text = "\(data)"
},
sizeProvider: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
}),
sizeSource: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
return CGSize(width: 50, height: 50)
}
)
Expand Down Expand Up @@ -219,18 +218,19 @@ class CollectionViewSpec: QuickSpec {

it("handles tap") {
var lastTappedIndex: Int = -1
provider = CollectionProvider(
data: [0, 1, 2, 3],
viewUpdater: { (label: UILabel, data: Int, index: Int) in
provider = BasicProvider(
dataSource: ArrayDataSource(data: [0, 1, 2, 3]),
viewSource: ClosureViewSource(viewUpdater: { (label: UILabel, data: Int, index: Int) in
label.text = "\(data)"
},
sizeProvider: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
}),
sizeSource: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
return CGSize(width: 50, height: 50)
},
tapHandler: { view, index, dataProvider in
lastTappedIndex = index
tapHandler: { context in
lastTappedIndex = context.index
}
)

collectionView.provider = provider
collectionView.frame = CGRect(x: 0, y: 0, width: 500, height: 50)
collectionView.layoutIfNeeded()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// CollectionComposerSpec.swift
// ComposedProviderSpec.swift
// CollectionKitTests
//
// Created by Luke Zhao on 2017-09-02.
Expand All @@ -10,16 +10,16 @@
import Quick
import Nimble

class CollectionComposerSpec: QuickSpec {
class ComposedProviderSpec: QuickSpec {

override func spec() {

describe("CollectionComposer") {
describe("ComposedProvider") {
it("combines multiple provider") {
let provider1 = SimpleTestProvider(data: [1, 2, 3, 4])
let provider2 = SimpleTestProvider(data: ["a", "b"])
let provider3 = SimpleTestProvider(data: ["hello", "collectionKit"])
let composer = CollectionComposer(provider1, provider2, provider3)
let composer = ComposedProvider(sections: [provider1, provider2, provider3])
let collectionView = CollectionView(provider: composer)
collectionView.frame = CGRect(x: 0, y: 0, width: 300, height: 500)
collectionView.layoutIfNeeded()
Expand All @@ -36,14 +36,16 @@ class CollectionComposerSpec: QuickSpec {
let provider3 = SimpleTestProvider(data: ["hello", "collectionKit"])
let provider4 = SimpleTestProvider(data: [])
let provider5 = SimpleTestProvider(data: [5.0])
let composer = CollectionComposer(
CollectionComposer(
CollectionComposer(provider1,
provider2),
provider3),
CollectionComposer(),
CollectionComposer(provider4,
provider5)
let composer = ComposedProvider(
sections: [
ComposedProvider(
sections: [
ComposedProvider(sections: [provider1, provider2]),
provider3
]),
ComposedProvider(),
ComposedProvider(sections: [provider4, provider5])
]
)
let collectionView = CollectionView(provider: composer)
collectionView.frame = CGRect(x: 0, y: 0, width: 300, height: 500)
Expand All @@ -60,7 +62,7 @@ class CollectionComposerSpec: QuickSpec {
let provider1 = SimpleTestProvider(data: [1, 2, 3, 4])
let provider2 = SimpleTestProvider(data: ["a", "b"])
let provider3 = SimpleTestProvider(data: ["hello", "collectionKit"])
let composer = CollectionComposer(provider1, provider2, provider3)
let composer = ComposedProvider(sections: [provider1, provider2, provider3])
let collectionView = CollectionView(provider: composer)
collectionView.frame = CGRect(x: 0, y: 0, width: 300, height: 500)
collectionView.layoutIfNeeded()
Expand All @@ -79,7 +81,7 @@ class CollectionComposerSpec: QuickSpec {
expect(collectionView.reloadCount) == 3
expect(collectionView.subviews[0].frame.origin) != CGPoint.zero

composer.presenter = ZoomPresenter()
composer.animator = ScaleAnimator()
collectionView.layoutIfNeeded()
expect(collectionView.reloadCount) == 4

Expand All @@ -100,15 +102,14 @@ class CollectionComposerSpec: QuickSpec {
let provider3 = SimpleTestProvider(data: ["hello", "collectionKit"])
let provider4 = SimpleTestProvider(data: [])
let provider5 = SimpleTestProvider(data: [5.0])
let composer = CollectionComposer(
CollectionComposer(
CollectionComposer(provider1,
provider2),
provider3),
CollectionComposer(),
CollectionComposer(provider4,
provider5)
)
let composer = ComposedProvider(sections: [
ComposedProvider(sections: [
ComposedProvider(sections: [provider1, provider2]),
provider3
]),
ComposedProvider(),
ComposedProvider(sections: [provider4, provider5])
])
let collectionView = CollectionView(provider: composer)
collectionView.frame = CGRect(x: 0, y: 0, width: 300, height: 500)
collectionView.layoutIfNeeded()
Expand All @@ -127,22 +128,28 @@ class CollectionComposerSpec: QuickSpec {
var lastTappedText: String?
let provider1 = SimpleTestProvider(data: [1, 2, 3, 4])
let provider2 = SimpleTestProvider(data: ["a", "b"])
let tapProvider = CollectionProvider(
data: [11, 12],
viewUpdater: { (label: UILabel, data: Int, index: Int) in

let tapProvider = BasicProvider(
dataSource: ArrayDataSource(data: [11, 12]),
viewSource: ClosureViewSource(viewUpdater: {
(label: UILabel, data: Int, index: Int) in
label.text = "\(data)"
},
sizeProvider: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
}),
sizeSource: { (index: Int, data: Int, collectionSize: CGSize) -> CGSize in
return CGSize(width: 50, height: 50)
},
tapHandler: { view, index, dataProvider in
lastTappedText = view.text
tapHandler: { context in
lastTappedText = context.view.text
}
)
let composer = CollectionComposer(
CollectionComposer(provider1,
provider2),
tapProvider

let composer = ComposedProvider(
sections: [
ComposedProvider(
sections: [provider1,
provider2]),
tapProvider
]
)
let collectionView = CollectionView(provider: composer)
collectionView.frame = CGRect(x: 0, y: 0, width: 200, height: 500)
Expand Down
12 changes: 6 additions & 6 deletions CollectionKitTests/FlowLayoutSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,39 @@ class FlowLayoutSpec: QuickSpec {
override func spec() {
describe("FlowLayout") {
it("should not crash when parent size is zero") {
let layout = FlowLayout<CGSize>()
let layout = FlowLayout()
layout.mockLayout(parentSize: (0, 0))
expect(layout.frames.count).to(equal(0))
layout.mockLayout(parentSize: (0, 0), (200, 50))
expect(layout.frames.count).to(equal(1))
}

it("should wrap child") {
let layout = FlowLayout<CGSize>()
let layout = FlowLayout()
layout.mockLayout(parentSize: (100, 100), (50, 50), (50, 50), (50, 50))
expect(layout.frames).to(equal(frames((0, 0, 50, 50), (50, 0, 50, 50), (0, 50, 50, 50))))
}

it("should work with linespacing") {
let layout = FlowLayout<CGSize>(lineSpacing: 10)
let layout = FlowLayout(lineSpacing: 10)
layout.mockLayout(parentSize: (100, 300), (100, 100), (100, 100), (100, 100))
expect(layout.frames).to(equal(frames((0, 0, 100, 100), (0, 110, 100, 100), (0, 220, 100, 100))))
}

it("should work with interitemspacing") {
let layout = FlowLayout<CGSize>(interitemSpacing: 10)
let layout = FlowLayout(interitemSpacing: 10)
layout.mockLayout(parentSize: (130, 100), (50, 50), (50, 50), (50, 50))
expect(layout.frames).to(equal(frames((0, 0, 50, 50), (60, 0, 50, 50), (0, 50, 50, 50))))
}

it("should work with interitemspacing and linespacing") {
let layout = FlowLayout<CGSize>(lineSpacing: 10, interitemSpacing: 10)
let layout = FlowLayout(lineSpacing: 10, interitemSpacing: 10)
layout.mockLayout(parentSize: (130, 130), (50, 50), (50, 50), (50, 50))
expect(layout.frames).to(equal(frames((0, 0, 50, 50), (60, 0, 50, 50), (0, 60, 50, 50))))
}

it("should not display cells outside of the visible area") {
let layout = FlowLayout<CGSize>().transposed()
let layout = FlowLayout().transposed()
layout.mockLayout(parentSize: (100, 50), (50, 50), (50, 50), (50, 50), (50, 50))
let visible = layout.visibleIndexes(visibleFrame: CGRect(x: 50, y: 0, width: 100, height: 50))
expect(visible).to(equal([1, 2]))
Expand Down
8 changes: 4 additions & 4 deletions CollectionKitTests/RowLayoutSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@ class RowLayoutSpec: QuickSpec {
override func spec() {
describe("RowLayout") {
it("should not crash when parent size is zero") {
let layout = RowLayout<CGSize>()
let layout = RowLayout()
layout.mockLayout(parentSize: (0, 0))
expect(layout.frames.count).to(equal(0))
layout.mockLayout(parentSize: (0, 0), (200, 50))
expect(layout.frames.count).to(equal(1))
}

it("should work without flex value") {
let layout = RowLayout<CGSize>()
let layout = RowLayout()
layout.mockLayout((200, 50), (200, 50), (200, 50))
expect(layout.frames).to(equal(frames((0,0,200,50), (200,0,200,50), (400,0,200,50))))
}

it("should expand flexed item when there is space") {
let layout = RowLayout<CGSize>("1")
let layout = RowLayout("1")
layout.mockLayout(parentSize: (700, 50), (200, 50), (200, 50), (200, 50))
expect(layout.frames).to(equal(frames((0,0,200,50), (200,0, 300, 50), (500,0,200,50))))
}

it("should not expand flex item where there is not enough space") {
let layout = RowLayout<CGSize>("1", "2")
let layout = RowLayout("1", "2")
layout.mockLayout(parentSize: (250, 300), (250, 200), (50, 200), (50, 200))
expect(layout.frames).to(equal(frames((0,0,250,200), (250,0,50,200), (300,0,50,200))))
}
Expand Down
44 changes: 30 additions & 14 deletions CollectionKitTests/TestUils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,51 @@ import UIKit
import CollectionKit
import UIKit.UIGestureRecognizerSubclass

extension CollectionLayout where Data == CGSize {
extension Layout {

func mockLayout(parentSize: (CGFloat, CGFloat) = (300, 300), _ childSizes: (CGFloat, CGFloat)...) {
layout(collectionSize: CGSize(width: parentSize.0, height: parentSize.1),
dataProvider: ArrayDataProvider(data: sizes(childSizes)),
sizeProvider: { (index, data, collectionSize) -> CGSize in
return data
})
struct MockLayoutContext: LayoutContext {
var parentSize: (CGFloat, CGFloat)
var childSizes: [(CGFloat, CGFloat)]
var collectionSize: CGSize {
return CGSize(width: parentSize.0, height: parentSize.1)
}
var numberOfItems: Int {
return childSizes.count
}
func data(at: Int) -> Any {
return childSizes[at]
}
func identifier(at: Int) -> String {
return "\(at)"
}
func size(at: Int, collectionSize: CGSize) -> CGSize {
let size = childSizes[at]
return CGSize(width: size.0, height: size.1)
}
}

func mockLayout(parentSize: (CGFloat, CGFloat) = (300, 300), _ childSizes: (CGFloat, CGFloat)...) {
layout(context: MockLayoutContext(parentSize: parentSize, childSizes: childSizes))
}
}

class SimpleTestProvider<Data>: CollectionProvider<Data, UILabel> {
class SimpleTestProvider<Data>: BasicProvider<Data, UILabel> {

var data: [Data] {
get { return (dataProvider as! ArrayDataProvider<Data>).data }
set { (dataProvider as! ArrayDataProvider<Data>).data = newValue }
get { return (dataSource as! ArrayDataSource<Data>).data }
set { (dataSource as! ArrayDataSource<Data>).data = newValue }
}

convenience init(data: [Data]) {
self.init(
dataProvider: ArrayDataProvider(data: data, identifierMapper: { _, data in "\(data)" }),
viewUpdater: { (label: UILabel, data: Data, index: Int) in
dataSource: ArrayDataSource(data: data, identifierMapper: { _, data in "\(data)" }),
viewSource: ClosureViewSource(viewUpdater: { (label: UILabel, data: Data, index: Int) in
label.backgroundColor = .red
label.layer.cornerRadius = 8
label.textAlignment = .center
label.text = "\(data)"
},
sizeProvider: { (index: Int, data: Data, collectionSize: CGSize) -> CGSize in
}),
sizeSource: { (index: Int, data: Data, collectionSize: CGSize) -> CGSize in
return CGSize(width: 50, height: 50)
}
)
Expand Down
Loading

0 comments on commit ad1ed87

Please sign in to comment.