iphone - 旋转拨盘控制的核心动画困难(非常详细)

标签 iphone core-animation

我正在尝试创建一个旋转拨号控件,基本上是一组 6 位数字,它们不断旋转以产生旋转数字计的效果(类似于您的电表/水表,或者可能是扑克机,实际上非常类似于现有的 UIPickerView 控件,但具有完全不同的外观和感觉)。

到目前为止,我几乎可以正常工作,但我正处于核心动画让我感到悲伤的阶段。

它非常复杂,因此代码片段很难很好地描述正在发生的事情,所以我认为伪代码就足够了。

首先,在 View 设置方面,我有 6 个独立的 UIView(称为 NumberView1、NumberView2 等),每个 View 对应控件中的每个数字。

在每个 NumberViewX 中,我有另一个 UIView,它是一个容器 View ,称为 ContainerView1、2 等...

然后,我将 10 个 UIImageView 以不同的 Y 偏移量堆叠在一起。这些图像都是 30x30,非常漂亮。首先是 9,然后是 y 偏移 30 处的 8,然后是 y 偏移 60 处的 7,等等...一直到 y 偏移 270 处的 0。

重要提示:我的号码只会向上滚动

这些数字代表 5 位小数点数字(即 2.34677),向上滚动(例如,滚动到 2.61722)。

我还有一些字典,其中保存每个数字的当前数值以及每个数字的偏移量:

NSArray *offsets = [[NSArray alloc] initWithObjects: 
                    [NSNumber numberWithInt:0],    //9
                    [NSNumber numberWithInt:-30],  //8
                    [NSNumber numberWithInt:-60],  //7
                    [NSNumber numberWithInt:-90],  //6
                    [NSNumber numberWithInt:-120], //5
                    [NSNumber numberWithInt:-150], //4
                    [NSNumber numberWithInt:-180], //3
                    [NSNumber numberWithInt:-210], //2
                    [NSNumber numberWithInt:-240], //1
                    [NSNumber numberWithInt:-270], //0
                    nil];

NSArray *keys = [[NSArray alloc] initWithObjects: 
                 [NSNumber numberWithInt:9],
                 [NSNumber numberWithInt:8],
                 [NSNumber numberWithInt:7],
                 [NSNumber numberWithInt:6],
                 [NSNumber numberWithInt:5],
                 [NSNumber numberWithInt:4],
                 [NSNumber numberWithInt:3],
                 [NSNumber numberWithInt:2],
                 [NSNumber numberWithInt:1],
                 [NSNumber numberWithInt:0], nil];

offsetDict = [[NSDictionary alloc] initWithObjects:offsets forKeys:keys];

[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:2] forKey: [NSValue valueWithNonretainedObject: self.containerViewDollarSlot1]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:8] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace1]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:3] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace2]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:3] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace3]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:8] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace4]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:5] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace5]];

现在对于逻辑,我开始将 ContainerView 的图层属性设置为 Y 的某些偏移量,这会将当前数字移动到正确的位置,如下所示:

self.containerViewDollarSlot1.layer.transform =   CATransform3DTranslate(self.containerViewDollarSlot1.layer.transform, 0, -210, 0);
self.containerViewDecimalPlace1.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace1.layer.transform, 0, -30, 0);
self.containerViewDecimalPlace2.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace2.layer.transform, 0, -180, 0);
self.containerViewDecimalPlace3.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace3.layer.transform, 0, -180, 0);
self.containerViewDecimalPlace4.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace4.layer.transform, 0, -30, 0);
self.containerViewDecimalPlace5.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace5.layer.transform, 0, -120, 0);

它将显示数字 2.83385,这是用于测试的任意数字。

然后我在文本框中输入另一个值,然后点击启动按钮,启动动画逻辑,这就是我在核心动画中遇到困难的地方(如标题所示)

我打电话:

[self animateDecimalPlace: 0
            withAnimation: self.dollarSlot1Animation
         andContainerView: self.containerViewDollarSlot1];
[self animateDecimalPlace: 1 
            withAnimation: self.decimalPlace1Animation 
         andContainerView: self.containerViewDecimalPlace1];
//etc... for all 6 numbers

其中dollarSlot1Animation是一个CABasicAnimation ivar

该方法定义如下:

