Skip to content
This repository has been archived by the owner on Dec 22, 2023. It is now read-only.

Implement delegate methods for header/footer reference size #121

Merged
merged 10 commits into from
Aug 5, 2019
2 changes: 1 addition & 1 deletion Example-OSX/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow?

func applicationDidFinishLaunching(_ aNotification: Notification) {
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection10.bundle")?.load()
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle")?.load()

guard let mainScreen = NSScreen.main ?? NSScreen.screens.first else {
fatalError("Expected a display.")
Expand Down
2 changes: 1 addition & 1 deletion Example-iOS/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
#if DEBUG
// Load InjectionIII bundle
// https://itunes.apple.com/us/app/injectioniii/id1380446739?mt=12
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection10.bundle")?.load()
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle")?.load()
#endif

window = UIWindow(frame: UIScreen.main.bounds)
Expand Down
81 changes: 54 additions & 27 deletions Sources/Shared/Core/BlueprintLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,37 +156,56 @@
}
}

/// Create supplementary layout attributes.
/// Resolve the size of SupplementaryView at index path.
/// If the collection view's delegate conforms to `(UI/NS)CollectionViewDelegateFlowLayout`, it will
/// query the delegate for the size of the SupplementaryView.
/// It defaults to using the `headerReferenceSize` or `footerReferenceSize` property on collection view flow layout.
///
/// - Parameters:
/// - kind: The supplementary kind, either header or footer.
/// - indexPath: The section index path for the supplementary view.
/// - x: The x coordinate of the header layout attributes.
/// - y: The y coordinate of the header layout attributes.
/// - Returns: A `LayoutAttributes` object of supplementary kind.
func createSupplementaryLayoutAttribute(ofKind kind: BlueprintSupplementaryKind,
indexPath: IndexPath,
atX x: CGFloat = 0,
atY y: CGFloat = 0) -> SupplementaryLayoutAttributes {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: kind.collectionViewSupplementaryType,
with: indexPath
)

/// - Parameter indexPath: The index path of the supplementaryView.
/// - Returns: The desired size of the item at the index path.
func resolveSizeForSupplementaryView(ofKind kind: BlueprintSupplementaryKind, at indexPath: IndexPath) -> CGSize {
switch kind {
case .header:
layoutAttribute.size.width = collectionView?.documentRect.width ?? headerReferenceSize.width
layoutAttribute.size.height = headerReferenceSize.height
let height = resolveCollectionView({ collectionView -> CGSize? in
return (collectionView.delegate as? CollectionViewFlowLayoutDelegate)?.collectionView?(collectionView,
layout: self,
referenceSizeForHeaderInSection: indexPath.section)
}, defaultValue: headerReferenceSize).height

let width: CGFloat
if headerReferenceSize.width > 0 {
width = headerReferenceSize.width
} else {
width = collectionView?.documentRect.width ?? headerReferenceSize.width
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you also be able to provide a width using the delegate or do you reckon that height would be enough?
Don't know how useful it would be to provide a width, what are your thoughts @christoff-1992

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm that's a good point, I don't know why you would have it anything other than the collection view's width but it's one of the options for the delegate methods since you provide the size. 🤷‍♂️

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the delegate should take precedence here if it exists. What do you think?

Copy link
Collaborator Author

@christoff-1992 christoff-1992 Aug 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm.. true but if the size is 0 it's not going to be visible hence why I took the collection views width before.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But wouldn't you expect it to be zero if you delegate method returns zero?
I think it would be more frustrating if you try and set the width and you don't get the expected result.


let size = CGSize(
width: width,
height: height
)

return size
case .footer:
layoutAttribute.size.width = collectionView?.documentRect.width ?? footerReferenceSize.width
layoutAttribute.size.height = footerReferenceSize.height
}
let height = resolveCollectionView({ collectionView -> CGSize? in
return (collectionView.delegate as? CollectionViewFlowLayoutDelegate)?.collectionView?(collectionView,
layout: self,
referenceSizeForFooterInSection: indexPath.section)
}, defaultValue: footerReferenceSize).height

let width: CGFloat
if footerReferenceSize.width > 0 {
width = footerReferenceSize.width
} else {
width = collectionView?.documentRect.width ?? footerReferenceSize.width
}

layoutAttribute.zIndex = indexPath.section
layoutAttribute.frame.origin.x = x
layoutAttribute.frame.origin.y = y
let size = CGSize(
width: width,
height: height
)

return layoutAttribute
return size
}
}

