Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

alignRowHeights per BrickSection #43

Merged
merged 1 commit into from Dec 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -35,15 +35,13 @@ class HorizontalCollectionViewController: BrickViewController {

self.view.backgroundColor = .brickBackground

self.brickCollectionView.layout.alignRowHeights = true

collectionSection = BrickSection(bricks: [
ImageBrick(width: .Ratio(ratio: 1/2), height: .Ratio(ratio: 1), dataSource: self),
BrickSection(width: .Ratio(ratio: 1/2), bricks: [
LabelBrick(RepeatCollectionBrickViewController.Identifiers.titleLabel, backgroundColor: .brickGray2, dataSource: self),
LabelBrick(RepeatCollectionBrickViewController.Identifiers.subTitleLabel, backgroundColor: .brickGray4, dataSource: self)
])
], inset: 10, edgeInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
], inset: 10, edgeInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5), alignRowHeights: true)

self.registerBrickClass(CollectionBrick.self)

Expand Down
Expand Up @@ -25,8 +25,6 @@ class SimpleRepeatBrickViewController: BrickViewController, LabelBrickCellDataSo

self.brickCollectionView.registerBrickClass(LabelBrick.self)

self.layout.alignRowHeights = false

self.view.backgroundColor = .brickBackground

let section = BrickSection(bricks: [
Expand All @@ -41,12 +39,12 @@ class SimpleRepeatBrickViewController: BrickViewController, LabelBrickCellDataSo

func updateNavigationItem() {
let selector: Selector = #selector(SimpleRepeatBrickViewController.toggleAlignBehavior)
let title = self.layout.alignRowHeights ? "Don't Align" : "Align"
let title = self.brickCollectionView.section.alignRowHeights ? "Don't Align" : "Align"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: title, style: .Plain, target: self, action: selector)
}

func toggleAlignBehavior() {
self.layout.alignRowHeights = !self.layout.alignRowHeights
self.brickCollectionView.section.alignRowHeights = !self.brickCollectionView.section.alignRowHeights
brickCollectionView.reloadData()
updateNavigationItem()
}
Expand Down
Expand Up @@ -31,8 +31,6 @@ class StickingFooterSectionsViewController: BrickApp.BaseBrickController {

self.brickCollectionView.registerBrickClass(LabelBrick.self)

self.layout.alignRowHeights = true

behavior = StickyFooterLayoutBehavior(dataSource: self)

repeatLabel = LabelBrick(BrickIdentifiers.repeatLabel, width: .Ratio(ratio:0.5), backgroundColor:.lightGrayColor(), dataSource:self)
Expand Down
Expand Up @@ -32,8 +32,6 @@ class StickingSectionsViewController: BrickApp.BaseBrickController {

self.brickCollectionView.registerBrickClass(LabelBrick.self)

self.layout.alignRowHeights = true

behavior = StickyLayoutBehavior(dataSource: self)

repeatLabel = LabelBrick(BrickIdentifiers.repeatLabel, width: .Ratio(ratio: 0.5), backgroundColor: .brickGray1, dataSource: self)
Expand Down
7 changes: 4 additions & 3 deletions Source/Layout/BrickFlowLayout.swift
Expand Up @@ -22,9 +22,6 @@ public class BrickFlowLayout: UICollectionViewLayout, BrickLayout {
return collectionView?.superview?.superview is CollectionBrickCell
}

/// Align Rowheights
public var alignRowHeights: Bool = false

public var behaviors: Set<BrickLayoutBehavior> = [] {
didSet {
for behavior in behaviors {
Expand Down Expand Up @@ -387,6 +384,10 @@ extension BrickFlowLayout: BrickLayoutSectionDataSource {
return _dataSource.brickLayout(self, insetForSection: section.sectionIndex)
}

func isAlignRowHeights(in section: BrickLayoutSection) -> Bool {
return _dataSource.brickLayout(self, isAlignRowHeightsForSection: section.sectionIndex)
}

func identifier(for index: Int, in section: BrickLayoutSection) -> String {
return _dataSource.brickLayout(self, identifierForIndexPath: NSIndexPath(forItem: index, inSection: section.sectionIndex))
}
Expand Down
6 changes: 5 additions & 1 deletion Source/Layout/BrickLayout.swift
Expand Up @@ -16,7 +16,6 @@ public protocol BrickLayout: class {
var contentSize: CGSize { get }
var zIndexBehavior: BrickLayoutZIndexBehavior { get set }
var maxZIndex: Int { get }
var alignRowHeights: Bool { get set }
var scrollDirection: UICollectionViewScrollDirection { get set }

weak var dataSource: BrickLayoutDataSource? { get set }
Expand Down Expand Up @@ -86,6 +85,7 @@ public protocol BrickLayoutDataSource: class {
func brickLayout(layout: BrickLayout, estimatedHeightForItemAtIndexPath indexPath: NSIndexPath, containedInWidth width: CGFloat) -> CGFloat
func brickLayout(layout: BrickLayout, edgeInsetsForSection section: Int) -> UIEdgeInsets
func brickLayout(layout: BrickLayout, insetForSection section: Int) -> CGFloat
func brickLayout(layout: BrickLayout, isAlignRowHeightsForSection section: Int) -> Bool
func brickLayout(layout: BrickLayout, brickLayoutTypeForItemAtIndexPath indexPath: NSIndexPath) -> BrickLayoutType
func brickLayout(layout: BrickLayout, identifierForIndexPath indexPath: NSIndexPath) -> String
func brickLayout(layout: BrickLayout, indexPathForSection section: Int) -> NSIndexPath?
Expand All @@ -102,6 +102,10 @@ extension BrickLayoutDataSource {
return true
}

func brickLayout(layout: BrickLayout, isAlignRowHeightsForSection section: Int) -> Bool {
return false
}

}

public protocol BrickLayoutDelegate: class {
Expand Down
13 changes: 5 additions & 8 deletions Source/Layout/BrickLayoutSection.swift
Expand Up @@ -14,10 +14,6 @@ protocol BrickLayoutSectionDelegate: class {

protocol BrickLayoutSectionDataSource: class {

/// Indicates if the rows need to be aligned
var alignRowHeights: Bool { get }


/// Scroll Direction of the layout
var scrollDirection: UICollectionViewScrollDirection { get }

Expand All @@ -30,6 +26,9 @@ protocol BrickLayoutSectionDataSource: class {
/// The inset for this section
func inset(in section: BrickLayoutSection) -> CGFloat

/// Flag that indicates if row heights need to be aligned
func isAlignRowHeights(in section: BrickLayoutSection) -> Bool

/// Function called right before the height is asked. This can be used to do some other pre-calcuations
func prepareForSizeCalculation(for attributes: BrickLayoutAttributes, containedIn width: CGFloat, origin: CGPoint, invalidate: Bool, in section: BrickLayoutSection, updatedAttributes: OnAttributesUpdatedHandler?)

Expand All @@ -40,7 +39,6 @@ protocol BrickLayoutSectionDataSource: class {
/// - Returns: width for attributes at a given index
func width(for index: Int, totalWidth: CGFloat, startingAt origin: CGFloat, in section: BrickLayoutSection) -> CGFloat


/// Returns the size for attributes at a given index
func size(for attributes: BrickLayoutAttributes, containedIn width: CGFloat, in section: BrickLayoutSection) -> CGSize

Expand Down Expand Up @@ -311,7 +309,7 @@ internal class BrickLayoutSection {
}

// If rows need to be aligned, make sure the previous lines are checked
if dataSource.alignRowHeights && dataSource.scrollDirection == .Vertical {
if dataSource.isAlignRowHeights(in: self) && dataSource.scrollDirection == .Vertical {
let maxHeight = maxY - y
updateHeightForRowsFromIndex(attributes.count - 1, maxHeight: maxHeight, updatedAttributes: updatedAttributes)
}
Expand Down Expand Up @@ -473,7 +471,7 @@ internal class BrickLayoutSection {
var nextY: CGFloat = y
var nextX: CGFloat = x
if shouldBeOnNextRow {
if dataSource.alignRowHeights && dataSource.scrollDirection == .Vertical {
if dataSource.isAlignRowHeights(in: self) && dataSource.scrollDirection == .Vertical {
let maxHeight = maxY - nextY
updateHeightForRowsFromIndex(index - 1, maxHeight: maxHeight, updatedAttributes: updatedAttributes)
}
Expand Down Expand Up @@ -552,7 +550,6 @@ internal class BrickLayoutSection {
width = size.width
}


var brickFrame = CGRect(origin: cellOrigin, size: CGSize(width: width, height: height))

brickAttributes.frame = brickFrame
Expand Down
4 changes: 3 additions & 1 deletion Source/Models/BrickModels.swift
Expand Up @@ -104,6 +104,7 @@ public class BrickSection: Brick {
public var bricks: [Brick]
public var inset: CGFloat
public var edgeInsets: UIEdgeInsets
public var alignRowHeights: Bool

internal private(set) var collectionIndex: Int = 0
internal private(set) var collectionIdentifier: String = ""
Expand All @@ -117,10 +118,11 @@ public class BrickSection: Brick {
}
}

public init(_ identifier: String = "", width: BrickDimension = .Ratio(ratio: 1), height: BrickDimension = .Auto(estimate: .Fixed(size: 0)), backgroundColor: UIColor = .clearColor(), backgroundView: UIView? = nil, bricks: [Brick], inset: CGFloat = 0, edgeInsets: UIEdgeInsets = UIEdgeInsetsZero) {
public init(_ identifier: String = "", width: BrickDimension = .Ratio(ratio: 1), height: BrickDimension = .Auto(estimate: .Fixed(size: 0)), backgroundColor: UIColor = .clearColor(), backgroundView: UIView? = nil, bricks: [Brick], inset: CGFloat = 0, edgeInsets: UIEdgeInsets = UIEdgeInsetsZero, alignRowHeights: Bool = false) {
self.bricks = bricks
self.inset = inset
self.edgeInsets = edgeInsets
self.alignRowHeights = alignRowHeights
super.init(identifier, width: width, height: height, backgroundColor: backgroundColor, backgroundView: backgroundView)
}

Expand Down
Expand Up @@ -61,6 +61,16 @@ extension BrickCollectionView: BrickLayoutDataSource {
return brickSection.inset
}

public func brickLayout(layout: BrickLayout, isAlignRowHeightsForSection section: Int) -> Bool {
guard
let indexPath = self.section.indexPathForSection(section, in: collectionInfo),
let brickSection = self.brick(at:indexPath) as? BrickSection
else {
return false
}
return brickSection.alignRowHeights
}

public func brickLayout(layout: BrickLayout, brickLayoutTypeForItemAtIndexPath indexPath: NSIndexPath) -> BrickLayoutType {
let brick = self.brick(at:indexPath)
if brick is BrickSection {
Expand Down
120 changes: 55 additions & 65 deletions Tests/Layout/AlignRowHeightTests.swift
Expand Up @@ -12,80 +12,70 @@ import XCTest
class AlignRowHeightTests: BrickFlowLayoutBaseTests {

func testAlignRowHeightsSingleRow() {
collectionView.layout.alignRowHeights = true

let half = CGFloat(1.0/2.0)
setDataSources(SectionsCollectionViewDataSource(sections: [2]), brickLayoutDataSource: SectionsLayoutDataSource(widthRatios: [[half, half]], heights: [[100, 200]], types: [[.Brick, .Brick]]))
collectionView.registerBrickClass(DummyBrick.self)

let section = BrickSection(bricks: [
DummyBrick(width: .Ratio(ratio: 1/2), height: .Fixed(size: 100)),
DummyBrick(width: .Ratio(ratio: 1/2), height: .Fixed(size: 200))
], alignRowHeights: true)
collectionView.setSection(section)
collectionView.layoutSubviews()

let expectedResult = [
0 : [
CGRect(x: 0, y: 0, width: 160, height: 200),
CGRect(x: 160, y: 0, width: 160, height: 200),
]
]

let attributes = layout.layoutAttributesForElementsInRect(collectionViewFrame)
XCTAssertNotNil(attributes)
XCTAssertTrue(verifyAttributesToExpectedResult(attributes!, expectedResult: expectedResult))
let cell1 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 0, inSection: 1))
XCTAssertEqual(cell1?.frame, CGRect(x: 0, y: 0, width: 160, height: 200))
let cell2 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 1, inSection: 1))
XCTAssertEqual(cell2?.frame, CGRect(x: 160, y: 0, width: 160, height: 200))
}

func testAlignRowHeightsSingleRowFirstRowHighest() {
collectionView.layout.alignRowHeights = true

let half = CGFloat(1.0/2.0)
setDataSources(SectionsCollectionViewDataSource(sections: [2]), brickLayoutDataSource: SectionsLayoutDataSource(widthRatios: [[half, half]], heights: [[200, 100]], types: [[.Brick, .Brick]]))


let expectedResult = [
0 : [
CGRect(x: 0, y: 0, width: 160, height: 200),
CGRect(x: 160, y: 0, width: 160, height: 200),
]
]

let attributes = layout.layoutAttributesForElementsInRect(collectionViewFrame)
XCTAssertNotNil(attributes)
XCTAssertTrue(verifyAttributesToExpectedResult(attributes!, expectedResult: expectedResult))
collectionView.registerBrickClass(DummyBrick.self)

let section = BrickSection(bricks: [
DummyBrick(width: .Ratio(ratio: 1/2), height: .Fixed(size: 200)),
DummyBrick(width: .Ratio(ratio: 1/2), height: .Fixed(size: 100))
], alignRowHeights: true)
collectionView.setSection(section)
collectionView.layoutSubviews()

let cell1 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 0, inSection: 1))
XCTAssertEqual(cell1?.frame, CGRect(x: 0, y: 0, width: 160, height: 200))
let cell2 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 1, inSection: 1))
XCTAssertEqual(cell2?.frame, CGRect(x: 160, y: 0, width: 160, height: 200))
}

func testAlignRowHeightsMultipleRows() {
collectionView.layout.alignRowHeights = true

let half = CGFloat(1.0/2.0)
let fourth = CGFloat(1.0/4.0)
setDataSources(SectionsCollectionViewDataSource(sections: [8]), brickLayoutDataSource: SectionsLayoutDataSource(widthRatios: [[half, half, fourth, fourth, fourth, fourth, fourth]], heights: [[100, 200, 100, 200, 100, 150, 100, 200]], types: [[.Brick, .Brick, .Brick, .Brick, .Brick, .Brick]]))


let layoutFourth = CGFloat(320.0/4.0)
let expectedResult = [
0 : [
CGRect(x: 0, y: 0, width: 160, height: 200),
CGRect(x: 160, y: 0, width: 160, height: 200),
CGRect(x: 0, y: 200, width: layoutFourth, height: 200),
CGRect(x: layoutFourth, y: 200, width: layoutFourth, height: 200),
CGRect(x: 2 * layoutFourth, y: 200, width: layoutFourth, height: 200),
CGRect(x: 3 * layoutFourth, y: 200, width: layoutFourth, height: 200),
CGRect(x: 0, y: 400, width: layoutFourth, height: 200),
CGRect(x: layoutFourth, y: 400, width: layoutFourth, height: 200),
]
]

let attributes = layout.layoutAttributesForElementsInRect(collectionViewFrame)
XCTAssertNotNil(attributes)
XCTAssertTrue(verifyAttributesToExpectedResult(attributes!, expectedResult: expectedResult))
}

func testAlignRowHeightsNoBricks() {
collectionView.layout.alignRowHeights = true

let half = CGFloat(1.0/2.0)
setDataSources(SectionsCollectionViewDataSource(sections: [0]), brickLayoutDataSource: SectionsLayoutDataSource(widthRatios: [[half]], heights: [[100]], types: [[.Brick]]))


let attributes = layout.layoutAttributesForElementsInRect(collectionViewFrame)
XCTAssertNotNil(attributes)
XCTAssertTrue(verifyAttributesToExpectedResult(attributes!, expectedResult: [:]))
collectionView.registerBrickClass(DummyBrick.self)

let section = BrickSection(bricks: [
DummyBrick(width: .Ratio(ratio: 1/2), height: .Fixed(size: 100)),
DummyBrick(width: .Ratio(ratio: 1/2), height: .Fixed(size: 200)),
DummyBrick(width: .Ratio(ratio: 1/4), height: .Fixed(size: 100)),
DummyBrick(width: .Ratio(ratio: 1/4), height: .Fixed(size: 200)),
DummyBrick(width: .Ratio(ratio: 1/4), height: .Fixed(size: 100)),
DummyBrick(width: .Ratio(ratio: 1/4), height: .Fixed(size: 150)),
DummyBrick(width: .Ratio(ratio: 1/4), height: .Fixed(size: 100)),
DummyBrick(width: .Ratio(ratio: 1/4), height: .Fixed(size: 200)),
], alignRowHeights: true)
collectionView.setSection(section)
collectionView.layoutSubviews()

let cell1 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 0, inSection: 1))
XCTAssertEqual(cell1?.frame, CGRect(x: 0, y: 0, width: 160, height: 200))
let cell2 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 1, inSection: 1))
XCTAssertEqual(cell2?.frame, CGRect(x: 160, y: 0, width: 160, height: 200))
let cell3 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 2, inSection: 1))
XCTAssertEqual(cell3?.frame, CGRect(x: 0, y: 200, width: 80, height: 200))
let cell4 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 3, inSection: 1))
XCTAssertEqual(cell4?.frame, CGRect(x: 80, y: 200, width: 80, height: 200))
let cell5 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 4, inSection: 1))
XCTAssertEqual(cell5?.frame, CGRect(x: 160, y: 200, width: 80, height: 200))
let cell6 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 5, inSection: 1))
XCTAssertEqual(cell6?.frame, CGRect(x: 240, y: 200, width: 80, height: 200))
let cell7 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 6, inSection: 1))
XCTAssertEqual(cell7?.frame, CGRect(x: 0, y: 400, width: 80, height: 200))
let cell8 = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 7, inSection: 1))
XCTAssertEqual(cell8?.frame, CGRect(x: 80, y: 400, width: 80, height: 200))
}

}
3 changes: 1 addition & 2 deletions Tests/Layout/BrickInvalidationContextTests.swift
Expand Up @@ -912,12 +912,11 @@ class BrickInvalidationContextTests: XCTestCase {

func testThatInvalidateWithAlignRowHeightsReportsCorrectly() {
brickViewController.brickCollectionView.registerBrickClass(DummyBrick.self)
brickViewController.brickCollectionView.layout.alignRowHeights = true

let section = BrickSection("Test Section", bricks: [
DummyBrick("Brick 1", width: .Ratio(ratio: 1/2), height: .Fixed(size: 50)),
DummyBrick("Brick 2", width: .Ratio(ratio: 1/2), height: .Fixed(size: 50)),
])
], alignRowHeights: true)

brickViewController.setSection(section)
brickViewController.collectionView!.layoutSubviews()
Expand Down
3 changes: 1 addition & 2 deletions Tests/Layout/LazyLoadingTests.swift
Expand Up @@ -138,8 +138,7 @@ class LazyLoadingTests: XCTestCase {
func testThatAlignRowHeightsWorksProperly() {
setupSection()
brickView.section.inset = 10

flowLayout.alignRowHeights = true
brickView.section.alignRowHeights = true

let attributes = brickView.collectionViewLayout.layoutAttributesForElementsInRect(CGRect(x: 0, y: 0, width: 320, height: 500))?.sort({$0.0.indexPath.item < $0.1.indexPath.item})
let lastAttributes = attributes?.last
Expand Down
8 changes: 7 additions & 1 deletion Tests/Utils/DataSources.swift
Expand Up @@ -185,7 +185,10 @@ class FixedBrickLayoutSectionDataSource: NSObject, BrickLayoutSectionDataSource
return downStreamIndexPaths
}

var alignRowHeights: Bool = false
func isAlignRowHeights(in section: BrickLayoutSection) -> Bool {
return false
}

var scrollDirection: UICollectionViewScrollDirection = .Vertical
}

Expand Down Expand Up @@ -232,6 +235,9 @@ class FixedBrickLayoutDataSource: NSObject, BrickLayoutDataSource {
return ""
}

func brickLayout(layout: BrickLayout, isAlignRowHeightsForSection section: Int) -> Bool {
return false
}
}

class FixedSpotlightLayoutBehaviorDataSource: SpotlightLayoutBehaviorDataSource {
Expand Down
2 changes: 1 addition & 1 deletion Tests/ViewControllers/BrickCollectionViewTests.swift
Expand Up @@ -621,7 +621,7 @@ class BrickCollectionViewTests: XCTestCase {
cell2 = brickView.cellForItemAtIndexPath(NSIndexPath(forItem: 1, inSection: 2))
XCTAssertEqual(cell2?.frame, CGRect(x: 60, y: 10, width: 410, height: 25))
}

func testThatDescriptionIsCorrect() {
XCTAssertTrue(self.brickView.description.hasSuffix("CollectionBrick: false"))
}
Expand Down