Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screen freezes (no crash just frozen) #451

Closed
jjjeeerrr111 opened this issue Apr 26, 2018 · 25 comments
Closed

Screen freezes (no crash just frozen) #451

jjjeeerrr111 opened this issue Apr 26, 2018 · 25 comments

Comments

@jjjeeerrr111
Copy link

jjjeeerrr111 commented Apr 26, 2018

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

@jjjeeerrr111
Copy link
Author

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

@lkzhao
Copy link
Collaborator

lkzhao commented Apr 27, 2018

Why do you have these line here:

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

@jjjeeerrr111
Copy link
Author

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.

@petard
Copy link

petard commented Jun 6, 2018

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.

@ManueGE
Copy link
Contributor

ManueGE commented Jun 13, 2018

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?

@pablogeek
Copy link

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!

@petard
Copy link

petard commented Jun 13, 2018

@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

@ManueGE
Copy link
Contributor

ManueGE commented Jun 13, 2018

@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?

@petard
Copy link

petard commented Jun 13, 2018

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

@ManueGE
Copy link
Contributor

ManueGE commented Jun 13, 2018

@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.

@tifroz
Copy link

tifroz commented Jun 13, 2018

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

@Satchitananda
Copy link

Satchitananda commented Jun 14, 2018

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

@emrekaranfil
Copy link

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

@petard
Copy link

petard commented Jun 19, 2018

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?

@emrekaranfil
Copy link

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

@krekeroff90
Copy link

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

@dmachacon
Copy link

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

@kluku
Copy link

kluku commented Sep 26, 2019

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...

@StainlessStlRat
Copy link

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.

@amodkanthe
Copy link

@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

@JoeMatt
Copy link
Collaborator

JoeMatt commented May 8, 2020

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

I haven’t 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’ve use have separate trees, the CA layers are used in quarts rendering and transitions, I’m 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’s 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’m not sure. If you create your view controllers it’s your responsibility to call non-thread safe methods on their allocating thread.

So even if there is a threading bag, again I don’t know the details of this code that well, it might not necessarily mean it’s 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.

@uonuon
Copy link

uonuon commented Jul 23, 2020

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)
            }
        }

    }

@forafontov98
Copy link

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"

@iKisliy
Copy link

iKisliy commented May 4, 2021

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 }

@sekny
Copy link

sekny commented Aug 22, 2023

2023 this issue still happened on the latest version, I noticed that it freezes during call Hero.shared.apply .
btw call function in the main thread like @kluku mention does not solve this problem

Below is my code and show where it stuck.

func handlePanGesture(_ panGesture: UIPanGestureRecognizer) {
		let percent = max(panGesture.translation(in: view).x, 0) / view.frame.width
		let direction = panGesture.direction(in: view)
		
		let translation = panGesture.translation(in: view)
		let newConstant = translation.x
		
		switch panGesture.state {
		case .began:
			if direction == .Right && !self.isKeyboardShow {
				if self.navigationController?.isHeroEnabled ?? false {
					isEnabledHero = true
					Hero.shared.containerColor = .white
					Hero.shared.defaultAnimation = .autoReverse(presenting: .push(direction: .left))
				} else {
					isEnabledHero = false
					navigationController?.delegate = self
				}
				navigationController?.popViewController(animated: true)
			} else {
				UIApplication.topViewController()?.view.endEditing(true)
				Hero.shared.cancelOnMain()
			}
		case .changed:
			let progress = translation.x / 1.5 / view.bounds.width
			Hero.shared.update(progress)
			if let toView = Hero.shared.toViewController?.view {
				Hero.shared.apply(modifiers: [.shadowOpacity(0.1),
											  .shadowColor(.clear),
											  .opacity(1),
											  .timingFunction(.easeOut)], to: toView)
			}
			
			if let fromView = Hero.shared.fromViewController?.view {
				Hero.shared.apply(modifiers: [.shadowOpacity(0.1),
											  .shadowColor(.clear),
											  .opacity(0.65),
											  .timingFunction(.easeOut)], to: fromView)
			}
			
//========~~~~~~~~~~~> IT STUCK HERE
			
			if let percentDrivenInteractiveTransition = percentDrivenInteractiveTransition {
				percentDrivenInteractiveTransition.update(percent)
			}
		case .ended:
			let velocity = panGesture.velocity(in: view).x
			if (percent > 0.5 || velocity > 1000) {
				Hero.shared.finishOnMain()
				navigationBar.removeFromSuperview()
			} else {
				Hero.shared.cancelOnMain()
			}
		case .cancelled, .failed:
			Hero.shared.cancelOnMain()
			
		default:
			break
		}
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests