0

When the screen of the iPhone orientation changes, I adjust my projection matrix of my 3D rendering to the new aspect value. However, doing this in either willRotateToInterfaceOrientation or didRotateFromInterfaceOrientation would cause the aspect ratio being wrong during the transition, because at the beginning of the animation, the screen size is still the same as before and is changed to the new bounds gradually while being rotated. Therefore I want the aspect value used for my 3D projection matrix to change gradually as well. To achieve this, I retrieve start time and duration for the rotation animation in willAnimateRotationToInterfaceOrientation:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    _aspect.isChanging = YES;
    _aspect.startedChanging = [[NSDate date] retain];
    _aspect.changeDuration = duration;
    _aspect.oldValue = self.renderer.aspect;
    _aspect.currentValue = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
}

Note that the view bound size is already set to the new value that will be valid after the animation.

Then, in update, I do the following:

- (void)update
{
    if (_aspect.isChanging) {
        float f = MIN(1, [[NSDate date] timeIntervalSinceDate:_aspect.startedChanging] / _aspect.changeDuration);
        self.renderer.aspect = _aspect.oldValue * (1-f) + _aspect.currentValue * f;
    } else {
        self.renderer.aspect = _aspect.currentValue;
    }
    [self.renderer update];
}

This already works quite well, but the render aspect change does not match the actual aspect change, which is because I'm interpolating it linearly. Therefore I tried to match the easing of the actual aspect change by throwing math functions at the problem: The best result I could get was by adding the following line:

f = 0.5f - 0.5f*cosf(f*M_PI); 

This results in almost no visible stretching of the image during the rotation, however if you look closely, it still seems to be a bit unmatched somewhere in between. I guess, the end user won't notice it, but I'm asking here if there might be a better solution, so these are my questions:

  • What is the actual easing function used for the change in aspect ratio during the rotation change animation?
  • Is there a way to get the actual width and height of the view as it is displayed during the orientation change animation? This would allow me to retrieve the in-between aspect directly.
Stefan Paul Noack
  • 3,654
  • 1
  • 27
  • 38

1 Answers1

3

On the first bullet point, you can use CAMediaTimingFunction (probably with kCAMediaTimingFunctionEaseInEaseOut) to get the control points for the curve that defines the transition. Then just use a cubic bezier curve formula.

On the second bullet point, you can use [[view layer] presentationLayer] to get a version of that view's CALayer with all current animations applied as per their current state. So if you check the dimensions of that you should get the then current values — I guess if you act upon a CADisplayLink callback then you'll be at most one frame behind.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • The solution using the `presentationLayer` dimensions seems nice and clean. I'm updating it in the `GLKViewController`'s `update` method, which is acting upon a `CADisplayLink`. The result looks perfect in the simulator's "slow motion" view, however it seems to lag behind by at least one frame on the device, which is visible, if you know it, but that's acceptable. I will just use it this way, because it is straightforward. – Stefan Paul Noack Aug 18 '12 at 19:33