- (void) animateDecimalPlace: (int) decimalIndex withAnimation: (CABasicAnimation*)animation andContainerView: (UIView*) containerView{

NSRange decimalRange = {decimalIndex == 0 ? 0 : 2,  
                        decimalIndex == 0 ? 1 : decimalIndex};

double diff = 0;
if (decimalIndex == 0)
{
    int decimalPartTarget = [[[NSString stringWithFormat:@"%f", targetNumber] substringWithRange: decimalRange] intValue];
    int decimalPartCurrent = [[[NSString stringWithFormat:@"%f", currentValue] substringWithRange: decimalRange] intValue]; 
    diff = decimalPartTarget - decimalPartCurrent;
}
else {
    double fullDiff = targetNumber - currentValue;
    diff = [[[NSString stringWithFormat:@"%f", fullDiff] substringWithRange: decimalRange] doubleValue];
}

animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeRemoved;
animation.duration = 5.0;

NSNumber *n = [currentValuesForDecimalPlace objectForKey:[NSValue valueWithNonretainedObject: containerView]];
NSNumber *offset = (NSNumber*)[offsetDict objectForKey: n];
int rotations = [self setupNumberForAnimation: diff decimalIndex: decimalIndex forContainer: containerView];
int finalOffset = (rotations*30)+([offset intValue]);
CATransform3D translation = CATransform3DMakeTranslation(0, finalOffset, 0);

animation.fromValue = [NSValue valueWithCATransform3D:containerView.layer.transform];
animation.toValue = [NSValue valueWithCATransform3D:translation];
animation.delegate = self;
[containerView.layer addAnimation: animation forKey: [NSString stringWithFormat:@"%i", decimalIndex] ];
}

正如你所看到的(或者也许不是:))我基本上独立地对待每个数字,并告诉它转换到新的 Y 位置,但你可能已经注意到其中有一个名为 setupNumberForAnimation 这是另一个大方法,可以动态地将更多 UIImageView 添加到容器 UIView 的上方初始 10 个图像图 block 。

例如,一开始有 10 个图 block ,如果我想将表盘向上滚动 21 个点(例如,从 3 到 24),我需要在 9 以上添加 15 个新数字,然后为翻译设置动画容器 View 一直到最顶部的图像图 block 。

滚动完成后,我删除动态添加的 UIImageView 并将容器 View 的 y 偏移重新定位到 0 到 -270 之间的值(本质上最终达到相同的数字) ,但删除所有不必要的 ImageView )。

这提供了我想要的流畅动画。它有效。相信我:)

我的问题有两个,首先,当动画停止时,核心动画将动画更改恢复到图层,因为我已经设置了 animation.fillMode = kCAFillModeRemoved; 我无法弄清楚动画之后是如何进行的完成,设置容器层的 y 偏移量,我知道这听起来很奇怪,但如果我将 fillMode 属性设置为 kCAFillModeForwards,我尝试的任何操作似乎都不会对该层产生影响。

其次,当动画停止并恢复更改时,在动画恢复平移和再次设置它之间会出现微小的闪烁,我不知道如何解决这个问题。

我知道这里有很多细节和特殊性,但是任何有关如何实现这一点的帮助或想法将不胜感激。

非常感谢

最佳答案

我记得WWDC10的CoreAnimation视频中有关于动画跳回之前状态的问题,您可以从苹果免费下载。

我必须稍后查找它,但是当然有委托(delegate)方法 - (void)animationDidStop:(CAAnimation *)animation finish:(BOOL)flag 当动画播放时会调用它完毕。这将是一个做家务的好地方。也许您可以通过查看动画的 toValue 属性来设置图层的 y 偏移。

这只是一个远景,但也许它指向了正确的方向。

关于iphone - 旋转拨盘控制的核心动画困难(非常详细),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3615021/

相关文章:

iphone - 在 UIImagePicker 中压缩视频时模拟器挂起

Iphone 应用程序在横向启动后没有响应

objective-c - CABasicAnimation 当前耗时

objective-c - 如何在两个 CATransform3D 之间手动插值?

objective-c - 使用 CA 制作动画时圆形 slider 的怪异行为

iphone - 在 iOS 中加密和解密密码

ios - CGImageRef 消耗大量内存

iphone - 如何显示indexPath值

iphone - 为什么 PNG 图像有时会根据其在 View 中的位置而变得模糊

swift - 使用 Swift3 子类化 CALayer + 隐式动画