Search code examples
objective-canimationcalayertvos

Animating a sub layer during focus change


Xcode 8 beta 6, tvOS Beta 6

I have a tvOS app where I want to animate a background of a control when it gets or looses focus. I have set the control to have 'Custom' focus and implemented didUpdateFocusInContext:withAnimationCoordinator: on the control. Here's the code:

-(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
  withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {

    // Create the layer if we don't have it.
    if (!self->_focusLayer) {
    // ... Create a new CALayer and store it in _focusLayer
    }

    // Animate in or out.
    if (context.nextFocusedView == self) {

        if (! self->_focusLayer.superlayer) {
            STLog(self, @"Adding focus");
            self->_focusLayer.opacity = 0.0f;
            [self.layer addSublayer:self->_focusLayer];
            [coordinator addCoordinatedAnimations:^{
                self->_focusLayer.opacity = 1.0f;
            }
                                       completion:NULL];
        }

    } else {

        if (self->_focusLayer.superlayer) {
            STLog(self, @"Removing focus");
            [coordinator addCoordinatedAnimations:^{
                self->_focusLayer.opacity = 0.0f;
            }
                                       completion:^{
                                           [self->_focusLayer removeFromSuperlayer];
                                       }];
        }
    }
}

Everything works except the animation of the opacity of the sub layer. I've searched the net and all the examples I've found indicate to me this should work. I've also tried switching to using a CABasicAnimation with no luck.

Anyone know why this is not working?


Solution

  • The focus coordinator is not an animation block itself. It just coordinates various animations to happen simultaneously. Since an opacity change is not an animation itself, you need to do your opacity or alpha changes within a UIView Animation block to make it an animation, which you add to the coordinator.

    Try this:

    -(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
          withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
    
        // Create the layer if we don't have it.
        if (!self->_focusLayer) {
            // ... Create a new CALayer and store it in _focusLayer
        }
    
        // Animate in or out.
        if (context.nextFocusedView == self) {
    
            if (! self->_focusLayer.superlayer) {
                STLog(self, @"Adding focus");
                self->_focusLayer.opacity = 0.0f;
                [self.layer addSublayer:self->_focusLayer];
                [coordinator addCoordinatedAnimations:^{
    
                    [UIView animateWithDuration: 0.4 animations:^{
                        self->_focusLayer.opacity = 1.0f;
                    } completion:NULL];
    
                } completion:NULL];
            }
    
        } else {
    
            if (self->_focusLayer.superlayer) {
                STLog(self, @"Removing focus");
                [coordinator addCoordinatedAnimations:^{
    
                    [UIView animateWithDuration:0.4 animations:^{
                        self->_focusLayer.opacity = 0.0f;
                    } completion:NULL];
    
                } completion:^{
                    [self->_focusLayer removeFromSuperlayer];
                }];
            }
        }
    }
    

    Please note that the above code is typed here and not tested in a project.

    Also note that the Animation duration is ignored by the animation coordinator and the default focus animation duration is used, unless you set an animation option to override inherited durations using:

    options:UIViewAnimationOptionOverrideInheritedDuration
    

    More about managing the focus animation coordinator here:

    https://developer.apple.com/reference/uikit/uifocusanimationcoordinator