ios - UIPercentDrivenInteractiveTransition doesn't get to animation's completion on fast gesture -
i have created interactive transition. func animatetransition(transitioncontext: uiviewcontrollercontexttransitioning)
quite normal, container uiview
, add 2 uiviewcontrollers
, animation changes in uiview.animatewithduration(duration, animations, completion)
.
i add uiscreenedgepangesturerecognizer
uiviewcontroller
. works except when quick pan.
in last scenario, app not responsive, still on same uiviewcontroller
(the transition seems not have worked) background tasks run. when run debug view hierarchy
, see new uiviewcontroller
instead of previous one, , previous 1 (at least uiview
) stands supposed stand @ end of transition.
i did print out , check points , can when problem occurs, the animation's completion (the 1 in animatetransition
method) is not reached, cannot call transitioncontext.completetransition
method complete or not transition.
i see pan goes uigesturerecognizerstate.began
straight uigesturerecognizerstate.ended
without going through uigesturerecognizerstate.changed
.
when goes through uigesturerecognizerstate.changed
, both translation
and velocity
stay same every uigesturerecognizerstate.changed
states.
edit :
here code:
animatetransition
method
func animatetransition(transitioncontext: uiviewcontrollercontexttransitioning) { self.transitioncontext = transitioncontext let containerview = transitioncontext.containerview() let screens: (from: uiviewcontroller, to: uiviewcontroller) = (transitioncontext.viewcontrollerforkey(uitransitioncontextfromviewcontrollerkey)!, transitioncontext.viewcontrollerforkey(uitransitioncontexttoviewcontrollerkey)!) let parentviewcontroller = presenting ? screens.from : screens.to let childviewcontroller = presenting ? screens.to : screens.from let parentview = parentviewcontroller.view let childview = childviewcontroller.view // positionning "to" viewcontroller's view animation if presenting { offstagechildviewcontroller(childview) } containerview.addsubview(parentview) containerview.addsubview(childview) let duration = transitionduration(transitioncontext) uiview.animatewithduration(duration, animations: { if self.presenting { self.onstageviewcontroller(childview) self.offstageparentviewcontroller(parentview) } else { self.onstageviewcontroller(parentview) self.offstagechildviewcontroller(childview) }}, completion: { finished in if transitioncontext.transitionwascancelled() { transitioncontext.completetransition(false) } else { transitioncontext.completetransition(true) } }) }
gesture , gesture handler:
weak var fromviewcontroller: uiviewcontroller! { didset { let screenedgepanrecognizer = uiscreenedgepangesturerecognizer(target: self, action: "presentingviewcontroller:") screenedgepanrecognizer.edges = edge fromviewcontroller.view.addgesturerecognizer(screenedgepanrecognizer) } } func presentingviewcontroller(pan: uipangesturerecognizer) { let percentage = getpercentage(pan) switch pan.state { case uigesturerecognizerstate.began: interactive = true presentviewcontroller(pan) case uigesturerecognizerstate.changed: updateinteractivetransition(percentage) case uigesturerecognizerstate.ended: interactive = false if finishpresenting(pan, percentage: percentage) { finishinteractivetransition() } else { cancelinteractivetransition() } default: break } }
any idea might happen?
edit 2:
here undisclosed methods:
override func getpercentage(pan: uipangesturerecognizer) -> cgfloat { let translation = pan.translationinview(pan.view!) return abs(translation.x / pan.view!.bounds.width) } override func onstageviewcontroller(view: uiview) { view.transform = cgaffinetransformidentity } override func offstageparentviewcontroller(view: uiview) { view.transform = cgaffinetransformmaketranslation(-view.bounds.width / 2, 0) } override func offstagechildviewcontroller(view: uiview) { view.transform = cgaffinetransformmaketranslation(view.bounds.width, 0) } override func presentviewcontroller(pan: uipangesturerecognizer) { let location = pan.locationinview((fromviewcontroller as! mainviewcontroller).tableview) let indexpath = (fromviewcontroller as! mainviewcontroller).tableview.indexpathforrowatpoint(location) if indexpath == nil { pan.state = .failed return } fromviewcontroller.performseguewithidentifier("chartsegue", sender: pan) }
- i remove "over" adding lines => didn't fix it
- i added
updateinteractivetransition
in.began
, in.ended
, in both => didn't fix it - i turned on shouldrasterize on layer of view of
toviewcontroller
, let on time => didn't fix it
but question why, when doing fast interactive gesture, not responding enough
it works fast interactive gesture long leave finger long enough. example, if pan fast on more (let say) 1cm, it's ok. it's not ok if pan fast on small surface (let again) less 1cm
possible candidates include views being animated complicated (or have complicated effects shading)
i thought complicated view don't think view complicated. there bunch of buttons , labels, custom uicontrol
acting segmented segment, chart (that loaded once controller appeared) , xib loaded inside viewcontroller.
ok created project minimum classes , objects in order trigger problem. trigger it, fast , brief swipe right left.
what noticed works pretty first time if drag view controller first time, harder trigger (even impossible?). while in full project, doesn't matter.
when diagnosing problem, noticed gesture's change , ended state events taking place before animatetransition
ran. animation canceled/finished before started!
i tried using gcd animation synchronization queue ensure updating of uipercentdriveninterativetransition
doesn't happen until after `animate:
private let animationsynchronizationqueue = dispatch_queue_create("com.domain.app.animationsynchronization", dispatch_queue_serial)
i had utility method use queue:
func dispatchtomainfromsynchronizationqueue(block: dispatch_block_t) { dispatch_async(animationsynchronizationqueue) { dispatch_sync(dispatch_get_main_queue(), block) } }
and gesture handler made sure changes , ended states routed through queue:
func handlepan(gesture: uipangesturerecognizer) { switch gesture.state { case .began: dispatch_suspend(animationsynchronizationqueue) fromviewcontroller.performseguewithidentifier("segueid", sender: gesture) case .changed: dispatchtomainfromsynchronizationqueue() { self.updateinteractivetransition(percentage) } case .ended: dispatchtomainfromsynchronizationqueue() { if isoktofinish { self.finishinteractivetransition() } else { self.cancelinteractivetransition() } } default: break } }
so, have gesture recognizer's .began
state suspend queue, , have animation controller resume queue in animationtransition
(ensuring queue starts again after method runs before gesture proceeds try update uipercentdriveninteractivetransition
object.
Comments
Post a Comment