代码之家  ›  专栏  ›  技术社区  ›  Mark

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

  •  6
  • Mark  · 技术社区  · 14 年前

    我试图创建一个旋转拨号控制,基本上是一组6位数字,旋转和周围给一个旋转数字表的效果(类似于你的电力/水表,或者扑克机,事实上非常类似于现有的UIPickerView控制,但有一个完全不同的外观和感觉)。

    到目前为止,我几乎有它的工作,但我在一个阶段,核心动画是给我的悲伤。

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

    UIView s(称为NumberView1、NumberView2等…),控件中每个数字对应一个。

    在每个数字里我都有另一个 UIView视图 ,这是一个容器视图,称为ContainerView1、2等。。。

    然后我有10个 UIImageView

    重要提示:我的数字只会向上滚动

    这些数字表示向上滚动的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
    

    其中DollarSlot1动画是 CABasicAnimation 伊瓦尔

    方法定义如下:

    - (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个图像块。

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

    这会产生我想要的平滑动画。而且很有效。相信我:)

    我的问题有两个,第一,当动画停止时,核心动画会还原动画,因为我设置了 animation.fillMode = kCAFillModeRemoved; 我不知道如何在动画完成后设置容器层的y偏移,我知道这听起来很奇怪,但是如果我将fillMode属性设置为kCAFillModeForwards,我尝试的任何操作似乎都不会对层产生影响。

    谢谢

    2 回复  |  直到 14 年前
        1
  •  1
  •   GorillaPatch    14 年前

    我以后还要查,当然还有delegate方法 - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag 当动画完成时调用。这是个整理房间的好地方。也许你可以通过观察 toValue

        2
  •  0
  •   walkytalky    14 年前

    我是否正确理解你想要的视觉效果是有一个数字轮(有点像飞镖板),它像我们长大时房子旁边的老式煤气表/水表一样旋转?

       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;
    

    为了防止闪烁,有许多技巧,具体取决于问题的性质和所需的结果,但常见的是使用两个几乎相同的视图,在view1上进行更改,然后在最后一刻隐藏view1和取消隐藏view2。

    从你的问题来看,听起来你对这件事的理解不错,所以如果我的回答低于你的水平,我很抱歉,但很难弄清楚你到底在做什么,出了什么问题。

    建议:两种方法都值得一试,一种是将问题缩减到重要的部分,另一种是编写一个只做这个动画而不做其他事情的精简示例应用程序,然后用最小的测试用例进行实验。模块化一切,记录一切,研究输出。重复,冲洗,褪色。