WIP: Replacing the calendar after DayView initializing#294
WIP: Replacing the calendar after DayView initializing#294RareScrap wants to merge 2 commits intorichardtop:masterfrom
Conversation
Source/DayView.swift
Outdated
| state = newState | ||
|
|
||
| // TODO: Shound we redraw dayView and its subvies? Or we should do this when calendar in state was changed? | ||
| // setNeedsDisplay() |
There was a problem hiding this comment.
I think yes, as the 1st day of the week is based on the Calendar, which could change. So it there should be a call to setNeedsDisplay.
Also all of the subviews have to be redrawn.
There was a problem hiding this comment.
What about dayview's state? Should we redrawing subvies in response to a changes in state?
There was a problem hiding this comment.
I believe so, since that wouldn't happen too often. A change in state means a change in what should be displayed, so that should trigger a full relayout & redraw cycle.
I think, the best is to use setNeedsLayout, as the redraw will be triggered by the system when needed.
| public var calendar: Calendar = Calendar.autoupdatingCurrent { | ||
| didSet { | ||
| pagingViewController.viewControllers?.forEach { | ||
| let vc = $0 as! TimelineContainerController |
There was a problem hiding this comment.
Please use an if-let or guard-let construct here.
There was a problem hiding this comment.
Do you think it's worth doing it? After all, TimelinePagerView only works with TimelineContainerControllers, and I think that it is better to have a crash in such a place than to continue working with a controller of a different type.
There was a problem hiding this comment.
It would be simpler to modify, e.g. to use a protocol instead of a concrete class TimelineContainerController.
I'd go for a safe solution without a crash here, although I agree that it's highly unlikely that a crash will happen.
Source/DayView.swift
Outdated
|
|
||
| // 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() } |
There was a problem hiding this comment.
Not sure whether the call should happen here, or at the respective subView. I'd recommend the latter. Think of using some of the subviews on their own, without being shown in the DayView.
There was a problem hiding this comment.
I agree with the same opinion. It seems to me that subviews redrawing must be triggered by a change in its state.
There was a problem hiding this comment.
Yeah, a state or a calendar property, depending on which way you decide to go.
Source/Header/DayHeaderView.swift
Outdated
| public final class DayHeaderView: UIView, DaySelectorDelegate, DayViewStateUpdating, UIPageViewControllerDataSource, UIPageViewControllerDelegate { | ||
| public private(set) var daysInWeek = 7 | ||
| public let calendar: Calendar | ||
| public var calendar: Calendar |
There was a problem hiding this comment.
Should there be some reaction to this event? e.g. re-drawing all of the subviews, or propagating the calendar change further?
Source/DayView.swift
Outdated
| dayHeaderView.calendar = self.calendar | ||
| timelinePagerView.calendar = self.calendar | ||
|
|
||
| let date = self.state?.selectedDate ?? Date() |
There was a problem hiding this comment.
If possible, please omit self. I suggest quickly looking thru CONTRIBUTING.md with the code style examples. Not a requirement, I could fix those issues myself, after your pull request gets merged. Good to keep the library in a common style.
|
Now I think, the best way to handle this is to simply propagate the Calendar change on a view-by-view basis (e. g. from DayView to Header and other sibling views) and not thru a common The reason for this is simple: some views may need to have the access to the Calendar property, but don't need to subscribe to the State notifications: public init(daysInWeek: Int = 7, calendar: Calendar = Calendar.autoupdatingCurrent) {Of course, we could propagate |
However, not all TODOs have been explained and solved
119e96c to
d78bfe5
Compare
|
After many weeks of work and bug fixing, I am very happy to present this solution to you (seriously, I think I’m one step closer to alcoholism). I can't say that the feature is fully implemented, but I think that a significant part of the work is over. I would love to hear your comments and ask you to help me with testing. To be honest, it is very difficult for me to come up with such a set of tests that would cover ALL possible options for the behavior of the CalendarKit when changing the timezone, but I checked everything I could and everything works well. I will update the example app a little later so that you can test this feature more easily. I will also fix conflicts a little later. |
|
Hi, thanks for the update. I'll get back to you soon. I hope to check it this weekend. |
|
|
||
| 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 |
There was a problem hiding this comment.
I suggest ensuring that changing the timezone will always create a new state object
|
|
||
| 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 |
There was a problem hiding this comment.
I think, adding if (date == selectedDate) { return } condition is not needed and would only lead to more complicated debugging. If there are some issues in the propagating the date to the modules (e.g. some methods are called twice), it should be visible as early as possible.
| } | ||
|
|
||
| /// Cuts off the time, leaving the beginning of the day for the new time zone | ||
| func dateOnly(calendar: Calendar, oldCalendar: Calendar) -> Date { |
There was a problem hiding this comment.
I suggest renaming to something like:
func dateOnly(from: oldCalendar: Calendar, to: newCalendar: Calendar) -> Date {
| 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)? |
There was a problem hiding this comment.
I think, the proper solution is to refactor the configure() function and extract the addSubview portion of it, so that it's called only when needed.
Otherwise, looks good.
| let vc = makeSelectorController(startDate: beginningOfWeek(previousSelectedDate)) | ||
| vc.selectedDate = previousSelectedDate | ||
| currentWeekdayIndex = vc.selectedIndex | ||
| pagingViewController.setViewControllers([vc], direction: .forward, animated: false, completion: nil) |
There was a problem hiding this comment.
I think, any glitches during the configuration process are OK, as it should happen "behind the scenes", i.e. when the DayViewController is not displayed or obscured by another view.
| 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)? |
There was a problem hiding this comment.
I suggest refactoring the configure method and extracting all of the code that should run only once to a separate method (which also calls configure). The new configure method should be able to be run multiple times during the lifecycle of the view.
| } | ||
|
|
||
| /// Cuts off the time, leaving the beginning of the day for the new time zone | ||
| func dateOnly(calendar: Calendar, oldCalendar: Calendar) -> Date { |
There was a problem hiding this comment.
I suggest renaming to something akin to func dateOnly(from: oldCalendar: Calendar, to: newCalendar: Calendar) -> Date {
|
|
||
| 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 |
There was a problem hiding this comment.
I suggest against including such statements. If willMoveTo is called multiple times, it might indicate an error. Errors should be visible as early as possible.
Returning early if date hasn't changed would only complicate debugging. I'm for very simple DayViewState with as little logic as possible.
|
|
||
| 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 |
There was a problem hiding this comment.
I suggest that changing TimeZone (or Calendar, in this case) should always trigger creation of a new DayViewState.
| // setting a new state | ||
| let newState = DayViewState(date: selectedDate, calendar: calendar) | ||
| state = newState | ||
| newState.move(to: selectedDate) // TODO: Why I added this :I? |
There was a problem hiding this comment.
.... To send the new date to all the clients?
No description provided.