我正在尝试学习图表并遇到麻烦
原因,我不希望将分隔符作为单独的拱形,而要同时将两个边都倒圆。在圆角重叠的另一层添加分隔符。点击时,第二层显示在顶部
高度赞赏任何帮助或指针。
import UIKit
import PlaygroundSupport
var str = "Hello, playground"
struct dataItem {
var color: UIColor
var percentage: CGFloat
}
typealias pieAngle = (start: CGFloat, end: CGFloat, color: UIColor)
let pieDataToDisplay = [dataItem(color: .red, percentage: 10),
dataItem(color: .blue, percentage: 20),
dataItem(color: .green, percentage: 25),
dataItem(color: .yellow, percentage: 25),
dataItem(color: .orange, percentage: 10)]
class USBCircleChart: UIView {
private var piesToDisplay: [dataItem] = [] { didSet { setNeedsLayout() } }
private var seperatorSpace: Double = 2.0 { didSet { setNeedsLayout() } }
func fillDataForChart(with items: [dataItem] ) {
self.piesToDisplay.append(contentsOf: items)
print("getting data \(self.piesToDisplay)")
layoutIfNeeded()
}
override func layoutSubviews() {
super.layoutSubviews()
guard piesToDisplay.count > 0 else { return }
print("laying out data")
let angles = calcualteStartAndEndAngle(items: piesToDisplay)
for i in angles {
var dataItem = i
addSpace(data: &dataItem)
addShapeToCircle(data: dataItem)
}
}
func addSpace(data:inout pieAngle) -> pieAngle {
// If space is not added, then its collated at the end, we have to scatter it between each item.
//data.end -= CGFloat(seperatorSpace)
return data
}
func addShapeToCircle(data : pieAngle, percent: CGFloat) {
let center = CGPoint(x: bounds.origin.x + bounds.size.width / 2, y: bounds.origin.y + bounds.size.height / 2)
var shapeLayer = CAShapeLayer()
// radians = degrees * pi / 180
// x*2 + y*2 = r*2
//cos teta = x/r --> x = r * cos teta
// sinn teta = y/ r --> y = r * sin teta
// let x = 100 * cos(data.start)
// let y = 100 * sin(data.end)
let radius = (bounds.origin.x + bounds.size.width / 2 - (sliceThickness)) / 2
//This is the circle path drawn.
let circularPath = UIBezierPath(arcCenter: .zero, radius: self.frame.width / 2, startAngle: data.start, endAngle: data.end, clockwise: true) //2*CGFloat.pi
shapeLayer.path = circularPath.cgPath
//Provide a bounding box for the shape layer to handle events
//Removing the below line works but will not handle touch events :(
shapeLayer.bounds = circularPath.cgPath.boundingBox
//Start the angle from anyplace you need { + - of Pi} // {0, 0.5 pi, 1 pi, 1.5pi}
// shapeLayer.transform = CATransform3DMakeRotation(-CGFloat.pi / 2 , 0, 0, 1)
// color of the stroke
shapeLayer.strokeColor = data.color.cgColor
//Width of stoke
shapeLayer.lineWidth = sliceThickness
//Starts from the center of the view
shapeLayer.position = center
//To provide a rounded cap on the stroke
shapeLayer.lineCap = .round
//Fills the entire circle with this color
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeEnd = 0
basicAnim(shapeLayer: &shapeLayer, percentage: percent)
layer.addSublayer(shapeLayer)
}
func basicAnim(shapeLayer: inout CAShapeLayer) {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 1
basicAnimation.duration = 10
//Forwards will hold the layer after completion
basicAnimation.fillMode = .forwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation, forKey: "shapeLayerAniamtion")
}
// //Calucate percentage based on given values
// public func calculateAngle(percentageVal:Double)-> CGFloat {
// return CGFloat((percentageVal / 100) * 360)
// let val = CGFloat (percentageVal / 100.0)
// return val * 2 * CGFloat.pi
// }
private func calcualteStartAndEndAngle(items : [dataItem])-> [pieAngle] {
var angle: pieAngle
var angleToStart: CGFloat = 0.0
//Add the total separator space to the circle so we can accurately measure the start point with space.
var totalSeperatorSpace = Double(items.count) * separatorSpace
var totalSum = items.reduce(CGFloat(totalSeperatorSpace)) { return $0 + $1.percentage }
var angleList: [pieAngle] = []
for item in items {
//Find the end angle based on the percentage in the total circle
let endAngle = (item.percentage / totalSum * 2 * .pi) + angleToStart
angle.0 = angleToStart
angle.1 = endAngle
angle.2 = item.color
angleList.append(angle)
angleToStart = endAngle
//print(angle)
}
return angleList
}
}
let container = UIView()
container.frame.size = CGSize(width: 360, height: 360)
container.backgroundColor = .white
PlaygroundPage.current.liveView = container
PlaygroundPage.current.needsIndefiniteExecution = true
let m = USBCircleChart(frame: CGRect(x: 0, y: 0, width: 215, height: 215))
m.center = CGPoint(x: container.bounds.size.width / 2, y: container.bounds.size.height / 2)
m.fillDataForChart(with: pieDataToDisplay)
container.addSubview(m)
您需要计算间距,然后从起点和终点角度相加并减去。.因此,用此一个更新您的calcualteStartAndEndAngle
方法
private func calcualteStartAndEndAngle(items : [dataItem])-> [pieAngle] {
var angle: pieAngle
var angleToStart: CGFloat = 0.0
//Add the total separator space to the circle so we can accurately measure the start point with space.
let totalSeperatorSpace = Double(items.count)
let totalSum = items.reduce(CGFloat(totalSeperatorSpace)) { return $0 + $1.percentage }
let spacing = CGFloat( totalSeperatorSpace + 1 ) / totalSum
print("total Sum:\(spacing)")
var angleList: [pieAngle] = []
for item in items {
//Find the end angle based on the percentage in the total circle
let endAngle = (item.percentage / totalSum * 2 * .pi) + angleToStart
print("start:\(angleToStart) end:\(endAngle)")
angle.0 = angleToStart + spacing
angle.1 = endAngle - spacing
angle.2 = item.color
angleList.append(angle)
angleToStart = endAngle + spacing
//print(angle)
}
return angleList
}
将产生此动画
如果需要线性动画,请更改动画方法
private var timeOffset:CFTimeInterval = 0
func basicAnim(shapeLayer: inout CAShapeLayer, percentage:CGFloat) {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 1
basicAnimation.duration = CFTimeInterval(percentage)
basicAnimation.beginTime = CACurrentMediaTime() + timeOffset
print("timeOffset:\(timeOffset),")
//Forwards will hold the layer after completion
basicAnimation.fillMode = .forwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation, forKey: "shapeLayerAniamtion")
timeOffset += CFTimeInterval(percentage)
}
如果您想了解更多信息,请参见此框架RingPieChart