QuickBirdEng / XCoordinator

🎌 Powerful navigation library for iOS based on the coordinator pattern
MIT License
2.24k stars 176 forks source link

Viewcontroller Can not release when use gesture to dismiss #187

Closed eyuxin closed 2 years ago

eyuxin commented 4 years ago

I use .present(LoginCoordinator()) in prepareTransition. I'm going to call the trigger method to call .dismiss when I click the close button, that's fine, of course. But when I use the slide to make my 'presentViewController' disappear, this 'presentViewController' has a memory leak. I know because the coordinator didn't call 'removeChildrenIfNeeded', but, what should I do?

eyuxin commented 4 years ago

I solved this issue in the following way:

override func presented(from presentable: Presentable?) {
       super.presented(from: presentable)
        hookPullToDismiss()
    }
private func hookPullToDismiss() {
        let block: AspectBlock = { [weak self] (object) in
            if let nav = object.instance as? UINavigationController, nav.isBeingDismissed {
                self?.unownedRouter.childTransitionCompleted()
            }
        }
        rootViewController.hook(#selector(UIViewController.viewDidDisappear(_:)), position: .after, usingBlock: block)
    }

If someone else has a better idea, bring it up.

iDevid commented 4 years ago

Have you checked if there is a memory leak inside your controller first?

eyuxin commented 4 years ago

Have you checked if there is a memory leak inside your controller first?

Sorry for the late reply. I checked that is not a memory leak inside my controller. This issue only happens when I use gesture to dismiss my controller.

pauljohanneskraft commented 4 years ago

There is not really a memory leak, but rather it is not releasing the viewController until there is any other transition being performed. We could not yet find a way to get notified about a viewController being dismissed which does not involve method swizzling - in case you are aware of a method, we would be glad to hear about it.

eyuxin commented 4 years ago

Yep, I have changed this issue's title. I'm also using method swizzling to solve this problem by hooking the coordinator.rootViewController' 'viewDidDisappear' method and judging UINavigationController.isBeingDismissed and then calling the unownedRouter.childTransitionCompleted() It seems to be working well so far, would you guys consider adding it to the project? As we know, after iOS 13, the automatic presentation style can easily cause this problem.

kevinrenskers commented 3 years ago

I'm running into the same problem where a coordinator that is shown modally is not released when you drag to close it. Is there a known workaround yet?

pauljohanneskraft commented 3 years ago

There is a workaround, but is not really nice... It would in one way or another involve method swizzling of viewDidAppear of any viewController to be presented. In case you are using RxSwift in your project already, this extension in the example project can help you building something like this:

class SomeCoordinator: <some coordinator class> {

    let disposeBag = DisposeBag()

    override func prepareTransition(for route: RouteType) -> TransitionType {
        switch route {
        case .someRoute:
            let presentable = // could be a router, coordinator or viewController
            presentable.rx.dismissal
                .subscribe(onNext: { [weak self] _ in
                    self?.removeChildrenIfNeeded()
                })
                .disposed(by: disposeBag)
            return .someTransition(presentable)
        }
    }
}

This should actually be a functional workaround although it is not really nice to work with, I'm afraid, especially when handling these cases many times over.