Hero: Screen freezes (no crash just frozen)

Created on 26 Apr 2018  路  24Comments  路  Source: HeroTransitions/Hero

I have implemented this logic on view controller A with presents view controller B. View controller be will sometimes just fail to dismiss and remain stuck on screen with no way out except by force killing the app. This does not cause any exception just a frozen app.

View controller A presenting B:

let vc = PrizeDetailsViewController() vc.hero.isEnabled = true vc.hero.modalAnimationType = .selectBy(presenting: .pageIn(direction: .left), dismissing: .pageOut(direction: .right))

This is the dismiss logic for view controller B, it has a pan gesture recognizer and a back button. A and B are regular UIViewController (no UINavigationController).

dismiss logic in B:

func backButtonPressed(sender: UIButton) { hero.dismissViewController() }

`func handlePan(gestureRecognizer:UIPanGestureRecognizer) {
let translation = panGR.translation(in: nil)
let progress = translation.x / 2 / view.bounds.width

    if translation.x < 0 {
        Hero.shared.cancel()
    }

    switch panGR.state {
    case .began:
        // begin the transition as normal
        //dismiss(animated: true, completion: nil)
        hero.dismissViewController()
    case .changed:
        Hero.shared.update(progress)

        // update views' position (limited to only vertical scroll)
        Hero.shared.apply(modifiers: [.position(CGPoint(x:translation.x/2.0 + view.center.x, y:view.center.y))], to: view)
        //            Hero.shared.apply(modifiers: [.position(CGPoint(x:nameLabel.center.x, y:translation.y + nameLabel.center.y))], to: nameLabel)
    //            Hero.shared.apply(modifiers: [.position(CGPoint(x:descriptionLabel.center.x, y:translation.y + descriptionLabel.center.y))], to: descriptionLabel)
    default:
        // end or cancel the transition based on the progress and user's touch velocity
        if progress + panGR.velocity(in: nil).x / view.bounds.width > 0.3 {
            Hero.shared.finish()
        } else {
            Hero.shared.cancel()
        }
    }
}`

This works 90% of the time but for some reason will just stop randomly. I am wondering if I am setting it up properly.

Thanks

Most helpful comment

Call "finish()" on the main thread.
This solved my problem when I use push transitions

All 24 comments

This happens on mutliple screens where this type of behaviour is implemented.

Why do you have these line here:

if translation.x < 0 {
Hero.shared.cancel()
}

Hi,

This makes sure the user cannot swipe in the opposite direction to dismiss the view. Without it the user can pan from right edge to left edge.

I'm facing this issue too, i.e. freezing of the app without crash. My app has a PageViewController (PVC), each parent view presents different child controllers using Hero. The child controllers may have child controllers too, e.g. search result -> product view -> product details view. Randomly the child views might freeze, the app is unresponsive but hasn't crashed.

I removed Hero and the problem doesn't happen. Hints on how to debug this are very welcome as the app doesn't crash and there's no exception.

Having exactly the same issue with a very similar code. I also have a PageViewController as @petard.

It seems that the view controller is properly dismissed (or popped) but there are a snapshot of the view controller on the screen which does nothing. Also, that snapshot does not appear in the view inspector.

@petard @jjjeeerrr111 did you find any solution for this?

Hello guys, this is also happening to me. @lkzhao @petard @jjjeeerrr111 would be awesome if you could provide more info about how this is going. Thanks!

@ManueGE thanks for providing more details! did you guys found a way to reproduce the issue? for me its totally random which is probably not very helpful to find a fix

@petard I am able to reproduce it now, it happens to me if I do a really quick pan gesture. Still I have no idea about why is it happening. I tried to do the same thing with the hero examples but no luck.

are you able to reproduce it this way?

@ManueGE I tried panning around a lot but it hasn't happened yet.

@petard finally we fixed it. We were using a pop/push transition and it failed randomly. Now we have migrated to a modal transition and it works perfectly. I couldn't say why this happened with the navigation controller, though.

Thanks @ManueGE I had the exact same issue - substituting a push transition with a modal transition fixed it!

Details of my implementation: interactive transition driven by a UIPanGestureRecognizer (panning too fast causes the UI to freeze quite frequently, either when bringing up the new screen or when dismissing it) - in my case no PageViewController involved, but I do have a view controller hierarchy with parent & child view controllers

Having same issue, but with modal transition, not push. Using Hero from master branch. When panning with gesture issue not being reproduced, but when I do dismiss(animated: true, completion: nil) or hero.dismissViewController() app gets frozen from time to time

I have this problem. But not often work. I follow this topic.

I've tried only applying modal transitions to the first VC on the top of the page view controller but the issues persists. Moving everything to modal would require quite a bit of refactoring as the app depends on the navigation bar.

@lkzhao Do you have any insights on how to fix this or what could be the root cause?

Hi everybody, my problem is as if solved. If you want try;

  1. Remove Pods folder
  2. Add Podfile in pod 'Hero', '~> 1.0.1'
  3. pod repo update && pod install

sometimes pods folder is break down

Call "finish()" on the main thread.
This solved my problem when I use push transitions

Having same issue, but with modal transition, not push. Using Hero from master branch. When panning with gesture issue not being reproduced, but when I do dismiss(animated: true, completion: nil) or hero.dismissViewController() app gets frozen from time to time

try adding:
Hero.shared.finish(animate: false)

after dismissing the view controller

Calling finish and cancel like this solved the problem for me:

extension HeroTransition {
    func cancelOnMain(animate: Bool = true) {
        DispatchQueue.main.async {
            Hero.shared.cancel(animate: animate)
        }
    }

    func finishOnMain(animate: Bool = true) {
        DispatchQueue.main.async {
            Hero.shared.finish(animate: animate)
        }
    }
}

I was already calling them on the main thread (tested) so it must have been this async that helped...

Calling finish and cancel like this solved the problem for me:

extension HeroTransition {
    func cancelOnMain(animate: Bool = true) {
        DispatchQueue.main.async {
            Hero.shared.cancel(animate: animate)
        }
    }

    func finishOnMain(animate: Bool = true) {
        DispatchQueue.main.async {
            Hero.shared.finish(animate: animate)
        }
    }
}

I was already calling them on the main thread (tested) so it must have been this async that helped...

I added this extension and never see these functions get called.

@jjjeeerrr111 @petard @emrekaranfil @ManueGE @kluku @tifroz @pablogeek

Any solution on this facing same issue with latest version of library on fast swipe I am using navigation controller and push

I鈥檓 only helping manage and review at this time, I don鈥檛 know enough about the inner workings to make code edits without spending a lot of time manually running tests.

I haven鈥檛 had a chance to read every comment in this thread, is there a code sample or a pull request someone can point me to I can review?

Edit:

This looks like a simple threading problem actually after glancing at some of the comments above. The theory about it being a screenshot that iOS makes for the animation layers is plausible. If I member correctly court animation layers and you I鈥檝e use have separate trees, the CA layers are used in quarts rendering and transitions, I鈥檓 speculating here but possibly iOS is snapshot interview controller as a core graphics Texture and the rendering tree is getting out of sync.

If that鈥檚 the case, it should be easy to detect if they dismissed if you controller calls dealloc()

I suspect as much due to some people mentioning turning off animations or forcing main thread solves the problem.

Whose responsibility the thread management is I鈥檓 not sure. If you create your view controllers it鈥檚 your responsibility to call non-thread safe methods on their allocating thread.

So even if there is a threading bag, again I don鈥檛 know the details of this code that well, it might not necessarily mean it鈥檚 something hero can resolve.

Another idea is to check thread sanity and print warnings in debug or add assertions to help debug threading issues in hero during development.

Hello guys,
I am having the same problem have anyone got to manage to fix it?

@objc func leftSwipeDismiss(gestureRecognizer: UIPanGestureRecognizer) {

        let translation = gestureRecognizer.translation(in: nil)
        let progress = translation.x / 2 / view.bounds.width
        let gestureView = gestureRecognizer.location(in: self.view)

        switch gestureRecognizer.state {
        case .began:
            if gestureView.x <= 30 {
                hero_dismissViewController()
            }

        case .changed:
            let translation = gestureRecognizer.translation(in: nil)
            let progress = translation.x / 2 / view.bounds.width
            Hero.shared.update(progress)
        default:
            if progress + gestureRecognizer.velocity(in: nil).x / view.bounds.width > 0.3 {
                Hero.shared.finish(animate: true)
            } else {
                Hero.shared.cancel(animate: true)
            }
        }

    }

Hi!
I have same problem, and I understood that it appears when you call Hero.shared.cancel() and then call self.hero.dismissViewController() (or self.dismiss()).
So, check:

  1. all places where you call dismiss/dismissViewController
  2. be sure that you don't call Hero.shared.cancel() before dismiss/dismissViewController
  3. change "default" case to ".ended"

Was facing same issue, placing transition flag check before all gesture recognizer states except began fixed. Main issue was that Hero.shared.update was involuntary set before dismiss was called
guard Hero.shared.transitioning else { return }

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cristinaITdeveloper picture cristinaITdeveloper  路  4Comments

CalebeNP picture CalebeNP  路  5Comments

imfractured picture imfractured  路  5Comments

patchthecode picture patchthecode  路  4Comments

brendan-guegan-orange picture brendan-guegan-orange  路  4Comments