美文网首页iOS DeveloperiOS程序猿
[iOS 动画]徒手做一个会动的icon

[iOS 动画]徒手做一个会动的icon

作者: stephenwzl | 来源:发表于2016-11-21 14:26 被阅读326次

难度:⭐️
最终效果:

最终效果最终效果

在平时堆UI的时候,避免不了要碰到UED要给我们出一点点难题,比如这次又叫我抄一下淘宝的下拉刷新了(手动斜眼)。
说干就干,首先新建一个View封装所有的代码:

import UIKit

class AnimatedArrow: UIView {  
  override init(frame: CGRect) {
    super.init(frame: frame)
    commonInit()
  }
}

init中调用我们添加子视图的代码。这时候来分析一下我们需要添加点什么:

  • 一个画圆圈的layer
  • 一个包含箭头的layer

其中箭头这个图层还要分为左中右三根直线。
我们先来画这个圆圈, 我们需要一个CAShapeLayer,把这个layer的frame设置一个正方形,然后利用贝塞尔曲线在这个正方形的Rect中画一个圆,把这个layer的路径设置为圆的路径。
然后设置一下layer基本的填充色、描边色、线帽(线的起点和终点两端的形状)。
为了做圆形的填充动画(其实就是描边),我们需要把描边的起点和终点设置一下。起点为0,终点由代码控制,设置为self.progress.

稍微注意一下UIKit Graphics 方法和 Core Graphics方法的区别,Core Graphics是从macOS继承下来的,UIGraphics是iOS独有的一套。所以贝塞尔曲线类UIBezierPath是在UIKit里面的。

var circleLayer:CAShapeLayer?
  func loadCircleLayer() {
    let layer = CAShapeLayer()

    //incase self.bounds.size is not a square
    let radius = min(self.bounds.width, self.bounds.height)
    let frame = CGRect(x: 0, y: 0, width: radius, height: radius)
    layer.frame = frame
    layer.strokeColor = UIColor.black.cgColor
    layer.fillColor = UIColor.clear.cgColor
    
    let path = UIBezierPath(ovalIn: frame)
    layer.path = path.cgPath
    layer.lineWidth = 1
    layer.lineCap = kCALineCapRound
    layer.strokeStart = 0
    layer.strokeEnd = self.progress
    
    self.layer.addSublayer(layer)
    self.circleLayer = layer
  }

再来看一下self.progress,复写了一下它的set方法,在set的时候赋值并且改变circleLayer的描边终点。至于最大值是0.95,那是为了留一个缺口,好看出旋转动画。

private var _progress:CGFloat = 0  
var progress:CGFloat {
    get {
      return _progress
    }
    set {
      _progress = newValue
      self.circleLayer?.strokeEnd = min(0.95, newValue)
    }
 }

现在我们来画中间的这个箭头,首先判断一下比例:

可以看出来中间这根线大概是边长的二分之一,然后水平垂直都居中。我们来创建一下它的路径:

为什么有个 0.5 ? 因为线宽1,为了居中,就要左偏移0.5

func middlePath() -> CGPath {
    let width = self.bounds.size.width / 2;
    let path = UIBezierPath()
    path.move(to: CGPoint(x: width - 0.5, y: width / 2))
    path.addLine(to: CGPoint(x: width - 0.5, y: 3 * width / 2))
    return path.cgPath
}

左边和右边这两条线起点是中间线的下端点,终点是左右一半的水平终点和垂直居中处,那就好办了,画一下他们俩的路径:

  func leftPath() -> CGPath {
    let width = self.bounds.size.width / 2;
    let path = UIBezierPath()
    path.move(to: CGPoint(x: width / 2, y: width))
    path.addLine(to: CGPoint(x: width - 0.5, y: 3 * width / 2))
    return path.cgPath
  }
  
  func rightPath() -> CGPath {
    let width = self.bounds.size.width / 2;
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 3 * width / 2, y: width))
    path.addLine(to: CGPoint(x: width - 0.5, y: 3 * width / 2))
    return path.cgPath
  }
  

