考虑两个在时间上重叠的变化如下所示:
|---- change x ---|
|---- change y ----|
如果两个区间是任意且重叠的,则可以用三个动画来表示:一个单独改变一个维度,一个同时改变两个维度,另一个单独改变一个维度。
您可以看到有很多方法可以指定这一点,但让我们尝试一种简单的方法。为了避免复合比例的算术,让我们指定一个维度、一个像素变化和一个持续时间。比如……
@[ @{@"dimension":@"width", @"delta":@10, @"duration":0.2},
@{@"dimension":@"both", @"delta":@40, @"duration":0.8},
@{@"dimension":@"width", @"delta":@10, @"duration":0.2} ]
... 意味着较长的宽度变化跨越较短的高度变化。你可以看到这是一个非常完整的语言来完成你想要的。
我们需要一个动画方法来连续执行更改。一种简单的方法是将操作数组视为待办事项列表。递归算法说:要做一列事情,先做第一个,然后再做剩下的……
- (void)animateView:(UIView *)view actions:(NSArray *)actions completion:(void (^)(BOOL))completion {
if (actions.count == 0) return completion(YES);
NSDictionary *action = actions[0];
NSArray *remainingActions = [actions subarrayWithRange:NSMakeRange(1, actions.count-1)];
[self animateView:view action:action completion:^(BOOL finished) {
[self animateView:view actions:remainingActions completion:completion];
}];
}
对于动画,您可能希望对中间动画使用线性时序曲线,但我可以看到您变得更加精细,并在列表的开头和结尾更改时序曲线。
- (void)animateView:(UIView *)view action:(NSDictionary *)action completion:(void (^)(BOOL))completion {
NSString *dimension = action[@"dimension"];
CGFloat delta = [action[@"delta"] floatValue];
NSTimeInterval duration = [action[@"duration"] floatValue];
CGRect frame = view.frame;
if ([dimension isEqualToString:@"width"]) {
frame = CGRectInset(frame, -delta, 0);
} else if ([dimension isEqualToString:@"height"]) {
frame = CGRectInset(frame, 0, -delta);
} else {
frame = CGRectInset(frame, -delta, -delta);
}
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
view.frame = frame;
} completion:completion];
}
如果字典数组太笨拙而无法指定(而且它相当笼统),您可以在顶部添加一些便利方法,为调用者提供一些更简单的方案并构建更通用的表示数组。