Swift 扇形排列成圓

Brocadecarp發表於2018-04-23

由於專案需求,需要做個這種的東西來實現某種功能,我在網上找到了很多資料卻始終不行,最終和本公司的安卓商量出來這個方法,基本思想就是畫四十八個扇形,圍繞圓的中心繪製成這樣一個圓環,如圖所示:

圓.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

相關文章