FloatingPanel icon indicating copy to clipboard operation
FloatingPanel copied to clipboard

crash when modal presenting a floating panel with UINavigationController as contentViewController

Open piemonte opened this issue 4 years ago • 7 comments

Description

First off, amazing library! 🙌

I discovered that after updating from 2.2.0 to all later versions (2.3.1, 2.4.0), a crash occurs when modally presenting a floating panel that contains a UINavigationController as the contentViewController. Other presentations with UIViewController types present as expected. As a workaround, presenting as a child view controller avoids the exception.

Expected behavior

Presentation without crash exception.

Actual behavior

An NSException occurs when presenting a UINavigationController as the contentViewController:

libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'finishAnimationAtPosition: should only be called on a stopped animator!'
terminating with uncaught exception of type NSException
Thread#0	0x00000001803f7964 in __exceptionPreprocess ()
#1	0x0000000180188800 in objc_exception_throw ()
#2	0x00000001803f77dc in +[NSException raise:format:arguments:] ()
#3	0x000000018074b3bc in -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] ()
#4	0x0000000183969170 in -[UIViewPropertyAnimator finishAnimationAtPosition:] ()
#5	0x0000000104cf2484 in Core.interruptAnimationIfNeeded() at XXX/Pods/FloatingPanel/Sources/Core.swift:543
#6	0x0000000104cebf08 in Core.move(from:to:animated:completion:) at XXX/Pods/FloatingPanel/Sources/Core.swift:115
#7	0x0000000104cebb48 in Core.move(to:animated:completion:) at XXX/Pods/FloatingPanel/Sources/Core.swift:101
#8	0x0000000104ce2af4 in FloatingPanelController.move(to:animated:completion:) at XXX/Pods/FloatingPanel/Sources/Controller.swift:536
#9	0x0000000104cdf7b0 in FloatingPanelController.show(animated:completion:) at XXX/Pods/FloatingPanel/Sources/Controller.swift:446
#10	0x0000000104d35910 in ModalPresentTransition.interruptibleAnimator(using:) at XXX/Pods/FloatingPanel/Sources/Transitioning.swift:103
#11	0x0000000104d35c10 in ModalPresentTransition.animateTransition(using:) at XXX/Pods/FloatingPanel/Sources/Transitioning.swift:111
#12	0x0000000104d35c7c in @objc ModalPresentTransition.animateTransition(using:) ()
#13	0x0000000183d5fad8 in ___UIViewControllerTransitioningRunCustomTransition_block_invoke_2 ()
#14	0x0000000183e8ca2c in +[UIInputResponderController _pinInputViewsForInputResponderController:onBehalfOfResponder:duringBlock:] ()
#15	0x0000000183d5fa58 in ___UIViewControllerTransitioningRunCustomTransition_block_invoke.641 ()
#16	0x00000001848d9104 in +[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:] ()
#17	0x0000000183d5f8c0 in _UIViewControllerTransitioningRunCustomTransition ()
#18	0x0000000183c27298 in __56-[UIPresentationController runTransitionForCurrentState]_block_invoke.466 ()
#19	0x0000000184887504 in -[_UIAfterCACommitBlock run] ()
#20	0x00000001843feea0 in _runAfterCACommitDeferredBlocks ()
#21	0x00000001843eeffc in _cleanUpAfterCAFlushAndRunDeferredBlocks ()
#22	0x000000018441f14c in _afterCACommitHandler ()
#23	0x0000000180364fc4 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#24	0x000000018035f73c in __CFRunLoopDoObservers ()
#25	0x000000018035fc9c in __CFRunLoopRun ()
#26	0x000000018035f3bc in CFRunLoopRunSpecific ()
#27	0x000000018afdd70c in GSEventRunModal ()
#28	0x00000001843f03d0 in -[UIApplication _run] ()
#29	0x00000001843f51ac in UIApplicationMain ()
#30	0x0000000102f11658 in main a XXX/AppDelegate.swift:21
#31	0x0000000180224554 in start ()

