richardtop / CalendarKit

📅 Calendar for Apple platforms in Swift
https://www.youtube.com/watch?v=cJ63-_z1qg8
MIT License
2.49k stars 334 forks source link

Bug: Strange behavior of scrolling beyond the upper boundary of the TimelineView during autoscroll to first event #281

Open RareScrap opened 3 years ago

RareScrap commented 3 years ago

New Issue Checklist

Issue Description

I noticed that in some cases the TimelineContainer cannot correctly calculate the distance to the nearest EventView. I was able to reproduce this error today. The project demonstrating the bug is located here. Just switch to segment 3 and you will see this:

I did a little digging in the code and noticed that this happens due to the fact that in the TimelineContainer#setTimelineOffset(offset:animated:) method changing the bottomOfScrollView to a negative value. This, in turn, is due to the fact that the contentSize.height at the time of calculation is 0. I suspect this is due to the fact that the calculation takes place before the subviews were layout.

Code I'm using with CalendarKit

In short, I have a controller for SegmentedControll that will switch tabs inside contentView. The tab bar is hidden, so it looks like the SegmentedControll screens just like the TabBarController does. I highly recommend looking at the entire bug demostrate project to better understand how everything works.

SegmentedHostViewController.swift

import UIKit
class SegmentedHostViewController: UIViewController {

    @IBOutlet weak var segmentedControl: UISegmentedControl!
    @IBOutlet weak var containerView: UIView!

    private var contentViewContoller: UITabBarController?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Убираем линию под navbar'ом
        self.navigationController?.navigationBar.shadowImage = UIImage()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "segueSegmentedHostContent") {
            contentViewContoller = segue.destination as! UITabBarController
        }
    }

    @IBAction func onSegmentChanged(_ sender: UISegmentedControl) {
        contentViewContoller?.selectedIndex = sender.selectedSegmentIndex
    }
}

MyDayviewController.swift

import Foundation
import CalendarKit

class MyDayviewController: UIViewController {

    @IBOutlet weak var dayView: DayView!

    open override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)
      dayView.scrollToFirstEventIfNeeded()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        dayView.isHeaderViewVisible = false
        dayView.dataSource = self
        dayView.autoScrollToFirstEvent = true

        var style = CalendarStyle()
        style.timeline.eventGap = 10
        dayView.updateStyle(style)

        dayView.reloadData()
    }
}

// MARK: EventDataSource
extension MyDayviewController: EventDataSource {
    func eventsForDate(_ date: Date) -> [EventDescriptor] {
        var events = [Event]()
        let event = Event()
        event.startDate = Date(timeIntervalSince1970: date.timeIntervalSince1970 + Double(6000 * 5))
        event.endDate = Date(timeIntervalSince1970: event.startDate.timeIntervalSince1970 + Double(6000 * 60))
        events.append(event)
        return events
    }
}
richardtop commented 3 years ago

Hi, thanks for the report. Looking at your description, it seems like it's a lifecycle issue. Could you please try to analyze the root cause and try to narrow down the prbolem?