/// Resolve collection collection view from layout and return
Expand Down Expand Up @@ -378,9 +397,17 @@
let results = cachedSupplementaryAttributes.filter({
switch scrollDirection {
case .vertical:
return (visibleRect.origin.y >= $0.min && visibleRect.origin.y <= $0.max) || $0.frame.intersects(visibleRect)
if visibleRect.origin.y < 0 {
return $0.frame.intersects(visibleRect)
} else {
return (visibleRect.origin.y >= $0.min && visibleRect.origin.y <= $0.max)
}
case .horizontal:
return (visibleRect.origin.x >= $0.min && visibleRect.origin.x <= $0.max) || $0.frame.intersects(visibleRect)
if visibleRect.origin.x < 0 {
return $0.frame.intersects(visibleRect)
} else {
return (visibleRect.origin.x >= $0.min && visibleRect.origin.x <= $0.max)
}
@unknown default:
fatalError("Case not implemented in current implementation")
}
Expand Down
27 changes: 16 additions & 11 deletions Sources/Shared/Core/HorizontalBlueprintLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,18 @@
var footerAttribute: SupplementaryLayoutAttributes? = nil
let sectionIndexPath = IndexPath(item: 0, section: section)

if headerReferenceSize.height > 0 {
let layoutAttribute: SupplementaryLayoutAttributes = createSupplementaryLayoutAttribute(
ofKind: .header,
indexPath: sectionIndexPath,
atX: nextX
if resolveSizeForSupplementaryView(ofKind: .header, at: sectionIndexPath).height > 0 {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: BlueprintSupplementaryKind.header.collectionViewSupplementaryType,
with: sectionIndexPath
)
layoutAttribute.size = resolveSizeForSupplementaryView(ofKind: .header, at: sectionIndexPath)
layoutAttribute.zIndex = section + numberOfItemsInSection(section)
layoutAttribute.min = nextX
headerAttribute = layoutAttribute
layoutAttribute.frame.origin.x = nextX
layoutAttribute.frame.origin.y = 0
layoutAttributes.append([layoutAttribute])
headerAttribute = layoutAttribute
}

for item in 0..<numberOfItemsInSection(section) {
Expand Down Expand Up @@ -198,13 +201,15 @@
if let previousItem = previousItem, let firstItem = firstItem {
contentSize.width = previousItem.frame.maxX + sectionInset.right

if footerReferenceSize.height > 0 {
let layoutAttribute = createSupplementaryLayoutAttribute(
ofKind: .footer,
indexPath: sectionIndexPath,
atX: nextX
if resolveSizeForSupplementaryView(ofKind: .footer, at: sectionIndexPath).height > 0 {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: BlueprintSupplementaryKind.footer.collectionViewSupplementaryType,
with: sectionIndexPath
)
layoutAttribute.size = resolveSizeForSupplementaryView(ofKind: .footer, at: sectionIndexPath)
layoutAttribute.zIndex = section + numberOfItemsInSection(section)
layoutAttribute.min = nextX
layoutAttribute.frame.origin.x = 0
layoutAttribute.frame.origin.y = contentSize.height + footerReferenceSize.height
layoutAttributes[section].append(layoutAttribute)
footerAttribute = layoutAttribute
Expand Down
34 changes: 20 additions & 14 deletions Sources/Shared/Core/VerticalBlueprintLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,16 @@
var footerAttribute: SupplementaryLayoutAttributes? = nil
let sectionIndexPath = IndexPath(item: 0, section: section)

if headerReferenceSize.height > 0 {
let layoutAttribute = createSupplementaryLayoutAttribute(
ofKind: .header,
indexPath: sectionIndexPath,
atY: nextY
if resolveSizeForSupplementaryView(ofKind: .header, at: sectionIndexPath).height > 0 {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: BlueprintSupplementaryKind.header.collectionViewSupplementaryType,
with: sectionIndexPath
)
layoutAttribute.zIndex = numberOfSections
layoutAttribute.size = resolveSizeForSupplementaryView(ofKind: .header, at: sectionIndexPath)
layoutAttribute.zIndex = section + numberOfItemsInSection(section)
layoutAttribute.min = nextY
layoutAttribute.frame.size.width = collectionView?.documentRect.width ?? headerReferenceSize.width
layoutAttribute.frame.origin.x = 0
layoutAttribute.frame.origin.y = nextY
layoutAttributes.append([layoutAttribute])
headerAttribute = layoutAttribute
nextY = layoutAttribute.frame.maxY
Expand Down Expand Up @@ -198,16 +199,21 @@
}

if let previousItem = previousItem {
let previousY = nextY
nextY = previousItem.frame.maxY
if footerReferenceSize.height > 0 {
let layoutAttribute = createSupplementaryLayoutAttribute(
ofKind: .footer,
indexPath: sectionIndexPath,
atY: sectionMaxY + sectionInset.bottom
if resolveSizeForSupplementaryView(ofKind: .footer, at: sectionIndexPath).height > 0 {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: BlueprintSupplementaryKind.footer.collectionViewSupplementaryType,
with: sectionIndexPath
)
layoutAttribute.size = resolveSizeForSupplementaryView(ofKind: .footer, at: sectionIndexPath)
layoutAttribute.zIndex = section + numberOfItemsInSection(section)
layoutAttribute.min = headerAttribute?.min ?? previousY
layoutAttribute.max = sectionMaxY + sectionInset.bottom
layoutAttribute.frame.origin.x = 0
layoutAttribute.frame.origin.y = sectionMaxY + sectionInset.bottom
layoutAttributes[section].append(layoutAttribute)
layoutAttribute.zIndex = numberOfSections
layoutAttribute.min = headerAttribute?.frame.origin.y ?? nextY
headerAttribute?.max = sectionMaxY + sectionInset.bottom
footerAttribute = layoutAttribute
nextY = layoutAttribute.frame.maxY
} else {
Expand Down
35 changes: 20 additions & 15 deletions Sources/Shared/Mosaic/VerticalMosaicBlueprintLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,18 @@
var footerAttribute: SupplementaryLayoutAttributes? = nil
let sectionIndexPath = IndexPath(item: 0, section: section)

if headerReferenceSize.height > 0 {
let layoutAttribute = createSupplementaryLayoutAttribute(
ofKind: .header,
indexPath: sectionIndexPath,
atY: nextY
if resolveSizeForSupplementaryView(ofKind: .header, at: sectionIndexPath).height > 0 {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: BlueprintSupplementaryKind.header.collectionViewSupplementaryType,
with: sectionIndexPath
)
layoutAttribute.size = resolveSizeForSupplementaryView(ofKind: .header, at: sectionIndexPath)
layoutAttribute.zIndex = section + numberOfItemsInSection(section)
layoutAttribute.min = nextY
layoutAttribute.frame.size.width = collectionView?.documentRect.width ?? headerReferenceSize.width
layoutAttribute.frame.origin.x = 0
layoutAttribute.frame.origin.y = nextY
layoutAttributes.append([layoutAttribute])
headerAttribute = layoutAttribute
headerAttribute?.zIndex = numberOfSections
nextY = layoutAttribute.frame.maxY
}

Expand Down Expand Up @@ -134,18 +135,22 @@
}

if let previousAttribute = previousAttribute {
let previousY = nextY
nextY = previousAttribute.frame.maxY
if footerReferenceSize.height > 0 {
let layoutAttribute = createSupplementaryLayoutAttribute(
ofKind: .footer,
indexPath: sectionIndexPath,
atY: nextY + sectionInset.bottom
if resolveSizeForSupplementaryView(ofKind: .footer, at: sectionIndexPath).height > 0 {
let layoutAttribute = SupplementaryLayoutAttributes(
forSupplementaryViewOfKind: BlueprintSupplementaryKind.footer.collectionViewSupplementaryType,
with: sectionIndexPath
)
layoutAttribute.zIndex = numberOfSections
layoutAttribute.min = headerAttribute?.frame.origin.y ?? nextY
layoutAttribute.size = resolveSizeForSupplementaryView(ofKind: .footer, at: sectionIndexPath)
layoutAttribute.zIndex = section + numberOfItemsInSection(section)
layoutAttribute.min = headerAttribute?.min ?? previousY
layoutAttribute.max = sectionMaxY + sectionInset.bottom
layoutAttribute.frame.origin.x = 0
layoutAttribute.frame.origin.y = nextY + sectionInset.bottom
layoutAttributes[section].append(layoutAttribute)
nextY = layoutAttribute.frame.maxY
footerAttribute = layoutAttribute
nextY = layoutAttribute.frame.maxY
} else {
nextY = nextY + sectionInset.bottom
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/Shared/VerticalBlueprintLayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ class VerticalBlueprintLayoutTests: XCTestCase {
}

func testVerticalLayoutAttributesWithHeaderAndFooter() {
verticalLayout.headerReferenceSize = CGSize(width: 100, height: 100)
verticalLayout.footerReferenceSize = CGSize(width: 100, height: 100)
verticalLayout.headerReferenceSize = CGSize(width: collectionView.frame.width, height: 100)
verticalLayout.footerReferenceSize = CGSize(width: collectionView.frame.width, height: 100)
verticalLayout.prepare()

let expectedHeaderAndFooterSize: CGSize = .init(width: 200, height: 100)
Expand Down