什么是创build圆圈animation的正确方法?

我刚刚看到这个图像,我很感兴趣,如何在Swift中创build这种types的animation:

在这里输入图像说明

所以,我有很多灰色的牙齿在圈子里,当我设置angular度,例如45度,它会在0..45度内将这些灰色的牙齿填充成蓝色。

你可以向我解释正确的方法,或者你可以显示不同的片段(这将是伟大的)。 后来我会search或阅读。

提前致谢!

如果你只需要个人的“牙齿”改变颜色,而不是使用牙齿作为一个固体填充的面具,你可以使用核心graphics,而不是核心animation(虽然核心animation通常是首选)。 所以为了做到这一点,我们应该做到以下几点:

  1. 子类UIView来插入我们的绘图代码
  2. 创build一个包装在UIBezierPath的path对象数组
  3. 设置一个计时器来更新进度值和setNeedsDisplay
  4. drawRect: ,绘制path并根据进度分配一个填充

首先,让我们定义我们要在这个UIView子类中使用的variables。

 class TeethLoaderView : UIView { let numberOfTeeth = UInt(60) // Number of teeth to render let teethSize = CGSize(width:8, height:45) // The size of each individual tooth let animationDuration = NSTimeInterval(5.0) // The duration of the animation let highlightColor = UIColor(red: 29.0/255.0, green: 175.0/255.0, blue: 255.0/255.0, alpha: 1) // The color of a tooth when it's 'highlighted' let inactiveColor = UIColor(red: 233.0/255.0, green: 235.0/255.0, blue: 236.0/255.0, alpha: 1) // The color of a tooth when it isn't 'hightlighted' var progress = NSTimeInterval(0.0) // The progress of the loader var paths = [UIBezierPath]() // The array containing the UIBezier paths var displayLink = CADisplayLink() // The display link to update the progress var teethHighlighted = UInt(0) // Number of teeth highlighted ... 

现在我们添加一个函数来创build我们的path。

 func getPaths(size:CGSize, teethCount:UInt, teethSize:CGSize, radius:CGFloat) -> [UIBezierPath] { let halfHeight = size.height*0.5; let halfWidth = size.width*0.5; let deltaAngle = CGFloat(2*M_PI)/CGFloat(teethCount); // The change in angle between paths // Create the template path of a single shape. let p = CGPathCreateWithRect(CGRectMake(-teethSize.width*0.5, radius, teethSize.width, teethSize.height), nil); var pathArray = [UIBezierPath]() for i in 0..<teethCount { // Copy, translate and rotate shapes around let translate = CGAffineTransformMakeTranslation(halfWidth, halfHeight); var rotate = CGAffineTransformRotate(translate, deltaAngle*CGFloat(i)) let pathCopy = CGPathCreateCopyByTransformingPath(p, &rotate)! pathArray.append(UIBezierPath(CGPath: pathCopy)) // Populate the array } return pathArray } 

这很简单。 我们只是创build一个单一的“牙齿”的path,然后复制这条path,我们需要多less牙齿,翻译和旋转每个人的path。

接下来我们要设置我们的观点。 我要去计时器的CADisplayLink ,以便animation在所有设备上以相同的速度执行。

 override init(frame: CGRect) { super.init(frame: frame) commonSetup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonSetup() } private func commonSetup() { self.backgroundColor = UIColor.whiteColor() paths = getPaths(frame.size, teethCount: numberOfTeeth, teethSize: teethSize, radius: ((frame.width*0.5)-teethSize.height)) displayLink = CADisplayLink(target: self, selector: #selector(displayLinkDidFire)); displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) } 

在这里,我们只是设置背景颜色,以及设置我们的计时器,并初始化我们将要使用的path。 接下来,我们要设置一个函数来在CADisplayLink触发时更改视图的CADisplayLink

 func displayLinkDidFire() { progress += displayLink.duration/animationDuration if (progress > 1) { progress -= 1 } let t = teethHighlighted teethHighlighted = UInt(round(progress*NSTimeInterval(numberOfTeeth))) // Calculate the number of teeth to highlight if (t != teethHighlighted) { // Only call setNeedsDisplay if the teethHighlighted changed setNeedsDisplay() } } 

这里没有什么复杂的,我们只是更新进度和teethHighlighted并调用setNeedsDisplay()来重绘视图,如果teethHighlighted改变。

最后,我们要画出看法。

 override func drawRect(rect: CGRect) { let ctx = UIGraphicsGetCurrentContext() CGContextScaleCTM(ctx, -1, -1) // Flip the context to the correct orientation CGContextTranslateCTM(ctx, -rect.size.width, -rect.size.height) for (index, path) in paths.enumerate() { // Draw each 'tooth' CGContextAddPath(ctx, path.CGPath); let fillColor = (UInt(index) <= teethHighlighted) ? highlightColor:inactiveColor; CGContextSetFillColorWithColor(ctx, fillColor.CGColor) CGContextFillPath(ctx) } } 

如果你想去核心animation的path, 我调整了这个代码到一个核心animation层


最后结果

在这里输入图像说明


完整的项目: https : //github.com/originaluser2/Circle-Loader

那么,本着“ 走出去或回家 ”的精神(因为我实际上有一些乐趣),我创build了一个核心graphics答案的核心animation版本。 它的代码更less,animation更stream畅,所以我更喜欢使用它。

首先,让我们再次UIView一个UIView (这不是严格必要的,但是在一个视图中包含所有东西是很好的)并定义我们的variables:

 class TeethLoaderViewCA : UIView { let numberOfTeeth = UInt(60) // Number of teetch to render let teethSize = CGSize(width:8, height:45) // The size of each individual tooth let animationDuration = NSTimeInterval(5.0) // The duration of the animation let highlightColor = UIColor(red: 29.0/255.0, green: 175.0/255.0, blue: 255.0/255.0, alpha: 1) // The color of a tooth when it's 'highlighted' let inactiveColor = UIColor(red: 233.0/255.0, green: 235.0/255.0, blue: 236.0/255.0, alpha: 1) // The color of a tooth when it isn't 'hightlighted' let shapeLayer = CAShapeLayer() // The teeth shape layer let drawLayer = CAShapeLayer() // The arc fill layer let anim = CABasicAnimation(keyPath: "strokeEnd") // The stroke animation ... 

这大部分与Core Graphics版本相同,但是有一些Core Animation对象,没有时序逻辑。 接下来,我们几乎可以复制我们在其他版本中创build的getPaths函数,除了一些调整。

 func getPathMask(size:CGSize, teethCount:UInt, teethSize:CGSize, radius:CGFloat) -> CGPathRef? { let halfHeight = size.height*0.5 let halfWidth = size.width*0.5 let deltaAngle = CGFloat(2*M_PI)/CGFloat(teethCount); // The change in angle between paths // Create the template path of a single shape. let p = CGPathCreateWithRect(CGRectMake(-teethSize.width*0.5, radius, teethSize.width, teethSize.height), nil) let returnPath = CGPathCreateMutable() for i in 0..<teethCount { // Copy, translate and rotate shapes around let translate = CGAffineTransformMakeTranslation(halfWidth, halfHeight) var rotate = CGAffineTransformRotate(translate, deltaAngle*CGFloat(i)) CGPathAddPath(returnPath, &rotate, p) } return CGPathCreateCopy(returnPath) } 

这一次,所有的path被分成一个大的path,函数返回该path。

最后,我们只需创build图层对象并设置animation。

 private func commonSetup() { // set your background color self.backgroundColor = UIColor.whiteColor() // Get the group of paths we created. shapeLayer.path = getPathMask(frame.size, teethCount: numberOfTeeth, teethSize: teethSize, radius: ((frame.width*0.5)-teethSize.height)) let halfWidth = frame.size.width*0.5 let halfHeight = frame.size.height*0.5 let halfDeltaAngle = CGFloat(M_PI/Double(numberOfTeeth)) // Creates an arc path, with a given offset to allow it to be presented nicely drawLayer.path = UIBezierPath(arcCenter: CGPointMake(halfWidth, halfHeight), radius: halfWidth, startAngle: CGFloat(-M_PI_2)-halfDeltaAngle, endAngle: CGFloat(M_PI*1.5)+halfDeltaAngle, clockwise: true).CGPath drawLayer.frame = frame drawLayer.fillColor = inactiveColor.CGColor drawLayer.strokeColor = highlightColor.CGColor drawLayer.strokeEnd = 0 drawLayer.lineWidth = halfWidth drawLayer.mask = shapeLayer layer.addSublayer(drawLayer) // Optional, but looks nice anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) } 

我们在这里所做的是将一组path分配给一个CAShapeLayer ,我们将在drawLayer使用这个path作为一个遮罩,我们将围绕这个视图进行animation处理(在一个拱形path上使用一个笔划)。


最后结果

在这里输入图像说明


完整的项目: https : //github.com/originaluser2/Circle-Loader