Steps to reproduce

Code example that reproduces the issue

        let appOptionsViewController = AppOptionsViewController()

        let navigationController = UINavigationController(rootViewController: appOptionsViewController)
        navigationController.delegate = self

        // always re-allocate floating panel because it has some annoying re-use bugs
        let floatingPanel = FloatingPanelController.customFloatingPanel()
        floatingPanel.delegate = self

        floatingPanel.set(contentViewController: navigationController)
        switch floatingPanel.traitCollection.userInterfaceIdiom {
        case .pad:
            floatingPanel.layout = MenuFullFloatingPaneliPadLayout()
        default:
            floatingPanel.layout = MenuFullFloatingPaneliPhoneLayout()
        }

        self._routerController = RouterController(viewController: floatingPanel)
        self._floatingPanel = floatingPanel
        self.viewController.present(floatingPanel, animated: true)

How do you display panel(s)?

  • Present modally

How many panels do you displays?

  • 1
  • 2+

Environment

Library version

Installation method

  • CocoaPods

iOS version(s)

14.5

Xcode version

12.5 (12E262)

piemonte avatar Jun 05 '21 17:06 piemonte

9958fc50178c298b9a57b4f0bce3c623dba3bc30 commit probably causes this issue.


I don't know why UIViewPropertyAnimator.finishAnimation is called at #5 frame because it's called only when fpc.transitionAnimator is NOT nil 🤔

#5	0x0000000104cf2484 in Core.interruptAnimationIfNeeded() at XXX/Pods/FloatingPanel/Sources/Core.swift:543

On the other hand, FloatingPanelController.show is called at #10 frame when fpc.transitionAnimator is nil.

#10	0x0000000104d35910 in ModalPresentTransition.interruptibleAnimator(using:) at XXX/Pods/FloatingPanel/Sources/Transitioning.swift:103

scenee avatar Jul 03 '21 05:07 scenee

Got exactly the same thing here. animator.finishAnimation() crashes with a finishAnimationAtPosition: should only be called on a stopped animation.

fuggly avatar Jul 27 '21 05:07 fuggly

OMG... I would like to fix this as soon as possible...

scenee avatar Jul 31 '21 01:07 scenee

Hey, @scenee

I have the same bug but I don't give a UINavigationController, just a UIViewcontroller

version : 2.4.0

Thank you

