lukech lukech - 3 months ago 24
iOS Question

Animate UITableView index, a la Apple Music?

Simple question:

Apple Music's table views only show an A-Z section index on the right edge once they've been scrolled down past a certain threshold, and that index animates in and out nicely.

I've been able to trigger the appearing / disappearing behaviour with the code below, but the index just pops in and out, there's no animation, and I can't find any way to get one to show up.

func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]! {
if tableView.contentOffset.y > 88 {
return DataManager.sharedManager.frc!.sectionIndexTitles
} else {
return []
}
}

func scrollViewDidScroll(scrollView: UIScrollView) {
tableView.reloadSectionIndexTitles()
}


This basically means each time the scroll view ticks, it'll reload the section indexes, then conditionally hide or show the index based on the offset of the table. As I say, it works, but it doesn't animate the index, and I'd really love that functionality if possible.

Answer

Seems like it is not possible to do that, ate least apple doesn't provide any API to animate the section index view. I am able to slide in/out the indexes, but it doesn't resize the cells' contentView.

When you animate the section index view, right after the animation it comes back to its initial position. So I am basically setting the color to clearColor/black when hidden/visible.

I am not sure if apple approves this code since it's kind of using undocumented APIs

- (UIView *)indexTitlesView {
    NSArray *subViews = [self.tableView subviews];
    for (UIView *view in subViews) {
        if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewIndex"]) {
            return view;
        }
    }
    return nil;
}

- (void)slideIndexTitlesViewIn {
    UIView *indexTitlesView = [self indexTitlesView];

    CABasicAnimation *contentPositionAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
    contentPositionAnimation.fromValue = @(CGRectGetWidth(indexTitlesView.frame));
    contentPositionAnimation.toValue = @(0);
    contentPositionAnimation.additive = YES;
    contentPositionAnimation.duration = 0.3;
    contentPositionAnimation.delegate = self;
    contentPositionAnimation.removedOnCompletion = NO;
    contentPositionAnimation.fillMode = kCAFillModeForwards;

    [indexTitlesView.layer removeAllAnimations];
    [indexTitlesView.layer addAnimation:contentPositionAnimation forKey:@"slideInAnimation"];

    self.tableView.sectionIndexColor = [UIColor blackColor];
}

- (void)slideIndexTitlesViewOut {
    UIView *indexTitlesView = [self indexTitlesView];

    CABasicAnimation *contentPositionAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
    contentPositionAnimation.fromValue = @(0);
    contentPositionAnimation.toValue = @(CGRectGetWidth(indexTitlesView.frame));
    contentPositionAnimation.additive = YES;
    contentPositionAnimation.duration = 0.3;
    contentPositionAnimation.delegate = self;
    contentPositionAnimation.removedOnCompletion = NO;
    contentPositionAnimation.fillMode = kCAFillModeForwards;

    [indexTitlesView.layer removeAllAnimations];
    [indexTitlesView.layer addAnimation:contentPositionAnimation forKey:@"slideOutAnimation"];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    UIView *indexTitlesView = [self indexTitlesView];
    NSArray *keys = [indexTitlesView.layer animationKeys];
    for (NSString *key in keys) {
        if ([indexTitlesView.layer animationForKey:key] == anim) {
            if ([key isEqualToString:@"slideOutAnimation"] && flag == YES) {
                self.tableView.sectionIndexColor = [UIColor clearColor];
            }
            [indexTitlesView.layer removeAllAnimations];
            return;
        }
    }
}