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

时间:2010-09-01 04:18:15

标签: iphone core-animation

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

到目前为止,我几乎已经开始工作了,但我处于核心动画给我带来悲伤的阶段。

它相当复杂,所以代码片段很难给出正常的快照,所以我认为伪代码就足够了。

首先,就视图设置而言,我有6个单独的UIView s(称为NumberView1,NumberView2等...),每个都对应于控件中的每个数字。

在每个NumberViewX中,我有另一个UIView,这是一个容器视图,名为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,这是一个任意数字,用于测试。

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

我打电话:

[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 s添加到初始10个图像切片的容器UIView 上面

例如,有10个瓷砖开始,如果我想滚动拨号UP 21个点(例如从3到24)我需要在9以上添加15个新数字,然后动画翻译容器视图直到最顶部的图像拼贴。

滚动完成后,我删除动态添加的UIImageView并将容器视图的y偏移重新定位回0到-270之间的值(基本上以相同的数字结束,但删除所有不必要的图像视图)。

这给了我以后的流畅动画。它有效。相信我:))

我的问题有两个,首先当动画停止时Core Animation将动画更改恢复为图层,因为我设置了animation.fillMode = kCAFillModeRemoved;我无法弄清楚动画完成后如何设置容器图层的y offset,我知道这听起来很奇怪,但是如果我将fillMode属性设置为kCAFillModeForwards,那么我尝试的任何东西似乎都对图层产生了影响。

其次,当动画停止并且恢复变化时,动画恢复翻译和再次设置之间会有微小的闪烁,我无法弄清楚如何解决这个问题。

我知道这里有一堆细节和特殊性,但是对于如何实现这一点的任何帮助或想法都将非常感激。

非常感谢

2 个答案:

答案 0 :(得分:1)

我记得有一些关于动画在WWDC10的CoreAnimation视频中重新回到之前状态的问题,你可以从苹果免费下载。

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

这只是一个很长的镜头,但也许它正指向正确的方向。

答案 1 :(得分:0)

我是否正确理解你想要的视觉效果就是拥有一个数字轮(有点像飞镖),就像我们房子旁边的旧式燃气/水表一样旋转成长?

如果是这样,为什么你不是只有一个图像是一个圆圈中的数字,然后将那个图像旋转到所需的数量?像(在浏览器中输入的未经测试的代码):

   const double degreesPerDigit = 360.0 / 10.0;
   const double radPerDigit = degreesPerDigit * M_PI / 180.0;
   CGAffineTransform xfm = CGAffineTransformMakeRotation(radPerDigit * digitUp);
   // animated or otherwise:
   myView.transform = xfm;

直接问题(再次,在浏览器中输入代码):

设置容器图层的偏移量:

CGRect rect = myLayer.frame;
rect.origin.y = newYValue;
myLayer.frame = rect;

为了防止闪烁,有许多技术,取决于问题的确切性质和期望的结果,但常见的是使用几乎相同的2个视图,然后在view1上进行更改片刻,隐藏view1并取消隐藏view2。

从你的问题来看,听起来你对这些东西有一个很好的理解,所以如果我的回答低于你的水平,我很抱歉,但很难弄清楚你正在做什么以及出了什么问题。

建议:你可能值得花时间尝试将这个问题刻后回到重要的位置,同样地,编写一个只有这个动画的精简样本应用程序,然后进行实验最小的测试用例。模块化一切,记录所有内容,研究输出。重复,冲洗,褪色。