ios开发 方形到圆的动画_画个圆动画,的两种实现。iOS 动画由很浅,入浅,当然是 Swift...
方法一,使用 CAShapeLayer 和 UIBezierPath
加上 CABasicAnimation 有一個(gè)動(dòng)畫(huà)屬性 strokeEnd
就算完
方法二,復(fù)雜一些。頻繁調(diào)用 CALayer 的 func draw(in ctx: CGContext) 也是可以的
通過(guò)定制 CALayer, 還要有一個(gè)使用該定制 CALayer 的 custom 視圖。使用 @NSManaged, 方便自定制的 CALayer 鍵值觀察 KVC
重寫 CALayer 的方法 action(forKey:), 指定需要的動(dòng)畫(huà)
重寫 CALayer 的方法 needsDisplay(forKey:), 先指定刷新渲染,再出 action(forKey:) 的動(dòng)畫(huà)
方法一的,具體實(shí)現(xiàn)
class CircleView: UIView {
let circleLayer: CAShapeLayer = {
// 形狀圖層,初始化與屬性配置
let circle = CAShapeLayer()
circle.fillColor = UIColor.clear.cgColor
circle.strokeColor = UIColor.red.cgColor
circle.lineWidth = 5.0
circle.strokeEnd = 0.0
return circle
}()
// 視圖創(chuàng)建,通過(guò)指定 frame
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
// 視圖創(chuàng)建,通過(guò)指定 storyboard
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup(){
backgroundColor = UIColor.clear
// 添加上,要?jiǎng)赢?huà)的圖層
layer.addSublayer(circleLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
// 考慮到視圖的布局,如通過(guò) auto layout,
// 需動(dòng)畫(huà)圖層的布局,放在這里
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(Double.pi * 2.0), clockwise: true)
circleLayer.path = circlePath.cgPath
}
// 動(dòng)畫(huà)的方法
func animateCircle(duration t: TimeInterval) {
// 畫(huà)圓形,就是靠 `strokeEnd`
let animation = CABasicAnimation(keyPath: "strokeEnd")
// 指定動(dòng)畫(huà)時(shí)長(zhǎng)
animation.duration = t
// 動(dòng)畫(huà)是,從沒(méi)圓,到滿圓
animation.fromValue = 0
animation.toValue = 1
// 指定動(dòng)畫(huà)的時(shí)間函數(shù),保持勻速
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
// 視圖具體的位置,與動(dòng)畫(huà)結(jié)束的效果一致
circleLayer.strokeEnd = 1.0
// 開(kāi)始動(dòng)畫(huà)
circleLayer.add(animation, forKey: "animateCircle")
}
}
使用的代碼 : 很簡(jiǎn)單
class ViewController: UIViewController {
// storyboard 布局
@IBOutlet weak var circleV: CircleView!
@IBAction func animateFrame(_ sender: UIButton) {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*30)
let circleEdge = CGFloat(200)
// 直接指定 frame 布局
let circleView = CircleView(frame: CGRect(x: 50, y: diceRoll, width: circleEdge, height: circleEdge))
view.addSubview(circleView)
// 開(kāi)始動(dòng)畫(huà)
circleView.animateCircle(duration: 1.0)
}
@IBAction func animateAutolayout(_ sender: UIButton) {
// auto layout 布局
let circleView = CircleView(frame: CGRect.zero)
circleView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(circleView)
circleView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
circleView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
circleView.widthAnchor.constraint(equalToConstant: 250).isActive = true
circleView.heightAnchor.constraint(equalToConstant: 250).isActive = true
// 開(kāi)始動(dòng)畫(huà)
circleView.animateCircle(duration: 1.0)
}
@IBAction func animateStoryboard(_ sender: UIButton) {
// 開(kāi)始動(dòng)畫(huà)
circleV.animateCircle(duration: 1.0)
}
}
方法二的實(shí)現(xiàn)
核心類 UICircularRingLayer 的技術(shù)注意:
先要自定制一個(gè)基于 CAShapeLayer 的圖層
對(duì) @NSManaged var val: CGFloat KVC,
觸發(fā) override class func needsDisplay(forKey key: String) -> Bool ,
調(diào)用 setNeedsDisplay(),重新渲染,
接著觸發(fā) override func action(forKey event: String) -> CAAction?, 指定動(dòng)畫(huà),
頻繁調(diào)用繪制方法 override func draw(in ctx: CGContext), 就是可見(jiàn)的動(dòng)畫(huà)
@NSManaged 關(guān)鍵字,類似 Objective-C 里面的 @dynamic 關(guān)鍵字
@NSManaged 關(guān)鍵字,方便鍵值編碼
@NSManaged 通知編譯器,不要初始化,運(yùn)行時(shí)保證有值
override class func needsDisplay(forKey key: String) -> Bool 返回 true
就是需要重新渲染,調(diào)用 setNeedsDisplay() 方法
下面的
override class func needsDisplay(forKey key: String) -> Bool {
if key == "val" {
return true
} else {
return super.needsDisplay(forKey: key)
}
}
相當(dāng)于
override class func needsDisplay(forKey key: String) -> Bool {
if key == "val" {
return true
} else {
return false
}
}
override func action(forKey event: String) -> CAAction?, 返回協(xié)議對(duì)象 CAAction
CAAnimation 遵守 CAAction 協(xié)議,這里一般返回個(gè) CAAnimation
一個(gè) CALayer 圖層,可以有動(dòng)態(tài)的動(dòng)畫(huà)行為。
發(fā)起動(dòng)畫(huà)時(shí),可以設(shè)置該圖層的動(dòng)畫(huà)屬性,操作關(guān)聯(lián)出來(lái)的具體動(dòng)畫(huà)
下面的
override func action(forKey event: String) -> CAAction? {
if event == "val"{
// 實(shí)際動(dòng)畫(huà)部分
let animation = CABasicAnimation(keyPath: "val")
// ...
return animation
} else {
return super.action(forKey: event)
}
}
相當(dāng)于
override func action(forKey event: String) -> CAAction? {
if event == "val"{
// 實(shí)際動(dòng)畫(huà)部分
let animation = CABasicAnimation(keyPath: "val")
// ...
return animation
} else {
return nil
}
}
方法二的,具體實(shí)現(xiàn)
/**
動(dòng)畫(huà)起作用的樞紐,
負(fù)責(zé)處理繪制和動(dòng)畫(huà),
對(duì)于使用者隱藏,使用者操作外部的視圖類就好
*/
class UICircularRingLayer: CAShapeLayer {
// MARK: 屬性
@NSManaged var val: CGFloat
let ringWidth: CGFloat = 20
let startAngle = CGFloat(-90).rads
// MARK: 初始化
override init() {
super.init()
}
override init(layer: Any) {
// 確保使用姿勢(shì)
guard let layer = layer as? UICircularRingLayer else { fatalError("unable to copy layer") }
super.init(layer: layer)
}
required init?(coder aDecoder: NSCoder) { return nil }
// MARK: 視圖渲染部分
/**
重寫 draw(in 方法,畫(huà)圓環(huán)
*/
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
UIGraphicsPushContext(ctx)
// 畫(huà)圓環(huán)
drawRing(in: ctx)
UIGraphicsPopContext()
}
// MARK: 動(dòng)畫(huà)部分
/**
監(jiān)聽(tīng) val 屬性的變化,重新渲染
*/
override class func needsDisplay(forKey key: String) -> Bool {
if key == "val" {
return true
} else {
return super.needsDisplay(forKey: key)
}
}
/**
監(jiān)聽(tīng) val 屬性的變化,指定動(dòng)畫(huà)行為
*/
override func action(forKey event: String) -> CAAction? {
if event == "val"{
// 實(shí)際動(dòng)畫(huà)部分
let animation = CABasicAnimation(keyPath: "val")
animation.fromValue = presentation()?.value(forKey: "val")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.duration = 2
return animation
} else {
return super.action(forKey: event)
}
}
/**
畫(huà)圓,通過(guò)路徑布局。主要是指定 UIBezierPath 曲線的角度
*/
private func drawRing(in ctx: CGContext) {
let center: CGPoint = CGPoint(x: bounds.midX, y: bounds.midY)
let radiusIn: CGFloat = (min(bounds.width, bounds.height) - ringWidth)/2
// 開(kāi)始畫(huà)
let innerPath: UIBezierPath = UIBezierPath(arcCenter: center,
radius: radiusIn,
startAngle: startAngle,
endAngle: toEndAngle,
clockwise: true)
// 具體路徑
ctx.setLineWidth(ringWidth)
ctx.setLineJoin(.round)
ctx.setLineCap(CGLineCap.round)
ctx.setStrokeColor(UIColor.red.cgColor)
ctx.addPath(innerPath.cgPath)
ctx.drawPath(using: .stroke)
}
// 本例子中,起始角度固定,終點(diǎn)角度通過(guò) val 設(shè)置
var toEndAngle: CGFloat {
return (val * 360.0).rads + startAngle
}
}
輔助方法,用于角度轉(zhuǎn)弧度
extension CGFloat {
var rads: CGFloat { return self * CGFloat.pi / 180 }
}
觸發(fā)類
自定制 UIView,指定其圖層為,之前的定制圖層
@IBDesignable open class UICircularRing: UIView {
/**
將 UIView 自帶的 layer,強(qiáng)轉(zhuǎn)為上面的 UICircularRingLayer, 方便使用
*/
var ringLayer: UICircularRingLayer {
return layer as! UICircularRingLayer
}
/**
將 UIView 自帶的 layer,重寫為 UICircularRingLayer
*/
override open class var layerClass: AnyClass {
return UICircularRingLayer.self
}
/**
通過(guò) frame 初始化,的設(shè)置
*/
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
}
/**
通過(guò) storyboard 初始化,的設(shè)置
*/
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
/**
初始化的配置
*/
func setup(){
// 設(shè)置光柵化
// 將光柵化后的內(nèi)容緩存起來(lái),方便復(fù)用
ringLayer.contentsScale = UIScreen.main.scale
ringLayer.shouldRasterize = true
ringLayer.rasterizationScale = UIScreen.main.scale * 2
ringLayer.masksToBounds = false
backgroundColor = UIColor.clear
ringLayer.backgroundColor = UIColor.clear.cgColor
ringLayer.val = 0
}
func startAnimation() {
ringLayer.val = 1
}
}
使用的代碼,很簡(jiǎn)單
class ViewController: UIViewController {
let progressRing = UICircularRing(frame: CGRect(x: 100, y: 100, width: 250, height: 250))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(progressRing)
}
@IBAction func animate(_ sender: UIButton) {
progressRing.startAnimation()
}
}
方法二,設(shè)置線條帽,為圓頭,比較方便
ctx.setLineCap(CGLineCap.round)
iOS 設(shè)置角度的坐標(biāo)圖
總結(jié)
以上是生活随笔為你收集整理的ios开发 方形到圆的动画_画个圆动画,的两种实现。iOS 动画由很浅,入浅,当然是 Swift...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: lock交替打印_面试题Synchron
- 下一篇: quartus2管教锁定出不来_Quar