Closed jjjeeerrr111 closed 2 years ago
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;
pod 'Hero', '~> 1.0.1'
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
andcancel
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’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.
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:
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 }
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
}
}
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
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