Swift 扇形排列成圆

1,748 阅读3分钟

由于项目需求,需要做个这种的东西来实现某种功能,我在网上找到了很多资料却始终不行,最终和本公司的安卓商量出来这个方法,基本思想就是画四十八个扇形,围绕圆的中心绘制成这样一个圆环,如图所示:

圆.gif
我是继承 UIControl 类来实现的,主要就是重写它的 drawRect 方法,来绘制这个"圆", 不多说,看代码:

    override func draw(_ rect: CGRect) {
        
        var blockBeginAngle = degreesToRandinas(deg: beginAngle)
        var blockEndAngle = degreesToRandinas(deg: beginAngle + blockAngle)
        let context = UIGraphicsGetCurrentContext()
        
        for index in 0..<blockCount {
            drawImage(context: context!,
                      startAngle: blockBeginAngle,
                      endAngle: blockEndAngle,
                      colorIndex: index)

            // 下一块的起始角度和终止角度
            blockBeginAngle = blockBeginAngle + degreesToRandinas(deg: blockAngle + sweepAngle)
            blockEndAngle = blockEndAngle + degreesToRandinas(deg: blockAngle + sweepAngle)
        }
    }

    private func drawImage(context: CGContext,
                   startAngle: CGFloat,
                   endAngle: CGFloat,
                   colorIndex: Int) {
        
        // 画扇形
        context.addArc(center: CGPoint(x: pointX, y: pointY),
                       radius: outerRadius,
                       startAngle: startAngle,
                       endAngle: endAngle,
                       clockwise: false)
        
        context.addArc(center: CGPoint(x: pointX, y: pointY),
                       radius: innerRadius,
                       startAngle: endAngle,
                       endAngle: startAngle,
                       clockwise: true)
        
        context.closePath()
        // 设置参数
        context.setFillColor(fillOrUnFill[currentValueArr[(colorIndex + 36) % 48]].cgColor) // 填充扇形颜色
        print((fillOrUnFill[currentValueArr[(colorIndex + 36) % 48]]))
        // 绘制
        context.drawPath(using: .fill)
        
    }

context.addArc 这个方法,第一个参数:圆心,第二个参数:半径,第三个参数:开始弧度,第四个参数:结束弧度,第五个参数:false(顺时针), true(逆时针)。大概也就是这样子:context.addArc(center: 圆心, radius: 半径, startAngle: 开始弧度, endAngle: 结束弧度, clockwise: false|true) 其中,degreesToRandinas是计算角度转弧度的,degreesToRandinas是计算弧度转角度的,很多画圆都需要这两个方法,相信大家已经很熟悉了

private func degreesToRandinas(deg: CGFloat) -> CGFloat { // 角度转弧度
      return (CGFloat(M_PI) / 180 * deg)
}    
private func randinsToDegrees(rad: CGFloat) -> CGFloat { // 弧度转角度
      return (180 / CGFloat(M_PI) * rad)
}

现在圆是画出来了,接下来就是手指滑动,颜色的变换了。 首先我是用的一个数组来装颜色的,大家可以随意弄多种颜色(如果大家弄的颜色过多的话,就需要自己算数组的下标哟)。这里我就弄了三种颜色,一种是方块默认的颜色,其余两种就是根据情况而定手指滑动的颜色。手指滑动小方块变色的第一步,就是算出手指滑动的是哪块方块,这里我是根据算弧度来确定的。首先,重写 UIControl 的手势开始和手势持续的方法:

// 手势开始
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        super.beginTracking(touch, with: event)
        let point = touch .location(in: self)
        moveHandles(point: point)
        return true
    }
    
    
    // 手势持续中
    override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        super.continueTracking(touch, with: event)
        let point = touch.location(in: self)
        moveHandles(point: point)
        self.sendActions(for: .valueChanged)
        return true
    }

moveHandles 这个方法就是变颜色的核心部分了

    func moveHandles(point: CGPoint) {
        let number = getPointLocation(touch: point)
        if number != oldNumber {
            if number >= 0 && number < 48 {
                // 返回扇形颜色
                if isNight == 1 {
                    currentValueArr[number] = currentValueArr[number] == 1 ? 0 : 1
                }
                
                if isNight == 2 {
                    currentValueArr[number] = currentValueArr[number] == 2 ? 0 : 2
                }
                
                if isNight == 3 {
                    currentValueArr[number] = currentValueArr[number] == 0 ? 0 : 3
                }
            }
            oldNumber = number
        }
        self.setNeedsDisplay()
    }

getPointLocation 这个方法返回的就是哪一个方块了

    private func getPointLocation(touch: CGPoint) -> Int {
        let centerPoint = CGPoint(x: self.pointX, y: self.pointY)
    
        let ang = getAngle(center: centerPoint, touch: touch)
        
        return Int(round(ang / 7.5))
    }
    
// 计算角度
    private func getAngle(center: CGPoint, touch: CGPoint) -> CGFloat {
        let lenA = touch.x - center.x
        let lenB = touch.y - center.y
        let lenC = sqrt(lenA * lenA + lenB * lenB)
        var ang = acos(lenA / lenC)
        
        ang = randinsToDegrees(rad: ang)
        ang = ang * (touch.y < center.y ? -1 : 1)
        if ang < 0 {
            ang = 361 + ang
        }
        ang = CGFloat(Int(ang + 270) % 360)
        
        return ang
    }

如此,就算完成了,调用的也很简单,下面是调用的代码:

circleLayer = CircleLayer(frame: CGRect(origin: CGPoint(x: 0, y: 100), size:
            CGSize(width: self.view.bounds.width, height: self.view.bounds.width)))
self.view.addSubview(circleLayer)

附上完整demo