现在,根据这三条路径创建图层:

  func templateLayer(path:CGPath) -> CAShapeLayer {
    let layer = CAShapeLayer()
    layer.frame = self.bounds
    layer.strokeColor = UIColor.black.cgColor
    layer.path = path
    layer.lineWidth = 1
    layer.lineCap = kCALineCapRound
    layer.fillColor = UIColor.clear.cgColor
    return layer
  }
  
  var containerLayer:CALayer?
  func loadArrowLayer() {
    self.containerLayer = CALayer()
    self.containerLayer?.frame = self.bounds
    self.containerLayer?.addSublayer(templateLayer(path: middlePath()))
    self.containerLayer?.addSublayer(templateLayer(path: leftPath()))
    self.containerLayer?.addSublayer(templateLayer(path: rightPath()))
    self.layer.addSublayer(self.containerLayer!)
  }  

然后把它们组合起来:

func commonInit() {
    loadCircleLayer()
    loadArrowLayer()
}

创建动画:
圆形的填充progress是根据操作进行的,这个我们可以通过监听UIScrollView的contentOffset实现。还有一个就是箭头的隐藏和转圈,几行代码就可以了:

  func startAnimation() {
    self.containerLayer?.isHidden = true
    let animation = CABasicAnimation(keyPath: "transform.rotation.z")
    animation.fromValue = 0
    animation.toValue = M_PI * 2
    animation.duration = 1
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.repeatCount = Float.infinity
    self.circleLayer?.add(animation, forKey: "infinity_rotate")
  }
  
  func stopAnimation() {
    self.containerLayer?.isHidden = false
    self.circleLayer?.removeAnimation(forKey: "infinity_rotate")
  }

剩下要做的事情就是把这些操作组合一下,比如只在 progress 为0.95的时候才能触发 startAnimation() 这样可以避免视图显示不全。
最后,我把这个View添加到视图中,通过 set progress 来进行填充,然后调用动画就可以了。

- EOF -

相关文章

  • [iOS 动画]徒手做一个会动的icon

    难度:⭐️最终效果: 在平时堆UI的时候,避免不了要碰到UED要给我们出一点点难题,比如这次又叫我抄一下淘宝的下拉...

  • 【IOS】动画-会动的翅膀

    1.新建一个View当做画布; FlyView *flyView = [[FlyView alloc]initWi...

  • iOS 开源库、文章、源码整理

    支付相关 iOS开发内购全套图文教程微信支付 上架 APP上架流程 动画 文字动画icon动画 设备 TouchI...

  • iOS动画(一):时钟动画(Swift)

    参考:iOS动画系列之一:通过实战学习CALayer和透视的原理。做一个带时分秒指针的时钟动画(上)iOS动画系列...

  • Flutter-->常用工具

    一键生成ios的icon:https://icon.wuruihong.com/#/ios

  • iOS项目从0-1过程

    最近从0开始做一个项目,记录一下过程(~~持续更新) 证书配置 iOS证书配置及上架流程 iOS App Icon...

  • 动画公司分析年会动画制作三大要点

    动画公司认为年会动画可以是企业年终总结汇报片,可以对企业一整年的经营状况以及产品的销售情况做一个回顾以及总结,对公...

  • ios动画

    ios动画 ios动画2 ios动画3

  • iOS 11 app启动icon的黑边

    iOS11 app开启会有个启动动画,但是发现启动的时候下边会有黑边。 解决方案:发现是由于icon切圆角导致的,...

  • iOS动画

    iOS动画-从UIView动画说起iOS动画-Transform和KeyFrame动画iOS动画-layout动画...

网友评论

    本文标题:[iOS 动画]徒手做一个会动的icon

    本文链接:https://www.haomeiwen.com/subject/iudupttx.html