From d78bfe5d31b13afd6322059cdd655afe27257de6 Mon Sep 17 00:00:00 2001 From: RareScrap Date: Wed, 20 Jan 2021 11:39:25 +0800 Subject: [PATCH] Bugfixes and the first result that can be called stable However, not all TODOs have been explained and solved --- Source/DayView.swift | 19 ++++++++-------- Source/DayViewState.swift | 2 ++ Source/Extensions/Date+DateOnly.swift | 8 +++++++ Source/Header/DayHeaderView.swift | 16 +++++++++++++- Source/Header/DaySymbolsView.swift | 10 ++++++++- Source/Header/SwipeLabelView.swift | 8 ++++++- Source/Timeline/TimelinePagerView.swift | 29 ++++++++++++++++++++----- 7 files changed, 75 insertions(+), 17 deletions(-) diff --git a/Source/DayView.swift b/Source/DayView.swift index f5d4e9ee..3787cb52 100644 --- a/Source/DayView.swift +++ b/Source/DayView.swift @@ -61,19 +61,20 @@ public final class DayView: UIView, TimelinePagerViewDelegate { } public var calendar: Calendar = Calendar.autoupdatingCurrent { - didSet { - // TODO: Should we observe calendar in state rather then change it manualy? + didSet { // TODO: willSet? dayHeaderView.calendar = self.calendar timelinePagerView.calendar = self.calendar - let date = self.state?.selectedDate ?? Date() - let newState = DayViewState(date: date, calendar: calendar) - newState.move(to: date) - state = newState + // recalculate the selectedDate taking into account + // the difference between new and old timezones + let oldCalendar = state!.calendar + let selectedDate = (self.state?.selectedDate ?? Date()) + .dateOnly(calendar: calendar, oldCalendar: oldCalendar) - // TODO: Shound we redraw dayView and its subvies? Or we should do this when calendar in state was changed? -// setNeedsDisplay() -// self.subviews.forEach { $0.setNeedsDisplay() } + // setting a new state + let newState = DayViewState(date: selectedDate, calendar: calendar) + state = newState + newState.move(to: selectedDate) // TODO: Why I added this :I? } } diff --git a/Source/DayViewState.swift b/Source/DayViewState.swift index 94ea54a3..7b45330b 100644 --- a/Source/DayViewState.swift +++ b/Source/DayViewState.swift @@ -6,6 +6,7 @@ public protocol DayViewStateUpdating: AnyObject { public final class DayViewState { public private(set) var calendar: Calendar +// willSet {} // TODO: I think this needs to be implemented, cause timezone can change without creating a new state object public private(set) var selectedDate: Date private var clients = [DayViewStateUpdating]() @@ -17,6 +18,7 @@ public final class DayViewState { public func move(to date: Date) { let date = date.dateOnly(calendar: calendar) +// if (date == selectedDate) { return } // TODO: If necessary? Just trying to make sure that willMoveTo not be called if we are already on this page notify(clients: clients, moveTo: date) selectedDate = date } diff --git a/Source/Extensions/Date+DateOnly.swift b/Source/Extensions/Date+DateOnly.swift index 7f401eb4..b284fdf1 100644 --- a/Source/Extensions/Date+DateOnly.swift +++ b/Source/Extensions/Date+DateOnly.swift @@ -14,4 +14,12 @@ extension Date { let returnValue = calendar.date(from: newComponents) return returnValue! } + + /// Cuts off the time, leaving the beginning of the day for the new time zone + func dateOnly(calendar: Calendar, oldCalendar: Calendar) -> Date { + var newDate = self.dateOnly(calendar: oldCalendar) + let diff = oldCalendar.timeZone.secondsFromGMT() - calendar.timeZone.secondsFromGMT() + newDate.addTimeInterval(TimeInterval(diff)) + return newDate + } } diff --git a/Source/Header/DayHeaderView.swift b/Source/Header/DayHeaderView.swift index ece91cd9..286fd920 100644 --- a/Source/Header/DayHeaderView.swift +++ b/Source/Header/DayHeaderView.swift @@ -4,7 +4,13 @@ import DateToolsSwift public final class DayHeaderView: UIView, DaySelectorDelegate, DayViewStateUpdating, UIPageViewControllerDataSource, UIPageViewControllerDelegate { public private(set) var daysInWeek = 7 - public var calendar: Calendar + public var calendar: Calendar { + didSet { + daySymbolsView.calendar = calendar + swipeLabelView.calendar = calendar + configure() // TODO: Should I do it that way or better just to set a new vc without adding subview (like in state#didSet)? + } + } private var style = DayHeaderStyle() private var currentSizeClass = UIUserInterfaceSizeClass.compact @@ -16,6 +22,14 @@ public final class DayHeaderView: UIView, DaySelectorDelegate, DayViewStateUpdat didSet { state?.subscribe(client: self) swipeLabelView.state = state + + // Fixes the "jump" of the pager from today to the selected date. This is noticeable + // when we are on a date outside of today's week, and try to change the time zone. + let previousSelectedDate = state!.selectedDate // day selected before timezone change + let vc = makeSelectorController(startDate: beginningOfWeek(previousSelectedDate)) + vc.selectedDate = previousSelectedDate + currentWeekdayIndex = vc.selectedIndex + pagingViewController.setViewControllers([vc], direction: .forward, animated: false, completion: nil) } } diff --git a/Source/Header/DaySymbolsView.swift b/Source/Header/DaySymbolsView.swift index 5bb145e0..2bb0f7d3 100644 --- a/Source/Header/DaySymbolsView.swift +++ b/Source/Header/DaySymbolsView.swift @@ -3,7 +3,15 @@ import UIKit public final class DaySymbolsView: UIView { public private(set) var daysInWeek = 7 - private var calendar = Calendar.autoupdatingCurrent + public var calendar = Calendar.autoupdatingCurrent { + didSet { + // TODO: I honestly don't know if I should be doing this. Changes to + // the timezone should not affect the displayed days of the week, + // however, just in case, I am reconfiguring this view + configure() + setNeedsLayout() + } + } private var labels = [UILabel]() private var style: DaySymbolsStyle = DaySymbolsStyle() diff --git a/Source/Header/SwipeLabelView.swift b/Source/Header/SwipeLabelView.swift index b576a830..90761404 100644 --- a/Source/Header/SwipeLabelView.swift +++ b/Source/Header/SwipeLabelView.swift @@ -7,7 +7,13 @@ public final class SwipeLabelView: UIView, DayViewStateUpdating { case Backward } - public private(set) var calendar = Calendar.autoupdatingCurrent + public var calendar = Calendar.autoupdatingCurrent { + didSet { + // I doubt that someone will use this view without a state, but + // I consider it necessary to update text label for such a situation + updateLabelText() + } + } public weak var state: DayViewState? { willSet(newValue) { state?.unsubscribe(client: self) diff --git a/Source/Timeline/TimelinePagerView.swift b/Source/Timeline/TimelinePagerView.swift index 0cf9efb8..c5f3f7ae 100644 --- a/Source/Timeline/TimelinePagerView.swift +++ b/Source/Timeline/TimelinePagerView.swift @@ -22,8 +22,11 @@ public final class TimelinePagerView: UIView, UIGestureRecognizerDelegate, UIScr public var calendar: Calendar = Calendar.autoupdatingCurrent { didSet { - pagingViewController.viewControllers?.forEach { + // changing timezone in loaded pages + pagingViewController.children.forEach { let vc = $0 as! TimelineContainerController + let oldCalendar = vc.timeline.calendar + vc.timeline.date = vc.timeline.date.dateOnly(calendar: calendar, oldCalendar: oldCalendar) vc.timeline.calendar = calendar } } @@ -180,7 +183,11 @@ public final class TimelinePagerView: UIView, UIGestureRecognizerDelegate, UIScr private func updateTimeline(_ timeline: TimelineView) { guard let dataSource = dataSource else {return} - let date = timeline.date.dateOnly(calendar: calendar) + // I don't know under what circumstances, but I know for sure that + // a situation is possible when these calendars have different time + // zones. It is in this case that the call below will prevent a bug + // that leads to "jumps" after days when you swipe to the left + let date = timeline.date.dateOnly(calendar: calendar, oldCalendar: timeline.calendar) let events = dataSource.eventsForDate(date) let day = TimePeriod(beginning: date, chunk: TimeChunk.dateComponents(days: 1)) @@ -444,7 +451,7 @@ public final class TimelinePagerView: UIView, UIGestureRecognizerDelegate, UIScr // MARK: UIPageViewControllerDataSource public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { - guard let containerController = viewController as? TimelineContainerController else {return nil} + guard let containerController = viewController as? TimelineContainerController else {return nil} let previousDate = containerController.timeline.date.add(TimeChunk.dateComponents(days: -1), calendar: calendar) let vc = configureTimelineController(date: previousDate) let offset = (pageViewController.viewControllers?.first as? TimelineContainerController)?.container.contentOffset @@ -453,7 +460,7 @@ public final class TimelinePagerView: UIView, UIGestureRecognizerDelegate, UIScr } public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { - guard let containerController = viewController as? TimelineContainerController else {return nil} + guard let containerController = viewController as? TimelineContainerController else {return nil} let nextDate = containerController.timeline.date.add(TimeChunk.dateComponents(days: 1), calendar: calendar) let vc = configureTimelineController(date: nextDate) let offset = (pageViewController.viewControllers?.first as? TimelineContainerController)?.container.contentOffset @@ -466,7 +473,19 @@ public final class TimelinePagerView: UIView, UIGestureRecognizerDelegate, UIScr public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { guard completed else {return} if let timelineContainerController = pageViewController.viewControllers?.first as? TimelineContainerController { - let selectedDate = timelineContainerController.timeline.date + // Unfortunately, I cannot explain this change in detail. I fiddled + // with a bug in which some days were skipped when swiping, completely + // desperate and wrote this. To my surprise, this solved the problem. + // I'll do some research on the reasons later. + + // Попытка пофиксить сбой хедера с перескоком через число + var selectedDate = timelineContainerController.timeline.date + selectedDate = selectedDate.dateOnly(calendar: self.state!.calendar, oldCalendar: timelineContainerController.container.timeline.calendar) + + // попытка пофиксить баг с уменьшением и свайпом + timelineContainerController.timeline.date = selectedDate // Я хз как и какие последствия оно имеет, но это сработало + timelineContainerController.timeline.calendar = self.state!.calendar + delegate?.timelinePager(timelinePager: self, willMoveTo: selectedDate) state?.client(client: self, didMoveTo: selectedDate) scrollToFirstEventIfNeeded()