patchthecode / JTAppleCalendar

The Unofficial Apple iOS Swift Calendar View. Swift calendar Library. iOS calendar Control. 100% Customizable
https://patchthecode.com
MIT License
7.57k stars 809 forks source link

Fatal Exception: CALayerInvalidGeometry CALayer bounds contains NaN #1173

Open rzzvn opened 5 years ago

rzzvn commented 5 years ago

Hi I have encountered the following crashes on my app, happening in both iOS 12.4 and 13.x devices. However I am not able to reproduce this crash. Is there a way to solve this? Thanks for the amazing library.

(Required) Version Number: 7.1.8

Fatal Exception: CALayerInvalidGeometry CALayer bounds contains NaN: [nan 0; 0 0]. Layer: <CALayer:0x281d81e00; position = CGPoint (0 0); bounds = CGRect (0 0; 0 0); delegate = <JTAppleCalendar.JTAppleCalendarView: 0x1048a5400; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x281300210>; layer = <CALayer: 0x281d81e00>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <JTAppleCalendar.JTAppleCalendarLayout: 0x11b574e20>; dataSource: <JTAppleCalendar.JTAppleCalendarView: 0x1048a5400; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x281300210>; layer = <CALayer: 0x281d81e00>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <JTAppleCalendar.JTAppleCalendarLayout: 0x11b574e20>; dataSource: <JTAppleCalendar.JTAppleCalendarView: 0x1048a5400>>>; sublayers = (<CALayer: 0x281dcc740>); masksToBounds = YES; allowsGroupOpacity = YES; backgroundColor = <CGColor 0x283ebcff0> [<CGColorSpace 0x28398a940> (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Generic Gray Gamma 2.2 Profile; extended range)] ( 1 1 )>

Last Exception Backtrace (0)#0 (null) in __exceptionPreprocess ()

1 (null) in objc_exception_throw ()

2 (null) in +[NSException raise:format:] ()

3 (null) in CA::Layer::set_bounds(CA::Rect const&, bool) ()

4 (null) in -[CALayer setBounds:] ()

5 (null) in -[UIView(Geometry) setBounds:] ()

6 (null) in -[UIScrollView setBounds:] ()

7 (null) in -[UICollectionView setBounds:] ()

8 (null) in -[UIScrollView setContentOffset:] ()

9 (null) in -[UICollectionView setContentOffset:] ()

10 (null) in -[UIScrollView _setContentOffset:animated:animationCurve:animationAdjustsForContentOffsetDelta:an... ()

11 (null) in -[UICollectionView setContentOffset:animated:] ()

12 0x101f6622c in JTAppleCalendarLayout.prepare() at /Users/rm/Documents/Project/TripO/Pods/JTAppleCalendar/Sources/JTAppleCalendarLayout.swift:154

13 (null) in @objc JTAppleCalendarLayout.prepare() ()

14 (null) in -[UICollectionViewData _prepareToLoadData] ()

15 (null) in -[UICollectionViewData validateLayoutInRect:] ()

16 (null) in -[UICollectionView layoutSubviews] ()

17 0x101f4f698 in JTAppleCalendarView.layoutSubviews() at /Users/rm/Documents/Project/TripO/Pods/JTAppleCalendar/Sources/InternalActionFunctions.swift:28

18 (null) in @objc JTAppleCalendarView.layoutSubviews() ()

19 (null) in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()

20 (null) in -[CALayer layoutSublayers] ()

21 (null) in CA::Layer::layout_if_needed(CA::Transaction*) ()

22 (null) in CA::Layer::layout_and_display_if_needed(CA::Transaction*) ()

23 (null) in CA::Context::commit_transaction(CA::Transaction*, double) ()

24 (null) in CA::Transaction::commit() ()

25 (null) in CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) ()

26 (null) in display_timer_callback(__CFMachPort, void, long, void*) ()

27 (null) in __CFMachPortPerform ()

28 (null) in CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION ()

29 (null) in __CFRunLoopDoSource1 ()

30 (null) in __CFRunLoopRun ()

31 (null) in CFRunLoopRunSpecific ()

32 (null) in GSEventRunModal ()

33 (null) in UIApplicationMain ()

34 0x100c43018 in main at /Users/j/Documents/Project/Navagio/Controller/DateEditController.swift:24

patchthecode commented 5 years ago

What version are you using?

jansvancer commented 5 years ago

Hi, I'm facing the same issue:

*** Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer bounds contains NaN: [nan 0; 0 0]. Layer: <CALayer:0x281f7c1c0; position = CGPoint (0 0); bounds = CGRect (0 0; 0 0); delegate = <JTAppleCalendar.JTACMonthView: 0x10c82f400; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x2810f0150>; layer = <CALayer: 0x281f7c1c0>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <JTAppleCalendar.JTACMonthLayout: 0x10ab38e80>; dataSource: <JTAppleCalendar.JTACMonthView: 0x10c82f400; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x2810f0150>; layer = <CALayer: 0x281f7c1c0>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <JTAppleCalendar.JTACMonthLayout: 0x10ab38e80>; dataSource: <JTAppleCalendar.JTACMonthView: 0x10c82f400>>>; sublayers = (<CALayer: 0x281f7cb80>); masksToBounds = YES; allowsGroupOpacity = YES; backgroundColor = <CGColor 0x283bcbc60> [<CGColorSpace 0x283bdab20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.941176 0.952941 0.960784 1 )>

