diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index c2abebc..26342e7 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -24,6 +24,6 @@ jobs: steps: - uses: actions/checkout@v2 - name: Lint Swift - run: swift package format --allow-writing-to-package-directory format --lint + run: swift package --allow-writing-to-package-directory format --lint diff --git a/CHANGELOG.md b/CHANGELOG.md index 191cf94..66f1f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added the ability to change the aspect ratio of individual day-of-the-week items - Added support for self-sizing month headers - Added a new `setContent(_:animated:)` function, enabling developers to perform animated content updates +- Added Swift format integration to enforce consistent code style ### Fixed - Fixed an issue that could cause the calendar to programmatically scroll to a month or day to which it had previously scrolled diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/AppDelegate.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/AppDelegate.swift index 754edb3..80eb2bb 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/AppDelegate.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/AppDelegate.swift @@ -20,13 +20,11 @@ import UIKit @UIApplicationMain final class AppDelegate: UIResponder, UIApplicationDelegate { - // MARK: Internal - var window: UIWindow? func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) + _: UIApplication, + didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) @@ -38,4 +36,3 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } } - diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeIndicatorView.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeIndicatorView.swift index 9f58801..3de8aed 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeIndicatorView.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeIndicatorView.swift @@ -30,13 +30,13 @@ final class DayRangeIndicatorView: UIView { backgroundColor = .clear } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: Internal - override func draw(_ rect: CGRect) { + override func draw(_: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setFillColor(indicatorColor.cgColor) diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeSelectionTracker.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeSelectionTracker.swift index 0d6206a..53a49d4 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeSelectionTracker.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/DayRangeSelectionTracker.swift @@ -39,7 +39,7 @@ enum DayRangeSelectionHelper { { switch state { case .began: - if day != existingDayRange?.lowerBound && day != existingDayRange?.upperBound { + if day != existingDayRange?.lowerBound, day != existingDayRange?.upperBound { existingDayRange = day...day } initialDayRange = existingDayRange diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift index c0a73d4..a0aac3d 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift @@ -30,9 +30,9 @@ final class DayRangeSelectionDemoViewController: BaseDemoViewController { DayRangeSelectionHelper.updateDayRange( afterTapSelectionOf: day, - existingDayRange: &self.selectedDayRange) + existingDayRange: &selectedDayRange) - self.calendarView.setContent(self.makeContent()) + calendarView.setContent(makeContent()) } calendarView.multiDaySelectionDragHandler = { [weak self, calendar] day, state in @@ -40,12 +40,12 @@ final class DayRangeSelectionDemoViewController: BaseDemoViewController { DayRangeSelectionHelper.updateDayRange( afterDragSelectionOf: day, - existingDayRange: &self.selectedDayRange, - initialDayRange: &self.selectedDayRangeAtStartOfDrag, + existingDayRange: &selectedDayRange, + initialDayRange: &selectedDayRangeAtStartOfDrag, state: state, calendar: calendar) - self.calendarView.setContent(self.makeContent()) + calendarView.setContent(makeContent()) } } diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DemoViewController.swift index bcefd7b..7661ba3 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DemoViewController.swift @@ -27,7 +27,7 @@ class BaseDemoViewController: UIViewController, DemoViewController { super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/MonthBackgroundDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/MonthBackgroundDemoViewController.swift index 98b5a05..fec56ca 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/MonthBackgroundDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/MonthBackgroundDemoViewController.swift @@ -51,4 +51,3 @@ final class MonthBackgroundDemoViewController: BaseDemoViewController { private var selectedDate: Date? } - diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/PartialMonthVisibilityDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/PartialMonthVisibilityDemoViewController.swift index 5a1a5b1..2c83db2 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/PartialMonthVisibilityDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/PartialMonthVisibilityDemoViewController.swift @@ -16,8 +16,8 @@ final class PartialMonthVisibilityDemoViewController: BaseDemoViewController { calendarView.daySelectionHandler = { [weak self] day in guard let self else { return } - self.selectedDate = self.calendar.date(from: day.components) - self.calendarView.setContent(self.makeContent()) + selectedDate = calendar.date(from: day.components) + calendarView.setContent(makeContent()) } } @@ -25,7 +25,7 @@ final class PartialMonthVisibilityDemoViewController: BaseDemoViewController { let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 16))! let endDate = calendar.date(from: DateComponents(year: 2020, month: 12, day: 05))! - let selectedDate = self.selectedDate + let selectedDate = selectedDate return CalendarViewContent( calendar: calendar, @@ -59,4 +59,3 @@ final class PartialMonthVisibilityDemoViewController: BaseDemoViewController { private var selectedDate: Date? } - diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SelectedDayTooltipDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SelectedDayTooltipDemoViewController.swift index 79cf40d..2c5ed83 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SelectedDayTooltipDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SelectedDayTooltipDemoViewController.swift @@ -28,8 +28,8 @@ final class SelectedDayTooltipDemoViewController: BaseDemoViewController { calendarView.daySelectionHandler = { [weak self] day in guard let self else { return } - self.selectedDate = self.calendar.date(from: day.components) - self.calendarView.setContent(self.makeContent()) + selectedDate = calendar.date(from: day.components) + calendarView.setContent(makeContent()) } } @@ -37,10 +37,10 @@ final class SelectedDayTooltipDemoViewController: BaseDemoViewController { let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 01))! let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))! - let selectedDate = self.selectedDate + let selectedDate = selectedDate let overlaidItemLocations: Set - if let selectedDate = selectedDate { + if let selectedDate { overlaidItemLocations = [.day(containingDate: selectedDate)] } else { overlaidItemLocations = [] diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SingleDaySelectionDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SingleDaySelectionDemoViewController.swift index a3c74fd..d61bcc1 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SingleDaySelectionDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SingleDaySelectionDemoViewController.swift @@ -25,10 +25,10 @@ final class SingleDaySelectionDemoViewController: BaseDemoViewController { selectedDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 19))! } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } - + // MARK: Internal override func viewDidLoad() { @@ -39,8 +39,8 @@ final class SingleDaySelectionDemoViewController: BaseDemoViewController { calendarView.daySelectionHandler = { [weak self] day in guard let self else { return } - self.selectedDate = self.calendar.date(from: day.components) - self.calendarView.setContent(self.makeContent()) + selectedDate = calendar.date(from: day.components) + calendarView.setContent(makeContent()) } } @@ -48,7 +48,7 @@ final class SingleDaySelectionDemoViewController: BaseDemoViewController { let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 01))! let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))! - let selectedDate = self.selectedDate + let selectedDate = selectedDate return CalendarViewContent( calendar: calendar, diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIItemModelsDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIItemModelsDemoViewController.swift index 2d56107..af2f158 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIItemModelsDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIItemModelsDemoViewController.swift @@ -14,8 +14,8 @@ // limitations under the License. import HorizonCalendar -import UIKit import SwiftUI +import UIKit final class SwiftUIItemModelsDemoViewController: BaseDemoViewController { @@ -29,8 +29,8 @@ final class SwiftUIItemModelsDemoViewController: BaseDemoViewController { calendarView.daySelectionHandler = { [weak self] day in guard let self else { return } - self.selectedDate = self.calendar.date(from: day.components) - self.calendarView.setContent(self.makeContent()) + selectedDate = calendar.date(from: day.components) + calendarView.setContent(makeContent()) } } @@ -38,7 +38,7 @@ final class SwiftUIItemModelsDemoViewController: BaseDemoViewController { let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 01))! let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))! - let selectedDate = self.selectedDate + let selectedDate = selectedDate return CalendarViewContent( calendar: calendar, diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIScreenDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIScreenDemoViewController.swift index 328862c..8ceab14 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIScreenDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/SwiftUIScreenDemoViewController.swift @@ -27,7 +27,7 @@ final class SwiftUIScreenDemoViewController: UIViewController, DemoViewControlle super.init(nibName: nil, bundle: nil) } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -92,78 +92,78 @@ struct SwiftUIScreenDemo: View { dataDependency: selectedDayRange, proxy: calendarViewProxy) - .interMonthSpacing(24) - .verticalDayMargin(8) - .horizontalDayMargin(8) - - .monthHeaders { month in - let monthHeaderText = monthDateFormatter.string(from: calendar.date(from: month.components)!) - if case .vertical = monthsLayout { - HStack { + .interMonthSpacing(24) + .verticalDayMargin(8) + .horizontalDayMargin(8) + + .monthHeaders { month in + let monthHeaderText = monthDateFormatter.string(from: calendar.date(from: month.components)!) + if case .vertical = monthsLayout { + HStack { + Text(monthHeaderText) + .font(.title2) + Spacer() + } + .padding() + } else { Text(monthHeaderText) .font(.title2) - Spacer() + .padding() } - .padding() - } else { - Text(monthHeaderText) - .font(.title2) - .padding() } - } - .days { day in - SwiftUIDayView(dayNumber: day.day, isSelected: isDaySelected(day)) - } - - .dayRangeItemProvider(for: selectedDateRanges) { dayRangeLayoutContext in - let framesOfDaysToHighlight = dayRangeLayoutContext.daysAndFrames.map { $0.frame } - // UIKit view - return DayRangeIndicatorView.calendarItemModel( - invariantViewProperties: .init(), - content: .init(framesOfDaysToHighlight: framesOfDaysToHighlight)) - } + .days { day in + SwiftUIDayView(dayNumber: day.day, isSelected: isDaySelected(day)) + } - .onDaySelection { day in - DayRangeSelectionHelper.updateDayRange( - afterTapSelectionOf: day, - existingDayRange: &selectedDayRange) - } + .dayRangeItemProvider(for: selectedDateRanges) { dayRangeLayoutContext in + let framesOfDaysToHighlight = dayRangeLayoutContext.daysAndFrames.map { $0.frame } + // UIKit view + return DayRangeIndicatorView.calendarItemModel( + invariantViewProperties: .init(), + content: .init(framesOfDaysToHighlight: framesOfDaysToHighlight)) + } - .onMultipleDaySelectionDrag( - began: { day in - DayRangeSelectionHelper.updateDayRange( - afterDragSelectionOf: day, - existingDayRange: &selectedDayRange, - initialDayRange: &selectedDayRangeAtStartOfDrag, - state: .began, - calendar: calendar) - }, - changed: { day in - DayRangeSelectionHelper.updateDayRange( - afterDragSelectionOf: day, - existingDayRange: &selectedDayRange, - initialDayRange: &selectedDayRangeAtStartOfDrag, - state: .changed, - calendar: calendar) - }, - ended: { day in + .onDaySelection { day in DayRangeSelectionHelper.updateDayRange( - afterDragSelectionOf: day, - existingDayRange: &selectedDayRange, - initialDayRange: &selectedDayRangeAtStartOfDrag, - state: .ended, - calendar: calendar) - }) - - .onAppear { - calendarViewProxy.scrollToDay( - containing: calendar.date(from: DateComponents(year: 2023, month: 07, day: 19))!, - scrollPosition: .centered, - animated: false) - } + afterTapSelectionOf: day, + existingDayRange: &selectedDayRange) + } + + .onMultipleDaySelectionDrag( + began: { day in + DayRangeSelectionHelper.updateDayRange( + afterDragSelectionOf: day, + existingDayRange: &selectedDayRange, + initialDayRange: &selectedDayRangeAtStartOfDrag, + state: .began, + calendar: calendar) + }, + changed: { day in + DayRangeSelectionHelper.updateDayRange( + afterDragSelectionOf: day, + existingDayRange: &selectedDayRange, + initialDayRange: &selectedDayRangeAtStartOfDrag, + state: .changed, + calendar: calendar) + }, + ended: { day in + DayRangeSelectionHelper.updateDayRange( + afterDragSelectionOf: day, + existingDayRange: &selectedDayRange, + initialDayRange: &selectedDayRangeAtStartOfDrag, + state: .ended, + calendar: calendar) + }) + + .onAppear { + calendarViewProxy.scrollToDay( + containing: calendar.date(from: DateComponents(year: 2023, month: 07, day: 19))!, + scrollPosition: .centered, + animated: false) + } - .frame(maxWidth: 375, maxHeight: .infinity) + .frame(maxWidth: 375, maxHeight: .infinity) } // MARK: Private diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/DemoPickerViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/DemoPickerViewController.swift index ced2f7d..23c0ff5 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/DemoPickerViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/DemoPickerViewController.swift @@ -107,11 +107,11 @@ final class DemoPickerViewController: UIViewController { } -// MARK: - UITableViewDataSource +// MARK: UITableViewDataSource extension DemoPickerViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { monthsLayoutPicker.selectedSegmentIndex == 0 ? verticalDemoDestinations.count : horizontalDemoDestinations.count @@ -130,11 +130,11 @@ extension DemoPickerViewController: UITableViewDataSource { } -// MARK: - UITableViewDelegate +// MARK: UITableViewDelegate extension DemoPickerViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { let demoDestination = monthsLayoutPicker.selectedSegmentIndex == 0 ? verticalDemoDestinations[indexPath.item] : horizontalDemoDestinations[indexPath.item] diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/SwiftUIDayView.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/SwiftUIDayView.swift index 1b112b7..6e6f046 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/SwiftUIDayView.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/SwiftUIDayView.swift @@ -16,6 +16,8 @@ import HorizonCalendar import SwiftUI +// MARK: - SwiftUIDayView + struct SwiftUIDayView: View { let dayNumber: Int @@ -36,6 +38,8 @@ struct SwiftUIDayView: View { } +// MARK: - SwiftUIDayView_Previews + struct SwiftUIDayView_Previews: PreviewProvider { // MARK: Internal diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/TooltipView.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/TooltipView.swift index a813c06..fcf6fe8 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/TooltipView.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/TooltipView.swift @@ -42,7 +42,7 @@ final class TooltipView: UIView { addSubview(label) } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -51,7 +51,7 @@ final class TooltipView: UIView { override func layoutSubviews() { super.layoutSubviews() - guard let frameOfTooltippedItem = frameOfTooltippedItem else { return } + guard let frameOfTooltippedItem else { return } label.sizeToFit() let labelSize = CGSize( diff --git a/README.md b/README.md index fe63e62..d8b82ca 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A declarative, performant, calendar UI component that supports use cases ranging [![License](https://img.shields.io/cocoapods/l/HorizonCalendar.svg)](https://cocoapods.org/pods/HorizonCalendar) [![Platform](https://img.shields.io/cocoapods/p/HorizonCalendar.svg)](https://cocoapods.org/pods/HorizonCalendar) ![Swift](https://github.com/airbnb/HorizonCalendar/workflows/Swift/badge.svg) +[![Swift Package Manager compatible](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fairbnb%2FHorizonCalendar%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/airbnb/HorizonCalendar) ## Introduction `HorizonCalendar` is an interactive calendar component for iOS (compatible with UIKit and SwiftUI). Its declarative API makes updating the calendar straightforward, while also providing many customization points to support a diverse set of designs and use cases. diff --git a/Sources/Internal/Calendar+Helpers.swift b/Sources/Internal/Calendar+Helpers.swift index 2debc72..723fabb 100644 --- a/Sources/Internal/Calendar+Helpers.swift +++ b/Sources/Internal/Calendar+Helpers.swift @@ -20,7 +20,7 @@ import Foundation extension Calendar { func month(containing date: Date) -> Month { - return Month( + Month( era: component(.era, from: date), year: component(.year, from: date), month: component(.month, from: date), @@ -36,7 +36,7 @@ extension Calendar { } func lastDate(of month: Month) -> Date { - let firstDate = self.firstDate(of: month) + let firstDate = firstDate(of: month) guard let numberOfDaysInMonth = range(of: .day, in: .month, for: firstDate)?.count else { preconditionFailure("Could not get number of days in month from \(firstDate).") } @@ -59,8 +59,7 @@ extension Calendar { byAdding: .month, value: numberOfMonths, to: firstDate(of: month)) - else - { + else { preconditionFailure("Failed to advance \(month) by \(numberOfMonths) months.") } @@ -93,8 +92,7 @@ extension Calendar { func day(byAddingDays numberOfDays: Int, to day: Day) -> Day { guard let firstDateOfNextDay = date(byAdding: .day, value: numberOfDays, to: startDate(of: day)) - else - { + else { preconditionFailure("Failed to advance \(day) by \(numberOfDays) days.") } @@ -116,8 +114,8 @@ extension Calendar { guard let dayOfWeekPosition = DayOfWeekPosition(rawValue: weekdayIndex + 1) else { preconditionFailure(""" - Could not find a day of the week position for date \(date) in calendar \(self). - """) + Could not find a day of the week position for date \(date) in calendar \(self). + """) } return dayOfWeekPosition @@ -149,7 +147,7 @@ extension Calendar { isInGregorianCalendar: identifier == .gregorian)) let numberOfPositions = DayOfWeekPosition.numberOfPositions - let dayOfWeekPosition = self.dayOfWeekPosition(for: firstDateOfMonth) + let dayOfWeekPosition = dayOfWeekPosition(for: firstDateOfMonth) let daysFromEndOfWeek = numberOfPositions - (dayOfWeekPosition.rawValue - 1) let isFirstDayInFirstWeek = daysFromEndOfWeek >= minimumDaysInFirstWeek diff --git a/Sources/Internal/DoubleLayoutPassHelpers.swift b/Sources/Internal/DoubleLayoutPassHelpers.swift index 49042ed..3e045dd 100644 --- a/Sources/Internal/DoubleLayoutPassHelpers.swift +++ b/Sources/Internal/DoubleLayoutPassHelpers.swift @@ -41,21 +41,7 @@ import UIKit extension CalendarView { - func installDoubleLayoutPassSizingLabel() { - doubleLayoutPassSizingLabel.removeFromSuperview() - addSubview(doubleLayoutPassSizingLabel) - subviews.first.map(doubleLayoutPassSizingLabel.sendSubviewToBack(_:)) - - doubleLayoutPassSizingLabel.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - doubleLayoutPassSizingLabel.leadingAnchor.constraint( - equalTo: layoutMarginsGuide.leadingAnchor), - doubleLayoutPassSizingLabel.trailingAnchor.constraint( - equalTo: layoutMarginsGuide.trailingAnchor), - doubleLayoutPassSizingLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), - doubleLayoutPassSizingLabel.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor), - ]) - } + // MARK: Public public override func invalidateIntrinsicContentSize() { doubleLayoutPassSizingLabel.invalidateIntrinsicContentSize() @@ -75,6 +61,24 @@ extension CalendarView { doubleLayoutPassSizingLabel.setContentCompressionResistancePriority(priority, for: axis) } + // MARK: Internal + + func installDoubleLayoutPassSizingLabel() { + doubleLayoutPassSizingLabel.removeFromSuperview() + addSubview(doubleLayoutPassSizingLabel) + subviews.first.map(doubleLayoutPassSizingLabel.sendSubviewToBack(_:)) + + doubleLayoutPassSizingLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + doubleLayoutPassSizingLabel.leadingAnchor.constraint( + equalTo: layoutMarginsGuide.leadingAnchor), + doubleLayoutPassSizingLabel.trailingAnchor.constraint( + equalTo: layoutMarginsGuide.trailingAnchor), + doubleLayoutPassSizingLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), + doubleLayoutPassSizingLabel.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor), + ]) + } + } // MARK: - WidthDependentIntrinsicContentHeightProviding @@ -110,7 +114,7 @@ final class DoubleLayoutPassSizingLabel: UILabel { // MARK: Internal override var intrinsicContentSize: CGSize { - guard let provider = provider else { + guard let provider else { preconditionFailure( "The sizing label's `provider` should not be `nil` for the duration of the its life") } @@ -126,4 +130,3 @@ final class DoubleLayoutPassSizingLabel: UILabel { private weak var provider: WidthDependentIntrinsicContentHeightProviding? } - diff --git a/Sources/Internal/FrameProvider.swift b/Sources/Internal/FrameProvider.swift index 7d5b4bd..deeae7c 100644 --- a/Sources/Internal/FrameProvider.swift +++ b/Sources/Internal/FrameProvider.swift @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import os.log import UIKit /// Provides frame and size information about all core layout items. The calendar is laid out lazily, starting with an initial known layout @@ -54,7 +55,11 @@ final class FrameProvider { dayOfWeekSize = CGSize(width: width, height: dayOfWeekHeight) if daySize.width <= 0 || daySize.height <= 0 { - print("Calendar metrics and size resulted in a negative-or-zero size of (\(daySize.debugDescription) points for each day. If ignored, this will cause incorrect / unexpected layouts.") + os_log( + "Calendar metrics and size resulted in a negative-or-zero size of (%@ points for each day. If ignored, this will cause incorrect / unexpected layouts.", + log: .default, + type: .debug, + daySize.debugDescription) } } @@ -215,8 +220,7 @@ final class FrameProvider { guard day.month == adjacentDay.month, abs(distanceFromAdjacentDay) == 1 - else - { + else { preconditionFailure("\(day) must be adjacent to \(adjacentDay) (one day apart, same month).") } @@ -395,7 +399,7 @@ final class FrameProvider { } private func heightOfMonth(_ month: Month, monthHeaderHeight: CGFloat) -> CGFloat { - let numberOfWeekRows = self.numberOfWeekRows(in: month) + let numberOfWeekRows = numberOfWeekRows(in: month) return monthHeaderHeight + content.monthDayInsets.top + heightOfDaysOfTheWeekRowInMonth() + @@ -408,9 +412,9 @@ final class FrameProvider { private func adjustedRowInMonth(for day: Day) -> Int { guard day >= content.dayRange.lowerBound else { preconditionFailure(""" - Cannot get the adjusted row for \(day), which is lower than the first day in the visible day - range (\(content.dayRange)). - """) + Cannot get the adjusted row for \(day), which is lower than the first day in the visible day + range (\(content.dayRange)). + """) } let missingRows: Int @@ -431,7 +435,7 @@ final class FrameProvider { // boundary month that's only showing a subset of days. private func numberOfWeekRows(in month: Month) -> Int { let rowOfLastDateInMonth: Int - if month == content.monthRange.lowerBound && month == content.monthRange.upperBound { + if month == content.monthRange.lowerBound, month == content.monthRange.upperBound { let firstDayOfOnlyMonth = content.dayRange.lowerBound let lastDayOfOnlyMonth = content.dayRange.upperBound let rowOfFirstDayOfOnlyMonth = adjustedRowInMonth(for: firstDayOfOnlyMonth) diff --git a/Sources/Internal/ItemView.swift b/Sources/Internal/ItemView.swift index dd6b231..3ab7c5a 100644 --- a/Sources/Internal/ItemView.swift +++ b/Sources/Internal/ItemView.swift @@ -15,6 +15,8 @@ import UIKit +// MARK: - ItemView + /// The container view for every visual item that can be displayed in the calendar. final class ItemView: UIView { @@ -32,23 +34,29 @@ final class ItemView: UIView { updateContent() } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: Internal + override class var layerClass: AnyClass { + CATransformLayer.self + } + let contentView: UIView var selectionHandler: (() -> Void)? + var itemType: VisibleItem.ItemType? + var calendarItemModel: AnyCalendarItemModel { didSet { guard calendarItemModel._itemViewDifferentiator == oldValue._itemViewDifferentiator else { preconditionFailure(""" - Cannot configure a reused `ItemView` with a calendar item model that was created with a - different instance of invariant view properties. - """) + Cannot configure a reused `ItemView` with a calendar item model that was created with a + different instance of invariant view properties. + """) } // Only update the content if it's different from the old one. @@ -58,12 +66,6 @@ final class ItemView: UIView { } } - var itemType: VisibleItem.ItemType? - - override class var layerClass: AnyClass { - CATransformLayer.self - } - override func layoutSubviews() { super.layoutSubviews() diff --git a/Sources/Internal/ItemViewReuseManager.swift b/Sources/Internal/ItemViewReuseManager.swift index ebbd228..adccf8c 100644 --- a/Sources/Internal/ItemViewReuseManager.swift +++ b/Sources/Internal/ItemViewReuseManager.swift @@ -91,9 +91,9 @@ final class ItemViewReuseManager { guard let previousView = viewsForVisibleItems[visibleItem] else { preconditionFailure(""" - `viewsForVisibleItems` must have a key for every member in - `visibleItemsForItemViewDifferentiators`'s values. - """) + `viewsForVisibleItems` must have a key for every member in + `visibleItemsForItemViewDifferentiators`'s values. + """) } view = previousView @@ -108,9 +108,9 @@ final class ItemViewReuseManager { guard let previousView = viewsForVisibleItems[previouslyVisibleItem] else { preconditionFailure(""" - `viewsForVisibleItems` must have a key for every member in - `visibleItemsForItemViewDifferentiators`'s values. - """) + `viewsForVisibleItems` must have a key for every member in + `visibleItemsForItemViewDifferentiators`'s values. + """) } view = previousView @@ -128,8 +128,7 @@ final class ItemViewReuseManager { isReusedViewSameAsPreviousView = false } } - } - else { + } else { // No previously-visible item is available for reuse, so create a new view. view = ItemView(initialCalendarItemModel: visibleItem.calendarItemModel) previousBackingVisibleItem = nil diff --git a/Sources/Internal/LayoutItem.swift b/Sources/Internal/LayoutItem.swift index e7ed648..26e1f05 100644 --- a/Sources/Internal/LayoutItem.swift +++ b/Sources/Internal/LayoutItem.swift @@ -23,7 +23,7 @@ struct LayoutItem { let frame: CGRect } -// MARK: - LayoutItem.ItemType +// MARK: LayoutItem.ItemType extension LayoutItem { @@ -45,33 +45,33 @@ extension LayoutItem { } -// MARK: Comparable +// MARK: - LayoutItem.ItemType + Comparable extension LayoutItem.ItemType: Comparable { static func < (lhs: LayoutItem.ItemType, rhs: LayoutItem.ItemType) -> Bool { switch (lhs, rhs) { - case let (.monthHeader(lhsMonth), .monthHeader(rhsMonth)): + case (.monthHeader(let lhsMonth), .monthHeader(let rhsMonth)): return lhsMonth < rhsMonth - case let (.monthHeader(lhsMonth), .dayOfWeekInMonth(_, rhsMonth)): + case (.monthHeader(let lhsMonth), .dayOfWeekInMonth(_, let rhsMonth)): return lhsMonth <= rhsMonth - case let (.monthHeader(lhsMonth), .day(rhsDay)): + case (.monthHeader(let lhsMonth), .day(let rhsDay)): return lhsMonth <= rhsDay.month - case let ( - .dayOfWeekInMonth(lhsPosition, lhsMonth), - .dayOfWeekInMonth(rhsPosition, rhsMonth)): + case ( + .dayOfWeekInMonth(let lhsPosition, let lhsMonth), + .dayOfWeekInMonth(let rhsPosition, let rhsMonth)): return lhsMonth < rhsMonth || (lhsMonth == rhsMonth && lhsPosition < rhsPosition) - case let (.dayOfWeekInMonth(_, lhsMonth), .monthHeader(rhsMonth)): + case (.dayOfWeekInMonth(_, let lhsMonth), .monthHeader(let rhsMonth)): return lhsMonth < rhsMonth - case let (.dayOfWeekInMonth(_, lhsMonth), .day(rhsDay)): + case (.dayOfWeekInMonth(_, let lhsMonth), .day(let rhsDay)): return lhsMonth <= rhsDay.month - case let (.day(lhsDay), .day(rhsDay)): + case (.day(let lhsDay), .day(let rhsDay)): return lhsDay < rhsDay - case let (.day(lhsDay), .monthHeader(rhsMonth)): + case (.day(let lhsDay), .monthHeader(let rhsMonth)): return lhsDay.month < rhsMonth - case let (.day(lhsDay), .dayOfWeekInMonth(_, rhsMonth)): + case (.day(let lhsDay), .dayOfWeekInMonth(_, let rhsMonth)): return lhsDay.month < rhsMonth } } diff --git a/Sources/Internal/LayoutItemTypeEnumerator.swift b/Sources/Internal/LayoutItemTypeEnumerator.swift index cbce778..f1ea88e 100644 --- a/Sources/Internal/LayoutItemTypeEnumerator.swift +++ b/Sources/Internal/LayoutItemTypeEnumerator.swift @@ -82,7 +82,7 @@ final class LayoutItemTypeEnumerator { let lastDateOfPreviousMonth = calendar.lastDate(of: previousMonth) return .day(calendar.day(containing: lastDateOfPreviousMonth)) - case let .dayOfWeekInMonth(position, month): + case .dayOfWeekInMonth(let position, let month): if position == .first { return .monthHeader(month) } else { @@ -114,7 +114,7 @@ final class LayoutItemTypeEnumerator { return .dayOfWeekInMonth(position: .first, month: month) } - case let .dayOfWeekInMonth(position, month): + case .dayOfWeekInMonth(let position, let month): if position == .last { return .day(firstDayInRange(in: month)) } else { diff --git a/Sources/Internal/PaginationHelpers.swift b/Sources/Internal/PaginationHelpers.swift index f34919c..beaa84d 100644 --- a/Sources/Internal/PaginationHelpers.swift +++ b/Sources/Internal/PaginationHelpers.swift @@ -16,11 +16,11 @@ import CoreGraphics enum PaginationHelpers { - + static func closestPageIndex(forOffset offset: CGFloat, pageSize: CGFloat) -> Int { Int((offset / pageSize).rounded()) } - + /// Returns the closest valid page offset to the target offset. This function is used when the horizontal pagination resting affinity is /// set to `.atPositionsClosestToTargetOffset`. static func closestPageOffset( @@ -33,9 +33,9 @@ enum PaginationHelpers { let closestTargetPageIndex = closestPageIndex(forOffset: targetOffset, pageSize: pageSize) let proposedFinalOffset = CGFloat(closestTargetPageIndex) * pageSize - if velocity > 0 && proposedFinalOffset < touchUpOffset { + if velocity > 0, proposedFinalOffset < touchUpOffset { return proposedFinalOffset + pageSize - } else if velocity < 0 && proposedFinalOffset > touchUpOffset { + } else if velocity < 0, proposedFinalOffset > touchUpOffset { return proposedFinalOffset - pageSize } else { return proposedFinalOffset @@ -52,17 +52,17 @@ enum PaginationHelpers { -> CGFloat { let closestTargetPageIndex = closestPageIndex(forOffset: targetOffset, pageSize: pageSize) - + let pageIndex: Int if velocity > 0 || closestTargetPageIndex > previousPageIndex { pageIndex = previousPageIndex + 1 - } else if velocity < 0 || closestTargetPageIndex < previousPageIndex { + } else if velocity < 0 || closestTargetPageIndex < previousPageIndex { pageIndex = previousPageIndex - 1 } else { pageIndex = previousPageIndex } - + return CGFloat(pageIndex) * pageSize } - + } diff --git a/Sources/Internal/ScreenPixelAlignment.swift b/Sources/Internal/ScreenPixelAlignment.swift index d6462d2..ae79f77 100644 --- a/Sources/Internal/ScreenPixelAlignment.swift +++ b/Sources/Internal/ScreenPixelAlignment.swift @@ -21,7 +21,7 @@ extension CGFloat { func alignedToPixel(forScreenWithScale scale: CGFloat) -> CGFloat { (self * scale).rounded() / scale } - + /// Tests `self` for approximate equality using the threshold value. For example, 1.48 equals 1.52 if the threshold is 0.05. /// `threshold` will be treated as a positive value by taking its absolute value. func isEqual(to rhs: CGFloat, threshold: CGFloat) -> Bool { diff --git a/Sources/Internal/ScrollMetricsMutator.swift b/Sources/Internal/ScrollMetricsMutator.swift index 8fcdcca..253582d 100644 --- a/Sources/Internal/ScrollMetricsMutator.swift +++ b/Sources/Internal/ScrollMetricsMutator.swift @@ -55,13 +55,13 @@ final class ScrollMetricsMutator { func updateScrollBoundaries(minimumScrollOffset: CGFloat?, maximumScrollOffset: CGFloat?) { let originalOffset = scrollMetricsProvider.offset(for: scrollAxis) - if let minimumScrollOffset = minimumScrollOffset { + if let minimumScrollOffset { scrollMetricsProvider.setStartInset(to: -minimumScrollOffset, for: scrollAxis) } else { scrollMetricsProvider.setStartInset(to: Self.BoundlessStartInset, for: scrollAxis) } - if let maximumScrollOffset = maximumScrollOffset { + if let maximumScrollOffset { let size = scrollMetricsProvider.size(for: scrollAxis) scrollMetricsProvider.setEndInset(to: -(size - maximumScrollOffset), for: scrollAxis) } else { @@ -83,7 +83,7 @@ final class ScrollMetricsMutator { } // MARK: Private - + // The scrollable region is from (-ContentSize, +ContentSize), which is enough to scroll past over // three-billion years in a vertically-scrolling calendar. Increasing this value by another order // of magnitude will start to cause rounding in the 3rd digit after the decimal point, due to a @@ -121,13 +121,13 @@ protocol ScrollMetricsProvider: AnyObject { func endInset(for scrollAxis: ScrollAxis) -> CGFloat func setEndInset(to inset: CGFloat, for scrollAxis: ScrollAxis) - + func minimumOffset(for scrollAxis: ScrollAxis) -> CGFloat func maximumOffset(for scrollAxis: ScrollAxis) -> CGFloat } -// MARK: UIScrollView+ScrollMetricsProvider +// MARK: - UIScrollView + ScrollMetricsProvider extension UIScrollView: ScrollMetricsProvider { @@ -186,7 +186,7 @@ extension UIScrollView: ScrollMetricsProvider { case .horizontal: contentInset.right = inset } } - + func minimumOffset(for scrollAxis: ScrollAxis) -> CGFloat { switch scrollAxis { case .vertical: return -contentInset.top diff --git a/Sources/Internal/ScrollToItemContext.swift b/Sources/Internal/ScrollToItemContext.swift index 4691d14..acd0a31 100644 --- a/Sources/Internal/ScrollToItemContext.swift +++ b/Sources/Internal/ScrollToItemContext.swift @@ -23,7 +23,7 @@ struct ScrollToItemContext { let animated: Bool } -// MARK: - ScrollToItemContext.TargetItem +// MARK: ScrollToItemContext.TargetItem extension ScrollToItemContext { @@ -34,7 +34,7 @@ extension ScrollToItemContext { } -// MARK: - ScrollToItemContext.PositionRelativeToVisibleBounds +// MARK: ScrollToItemContext.PositionRelativeToVisibleBounds extension ScrollToItemContext { diff --git a/Sources/Internal/SubviewInsertionIndexTracker.swift b/Sources/Internal/SubviewInsertionIndexTracker.swift index 81d700b..2abcb11 100644 --- a/Sources/Internal/SubviewInsertionIndexTracker.swift +++ b/Sources/Internal/SubviewInsertionIndexTracker.swift @@ -46,7 +46,7 @@ final class SubviewInsertionIndexTracker { pinnedDaysOfWeekRowBackgroundEndIndex += 1 pinnedDayOfWeekItemsEndIndex += 1 pinnedDaysOfWeekRowSeparatorEndIndex += 1 - + case .dayRange: index = dayRangeItemsEndIndex dayRangeItemsEndIndex += 1 diff --git a/Sources/Internal/VisibleItem.swift b/Sources/Internal/VisibleItem.swift index f2806e6..6c9c399 100644 --- a/Sources/Internal/VisibleItem.swift +++ b/Sources/Internal/VisibleItem.swift @@ -74,7 +74,7 @@ extension VisibleItem: Hashable { } -// MARK: - VisibleItem.ItemType +// MARK: VisibleItem.ItemType extension VisibleItem { diff --git a/Sources/Internal/VisibleItemsProvider.swift b/Sources/Internal/VisibleItemsProvider.swift index 71dee75..bf4124a 100644 --- a/Sources/Internal/VisibleItemsProvider.swift +++ b/Sources/Internal/VisibleItemsProvider.swift @@ -306,8 +306,7 @@ final class VisibleItemsProvider { let upperBoundMonth = calendar.month(byAddingMonths: 1, to: visibleMonthRange.upperBound) let monthRange = lowerBoundMonth...upperBoundMonth - let handleItem: (LayoutItem, Bool, inout Bool) -> Void = - { layoutItem, isLookingBackwards, shouldStop in + let handleItem: (LayoutItem, Bool, inout Bool) -> Void = { layoutItem, isLookingBackwards, shouldStop in let month: Month let calendarItemModel: AnyCalendarItemModel switch layoutItem.itemType { @@ -504,8 +503,8 @@ final class VisibleItemsProvider { monthHeaderHeight: monthHeaderHeight) } else { preconditionFailure(""" - Could not determine the origin of the month containing the layout item type \(itemType). - """) + Could not determine the origin of the month containing the layout item type \(itemType). + """) } context.originsForMonths[itemType.month] = monthOrigin @@ -514,7 +513,7 @@ final class VisibleItemsProvider { } private func monthHeaderHeight(for month: Month, context: inout VisibleItemsContext) -> CGFloat { - return context.heightsForVisibleMonthHeaders.value( + context.heightsForVisibleMonthHeaders.value( for: month, missingValueProvider: { let monthHeaderItemModel = content.monthHeaderItemProvider(month) @@ -551,7 +550,7 @@ final class VisibleItemsProvider { context: inout VisibleItemsContext) -> LayoutItem { - let monthOrigin = self.monthOrigin( + let monthOrigin = monthOrigin( for: itemType, lastHandledLayoutItem: lastHandledLayoutItem, monthHeaderHeight: monthHeaderHeight, @@ -604,31 +603,30 @@ final class VisibleItemsProvider { { guard dayRange.contains(day) else { preconditionFailure(""" - Cannot create day range items if the provided `day` (\(day)) is not contained in `dayRange` - (\(dayRange)). - """) + Cannot create day range items if the provided `day` (\(day)) is not contained in `dayRange` + (\(dayRange)). + """) } var daysAndFrames = [(day: Day, frame: CGRect)]() var boundingUnionRectOfDayFrames = frame - let handleItem: (LayoutItem, Bool, inout Bool) -> Void = - { layoutItem, isLookingBackwards, shouldStop in - guard case .day(let day) = layoutItem.itemType else { return } - guard dayRange.contains(day) else { - shouldStop = true - return - } - - let frame = layoutItem.frame - if isLookingBackwards { - daysAndFrames.insert((day, frame), at: 0) - } else { - daysAndFrames.append((day, frame)) - } + let handleItem: (LayoutItem, Bool, inout Bool) -> Void = { layoutItem, isLookingBackwards, shouldStop in + guard case .day(let day) = layoutItem.itemType else { return } + guard dayRange.contains(day) else { + shouldStop = true + return + } - boundingUnionRectOfDayFrames = boundingUnionRectOfDayFrames.union(frame) + let frame = layoutItem.frame + if isLookingBackwards { + daysAndFrames.insert((day, frame), at: 0) + } else { + daysAndFrames.append((day, frame)) } + boundingUnionRectOfDayFrames = boundingUnionRectOfDayFrames.union(frame) + } + let dayLayoutItem = LayoutItem(itemType: .day(day), frame: frame) var lastHandledLayoutItemEnumeratingBackwards = dayLayoutItem @@ -665,8 +663,7 @@ final class VisibleItemsProvider { daysAndFrames: daysAndFrames.map { ( $0.day, - $0.frame.applying(frameToBoundsTransform).alignedToPixels(forScreenWithScale: scale) - ) + $0.frame.applying(frameToBoundsTransform).alignedToPixels(forScreenWithScale: scale)) }, boundingUnionRectOfDayFrames: boundingUnionRectOfDayFrames .applying(frameToBoundsTransform) @@ -715,7 +712,7 @@ final class VisibleItemsProvider { _ layoutItem: LayoutItem, inBounds bounds: CGRect, extendedBounds: CGRect, - isLookingBackwards: Bool, + isLookingBackwards _: Bool, monthHeaderHeight: CGFloat, context: inout VisibleItemsContext, shouldStop: inout Bool) @@ -803,7 +800,7 @@ final class VisibleItemsProvider { monthHeaderHeight: monthHeaderHeight))) } - case let .dayOfWeekInMonth(dayOfWeekPosition, month): + case .dayOfWeekInMonth(let dayOfWeekPosition, let month): calendarItemModel = context.calendarItemModelCache.value( for: itemType, missingValueProvider: { @@ -826,7 +823,7 @@ final class VisibleItemsProvider { previousCalendarItemModelCache?[.dayBackground(day)] ?? content.dayBackgroundItemProvider?(day) }) - if let dayBackgroundItemModel = dayBackgroundItemModel { + if let dayBackgroundItemModel { context.visibleItems.insert( VisibleItem( calendarItemModel: dayBackgroundItemModel, @@ -913,8 +910,7 @@ final class VisibleItemsProvider { guard !context.handledDayRanges.contains(dayRange), dayRange.contains(day) - else - { + else { continue } @@ -933,13 +929,12 @@ final class VisibleItemsProvider { private func handleDayRange( _ dayRange: DayRange, with dayRangeLayoutContext: _DayRangeLayoutContext, - inBounds bounds: CGRect, + inBounds _: CGRect, context: inout VisibleItemsContext) { guard let dayRangeItemProvider = content.dayRangesAndItemProvider?.dayRangeItemProvider - else - { + else { preconditionFailure( "`content.dayRangesAndItemProvider` cannot be nil when handling a day range.") } @@ -1019,8 +1014,7 @@ final class VisibleItemsProvider { private func handleOverlayItemsIfNeeded(bounds: CGRect, context: inout VisibleItemsContext) { guard let (overlaidItemLocations, itemModelProvider) = content.overlaidItemLocationsAndItemProvider - else - { + else { return } @@ -1030,8 +1024,7 @@ final class VisibleItemsProvider { for: overlaidItemLocation, inBounds: bounds, context: &context) - else - { + else { continue } @@ -1224,7 +1217,7 @@ final class VisibleItemsProvider { .init(translationX: bounds.minX - contentStartBoundary + layoutMargins.leading, y: 0)) } else if let contentEndBoundary = context.contentEndBoundary, - contentEndBoundary <= bounds.maxX + contentEndBoundary <= bounds.maxX { return proposedFrame.applying( .init(translationX: bounds.maxX - contentEndBoundary - layoutMargins.trailing, y: 0)) @@ -1236,7 +1229,7 @@ final class VisibleItemsProvider { } -// MARK: VisibleItemsContext +// MARK: - VisibleItemsContext private struct VisibleItemsContext { var centermostLayoutItem: LayoutItem @@ -1291,9 +1284,9 @@ private struct _DayRangeLayoutContext { // MARK: CGPoint Distance Extension -private extension CGPoint { +extension CGPoint { - func distance(to otherPoint: CGPoint) -> CGFloat { + fileprivate func distance(to otherPoint: CGPoint) -> CGFloat { sqrt(pow(otherPoint.x - x, 2) + pow(otherPoint.y - y, 2)) } diff --git a/Sources/Public/CalendarItemModel.swift b/Sources/Public/CalendarItemModel.swift index b95875d..d965e40 100644 --- a/Sources/Public/CalendarItemModel.swift +++ b/Sources/Public/CalendarItemModel.swift @@ -120,7 +120,7 @@ extension CalendarItemModel where ViewRepresentable.Content == Never { invariantViewProperties: invariantViewProperties) self.invariantViewProperties = invariantViewProperties - self.content = nil + content = nil } } diff --git a/Sources/Public/CalendarView.swift b/Sources/Public/CalendarView.swift index 6cc34d0..635bb7d 100644 --- a/Sources/Public/CalendarView.swift +++ b/Sources/Public/CalendarView.swift @@ -57,28 +57,6 @@ public final class CalendarView: UIView { commonInit() } - private func commonInit() { - if #available(iOS 13.0, *) { - backgroundColor = .systemBackground - } else { - backgroundColor = .white - } - - // Must be the first subview so that `UINavigationController` can monitor its scroll position - // and make navigation bars opaque on scroll. - insertSubview(scrollView, at: 0) - - installDoubleLayoutPassSizingLabel() - - setContent(content) - - NotificationCenter.default.addObserver( - self, - selector: #selector(accessibilityElementFocused(_:)), - name: UIAccessibility.elementFocusedNotification, - object: nil) - } - // MARK: Public /// A closure (that is retained) that is invoked whenever a day is selected. It is the responsibility of your feature code to decide what to @@ -86,6 +64,15 @@ public final class CalendarView: UIView { /// `dayItemProvider` closure to add specific "selected" styling to a particular day view. public var daySelectionHandler: ((Day) -> Void)? + /// A closure (that is retained) that is invoked inside `scrollViewDidScroll(_:)` + public var didScroll: ((_ visibleDayRange: DayRange, _ isUserDragging: Bool) -> Void)? + + /// A closure (that is retained) that is invoked inside `scrollViewDidEndDragging(_: willDecelerate:)`. + public var didEndDragging: ((_ visibleDayRange: DayRange, _ willDecelerate: Bool) -> Void)? + + /// A closure (that is retained) that is invoked inside `scrollViewDidEndDecelerating(_:)`. + public var didEndDecelerating: ((_ visibleDayRange: DayRange) -> Void)? + /// A closure (that is retained) that is invoked during a multiple-selection-drag-gesture. Multiple selection is initiated with a long press, /// followed by a drag / pan. As the gesture crosses over more days in the calendar, this handler will be invoked with each new day. It /// is the responsibility of your feature code to decide what to do with this stream of days. For example, you might convert them to @@ -96,15 +83,6 @@ public final class CalendarView: UIView { } } - /// A closure (that is retained) that is invoked inside `scrollViewDidScroll(_:)` - public var didScroll: ((_ visibleDayRange: DayRange, _ isUserDragging: Bool) -> Void)? - - /// A closure (that is retained) that is invoked inside `scrollViewDidEndDragging(_: willDecelerate:)`. - public var didEndDragging: ((_ visibleDayRange: DayRange, _ willDecelerate: Bool) -> Void)? - - /// A closure (that is retained) that is invoked inside `scrollViewDidEndDecelerating(_:)`. - public var didEndDecelerating: ((_ visibleDayRange: DayRange) -> Void)? - /// Whether or not the calendar's scroll view is currently over-scrolling, i.e, whether the rubber-banding or bouncing effect is in /// progress. public var isOverScrolling: Bool { @@ -312,7 +290,7 @@ public final class CalendarView: UIView { /// - Returns: An accessibility element associated with the specified `date`, or `nil` if one cannot be found. public func accessibilityElementForVisibleDate(_ date: Date) -> Any? { let day = calendar.day(containing: date) - guard let visibleDayRange = visibleDayRange, visibleDayRange.contains(day) else { return nil } + guard let visibleDayRange, visibleDayRange.contains(day) else { return nil } for (visibleItem, visibleView) in visibleViewsForVisibleItems { guard case .layoutItemType(.day(day)) = visibleItem.itemType else { continue } @@ -341,9 +319,9 @@ public final class CalendarView: UIView { let month = calendar.month(containing: dateInTargetMonth) guard content.monthRange.contains(month) else { assertionFailure(""" - Attempted to scroll to month \(month), which is out of bounds of the total date range - \(content.monthRange). - """) + Attempted to scroll to month \(month), which is out of bounds of the total date range + \(content.monthRange). + """) return } @@ -382,9 +360,9 @@ public final class CalendarView: UIView { let day = calendar.day(containing: dateInTargetDay) guard content.dayRange.contains(day) else { assertionFailure(""" - Attempted to scroll to day \(day), which is out of bounds of the total date range - \(content.dayRange). - """) + Attempted to scroll to day \(day), which is out of bounds of the total date range + \(content.dayRange). + """) return } @@ -498,7 +476,7 @@ public final class CalendarView: UIView { newOffset = nil } - if let newOffset = newOffset { + if let newOffset { scrollView.performWithoutNotifyingDelegate { // Passing `false` for `animated` is necessary to stop the in-flight deceleration animation UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: { @@ -539,7 +517,7 @@ public final class CalendarView: UIView { private lazy var scrollViewDelegate = ScrollViewDelegate(calendarView: self) private lazy var gestureRecognizerDelegate = GestureRecognizerDelegate(calendarView: self) - + // Necessary to work around a `UIScrollView` behavior difference on Mac. See `scrollViewDidScroll` // and `preventLargeOverScrollIfNeeded` for more context. private lazy var isRunningOnMac: Bool = { @@ -548,11 +526,10 @@ public final class CalendarView: UIView { return true } } - + return false }() - private var isReadyForLayout: Bool { // There's no reason to attempt layout unless we have a non-zero `bounds.size`. We'll have a // non-zero size once the `frame` is set to something non-zero, either manually or via the @@ -638,6 +615,28 @@ public final class CalendarView: UIView { } } + private func commonInit() { + if #available(iOS 13.0, *) { + backgroundColor = .systemBackground + } else { + backgroundColor = .white + } + + // Must be the first subview so that `UINavigationController` can monitor its scroll position + // and make navigation bars opaque on scroll. + insertSubview(scrollView, at: 0) + + installDoubleLayoutPassSizingLabel() + + setContent(content) + + NotificationCenter.default.addObserver( + self, + selector: #selector(accessibilityElementFocused(_:)), + name: UIAccessibility.elementFocusedNotification, + object: nil) + } + private func anchorLayoutItem( for scrollToItemContext: ScrollToItemContext, visibleItemsProvider: VisibleItemsProvider) @@ -673,7 +672,7 @@ public final class CalendarView: UIView { for targetItem: ScrollToItemContext.TargetItem) -> ScrollToItemContext.PositionRelativeToVisibleBounds? { - guard let visibleItemsDetails = visibleItemsDetails else { return nil } + guard let visibleItemsDetails else { return nil } switch targetItem { case .month(let month): @@ -710,7 +709,7 @@ public final class CalendarView: UIView { } let anchorLayoutItem: LayoutItem - if let scrollToItemContext = scrollToItemContext, !scrollToItemContext.animated { + if let scrollToItemContext, !scrollToItemContext.animated { anchorLayoutItem = self.anchorLayoutItem( for: scrollToItemContext, visibleItemsProvider: visibleItemsProvider) @@ -763,7 +762,7 @@ public final class CalendarView: UIView { private func updateVisibleViews( withVisibleItems visibleItems: Set, - previouslyVisibleItems: Set) + previouslyVisibleItems _: Set) { var viewsToHideForVisibleItems = visibleViewsForVisibleItems visibleViewsForVisibleItems.removeAll(keepingCapacity: true) @@ -785,7 +784,7 @@ public final class CalendarView: UIView { visibleViewsForVisibleItems[visibleItem] = view - if let previousBackingVisibleItem = previousBackingVisibleItem { + if let previousBackingVisibleItem { // Don't hide views that were reused viewsToHideForVisibleItems.removeValue(forKey: previousBackingVisibleItem) } @@ -858,14 +857,13 @@ public final class CalendarView: UIView { @objc private func scrollToItemDisplayLinkFired() { guard - let scrollToItemContext = scrollToItemContext, + let scrollToItemContext, let animationStartTime = scrollToItemAnimationStartTime - else - { + else { preconditionFailure(""" - Expected `scrollToItemContext`, `animationStartTime`, and `scrollMetricsMutator` to be - non-nil when animating toward an item. - """) + Expected `scrollToItemContext`, `animationStartTime`, and `scrollMetricsMutator` to be + non-nil when animating toward an item. + """) } guard scrollToItemContext.animated else { @@ -941,8 +939,7 @@ public final class CalendarView: UIView { let framesForVisibleMonths = visibleItemsDetails?.framesForVisibleMonths, let firstVisibleMonth = visibleMonthRange?.lowerBound, let frameOfFirstVisibleMonth = framesForVisibleMonths[firstVisibleMonth] - else - { + else { return } @@ -1000,7 +997,7 @@ public final class CalendarView: UIView { switch gestureRecognizer.state { case .ended, .cancelled, .failed: - if let lastmultiDaySelectionDay = lastmultiDaySelectionDay { + if let lastmultiDaySelectionDay { multiDaySelectionDragHandler?(lastmultiDaySelectionDay, gestureRecognizer.state) } lastmultiDaySelectionDay = nil @@ -1021,8 +1018,7 @@ public final class CalendarView: UIView { let itemView = subview as? ItemView, case .layoutItemType(.day(let day)) = itemView.itemType, itemView.frame.contains(locationInScrollView) - else - { + else { continue } intersectedDay = day @@ -1150,6 +1146,8 @@ extension CalendarView: WidthDependentIntrinsicContentHeightProviding { extension CalendarView { + // MARK: Public + public override var isAccessibilityElement: Bool { get { false } set { } @@ -1161,10 +1159,9 @@ extension CalendarView { return cachedAccessibilityElements } guard - let visibleItemsDetails = visibleItemsDetails, - let visibleMonthRange = visibleMonthRange - else - { + let visibleItemsDetails, + let visibleMonthRange + else { return nil } @@ -1176,9 +1173,9 @@ extension CalendarView { for visibleItem in visibleItems { guard case .layoutItemType = visibleItem.itemType else { assertionFailure(""" - Only visible calendar items with itemType == .layoutItemType should be considered for - use as an accessibility element. - """) + Only visible calendar items with itemType == .layoutItemType should be considered for + use as an accessibility element. + """) continue } let element: Any @@ -1189,8 +1186,7 @@ extension CalendarView { let accessibilityElement = OffScreenCalendarItemAccessibilityElement( correspondingItem: visibleItem, scrollViewContainer: scrollView) - else - { + else { continue } @@ -1223,8 +1219,7 @@ extension CalendarView { from: firstVisibleMonthDate, to: lastVisibleMonthDate) .month - else - { + else { return false } @@ -1267,12 +1262,14 @@ extension CalendarView { return true } + // MARK: Private + @objc private func accessibilityElementFocused(_ notification: NSNotification) { guard let element = notification.userInfo?[UIAccessibility.focusedElementUserInfoKey] else { return } - + focusedAccessibilityElement = element if let contentView = element as? UIView, let itemView = contentView.superview as? ItemView { @@ -1330,14 +1327,14 @@ private final class ScrollViewDelegate: NSObject, UIScrollViewDelegate { } func scrollViewDidEndDragging( - _ scrollView: UIScrollView, + _: UIScrollView, willDecelerate decelerate: Bool) { guard let calendarView, let visibleDayRange = calendarView.visibleDayRange else { return } calendarView.didEndDragging?(visibleDayRange, decelerate) } - func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + func scrollViewDidEndDecelerating(_: UIScrollView) { guard let calendarView, let visibleDayRange = calendarView.visibleDayRange else { return } calendarView.didEndDecelerating?(visibleDayRange) } @@ -1347,8 +1344,7 @@ private final class ScrollViewDelegate: NSObject, UIScrollViewDelegate { let calendarView, case .horizontal(let options) = calendarView.content.monthsLayout, case .paginatedScrolling = options.scrollingBehavior - else - { + else { return } @@ -1369,8 +1365,7 @@ private final class ScrollViewDelegate: NSObject, UIScrollViewDelegate { let calendarView, case .horizontal(let options) = calendarView.content.monthsLayout, case .paginatedScrolling(let paginationConfiguration) = options.scrollingBehavior - else - { + else { return } @@ -1382,8 +1377,8 @@ private final class ScrollViewDelegate: NSObject, UIScrollViewDelegate { case .atPositionsAdjacentToPrevious: guard let previousPageIndex = calendarView.previousPageIndex else { preconditionFailure(""" - `previousPageIndex` was accessed before being set in `scrollViewWillBeginDragging`. - """) + `previousPageIndex` was accessed before being set in `scrollViewWillBeginDragging`. + """) } targetContentOffset.pointee.x = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: previousPageIndex, @@ -1400,7 +1395,7 @@ private final class ScrollViewDelegate: NSObject, UIScrollViewDelegate { } } - func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { + func scrollViewShouldScrollToTop(_: UIScrollView) -> Bool { guard let calendarView else { return false } if calendarView.content.monthsLayout.scrollsToFirstMonthOnStatusBarTap { @@ -1475,7 +1470,7 @@ private final class NoContentInsetAdjustmentScrollView: UIScrollView { contentInsetAdjustmentBehavior = .never } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -1491,10 +1486,10 @@ private final class NoContentInsetAdjustmentScrollView: UIScrollView { // MARK: Scroll View Silent Updating -private extension UIScrollView { +extension UIScrollView { - func performWithoutNotifyingDelegate(_ operations: () -> Void) { - let delegate = self.delegate + fileprivate func performWithoutNotifyingDelegate(_ operations: () -> Void) { + let delegate = delegate self.delegate = nil operations() diff --git a/Sources/Public/CalendarViewProxy.swift b/Sources/Public/CalendarViewProxy.swift index 76e3855..f30f8fb 100644 --- a/Sources/Public/CalendarViewProxy.swift +++ b/Sources/Public/CalendarViewProxy.swift @@ -81,7 +81,7 @@ public final class CalendarViewProxy: ObservableObject { weak var _calendarView: CalendarView? { didSet { - if oldValue != nil && _calendarView != oldValue { + if oldValue != nil, _calendarView != oldValue { fatalError("Attempted to use an existing `CalendarViewProxy` instance with a new `CalendarViewRepresentable`.") } } diff --git a/Sources/Public/CalendarViewRepresentable.swift b/Sources/Public/CalendarViewRepresentable.swift index 2b2b7b9..53d0e86 100644 --- a/Sources/Public/CalendarViewRepresentable.swift +++ b/Sources/Public/CalendarViewRepresentable.swift @@ -63,14 +63,14 @@ public struct CalendarViewRepresentable: UIViewRepresentable { // MARK: Public - public func makeUIView(context: Context) -> CalendarView { + public func makeUIView(context _: Context) -> CalendarView { let calendarView = CalendarView(initialContent: makeContent()) calendarView.directionalLayoutMargins = .zero proxy?._calendarView = calendarView return calendarView } - public func updateUIView(_ calendarView: CalendarView, context: Context) { + public func updateUIView(_ calendarView: CalendarView, context _: Context) { calendarView.backgroundColor = backgroundColor ?? calendarView.backgroundColor calendarView.directionalLayoutMargins = layoutMargins ?? calendarView.directionalLayoutMargins @@ -99,10 +99,11 @@ public struct CalendarViewRepresentable: UIViewRepresentable { fileprivate var daysOfTheWeekRowSeparatorOptions: DaysOfTheWeekRowSeparatorOptions? fileprivate var monthHeaderItemProvider: ((Month) -> AnyCalendarItemModel)? - fileprivate var dayOfWeekItemProvider: (( - _ month: Month?, - _ weekdayIndex: Int) - -> AnyCalendarItemModel)? + fileprivate var dayOfWeekItemProvider: ( + ( + _ month: Month?, + _ weekdayIndex: Int) + -> AnyCalendarItemModel)? fileprivate var dayItemProvider: ((Day) -> AnyCalendarItemModel)? fileprivate var dayBackgroundItemProvider: ((Day) -> AnyCalendarItemModel?)? fileprivate var monthBackgroundItemProvider: ((MonthLayoutContext) -> AnyCalendarItemModel?)? @@ -579,7 +580,7 @@ extension CalendarViewRepresentable { for dateRanges: Set>, _ dayRangeItemProvider: @escaping ( _ dayRangeLayoutContext: DayRangeLayoutContext) - -> AnyCalendarItemModel) + -> AnyCalendarItemModel) -> Self { var view = self @@ -635,7 +636,7 @@ extension CalendarViewRepresentable { for overlaidItemLocations: Set, _ overlayItemProvider: @escaping ( _ overlayLayoutContext: OverlayLayoutContext) - -> AnyCalendarItemModel) + -> AnyCalendarItemModel) -> Self { var view = self @@ -723,7 +724,7 @@ extension CalendarViewRepresentable { } public func onScroll( - _ scrollHandler: @escaping (_ visibleDayRange: DayRange, _ isUserDragging: Bool) -> Void) + _ scrollHandler: @escaping (_ visibleDayRange: DayRange, _ isUserDragging: Bool) -> Void) -> Self { var view = self @@ -836,7 +837,6 @@ extension CalendarViewRepresentable { return CGSize(width: proposedWidth, height: size.height) } - } } diff --git a/Sources/Public/DayRange.swift b/Sources/Public/DayRange.swift index d118cb8..f3fe7c5 100644 --- a/Sources/Public/DayRange.swift +++ b/Sources/Public/DayRange.swift @@ -21,8 +21,6 @@ public typealias DayRange = ClosedRange extension DayRange { - // MARK: Lifecycle - /// Instantiates a `DayRange` that encapsulates the `dateRange` in the `calendar` as closely as possible. For example, /// a date range of [2020-05-20T23:59:59, 2021-01-01T00:00:00] will result in a day range of [2020-05-20, 2021-01-01]. init(containing dateRange: ClosedRange, in calendar: Calendar) { diff --git a/Sources/Public/DayRangeLayoutContext.swift b/Sources/Public/DayRangeLayoutContext.swift index beabced..53c1897 100644 --- a/Sources/Public/DayRangeLayoutContext.swift +++ b/Sources/Public/DayRangeLayoutContext.swift @@ -33,7 +33,7 @@ public struct DayRangeLayoutContext: Hashable { public let boundingUnionRectOfDayFrames: CGRect public static func == (lhs: DayRangeLayoutContext, rhs: DayRangeLayoutContext) -> Bool { - return lhs.dayRange == rhs.dayRange && + lhs.dayRange == rhs.dayRange && lhs.daysAndFrames.elementsEqual( rhs.daysAndFrames, by: { $0.day == $1.day && $0.frame == $0.frame }) && diff --git a/Sources/Public/ItemViews/DayOfWeekView.swift b/Sources/Public/ItemViews/DayOfWeekView.swift index bf0d727..cecf165 100644 --- a/Sources/Public/ItemViews/DayOfWeekView.swift +++ b/Sources/Public/ItemViews/DayOfWeekView.swift @@ -48,7 +48,7 @@ public final class DayOfWeekView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -117,7 +117,7 @@ extension DayOfWeekView { } -// MARK: - DayView.Content +// MARK: DayOfWeekView.Content extension DayOfWeekView { @@ -142,7 +142,7 @@ extension DayOfWeekView { } -// MARK: - DayOfWeekView.InvariantViewProperties +// MARK: DayOfWeekView.InvariantViewProperties extension DayOfWeekView { @@ -214,7 +214,7 @@ extension DayOfWeekView { } -// MARK: - DayOfWeekView + CalendarItemViewRepresentable +// MARK: CalendarItemViewRepresentable extension DayOfWeekView: CalendarItemViewRepresentable { diff --git a/Sources/Public/ItemViews/DayView.swift b/Sources/Public/ItemViews/DayView.swift index 2af1859..e0775ce 100644 --- a/Sources/Public/ItemViews/DayView.swift +++ b/Sources/Public/ItemViews/DayView.swift @@ -39,7 +39,7 @@ public final class DayView: UIView { supportsPointerInteraction = false highlightLayer = nil - case let .enabled(_, _supportsPointerInteraction): + case .enabled(_, let _supportsPointerInteraction): isUserInteractionEnabled = true supportsPointerInteraction = _supportsPointerInteraction @@ -74,7 +74,7 @@ public final class DayView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -119,7 +119,7 @@ public final class DayView: UIView { setHighlightLayerVisibility(isHidden: false, animated: true) if - case let .enabled(playsHapticsOnTouchDown, _) = invariantViewProperties.interaction, + case .enabled(let playsHapticsOnTouchDown, _) = invariantViewProperties.interaction, playsHapticsOnTouchDown { feedbackGenerator = UISelectionFeedbackGenerator() @@ -162,7 +162,7 @@ public final class DayView: UIView { private var feedbackGenerator: UISelectionFeedbackGenerator? private func setHighlightLayerVisibility(isHidden: Bool, animated: Bool) { - guard let highlightLayer = highlightLayer else { return } + guard let highlightLayer else { return } let opacity: Float = isHidden ? 0 : 1 @@ -203,7 +203,7 @@ extension DayView: UIPointerInteractionDelegate { public func pointerInteraction( _ interaction: UIPointerInteraction, - styleFor region: UIPointerRegion) + styleFor _: UIPointerRegion) -> UIPointerStyle? { guard let interactionView = interaction.view else { return nil } @@ -218,7 +218,7 @@ extension DayView: UIPointerInteractionDelegate { } -// MARK: - DayView.Content +// MARK: DayView.Content extension DayView { @@ -250,7 +250,7 @@ extension DayView { } -// MARK: - DayView.InvariantViewProperties +// MARK: DayView.InvariantViewProperties extension DayView { @@ -343,7 +343,7 @@ extension DayView { } -// MARK: - DayView + CalendarItemViewRepresentable +// MARK: CalendarItemViewRepresentable extension DayView: CalendarItemViewRepresentable { diff --git a/Sources/Public/ItemViews/MonthGridBackgroundView.swift b/Sources/Public/ItemViews/MonthGridBackgroundView.swift index c7481ef..9466449 100644 --- a/Sources/Public/ItemViews/MonthGridBackgroundView.swift +++ b/Sources/Public/ItemViews/MonthGridBackgroundView.swift @@ -17,7 +17,7 @@ import UIKit // MARK: - MonthGridBackgroundView -/// A background grid view that draws separator lines between all days in a month. +/// A background grid view that draws separator lines between all days in a month. public final class MonthGridBackgroundView: UIView { // MARK: Lifecycle diff --git a/Sources/Public/ItemViews/MonthHeaderView.swift b/Sources/Public/ItemViews/MonthHeaderView.swift index e31bcc4..20aafa2 100644 --- a/Sources/Public/ItemViews/MonthHeaderView.swift +++ b/Sources/Public/ItemViews/MonthHeaderView.swift @@ -49,7 +49,7 @@ public final class MonthHeaderView: UIView { } @available(*, unavailable) - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -83,7 +83,7 @@ extension MonthHeaderView { } -// MARK: - DayView.Content +// MARK: MonthHeaderView.Content extension MonthHeaderView { @@ -109,7 +109,7 @@ extension MonthHeaderView { } -// MARK: - MonthHeaderView.InvariantViewProperties +// MARK: MonthHeaderView.InvariantViewProperties extension MonthHeaderView { @@ -165,7 +165,7 @@ extension MonthHeaderView { } -// MARK: - MonthHeaderView + CalendarItemViewRepresentable +// MARK: CalendarItemViewRepresentable extension MonthHeaderView: CalendarItemViewRepresentable { diff --git a/Sources/Public/ItemViews/SwiftUIWrapperView.swift b/Sources/Public/ItemViews/SwiftUIWrapperView.swift index d079dcf..86fc1d2 100644 --- a/Sources/Public/ItemViews/SwiftUIWrapperView.swift +++ b/Sources/Public/ItemViews/SwiftUIWrapperView.swift @@ -41,7 +41,7 @@ public final class SwiftUIWrapperView: UIView { layoutMargins = .zero } - required init?(coder: NSCoder) { + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -62,8 +62,8 @@ public final class SwiftUIWrapperView: UIView { public override func systemLayoutSizeFitting( _ targetSize: CGSize, - withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, - verticalFittingPriority: UILayoutPriority) + withHorizontalFittingPriority _: UILayoutPriority, + verticalFittingPriority _: UILayoutPriority) -> CGSize { hostingController.sizeThatFits(in: targetSize) @@ -134,13 +134,13 @@ extension SwiftUIWrapperView: CalendarItemViewRepresentable { // MARK: Public - public static func == (lhs: InvariantViewProperties, rhs: InvariantViewProperties) -> Bool { + public static func == (_: InvariantViewProperties, _: InvariantViewProperties) -> Bool { // Always true since two `SwiftUIWrapperView`'s with the same `Content` view are considered to // have the same "invariant view properties." true } - public func hash(into hasher: inout Hasher) { } + public func hash(into _: inout Hasher) { } // MARK: Fileprivate @@ -175,7 +175,7 @@ extension SwiftUIWrapperView: CalendarItemViewRepresentable { public static func makeView( withInvariantViewProperties invariantViewProperties: InvariantViewProperties) - -> SwiftUIWrapperView + -> SwiftUIWrapperView { SwiftUIWrapperView(contentAndID: invariantViewProperties.initialContentAndID) } @@ -236,10 +236,13 @@ private final class HostingController: UIHostingController Bool { - return lhs.month == rhs.month && + lhs.month == rhs.month && lhs.monthHeaderFrame == rhs.monthHeaderFrame && lhs.dayOfWeekPositionsAndFrames.elementsEqual( rhs.dayOfWeekPositionsAndFrames, diff --git a/Sources/Public/MonthRange.swift b/Sources/Public/MonthRange.swift index f64c894..ea19a3e 100644 --- a/Sources/Public/MonthRange.swift +++ b/Sources/Public/MonthRange.swift @@ -21,8 +21,6 @@ public typealias MonthRange = ClosedRange extension MonthRange { - // MARK: Lifecycle - /// Instantiates a `MonthRange` that encapsulates the `dateRange` in the `calendar` as closely as possible. For example, /// a date range of [2020-01-19, 2021-02-01] will result in a month range of [2020-01, 2021-02]. init(containing dateRange: ClosedRange, in calendar: Calendar) { diff --git a/Sources/Public/MonthsLayout.swift b/Sources/Public/MonthsLayout.swift index 9a7c693..121e9c1 100644 --- a/Sources/Public/MonthsLayout.swift +++ b/Sources/Public/MonthsLayout.swift @@ -62,13 +62,12 @@ public enum MonthsLayout: Hashable { case .horizontal: return true } } - + var isPaginationEnabled: Bool { guard case .horizontal(let options) = self, case .paginatedScrolling = options.scrollingBehavior - else - { + else { return false } @@ -163,18 +162,18 @@ public struct HorizontalMonthsLayoutOptions: Hashable { let visibleInterMonthSpacing = CGFloat(maximumFullyVisibleMonths) * interMonthSpacing return (calendarWidth - visibleInterMonthSpacing) / CGFloat(maximumFullyVisibleMonths) } - + func pageSize(calendarWidth: CGFloat, interMonthSpacing: CGFloat) -> CGFloat { guard case .paginatedScrolling(let configuration) = scrollingBehavior else { preconditionFailure( "Cannot get a page size for a calendar that does not have horizontal pagination enabled.") } - + switch configuration.restingPosition { case .atIncrementsOfCalendarWidth: return calendarWidth case .atLeadingEdgeOfEachMonth: - let monthWidth = self.monthWidth( + let monthWidth = monthWidth( calendarWidth: calendarWidth, interMonthSpacing: interMonthSpacing) return monthWidth + interMonthSpacing @@ -183,52 +182,52 @@ public struct HorizontalMonthsLayoutOptions: Hashable { } -// MARK: - HorizontalMonthsLayoutOptions.ScrollingBehavior +// MARK: HorizontalMonthsLayoutOptions.ScrollingBehavior extension HorizontalMonthsLayoutOptions { - + /// The scrolling behavior of the horizontally-scrolling calendar: either paginated-scrolling or free-scrolling. public enum ScrollingBehavior: Hashable { - + /// The calendar will come to a rest at specific scroll positions, defined by the `PaginationConfiguration`. case paginatedScrolling(PaginationConfiguration) - + /// The calendar will come to a rest at any scroll position. case freeScrolling } } -// MARK: - HorizontalMonthsLayoutOptions.PaginationConfiguration +// MARK: HorizontalMonthsLayoutOptions.PaginationConfiguration extension HorizontalMonthsLayoutOptions { - + /// The pagination behavior's configurable options. public struct PaginationConfiguration: Hashable { - + // MARK: Lifecycle - + public init(restingPosition: RestingPosition, restingAffinity: RestingAffinity) { self.restingPosition = restingPosition self.restingAffinity = restingAffinity } - + // MARK: Public - + /// The position at which the calendar will come to a rest when paginating. public let restingPosition: RestingPosition - + /// The calendar's affinity for stopping at a resting position. public let restingAffinity: RestingAffinity } - + } extension HorizontalMonthsLayoutOptions.PaginationConfiguration { - + // MARK: - HorizontalMonthsLayoutOptions.PaginationConfiguration.RestingPosition - + /// The position at which the calendar will come to a rest when paginating. public enum RestingPosition: Hashable { @@ -239,20 +238,20 @@ extension HorizontalMonthsLayoutOptions.PaginationConfiguration { case atIncrementsOfCalendarWidth } - + // MARK: - HorizontalMonthsLayoutOptions.PaginationConfiguration.RestingAffinity - + /// The calendar's affinity for stopping at a resting position. public enum RestingAffinity: Hashable { - + /// The calendar will come to a rest at the position adjacent to the previous resting position, regardless of how fast the user /// swipes. case atPositionsAdjacentToPrevious - + /// The calendar will come to a rest at the closest position to the target scroll offset, potentially skipping over many valid resting /// positions depending on how fast the user swipes. case atPositionsClosestToTargetOffset } - + } diff --git a/Tests/FrameProviderTests.swift b/Tests/FrameProviderTests.swift index 11a212c..048b28d 100644 --- a/Tests/FrameProviderTests.swift +++ b/Tests/FrameProviderTests.swift @@ -608,7 +608,7 @@ final class FrameProviderTests: XCTestCase { XCTAssert(frameBeforeRightDay == expectedFrameBeforeRightDay, "Incorrect frame for day.") XCTAssert(frameAfterRightDay == expectedFrameAfterRightDay, "Incorrect frame for day.") } - + func testAdjacentDayFrameFloatingPointPrecisionEdgeCase() { let frameProvider = FrameProvider( content: CalendarViewContent( @@ -619,7 +619,7 @@ final class FrameProviderTests: XCTestCase { size: CGSize(width: 375, height: 275), layoutMargins: .init(top: 8, leading: 8, bottom: 8, trailing: 8), scale: 3) - + let adjacentDayFrame = CGRect( x: 10218.857142857141, y: 104.4047619047619, @@ -632,7 +632,7 @@ final class FrameProviderTests: XCTestCase { day: 10), withFrame: adjacentDayFrame, inMonthWithOrigin: CGPoint(x: 10195.5, y: 7.9999999999999964)) - + XCTAssert( frameOfPreviousDay.minY == adjacentDayFrame.minY, "1500-02-09 and 1500-02-10 should have the same minY because they're in the same week.") @@ -835,6 +835,8 @@ final class FrameProviderTests: XCTestCase { private let calendar = Calendar(identifier: .gregorian) + // swiftlint:disable implicitly_unwrapped_optional + private var verticalFrameProvider: FrameProvider! private var verticalPinnedDaysOfWeekFrameProvider: FrameProvider! private var verticalPartialMonthFrameProvider: FrameProvider! @@ -845,11 +847,11 @@ final class FrameProviderTests: XCTestCase { // MARK: CGSize Pixel Alignment -private extension CGSize { +extension CGSize { // Rounds a `CGSize`'s origin `width` and `height` values so that they're aligned on pixel // boundaries for a screen with the provided scale. - func alignedToPixels(forScreenWithScale scale: CGFloat) -> CGSize { + fileprivate func alignedToPixels(forScreenWithScale scale: CGFloat) -> CGSize { CGSize( width: width.alignedToPixel(forScreenWithScale: scale), height: height.alignedToPixel(forScreenWithScale: scale)) diff --git a/Tests/HorizontalMonthsLayoutOptionsTests.swift b/Tests/HorizontalMonthsLayoutOptionsTests.swift index 6455c02..073ec91 100644 --- a/Tests/HorizontalMonthsLayoutOptionsTests.swift +++ b/Tests/HorizontalMonthsLayoutOptionsTests.swift @@ -17,7 +17,7 @@ import XCTest @testable import HorizonCalendar final class HorizontalMonthsLayoutOptionsTests: XCTestCase { - + func testMonthWidthOneVisibleMonth() { let options = HorizontalMonthsLayoutOptions(maximumFullyVisibleMonths: 1) diff --git a/Tests/ItemViewReuseManagerTests.swift b/Tests/ItemViewReuseManagerTests.swift index c051feb..ae6546f 100644 --- a/Tests/ItemViewReuseManagerTests.swift +++ b/Tests/ItemViewReuseManagerTests.swift @@ -430,9 +430,9 @@ final class ItemViewReuseManagerTests: XCTestCase { MockCalendarItemModel.variant1._itemViewDifferentiator: 3, ] let expectedNewViewCountsForDifferentiators: [_CalendarItemViewDifferentiator: Int] = [ - MockCalendarItemModel.variant0._itemViewDifferentiator: 1, - MockCalendarItemModel.variant1._itemViewDifferentiator: 2, - MockCalendarItemModel.variant2._itemViewDifferentiator: 1, + MockCalendarItemModel.variant0._itemViewDifferentiator: 1, + MockCalendarItemModel.variant1._itemViewDifferentiator: 2, + MockCalendarItemModel.variant2._itemViewDifferentiator: 1, ] XCTAssert( @@ -446,6 +446,7 @@ final class ItemViewReuseManagerTests: XCTestCase { // MARK: Private + // swiftlint:disable:next implicitly_unwrapped_optional private var reuseManager: ItemViewReuseManager! } @@ -515,12 +516,12 @@ private struct MockCalendarItemModel: AnyCalendarItemModel, Equatable { UIView() } - func _setContent(onViewOfSameType view: UIView) { } + func _setContent(onViewOfSameType _: UIView) { } - func _isContentEqual(toContentOf other: AnyCalendarItemModel) -> Bool { + func _isContentEqual(toContentOf _: AnyCalendarItemModel) -> Bool { false } - mutating func _setSwiftUIWrapperViewContentIDIfNeeded(_ id: AnyHashable) { } + mutating func _setSwiftUIWrapperViewContentIDIfNeeded(_: AnyHashable) { } } diff --git a/Tests/LayoutItemTypeEnumeratorTests.swift b/Tests/LayoutItemTypeEnumeratorTests.swift index b990a6d..5c9f87c 100644 --- a/Tests/LayoutItemTypeEnumeratorTests.swift +++ b/Tests/LayoutItemTypeEnumeratorTests.swift @@ -44,7 +44,7 @@ final class LayoutItemTypeEnumeratorTests: XCTestCase { horizontalItemTypeEnumerator = LayoutItemTypeEnumerator( calendar: calendar, monthsLayout: .horizontal( - options: HorizontalMonthsLayoutOptions(maximumFullyVisibleMonths: 305/300)), + options: HorizontalMonthsLayoutOptions(maximumFullyVisibleMonths: 305 / 300)), monthRange: monthRange, dayRange: dayRange) @@ -125,25 +125,25 @@ final class LayoutItemTypeEnumeratorTests: XCTestCase { .day(calendar.day(byAddingDays: 30, to: startDay)), .monthHeader(Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .first, + position: .first, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .second, + position: .second, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .third, + position: .third, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .fourth, + position: .fourth, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .fifth, + position: .fifth, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .sixth, + position: .sixth, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .dayOfWeekInMonth( - position: .last, + position: .last, month: Month(era: 1, year: 2021, month: 01, isInGregorianCalendar: true)), .day(calendar.day(byAddingDays: 31, to: startDay)), .day(calendar.day(byAddingDays: 32, to: startDay)), @@ -248,6 +248,8 @@ final class LayoutItemTypeEnumeratorTests: XCTestCase { month: Month(era: 1, year: 2020, month: 12, isInGregorianCalendar: true), day: 1) + // swiftlint:disable implicitly_unwrapped_optional + private var verticalItemTypeEnumerator: LayoutItemTypeEnumerator! private var verticalPinnedDaysOfWeekItemTypeEnumerator: LayoutItemTypeEnumerator! private var horizontalItemTypeEnumerator: LayoutItemTypeEnumerator! diff --git a/Tests/MonthTests.swift b/Tests/MonthTests.swift index ddd2ef7..9dea1ae 100644 --- a/Tests/MonthTests.swift +++ b/Tests/MonthTests.swift @@ -20,6 +20,8 @@ import XCTest final class MonthTests: XCTestCase { + // MARK: Internal + // MARK: - Advancing Months Tests func testAdvancingByNothing() { diff --git a/Tests/PaginationHelpersTests.swift b/Tests/PaginationHelpersTests.swift index 4701488..7d78d21 100644 --- a/Tests/PaginationHelpersTests.swift +++ b/Tests/PaginationHelpersTests.swift @@ -17,7 +17,7 @@ import XCTest @testable import HorizonCalendar final class PaginationHelpersTests: XCTestCase { - + func testClosestPageIndex() throws { XCTAssert(PaginationHelpers.closestPageIndex(forOffset: 0, pageSize: 100) == 0) XCTAssert(PaginationHelpers.closestPageIndex(forOffset: -100, pageSize: 100) == -1) @@ -31,9 +31,9 @@ final class PaginationHelpersTests: XCTestCase { XCTAssert(PaginationHelpers.closestPageIndex(forOffset: -799, pageSize: 100) == -8) XCTAssert(PaginationHelpers.closestPageIndex(forOffset: -801, pageSize: 100) == -8) } - + // MARK: Closest Page Offset Tests - + func testClosestPageOffsetsNoOffsetNoVelocity() { let offset1 = PaginationHelpers.closestPageOffset( toTargetOffset: 75, @@ -41,35 +41,35 @@ final class PaginationHelpersTests: XCTestCase { velocity: 0, pageSize: 100) XCTAssert(offset1 == 100) - + let offset2 = PaginationHelpers.closestPageOffset( toTargetOffset: -75, touchUpOffset: -75, velocity: 0, pageSize: 100) XCTAssert(offset2 == -100) - + let offset3 = PaginationHelpers.closestPageOffset( toTargetOffset: 25, touchUpOffset: 25, velocity: 0, pageSize: 100) XCTAssert(offset3 == 0) - + let offset4 = PaginationHelpers.closestPageOffset( toTargetOffset: -25, touchUpOffset: -25, velocity: 0, pageSize: 100) XCTAssert(offset4 == 0) - + let offset5 = PaginationHelpers.closestPageOffset( toTargetOffset: 150, touchUpOffset: 150, velocity: 0, pageSize: 100) XCTAssert(offset5 == 200) - + let offset6 = PaginationHelpers.closestPageOffset( toTargetOffset: -150, touchUpOffset: -150, @@ -77,7 +77,7 @@ final class PaginationHelpersTests: XCTestCase { pageSize: 100) XCTAssert(offset6 == -200) } - + func testClosestPageOffsetsSmallOffsetSmallVelocity() { let offset1 = PaginationHelpers.closestPageOffset( toTargetOffset: 20, @@ -85,35 +85,35 @@ final class PaginationHelpersTests: XCTestCase { velocity: 1, pageSize: 100) XCTAssert(offset1 == 100) - + let offset2 = PaginationHelpers.closestPageOffset( toTargetOffset: -20, touchUpOffset: -10, velocity: -1, pageSize: 100) XCTAssert(offset2 == -100) - + let offset3 = PaginationHelpers.closestPageOffset( toTargetOffset: 90, touchUpOffset: 80, velocity: 1, pageSize: 100) XCTAssert(offset3 == 100) - + let offset4 = PaginationHelpers.closestPageOffset( toTargetOffset: -80, touchUpOffset: -80, velocity: -1, pageSize: 100) XCTAssert(offset4 == -100) - + let offset5 = PaginationHelpers.closestPageOffset( toTargetOffset: 145, touchUpOffset: 135, velocity: 1, pageSize: 100) XCTAssert(offset5 == 200) - + let offset6 = PaginationHelpers.closestPageOffset( toTargetOffset: -145, touchUpOffset: -135, @@ -121,7 +121,7 @@ final class PaginationHelpersTests: XCTestCase { pageSize: 100) XCTAssert(offset6 == -200) } - + func testClosestPageOffsetsNormalOffsetNormalVelocity() { let offset1 = PaginationHelpers.closestPageOffset( toTargetOffset: 90, @@ -129,49 +129,49 @@ final class PaginationHelpersTests: XCTestCase { velocity: 5, pageSize: 100) XCTAssert(offset1 == 100) - + let offset2 = PaginationHelpers.closestPageOffset( toTargetOffset: -90, touchUpOffset: -50, velocity: -5, pageSize: 100) XCTAssert(offset2 == -100) - + let offset3 = PaginationHelpers.closestPageOffset( toTargetOffset: 260, touchUpOffset: 110, velocity: 10, pageSize: 100) XCTAssert(offset3 == 300) - + let offset4 = PaginationHelpers.closestPageOffset( toTargetOffset: -260, touchUpOffset: -110, velocity: -10, pageSize: 100) XCTAssert(offset4 == -300) - + let offset5 = PaginationHelpers.closestPageOffset( toTargetOffset: 969, touchUpOffset: 420, velocity: 13, pageSize: 100) XCTAssert(offset5 == 1000) - + let offset6 = PaginationHelpers.closestPageOffset( toTargetOffset: -969, touchUpOffset: -420, velocity: -13, pageSize: 100) XCTAssert(offset6 == -1000) - + let offset7 = PaginationHelpers.closestPageOffset( toTargetOffset: -175, touchUpOffset: 100, velocity: -12, pageSize: 100) XCTAssert(offset7 == -200) - + let offset8 = PaginationHelpers.closestPageOffset( toTargetOffset: 175, touchUpOffset: -100, @@ -179,9 +179,9 @@ final class PaginationHelpersTests: XCTestCase { pageSize: 100) XCTAssert(offset8 == 200) } - + // MARK: Adjacent Page Offset Tests - + func testAdjacentPageOffsetsNoOffsetNoVelocity() { let offset1 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, @@ -189,49 +189,49 @@ final class PaginationHelpersTests: XCTestCase { velocity: 0, pageSize: 100) XCTAssert(offset1 == 100) - + let offset2 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: -75, velocity: 0, pageSize: 100) XCTAssert(offset2 == -100) - + let offset3 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: 25, velocity: 0, pageSize: 100) XCTAssert(offset3 == 0) - + let offset4 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: -25, velocity: 0, pageSize: 100) XCTAssert(offset4 == 0) - + let offset5 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: 150, velocity: 0, pageSize: 100) XCTAssert(offset5 == 100) - + let offset6 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: -150, velocity: 0, pageSize: 100) XCTAssert(offset6 == -100) - + let offset7 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 5, targetOffset: 700, velocity: 0, pageSize: 100) XCTAssert(offset7 == 600) - + let offset8 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: -5, targetOffset: -700, @@ -239,7 +239,7 @@ final class PaginationHelpersTests: XCTestCase { pageSize: 100) XCTAssert(offset8 == -600) } - + func testAdjacentPageOffsetsSmallOffsetSmallVelocity() { let offset1 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, @@ -247,35 +247,35 @@ final class PaginationHelpersTests: XCTestCase { velocity: 1, pageSize: 100) XCTAssert(offset1 == 100) - + let offset2 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: -20, velocity: -1, pageSize: 100) XCTAssert(offset2 == -100) - + let offset3 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 1, targetOffset: 110, velocity: 1, pageSize: 100) XCTAssert(offset3 == 200) - + let offset4 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: -1, targetOffset: -110, velocity: -1, pageSize: 100) XCTAssert(offset4 == -200) - + let offset5 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 5, targetOffset: 501, velocity: 1, pageSize: 100) XCTAssert(offset5 == 600) - + let offset6 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: -5, targetOffset: -501, @@ -283,7 +283,7 @@ final class PaginationHelpersTests: XCTestCase { pageSize: 100) XCTAssert(offset6 == -600) } - + func testAdjacentPageOffsetsLargelOffsetNormalVelocity() { let offset1 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, @@ -291,35 +291,35 @@ final class PaginationHelpersTests: XCTestCase { velocity: 5, pageSize: 100) XCTAssert(offset1 == 100) - + let offset2 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 0, targetOffset: -310, velocity: -5, pageSize: 100) XCTAssert(offset2 == -100) - + let offset3 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 10, targetOffset: 1600, velocity: 10, pageSize: 100) XCTAssert(offset3 == 1100) - + let offset4 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: -10, targetOffset: -1600, velocity: -10, pageSize: 100) XCTAssert(offset4 == -1100) - + let offset5 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: -1, targetOffset: -10, velocity: 5, pageSize: 100) XCTAssert(offset5 == 0) - + let offset6 = PaginationHelpers.adjacentPageOffset( toPreviousPageIndex: 1, targetOffset: 10, @@ -327,5 +327,5 @@ final class PaginationHelpersTests: XCTestCase { pageSize: 100) XCTAssert(offset6 == 0) } - + } diff --git a/Tests/ScreenPixelAlignmentTests.swift b/Tests/ScreenPixelAlignmentTests.swift index e853301..b98b92e 100644 --- a/Tests/ScreenPixelAlignmentTests.swift +++ b/Tests/ScreenPixelAlignmentTests.swift @@ -121,19 +121,19 @@ final class ScreenPixelAlignmentTests: XCTestCase { rect.alignedToPixels(forScreenWithScale: 3) == expectedRect, "Incorrect screen pixel alignment") } - + // MARK: CGFloat Approximate Comparison Tests - + func testApproximateEquality() { XCTAssert(CGFloat(1.48).isEqual(to: 1.52, threshold: 0.05)) XCTAssert(!CGFloat(1.48).isEqual(to: 1.53, threshold: 0.05)) - + XCTAssert(CGFloat(1).isEqual(to: 10, threshold: 9)) XCTAssert(!CGFloat(1).isEqual(to: 11, threshold: 9)) - + XCTAssert(CGFloat(1).isEqual(to: 10, threshold: 9)) XCTAssert(!CGFloat(1).isEqual(to: 11, threshold: 9)) - + XCTAssert(CGFloat(1.333).isEqual(to: 1.666, threshold: 1 / 3)) XCTAssert(!CGFloat(1.332).isEqual(to: 1.666, threshold: 1 / 3)) } diff --git a/Tests/ScrollMetricsMutatorTests.swift b/Tests/ScrollMetricsMutatorTests.swift index 72c71a9..36a05f5 100644 --- a/Tests/ScrollMetricsMutatorTests.swift +++ b/Tests/ScrollMetricsMutatorTests.swift @@ -274,30 +274,30 @@ final class ScrollMetricsMutatorTests: XCTestCase { initialOffset2 - 1000 == horizontalScrollMetricsProvider.offset(for: .horizontal), "Scroll offset does not equal the initial offset - 1000.") } - + // MARK: Scroll Boundary Offsets - + func testVerticalMinimumScrollOffset() { verticalScrollMetricsProvider.setStartInset(to: 100, for: .vertical) XCTAssert( verticalScrollMetricsProvider.minimumOffset(for: .vertical) == -100, "The minimum offset should equal the negated top inset.") } - + func testHorizontalMinimumScrollOffset() { horizontalScrollMetricsProvider.setStartInset(to: 300, for: .horizontal) XCTAssert( horizontalScrollMetricsProvider.minimumOffset(for: .horizontal) == -300, "The minimum offset should equal the negated left inset.") } - + func testVerticalMaximumScrollOffset() { verticalScrollMetricsProvider.setEndInset(to: -50, for: .vertical) XCTAssert( verticalScrollMetricsProvider.maximumOffset(for: .vertical) == 9999999999470.0, "The maximum offset should equal the content height plus bottom inset minus bounds height.") } - + func testHorizontalMaximumScrollOffset() { horizontalScrollMetricsProvider.setEndInset(to: -80, for: .horizontal) XCTAssert( @@ -307,12 +307,14 @@ final class ScrollMetricsMutatorTests: XCTestCase { // MARK: Private + // swiftlint:disable implicitly_unwrapped_optional + private var verticalScrollMetricsProvider: ScrollMetricsProvider! private var horizontalScrollMetricsProvider: ScrollMetricsProvider! private var verticalScrollMetricsMutator: ScrollMetricsMutator! private var horizontalScrollMetricsMutator: ScrollMetricsMutator! - + private static func mockScrollMetricsProvider() -> ScrollMetricsProvider { let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 320, height: 480)) scrollView.contentInsetAdjustmentBehavior = .never diff --git a/Tests/SubviewsManagerTests.swift b/Tests/SubviewsManagerTests.swift index b9842c7..8f22888 100644 --- a/Tests/SubviewsManagerTests.swift +++ b/Tests/SubviewsManagerTests.swift @@ -16,7 +16,7 @@ import XCTest @testable import HorizonCalendar -// MARK: - SubviewsManagerTests +// MARK: - SubviewInsertionIndexTrackerTests final class SubviewInsertionIndexTrackerTests: XCTestCase { diff --git a/Tests/VisibleItemsProviderTests.swift b/Tests/VisibleItemsProviderTests.swift index 30b227c..a3378e7 100644 --- a/Tests/VisibleItemsProviderTests.swift +++ b/Tests/VisibleItemsProviderTests.swift @@ -380,7 +380,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-03-11)), frame: (142.0, 391.5, 36.0, 18.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-03-11)), frame: (142.0, 391.5, 36.0, 18.0)]", "Unexpected centermost layout item.") } @@ -462,7 +463,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-03-11)), frame: (142.0, 391.5, 36.0, 35.5)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-03-11)), frame: (142.0, 391.5, 36.0, 35.5)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == nil, "Unexpected content start offset.") @@ -541,7 +543,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-06-24)), frame: (142.0, 697.0, 36.0, 36.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-06-24)), frame: (142.0, 697.0, 36.0, 36.0)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == nil, "Unexpected content start offset.") @@ -592,7 +595,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-01-29)), frame: (142.0, 391.5, 36.0, 35.5)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-01-29)), frame: (142.0, 391.5, 36.0, 35.5)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == 200, "Unexpected content start offset.") @@ -677,7 +681,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-05-10)), frame: (255.0, 238.5, 33.0, 33.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-05-10)), frame: (255.0, 238.5, 33.0, 33.0)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == nil, "Unexpected content start offset.") @@ -756,7 +761,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-05-27)), frame: (142.0, 332.0, 36.0, 36.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-05-27)), frame: (142.0, 332.0, 36.0, 36.0)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == nil, "Unexpected content start offset.") @@ -904,7 +910,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-01-08)), frame: (142.0, 191.5, 36.0, 35.5)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-01-08)), frame: (142.0, 191.5, 36.0, 35.5)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == 0, "Unexpected content start offset.") @@ -982,7 +989,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-01-22)), frame: (142.0, 292.0, 36.0, 36.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-01-22)), frame: (142.0, 292.0, 36.0, 36.0)]", "Unexpected centermost layout item.") XCTAssert( @@ -1018,12 +1026,14 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-12-01)), frame: (96.5, 875.5, 35.5, 36.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-12-01)), frame: (96.5, 875.5, 35.5, 36.0)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == nil, "Unexpected content start offset.") XCTAssert( - details.contentEndBoundary?.alignedToPixel(forScreenWithScale: 3) == CGFloat(911.4285714285714).alignedToPixel(forScreenWithScale: 3), + details.contentEndBoundary?.alignedToPixel(forScreenWithScale: 3) == CGFloat(911.4285714285714) + .alignedToPixel(forScreenWithScale: 3), "Unexpected content end offset.") } @@ -1101,7 +1111,8 @@ final class VisibleItemsProviderTests: XCTestCase { "Unexpected visible items.") XCTAssert( - details.centermostLayoutItem.description == "[itemType: .layoutItemType(.day(2020-11-14)), frame: (1147.0, 235.5, 33.0, 33.0)]", + details.centermostLayoutItem + .description == "[itemType: .layoutItemType(.day(2020-11-14)), frame: (1147.0, 235.5, 33.0, 33.0)]", "Unexpected centermost layout item.") XCTAssert(details.contentStartBoundary == nil, "Unexpected content start offset.") @@ -1465,7 +1476,6 @@ final class VisibleItemsProviderTests: XCTestCase { "[itemType: .monthBackground(2020-04), frame: (-72.5, -76.5, 315.0, 480.0)]", "[itemType: .monthBackground(2020-05), frame: (242.5, -25.0, 315.0, 480.0)]", "[itemType: .monthBackground(2020-06), frame: (557.5, -51.5, 315.0, 480.0)]", - ] XCTAssert( @@ -2224,7 +2234,7 @@ final class VisibleItemsProviderTests: XCTestCase { calendar: calendar, visibleDateRange: dateRange, monthsLayout: .horizontal( - options: HorizontalMonthsLayoutOptions(maximumFullyVisibleMonths: 64/63)))), + options: HorizontalMonthsLayoutOptions(maximumFullyVisibleMonths: 64 / 63)))), size: size, layoutMargins: .zero, scale: 2, @@ -2261,7 +2271,7 @@ final class VisibleItemsProviderTests: XCTestCase { .verticalDayMargin(20) .horizontalDayMargin(10) .daysOfTheWeekRowSeparator(options: .init(height: 1, color: .gray)) - .monthHeaderItemProvider { month in + .monthHeaderItemProvider { month in if month.month % 4 == 0 { return mockCalendarItemModel(height: 100) } else { @@ -2273,7 +2283,7 @@ final class VisibleItemsProviderTests: XCTestCase { .dayItemProvider { _ in mockCalendarItemModel() } .dayBackgroundItemProvider { day in // Just test a few backgrounds to make sure they're working correctly - if day.day > 10 && day.day < 20 { + if day.day > 10, day.day < 20 { return mockCalendarItemModel() } else { return nil @@ -2283,26 +2293,24 @@ final class VisibleItemsProviderTests: XCTestCase { for: [ calendar.date(from: DateComponents(year: 2020, month: 03, day: 11))! ... - calendar.date(from: DateComponents(year: 2020, month: 04, day: 05))!, + calendar.date(from: DateComponents(year: 2020, month: 04, day: 05))!, calendar.date(from: DateComponents(year: 2020, month: 04, day: 30))! ... - calendar.date(from: DateComponents(year: 2020, month: 05, day: 14))!, - ], - { _ in mockCalendarItemModel() }) + calendar.date(from: DateComponents(year: 2020, month: 05, day: 14))!, + ]) { _ in mockCalendarItemModel() } .overlayItemProvider( for: [ .day( containingDate: calendar.date(from: DateComponents(year: 2020, month: 01, day: 19))!), .monthHeader( monthContainingDate: calendar.date(from: DateComponents(year: 2020, month: 11))!), - ], - { _ in mockCalendarItemModel() }) + ]) { _ in mockCalendarItemModel() } } } -// MARK: VisibleItem+CustomStringConvertible +// MARK: - VisibleItem + CustomStringConvertible extension VisibleItem: CustomStringConvertible { @@ -2348,7 +2356,7 @@ extension VisibleItem: CustomStringConvertible { } -// MARK: LayoutItem+CustomStringConvertible +// MARK: - LayoutItem + CustomStringConvertible extension LayoutItem: CustomStringConvertible { @@ -2359,7 +2367,7 @@ extension LayoutItem: CustomStringConvertible { } -// MARK: LayoutItem.ItemType+CustomStringConvertible +// MARK: - LayoutItem.ItemType + CustomStringConvertible extension LayoutItem.ItemType: CustomStringConvertible { @@ -2367,7 +2375,7 @@ extension LayoutItem.ItemType: CustomStringConvertible { switch self { case .monthHeader(let month): return ".layoutItemType(.monthHeader(\(month.description)))" - case let .dayOfWeekInMonth(position, month): + case .dayOfWeekInMonth(let position, let month): return ".layoutItemType(.dayOfWeekInMonth(\(position.description), \(month.description)))" case .day(let day): return ".layoutItemType(.day(\(day)))" @@ -2376,7 +2384,7 @@ extension LayoutItem.ItemType: CustomStringConvertible { } -// MARK: DayOfWeekPosition+CustomStringConvertible +// MARK: - DayOfWeekPosition + CustomStringConvertible extension DayOfWeekPosition: CustomStringConvertible {