CLWheelMenuViewSwift 轉(zhuǎn)盤菜單
前言
使用 Swift 實(shí)現(xiàn)的轉(zhuǎn)盤菜單,主要用到UIBezierPath、CALayer遮罩繪制扇形UIView,CATransform3DMakeRotation實(shí)現(xiàn)旋轉(zhuǎn)動(dòng)畫(huà)。代碼設(shè)計(jì)使用默認(rèn)configureCallback回調(diào)方便創(chuàng)建和設(shè)置基本屬性,參考UITableView代理和數(shù)據(jù)源模式,支持AutoLayout和Frame。
效果圖
1.遮罩繪制扇形View
計(jì)算扇形曲線位置,通過(guò)CALayer的mask屬性繪制出扇形UIView 核心代碼
func setMaskLayer(_ startAngle: CGFloat, endAngle: CGFloat) {
let center = CGPoint(x: bounds.width * 0.5, y: bounds.height * 0.5)
let layer = CAShapeLayer()
path.addArc(withCenter: center, radius: bounds.width * 0.5, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.addLine(to: center)
layer.path = path.cgPath
layer.rasterizationScale = UIScreen.main.scale
layer.shouldRasterize = true
self.layer.mask = layer
}
2.中間鏤空
func createHole(in view : UIView, radius: CGFloat) {
let path = CGMutablePath()
path.addArc(center: view.center, radius: radius, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: true)
path.addRect(CGRect(origin: .zero, size: view.bounds.size))
let maskLayer = CAShapeLayer()
maskLayer.path = path
maskLayer.fillRule = .evenOdd
view.layer.mask = maskLayer
view.clipsToBounds = true
}
3.旋轉(zhuǎn)動(dòng)畫(huà)
添加UIPanGestureRecognizer、UITapGestureRecognizer手勢(shì),根據(jù)手勢(shì)位置使用atan2函數(shù)計(jì)算旋轉(zhuǎn)角度,然后用CATransform3DMakeRotation圍繞Z軸旋轉(zhuǎn)做動(dòng)畫(huà) 核心代碼
func handlePanGesture(_ sender: UIPanGestureRecognizer) {
let location = sender.location(in: self)
switch sender.state {
case .began:
startPoint = location
case .changed:
let radian1 = -atan2(startPoint.x - menuLayerView.center.x, startPoint.y - menuLayerView.center.y)
let radian2 = -atan2(location.x - menuLayerView.center.x, location.y - menuLayerView.center.y)
menuLayerView.transform = menuLayerView.transform.rotated(by: radian2 - radian1)
startPoint = location
default:
let angle = 2 * CGFloat(Double.pi) / CGFloat(cells.count)
var menuViewAngle = atan2(menuLayerView.transform.b, menuLayerView.transform.a)
if menuViewAngle < 0 {
menuViewAngle += CGFloat(2 * Double.pi)
}
var index = cells.count - Int((menuViewAngle + CGFloat(Double.pi / 4)) / angle)
if index == cells.count {
index = 0
}
setSelectedIndex(index, animated: true)
}
}
func handleTapGesture(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: menuLayerView)
for (index, cell) in cells.enumerated() {
if cell.path.contains(location) {
setSelectedIndex(index, animated: true)
}
}
}
4.彈出收起動(dòng)畫(huà)
func openMenuView(withAnimate animate: Bool = true) {
openMenu = true
UIView.animate(withDuration: animate ? configure.animationDuration : 0, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 5.0, options: .curveEaseInOut) {
self.centerButton.transform = CGAffineTransform(rotationAngle: .pi * -0.5)
self.centerButton.setImage(self.configure.closeImage, for: .normal)
self.menuLayerView.transform = CGAffineTransform(scaleX: 1, y: 1).rotated(by: self.currentAngle)
}
}
func closeMenuView(withAnimate animate: Bool = true) {
openMenu = false
let scale = (configure.centerRadius * 2) / bounds.width
UIView.animate(withDuration: animate ? configure.animationDuration : 0, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 5.0, options: .curveEaseInOut) {
self.centerButton.transform = .identity
self.centerButton.setImage(self.configure.openImage, for: .normal)
self.menuLayerView.transform = CGAffineTransform(scaleX: scale, y: scale).rotated(by: self.currentAngle)
}
}
5.內(nèi)部細(xì)節(jié)
考慮到方便布局和使用,內(nèi)部使用UIView疊加旋轉(zhuǎn)實(shí)現(xiàn),這里也可以采用Layer直接繪制實(shí)現(xiàn),相對(duì)UIView,層次結(jié)構(gòu)會(huì)簡(jiǎn)單很多
總結(jié)
核心代碼已經(jīng)貼出,完整代碼請(qǐng)查看----->>>CLDemo,如果對(duì)你有所幫助,歡迎Star。
評(píng)論
圖片
表情
