日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ios开发 方形到圆的动画_画个圆动画,的两种实现。iOS 动画由很浅,入浅,当然是 Swift...

發(fā)布時(shí)間:2025/3/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ios开发 方形到圆的动画_画个圆动画,的两种实现。iOS 动画由很浅,入浅,当然是 Swift... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

方法一,使用 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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 在线观看中文字幕av | h网站在线看 | 免费在线播放视频 | 欧美综合视频在线观看 | 福利亚洲 | 国产女人高潮视频 | 国产免费久久久 | 日本肉体xxxx裸体137大胆图 | 日韩在线第一区 | 欧美精品韩国精品 | 91精品免费视频 | 日韩一区二区三区视频在线 | 黄色网址国产 | 噜噜噜网站| 久久影院午夜理论片无码 | 天天爽天天搞 | 中文字幕va | 日韩伦理中文字幕 | 大乳女喂男人吃奶视频 | 中文字幕精品久久久久人妻红杏1 | 99精品亚洲| 日本高清视频一区二区 | 国产精彩视频在线观看 | 久久久精品人妻一区二区三区色秀 | 99热这里有 | 性欧美www | 福利在线电影 | 黄色三级a | 国产一区二区三区四区三区四 | 99热中文| 小优视频污 | xxx日本少妇 | 美女扒开尿口给男人桶 | 秋霞成人网| 丝袜av网站| 91精选国产 | 欧美 日韩 人妻 高清 中文 | 成人人人人人欧美片做爰 | 欧美日韩1| 亚洲中文字幕一区二区 | 日韩三级国产精品 | 欧美性猛交xxxx乱 | 樱桃香蕉视频 | 日日热 | 理论片亚洲 | 日韩高清毛片 | 性欧美videos高清hd4k | 香蕉网站在线 | 在线观看一二区 | 午夜爱爱毛片xxxx视频免费看 | 成人动漫在线观看 | 激情五月开心婷婷 | 99999av| 久在操 | 人人澡人人透人人爽 | 国产在线高潮 | 国产午夜视频在线播放 | 午夜在线视频免费观看 | 精品人妻人人做人人爽夜夜爽 | 日韩理论在线观看 | 精品视频无码一区二区三区 | 日韩乱码视频 | 欧美亚洲 | 国产女人在线视频 | 亚洲第一黄网 | 青草精品 | 国产丝袜视频 | 久久久精品麻豆 | 夜夜精品一区二区无码 | 国产精品美女久久久久久久久 | 精品国产免费无码久久久 | 久久大综合 | 婷婷毛片| 国产亚洲色婷婷久久 | www.日本黄| 男男巨肉啪啪动漫3d | 日韩欧美无 | 日韩不卡一二三区 | 三年大片在线观看 | 激情另类小说 | 日韩午夜在线播放 | 亚洲欧美一区二区三区久久 | 亚洲图片自拍偷拍区 | 欧美日韩中字 | 成人黄色小视频在线观看 | 免费国产在线观看 | 日本中文字幕在线免费观看 | 国产精品野外户外 | 三级国产网站 | 又爽又黄视频 | 就要爱爱tv | 日本a级一区 | 三级网站免费观看 | 国产免费麻豆 | 日韩夜夜高潮夜夜爽无码 | 午夜免费看视频 | 人人澡人人爽 | 99成人精品| 成年人三级网站 |