简体   繁体   中英

UIView animated position jumping

I have a transition animation where I need to make it appear that an item on VC1 moves to a new location as VC2 is swiped into position. I've had no problems making this work on other transitions, but for this one I have an issue I can't figure out.

VC1 - has an image in the center at a particular size VC2 - same image is placed in the top right corner at a smaller size

So I need to move and scale, since I'm doing this in a transition I thought the right way to do this is:

  1. take image from VC2 translate and scale it to match image on VC1 (using transform)
  2. hide image on VC1
  3. animate image from VC2 back to .identity

I've successfully done this exact same thing when moving something to the top left corner. However when I do this particular animation I'm seeing my VC2 image jump as the animation starts to a new location. I only see this jump when I'm working on a device/simulator that is larger or smaller than a iPhone7, and it jumps differently based on the screen width (left for smaller, right for larger).

I've logged out the position of my image at all stages:

transform from: (x:336.0, y:35.5, w:38.0) to: (x:160.0, y:180.5, w:66.0)
xOffset: -176.0 yOffset: 145.0
transform: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: -176.0, ty: 145.0)
preanimation: (x: 160.0, y: 180.5
currentPosition: (midX: 105.0, midY: 180.5)
currentPosition: (midX: 112.312, midY: 174.476)

First line of this log shows that I'm moving my image from (336, 35) to (160, 180.5). Second line shows that my math is correct. Third line shows the transform that was created (scaling currently turned off). Fourth line shows that my image is in the correct spot just before the animation starts. Last 2 lines are returned in a cadisplaylink callback showing that my position jumps before the animation starts.

Has anyone run into something like this? Any clues how I can solve this? My images are all placed with LayoutConstraints, my animations are all run using transforms (transforming before animation then animating back to .identity).

EDIT: Some added information, when starting in landscape I noticed that my original frames are completely wrong:

transform from: (x:336.0, y:35.5, w:38.0) to: (x:368.0, y:147.666666666667, w:66.0)
xOffset: 32.0 yOffset: 112.166666666667
transform: CGAffineTransform(a: 2.66666666666667, b: 0.0, c: 0.0, d: 2.66666666666667, tx: 32.0, ty: 112.166666666667)
preanimation: (x: 368.0, y: 147.666666666667
currentPosition: (midX: 729.0, midY: 127.666666666667)

So at this point I'm thinking the issue is that my frames that I'm attempting to sync up to aren't correct before the animation because of the autolayout constraints. And when the animation starts all of the frames are re-calculated by the animation using the layout constraints, then my transforms are applied making the jump appear.

EDIT2:

I think I've got it figured out. The issue is that the view I'm animating in hasn't laid out it's subviews yet so the frames are off. By calling .setNeedsLayout() and .layoutIfNeeded() on my views during the animation setup I get the correct locations to animate between.

For the curious here are the same logs from my first edit with the addition of when the viewlifecycle methods are called:

viewDidLoad <Chalkboard.SwipeToMoveViewController: 0x7fb1f760fb70>
viewWillAppear <Chalkboard.SwipeToMoveViewController: 0x7fb1f760fb70>
transform from: (x:336.0, y:35.5, w:38.0) to: (x:368.0, y:147.666666666667, w:66.0)
xOffset: 32.0 yOffset: 112.166666666667
transform: CGAffineTransform(a: 2.66666666666667, b: 0.0, c: 0.0, d: 2.66666666666667, tx: 32.0, ty: 112.166666666667)
preanimation: (x: 368.0, y: 147.666666666667
animation Block: (to.x: 368.0, to.y: 147.666666666667 - (from.x: 368.0, from.y: 147.666666666667
viewWillLayoutSubviews <Chalkboard.SwipeToMoveViewController: 0x7fb1f760fb70>
viewDidLayoutSubviews <Chalkboard.SwipeToMoveViewController: 0x7fb1f760fb70>
currentPosition: (midX: 729.0, midY: 127.666666666667)

Just incase people don't want to read through the wall of text to find the solution the problem was that I was using the positions of views in different view controllers before all views had been laid out. I'd assumed that the entire view lifecycle had processed in my presenting view controller before the animation logic started, I was wrong.

Animation Call Order:

  1. viewDidLoad
  2. viewWillAppear
  3. animateTransition
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews

So the fix was to manually call setNeedsLayout() and layoutIfNeeded() as the start of my animateTransition so that the positions of my views were all known and could be used for transformations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM