Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visible scrolling when trying to start calendar at a certain date #263 #1304

Open
Phontaine opened this issue Jan 13, 2021 · 4 comments
Open

Comments

@Phontaine
Copy link

(Required) Version Number:
JTAppleCalendar 8.0.2

Description

Absolutely love JTAppleCalendar. Using it in production apps for about two years now. But, I recently ran into an issue. I am having the same issue as Issue #263.

I want the calendar to be on a selected date when it first appears, so I do calendarView.scrollToDate(selectedDate) at viewDidLoad().

When I go to the calendar's view controller, I can see the starting month flash very quickly, and then the selected month shows up (even if I use animateScroll = false). This is very quick in the simulator, but very apparent on my iPhone 6 (running iOS 12.4.9). For example, you can clearly see September 2020 show up before the calendar changes over to show January 2021.

Steps To Reproduce

Here is my code:

let startDate = Date(timeIntervalSince1970: FamilyData.setupCompletedDate)
let endDate = Date()

override func viewDidLoad() {
    super.viewDidLoad()
    
    setupUI()
}

// ---------------
// Collection View
// ---------------

func configureCalendar(_ calendar: JTACMonthView) -> ConfigurationParameters {
    
    // These are the dates the user can select from when choosing due dates
    // end date will always be at the end of their budget period
    // start date depends on which calendar they're looking at.
    // If they're looking at the first payment calendar, then the first selectable date should be the first day of their budget
    // BUT... if they're looking at the last payment calendar, then the first selectable date should be AFTER the first due date
    
    return ConfigurationParameters(startDate: startDate,
                                   endDate: endDate,
                                   calendar: FamilyData.calendar(),
                                   generateInDates: .forAllMonths,
                                   generateOutDates: .tillEndOfRow)
}

func configureCell(view: JTACDayCell?, cellState: CellState) {
    
    guard let cell = view as? DateCell else { return }
    
    cell.dateLabel.text = cellState.text
    
    handleCellTextColor(cell: cell,
                        cellState: cellState)
    
    handleCellSelected(cell: cell,
                       cellState: cellState)
}

func handleCellSelected(cell: DateCell, cellState: CellState) {
    
    // round corner of BG, whether it's showing or not
    cell.selectedView.layer.cornerRadius = cell.selectedView.layer.bounds.height / 2
    
    if cellState.date < startDate || cellState.date > endDate {
        cell.dateLabel.textColor = .mpGray40
        cell.selectedView.isHidden = true
        cell.isUserInteractionEnabled = false
        
    } else if cellState.isSelected {
        cell.dateLabel.textColor = .white
        cell.selectedView.backgroundColor = .mpBlue
        cell.selectedView.isHidden = false
        cell.isUserInteractionEnabled = true
        
    } else {
        cell.dateLabel.textColor = .black
        cell.selectedView.isHidden = true
        cell.isUserInteractionEnabled = true
    }
    
    if FamilyData.calendar().isDate(cellState.date, inSameDayAs: Date()) {
        if !cellState.isSelected {
            cell.dateLabel.textColor = .mpBlue
            cell.selectedView.isHidden = true
        }
    }
}

func handleCellTextColor(cell: DateCell, cellState: CellState) {
    
    if cellState.dateBelongsTo == .thisMonth {
        cell.isHidden = false
        
    } else {
        cell.isHidden = true
    }
}

func calendar(_ calendar: JTACMonthView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTACDayCell {
    
    let cell = calendar.dequeueReusableJTAppleCell(withReuseIdentifier: "dateCell", for: indexPath) as! DateCell
    
    self.calendar(calendar,
                  willDisplay: cell,
                  forItemAt: date,
                  cellState: cellState,
                  indexPath: indexPath)
    
    return cell
}

func calendar(_ calendar: JTACMonthView, willDisplay cell: JTACDayCell, forItemAt date: Date, cellState: CellState, indexPath: IndexPath) {
    
    configureCell(view: cell,
                  cellState: cellState)
}

func calendar(_ calendar: JTACMonthView, headerViewForDateRange range: (start: Date, end: Date), at indexPath: IndexPath) -> JTACMonthReusableView {
    
    let header = calendar.dequeueReusableJTAppleSupplementaryView(withReuseIdentifier: "DateHeader", for: indexPath) as! DateHeader
    header.monthTitle.text = DateFormatter().mpMonth(from: range.start)
    return header
}

func calendarSizeForMonths(_ calendar: JTACMonthView?) -> MonthSize? {
    return MonthSize(defaultSize: 50)
}

func calendar(_ calendar: JTACMonthView, didSelectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) {
    
    configureCell(view: cell,
                  cellState: cellState)
    
    // When a user selects a day, they get the start of day but not the time.
    // So, we'll transpose the current day's time onto the selected day.
    // This will ensure unique dates for transactions.
    let dateOffset = date.days(from: Date())
    
    selectedDate = FamilyData.calendar().date(byAdding: .day, value: dateOffset, to: Date())!
    
    calendar.reloadData()
}

func calendar(_ calendar: JTACMonthView, didDeselectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) {
    
    configureCell(view: cell,
                  cellState: cellState)
}
    
// ---------
// Functions
// ---------

func setupUI() {

    // Calendar views
    calendarView.ibCalendarDelegate = self
    calendarView.ibCalendarDataSource = self
    
    calendarView.scrollDirection = .horizontal
    calendarView.scrollingMode = .nonStopToSection(withResistance: 0.5)
    calendarView.showsHorizontalScrollIndicator = false
    
    // If user already had a date selected and is returning to calendar picker, then automatically scroll to their selected date
    // Otherwise, default is today
    calendarView.scrollToDate(selectedDate, animateScroll: false)
    calendarView.selectDates([selectedDate])
}`

Expected Behavior

As you can see, my code is pretty boilerplate. Not a whole lot of customization going on. But when I load the view, I expect it to go right to the selected date, not flash the initial month first.

Additional Context

I've tried putting the scrollToDate code in viewWillAppear as well as viewDidLayoutSubviews, but to no avail. I'm using Xcode 11.3.1, if that helps.

@patchthecode
Copy link
Owner

patchthecode commented Jan 14, 2021

Thanks for your support.
Will have to take a look over the weekend.
I'm a little tied up on work

@Phontaine
Copy link
Author

Phontaine commented Jan 14, 2021 via email

@vitaliichernysh
Copy link

vitaliichernysh commented Apr 15, 2024

Hi. Any progress on this?

I'm experiencing the same on 2024 which is a bit disappointing

@patchthecode
Copy link
Owner

@vitaliichernysh i think the fastest way to heklp me resolve this is to provide a minimal app which recreates this issue.

Edge case bugs are very hard to find based on code. Often, its one tiny setting based on your app.
So can you please provide an empty app for me which recreates this issue? No, extra designs or company codes etc.
Just a minimal calendar that recreates this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants