preface

Use Swift implementation of the rotary table menu, the main use UIBezierPath, CALayer mask drawing fan UIView, CATransform3DMakeRotation rotation animation. The code design uses the default configureCallback callback to facilitate the creation and setting of basic properties, referring to the UITableView proxy and data source schema, supporting AutoLayout and Frame.

rendering

1. Mask to draw sector View

Calculate the position of sector curve and draw the core code of sector UIView through the mask attribute of CALayer

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 }Copy the code

2. Hollow out in the middle

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: Origin) .zero, size: view.bounds.size)) let maskLayer = CAShapeLayer() maskLayer.path = path maskLayer.fillRule = .evenOdd view.layer.mask = maskLayer view.clipsToBounds = true }Copy the code

3. Rotate animation

Add UIPanGestureRecognizer, UITapGestureRecognizer gestures, according to the sign position using the atan2 function to calculate rotation Angle, and then made animation CATransform3DMakeRotation revolve around the Z axis core code

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)
        }
    }
}
Copy the code

4. Pop up the folding animation

func openMenuView(withAnimate animate: Bool = true) { openMenu = true UIView.animate(withDuration: animate ? Configure. AnimationDuration: 0, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 5.0, the options: .curveEaseInOut) { self.centerButton.transform = CGAffineTransform(rotationAngle: . PI * 0.5) self. CenterButton. SetImage (self. The 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, the 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) } }Copy the code

5. Internal details

Considering the convenience of layout and use, the internal use of UIView overlay rotation, here can also be directly drawn Layer implementation, compared with UIView, the hierarchy will be much simpler

conclusion

The core code has been posted, the complete code please see —–>>>CLDemo, if you help, welcome Star.