iOS: 13.1.3 Version number: 8.0.2

Thank you

patchthecode commented 5 years ago

@jansvancer are you experiencing this in production? or development. and if in dev, then how do i reproduce it?

jansvancer commented 5 years ago

@patchthecode both, production and development. In my case this happens after fresh app install only. In all other cases it looks, like it works. Which makes it really weird. I actually found a temporary solution to call:

calendarView.setNeedsLayout()
calendarView.layoutIfNeeded()

before calling reloadData. But I'm not sure if this is the right solution and why this happens only in one specific case.

The actual crash happens at line 157 in JTACMonthLayout.swift

        // Set the first content offset only once. This will prevent scrolling animation on viewDidload.
        if !firstContentOffsetWasSet {
            firstContentOffsetWasSet = true
            let firstContentOffset = delegate.requestedContentOffset
--->     collectionView!.setContentOffset(firstContentOffset, animated: false)
        }

firstContentOffset has the x = NaN.

Hope this helps at least a little bit.

Thank you

bsweett commented 4 years ago

We just updated our app to use JTAppleCalendar and we are also seeing this issue in production. We are calling reloadDataWithAnchor from viewDidAppear.

A simple fix for this on the libraries side could be to verify the x and y points using isNaN.

patchthecode commented 4 years ago

@bsweett are you also using version 7? (and not version 8)

patchthecode commented 4 years ago

The reason why I have been slow to update this is because. It is hard for me to update so many versions. The current version is now 8.0.3 and it has some improvements to work with latest iOS. I can focus on the version 8+ fixes. But will need assistance on the 7+ versions.

bsweett commented 4 years ago

We are using version 8.0.3. Here is our stack trace:

Fatal Exception: CALayerInvalidGeometry
0  CoreFoundation                 0x1ae082a48 __exceptionPreprocess
1  libobjc.A.dylib                0x1adda9fa4 objc_exception_throw
2  CoreFoundation                 0x1adf781c0 -[NSCache init]
3  QuartzCore                     0x1b4b10c24 CA::Layer::set_bounds(CA::Rect const&, bool)
4  QuartzCore                     0x1b4b0085c -[CALayer setBounds:]
5  UIKitCore                      0x1b2584780 -[UIView(Geometry) setBounds:]
6  UIKitCore                      0x1b2517868 -[UIScrollView setBounds:]
7  UIKitCore                      0x1b196ab7c -[UICollectionView setBounds:]
8  UIKitCore                      0x1b2518aa4 -[UIScrollView setContentOffset:]
9  UIKitCore                      0x1b197d3d4 -[UICollectionView setContentOffset:]
10 UIKitCore                      0x1b2531e64 -[UIScrollView _setContentOffset:animated:animationCurve:animationAdjustsForContentOffsetDelta:animation:]
11 UIKitCore                      0x1b197d344 -[UICollectionView setContentOffset:animated:]
12 JTAppleCalendar                0x10404e494 JTACMonthLayout.prepare() + 133 (JTACMonthLayout.swift:133)
13 JTAppleCalendar                0x10404e6a0 @objc JTACMonthLayout.prepare() (<compiler-generated>)
14 UIKitCore                      0x1b19a42e4 -[UICollectionViewData _prepareToLoadData]
15 UIKitCore                      0x1b19a4b78 -[UICollectionViewData validateLayoutInRect:]
16 UIKitCore                      0x1b1975f5c -[UICollectionView layoutSubviews]
17 JTAppleCalendar                0x10404760c JTACMonthView.layoutSubviews() (JTACMonthView.swift)
18 JTAppleCalendar                0x104047780 @objc JTACMonthView.layoutSubviews() (<compiler-generated>)
19 UIKitCore                      0x1b259e17c -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
20 QuartzCore                     0x1b4b062c0 -[CALayer layoutSublayers]
21 QuartzCore                     0x1b4b0c43c CA::Layer::layout_if_needed(CA::Transaction*)
22 QuartzCore                     0x1b4b17140 CA::Layer::layout_and_display_if_needed(CA::Transaction*)
23 QuartzCore                     0x1b4a5f884 CA::Context::commit_transaction(CA::Transaction*, double)
24 QuartzCore                     0x1b4a89574 CA::Transaction::commit()
25 QuartzCore                     0x1b4a89f68 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
26 CoreFoundation                 0x1adfffe68 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
27 CoreFoundation                 0x1adffad54 __CFRunLoopDoObservers
28 CoreFoundation                 0x1adffaafc CFRunLoopRunSpecific
29 GraphicsServices               0x1b7f9b328 GSEventRunModal
30 UIKitCore                      0x1b210863c UIApplicationMain
patchthecode commented 4 years ago

