Skip to content

Commit

Permalink
Fix sizing month header on every frame and add more calendar model ca…
Browse files Browse the repository at this point in the history
…ching (#321)
  • Loading branch information
bryankeller authored Nov 14, 2024
1 parent 8c36887 commit 697c6f2
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 33 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fixed an issue that could cause accessibility focus to shift unexpectedly
- Fixed a screen-pixel alignment issue
- Fixed a performance issue caused by month headers recalculating their size too often
- Fixed a performance issue caused by the item models for day ranges and month backgrounds not being cached

### Changed
- Rewrote accessibility code to avoid posting notifications, which causes poor Voice Over performance and odd focus bugs
Expand Down
81 changes: 48 additions & 33 deletions Sources/Internal/VisibleItemsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ final class VisibleItemsProvider {
// Handle background items
handleMonthBackgroundItemsIfNeeded(context: &context)

previousHeightsForVisibleMonthHeaders = context.heightsForVisibleMonthHeaders
previousCalendarItemModelCache = context.calendarItemModelCache

return VisibleItemsDetails(
Expand All @@ -301,10 +302,8 @@ final class VisibleItemsProvider {
private var sizingMonthHeaderViewsForViewDifferentiators = [
_CalendarItemViewDifferentiator: UIView
]()

private var previousCalendarItemModelCache: [
VisibleItem.ItemType: AnyCalendarItemModel
]?
private var previousHeightsForVisibleMonthHeaders: [Month: CGFloat]?
private var previousCalendarItemModelCache: [VisibleItem.ItemType: AnyCalendarItemModel]?

private var calendar: Calendar {
content.calendar
Expand Down Expand Up @@ -442,31 +441,35 @@ final class VisibleItemsProvider {
context.heightsForVisibleMonthHeaders.value(
for: month,
missingValueProvider: {
let monthHeaderItemModel = content.monthHeaderItemProvider(month)
let monthHeaderView = sizingMonthHeaderViewsForViewDifferentiators.value(
for: monthHeaderItemModel._itemViewDifferentiator,
missingValueProvider: {
monthHeaderItemModel._makeView()
})
monthHeaderItemModel._setContent(onViewOfSameType: monthHeaderView)

let monthWidth: CGFloat
switch content.monthsLayout {
case .vertical:
monthWidth = size.width
case .horizontal(let options):
monthWidth = options.monthWidth(
calendarWidth: size.width,
interMonthSpacing: content.interMonthSpacing)
}

let size = monthHeaderView.systemLayoutSizeFitting(
CGSize(width: monthWidth, height: UIView.layoutFittingCompressedSize.height),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel)
previousHeightsForVisibleMonthHeaders?[month] ?? monthHeaderHeight(for: month)
})
}

return size.height
private func monthHeaderHeight(for month: Month) -> CGFloat {
let monthHeaderItemModel = content.monthHeaderItemProvider(month)
let monthHeaderView = sizingMonthHeaderViewsForViewDifferentiators.value(
for: monthHeaderItemModel._itemViewDifferentiator,
missingValueProvider: {
monthHeaderItemModel._makeView()
})
monthHeaderItemModel._setContent(onViewOfSameType: monthHeaderView)

let monthWidth: CGFloat
switch content.monthsLayout {
case .vertical:
monthWidth = size.width
case .horizontal(let options):
monthWidth = options.monthWidth(
calendarWidth: size.width,
interMonthSpacing: content.interMonthSpacing)
}

let size = monthHeaderView.systemLayoutSizeFitting(
CGSize(width: monthWidth, height: UIView.layoutFittingCompressedSize.height),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel)

return size.height
}

private func layoutItem(
Expand Down Expand Up @@ -698,8 +701,7 @@ final class VisibleItemsProvider {
calendarItemModel = context.calendarItemModelCache.value(
for: itemType,
missingValueProvider: {
previousCalendarItemModelCache?[itemType]
?? content.monthHeaderItemProvider(month)
previousCalendarItemModelCache?[itemType] ?? content.monthHeaderItemProvider(month)
})

// Create a visible item for the separator view, if needed.
Expand Down Expand Up @@ -871,10 +873,15 @@ final class VisibleItemsProvider {
daysAndFrames: dayRangeLayoutContext.daysAndFrames,
boundingUnionRectOfDayFrames: dayRangeLayoutContext.boundingUnionRectOfDayFrames)

let itemType = VisibleItem.ItemType.dayRange(dayRange)
context.visibleItems.insert(
VisibleItem(
calendarItemModel: dayRangeItemProvider(dayRangeLayoutContext),
itemType: .dayRange(dayRange),
calendarItemModel: context.calendarItemModelCache.value(
for: itemType,
missingValueProvider: {
previousCalendarItemModelCache?[itemType] ?? dayRangeItemProvider(dayRangeLayoutContext)
}),
itemType: itemType,
frame: frame))
}

Expand Down Expand Up @@ -1027,10 +1034,18 @@ final class VisibleItemsProvider {
dayOfWeekPositionsAndFrames: dayOfWeekPositionsAndFrames,
daysAndFrames: daysAndFrames.sorted(by: { $0.day < $1.day }),
bounds: CGRect(origin: .zero, size: expandedMonthFrame.size))
if let itemModel = monthBackgroundItemProvider(monthLayoutContext) {

let itemType = VisibleItem.ItemType.monthBackground(month)
let itemModel = context.calendarItemModelCache.optionalValue(
for: itemType,
missingValueProvider: {
previousCalendarItemModelCache?[itemType] ??
monthBackgroundItemProvider(monthLayoutContext)
})
if let itemModel {
let visibleItem = VisibleItem(
calendarItemModel: itemModel,
itemType: .monthBackground(month),
itemType: itemType,
frame: expandedMonthFrame)
context.visibleItems.insert(visibleItem)
}
Expand Down

0 comments on commit 697c6f2

Please sign in to comment.