Sebastien

 func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
        guard
            let fpc = transitionContext.viewController(forKey: .to) as? FloatingPanelController
        else { fatalError() }

        if let animator = fpc.transitionAnimator {
            return animator
        }

        fpc.suspendTransitionAnimator(true)
        fpc.show(animated: true) { [weak fpc] in
            fpc?.suspendTransitionAnimator(false)
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
        return fpc.transitionAnimator! <<- crash here, transitionAnimator is nil
    }

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. FloatingPanel/Transitioning.swift:107: Fatal error: Unexpectedly found nil while unwrapping an Optional value 2021-08-18 11:18:21.975042+0200 ApplicationMaitre[22486:6259534] FloatingPanel/Transitioning.swift:107: Fatal error: Unexpectedly found nil while unwrapping an Optional value (lldb)

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = Fatal error: Unexpectedly found nil while unwrapping an Optional value
    frame #0: 0x00000001a7104060 libswiftCore.dylib`_swift_runtime_on_report
    frame #1: 0x00000001a716f6a4 libswiftCore.dylib`_swift_stdlib_reportFatalErrorInFile + 188
    frame #2: 0x00000001a6e41e90 libswiftCore.dylib`closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in Swift._assertionFailure(_: Swift.StaticString, _: Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 380
    frame #3: 0x00000001a6e415a0 libswiftCore.dylib`Swift._assertionFailure(_: Swift.StaticString, _: Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 376
  * frame #4: 0x000000010a2f7c0c FloatingPanel`ModalPresentTransition.interruptibleAnimator(transitionContext=0x000000011876b3d0, self=0x0000000115a4d1a0) at Transitioning.swift:107:38
    frame #5: 0x000000010a2f7ebc FloatingPanel`ModalPresentTransition.animateTransition(transitionContext=0x000000011876b3d0, self=0x0000000115a4d1a0) at Transitioning.swift:111:14
    frame #6: 0x000000010a2f7f28 FloatingPanel`@objc ModalPresentTransition.animateTransition(using:) at <compiler-generated>:0
    frame #7: 0x00000001a56bad78 UIKitCore`___UIViewControllerTransitioningRunCustomTransition_block_invoke_2 + 72
    frame #8: 0x00000001a57e9034 UIKitCore`+[UIInputResponderController _pinInputViewsForInputResponderController:onBehalfOfResponder:duringBlock:] + 104
    frame #9: 0x00000001a56bacf8 UIKitCore`___UIViewControllerTransitioningRunCustomTransition_block_invoke.641 + 176
    frame #10: 0x00000001a6246d88 UIKitCore`+[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:] + 172
    frame #11: 0x00000001a56bab60 UIKitCore`_UIViewControllerTransitioningRunCustomTransition + 572
    frame #12: 0x00000001a5581b74 UIKitCore`__56-[UIPresentationController runTransitionForCurrentState]_block_invoke.466 + 2200
    frame #13: 0x00000001a61f3cfc UIKitCore`-[_UIAfterCACommitBlock run] + 64
    frame #14: 0x00000001a5d637dc UIKitCore`_runAfterCACommitDeferredBlocks + 296
    frame #15: 0x00000001a5d52ca8 UIKitCore`_cleanUpAfterCAFlushAndRunDeferredBlocks + 200
    frame #16: 0x00000001a5d83ff8 UIKitCore`_afterCACommitHandler + 76
    frame #17: 0x00000001a342e588 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
    frame #18: 0x00000001a3428bb8 CoreFoundation`__CFRunLoopDoObservers + 588
    frame #19: 0x00000001a3429154 CoreFoundation`__CFRunLoopRun + 1012
    frame #20: 0x00000001a3428818 CoreFoundation`CFRunLoopRunSpecific + 572
    frame #21: 0x00000001b9b2e570 GraphicsServices`GSEventRunModal + 160
    frame #22: 0x00000001a5d540e8 UIKitCore`-[UIApplication _run] + 1052
    frame #23: 0x00000001a5d59664 UIKitCore`UIApplicationMain + 164
    frame #24: 0x00000001b654787c libswiftUIKit.dylib`UIKit.UIApplicationMain(Swift.Int32, Swift.Optional<Swift.UnsafeMutablePointer<Swift.UnsafeMutablePointer<Swift.Int8>>>, Swift.Optional<Swift.String>, Swift.Optional<Swift.String>) -> Swift.Int32 + 100
    frame #25: 0x0000000102b701dc ApplicationMaitre`static UIApplicationDelegate.main() at <compiler-generated>:0
    frame #26: 0x0000000102b70154 ApplicationMaitre`static AppDelegate.$main(self=AppDelegate) at AppDelegate.swift:20:1
    frame #27: 0x0000000102b71364 ApplicationMaitre`main at <compiler-generated>:0
    frame #28: 0x00000001a3107140 libdyld.dylib`start + 4
(lldb) 

sebastienL avatar Aug 18 '21 10:08 sebastienL

Thank you for your report, @sebastienL. Now I'm considering to remove the commit that causes this issue for now.

scenee avatar Aug 19 '21 23:08 scenee

Hi @scenee,

Do you know the date of the new version?

The crash seems to be reproduced 100% on ios15 I think I revert to an old version.

Thanks

sebastienL avatar Aug 30 '21 08:08 sebastienL

Hi @scenee

I think I found on my side

At the opening of the bottom sheet I made a move. I think that's what made it crash

I test my correction

Seb

sebastienL avatar Sep 02 '21 14:09 sebastienL