@bsweett hmm. Sounds like a divide by zero error.

By any chance does your calendar change size? or anything like that?

There are two places in the source code I suspect is casing this. This bug should have been caught in development though. Let me do a check. I suspect it has to do with this pull request -> https://github.com/patchthecode/JTAppleCalendar/pull/1154

I have not merged it yet though. Will take a look

bsweett commented 4 years ago

Yes we do change the calendar size for two reasons:

  1. When the user rotates the devices from landscape to portrait
  2. When the user changes their font size (dynamic type)

I'm not sure that this is the specific cause though as our logs just show the user launching the controller with the calendar. We reload the data in the calendar when the view appears incase something changed from a view deeper in the navigation stack.

It seems like some point being sent to the collection view is invalid: CALayer bounds contains NaN: [nan 0; 0 279]

bsweett commented 4 years ago

We resolved this on our end by changing the scrolling mode to none. In our case we didn't need scrolling.

I would still advise that the library verify the CGPoint that is constructed before passing it to the collection view.

rs658726 commented 4 years ago

EDIT: Nevermind, @bsweett 's fix did not work for me. I'm still seeing the error which results in a crash:

*** Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer bounds contains NaN: [nan 0; 0 0]. Layer: <CALayer:0x7b0800157660; position = CGPoint (0 0); bounds = CGRect (0 0; 0 0); delegate = <JTAppleCalendar.JTACMonthView: 0x7b7c000b0c00; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7b0c00062d30>; layer = <CALayer: 0x7b0800157660>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <JTAppleCalendar.JTACMonthLayout: 0x7b580012e700>; dataSource: <JTAppleCalendar.JTACMonthView: 0x7b7c000b0c00; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7b0c00062d30>; layer = <CALayer: 0x7b0800157660>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <JTAppleCalendar.JTACMonthLayout: 0x7b580012e700>; dataSource: <JTAppleCalendar.JTACMonthView: 0x7b7c000b0c00>>>; masksToBounds = YES; allowsEdgeAntialiasing = NO; backgroundColor = <CGColor 0x7b14000361f0> [<CGColorSpace 0x7b180004db80> (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Profil gamma 2.2 gris générique; extended range)] ( 0 0 )>'

@patchthecode This is only happening when I'm showing the calendar inside a popOver. I'm really not sure what's causing this. Do you have any idea what could be the culprit here?

Here is how I set up my calendar:

    fileprivate func setupCalendar() {

        calendar.calendarDelegate = self
        calendar.calendarDataSource = self

        calendar.scrollingMode = .stopAtEachCalendarFrame

        nextMonthButton.addTarget(self, action: #selector(nextMonth(_:)), for: .touchUpInside)
        previousMonthButton.addTarget(self, action: #selector(previousMonth(_:)), for: .touchUpInside)

        // Initial date selection
        if dueDate == nil || dueDate! < Date().startOfDay { dueDate = Date() }
        calendar.selectDates([dueDate!])

        calendar.scrollToDate(dueDate!, triggerScrollToDateDelegate: true, animateScroll: false) {
            self.calendar.visibleDates { visibleDates in
                DispatchQueue.main.async {
                     // ...
                }
            }
        }
    }

Regarding the layout, The calendar is inside a container view of fixed width 350 and is pin to the latter's 4 edges. Height and cellSize are calculated based off of the width.

rafalwojcik commented 4 years ago

@patchthecode We have the same issue with scrolling. At viewDidAppear we want to scroll to the actual month, but initially calendar is hidden in stack view. It is causing crash dividing by zero as in the examples above. I'm trying to fix this without hacking but it seems impossible.

rs658726 commented 4 years ago

Hey @rafalwojcik, is your calendar presented in a popover?

rafalwojcik commented 4 years ago

No, the issue occurs only when the calendar is initially hidden and we are using scrollToDate method.

It crashes right there (JTACMonthLayout.swift:158 in latest release):

// Set the first content offset only once. This will prevent scrolling animation on viewDidload.
if !firstContentOffsetWasSet {
    firstContentOffsetWasSet = true
    let firstContentOffset = delegate.requestedContentOffset
>>>    collectionView!.setContentOffset(firstContentOffset, animated: false)
}
patchthecode commented 4 years ago

Thank you for letting me know how to reproduce it i'll look into a fix this weekend

rafalwojcik commented 4 years ago

@patchthecode nice. Thank you when do you have a plan to release new version? I can create PR to fix that but we need this fix to be oficially released :)

rs658726 commented 4 years ago

2 solutions I can think of: 1 - Try scrolling on viewDidLoad and hiding the calendar on viewWillAppear? 2- Use scrollToDate's completion handler to hide your calendar. In other words, your calendar is showing, scrolling is executed and then on completion you hide your calendar. All of this code can be added to viewDidLoad.

Solution 2 might be the better one here.

patchthecode commented 4 years ago

@rafalwojcik i can create a minor release probably the same time as well. It will be version 8.0.4

patchthecode commented 4 years ago

@rafalwojcik If you can create a PR this will be awesome, as I am swamped with work. @rs658726 I will take a look