我需要在我的iOS应用中绘制类似下图的内容,但弧线可能包含更多颜色:
我知道如何绘制它,但我正在寻找一种方法来绘制路径的绘图。
有一个类似的问题here,但圈子没有动画。 This是另一个问题,它解释了如何为圆圈设置动画,并且在我的情况下它可以正常工作,除了它不会处理路径中的多种颜色。
如何做到这一点?
答案 0 :(得分:7)
我找到了一个非常有效的通用解决方案。由于无法绘制不同颜色的独特路径,因此我只绘制了不带动画的所有不同颜色的路径,这些路径构成了我想要的路径。之后,我在反方向绘制了一条覆盖所有路径的独特路径,并将动画应用于此路径。
例如,在上面的例子中,我使用以下代码绘制两个弧:
class CircleView: UIView {
let borderWidth: CGFloat = 20
let startAngle = CGFloat(Double.pi)
let middleAngle = CGFloat(Double.pi + Double.pi / 2)
let endAngle = CGFloat(2 * Double.pi)
var primaryColor = UIColor.red
var secondaryColor = UIColor.blue
var currentStrokeValue = CGFloat(0)
override func draw(_ rect: CGRect) {
let center = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
let radius = CGFloat(self.frame.width / 2 - borderWidth)
let path1 = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: middleAngle, clockwise: true)
let path2 = UIBezierPath(arcCenter: center, radius: radius, startAngle: middleAngle, endAngle: endAngle, clockwise: true)
path1.lineWidth = borderWidth
primaryColor.setStroke()
path1.stroke()
path2.lineWidth = borderWidth
secondaryColor.setStroke()
path2.stroke()
}
}
之后,我获得路径path3
,然后将其添加到将添加到视图的图层中:
var path3 = UIBezierPath(arcCenter: center, radius: radius, startAngle: endAngle, endAngle: startAngle, clockwise: true)
注意在此路径中它以相反的顺序覆盖前两个路径,因为它的startAngle等于endAngle
的值,其endAngle等于startAngle
并且设置了顺时针属性到true
。这条路是我要去动画的路径。
例如,如果我想显示整个(虚构)路径的40%(由不同颜色的路径组成的路径),我将其翻译为显示我的封面路径的{60%path3
。我们可以在问题中提供的链接中找到path3
动画的方式。
答案 1 :(得分:0)
[self.collectionView reloadData];
for (int i=0; i<self.dataSource_mix.count; i++) { //The datasource here is only for example, please use the corresponding one.
YourModel *model = self.dataSource_mix[i];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
if (model.selected) {
[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
}
else{
[collectionView deselectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
}
}
答案 2 :(得分:0)
@Reynaldo Aguilar的答案很好。您可以通过创建遮罩层并对框架进行动画处理来实现相同的目的。这种方法的好处是可以在任何(和多色)背景下使用。这种方法的缺点是,如果生产线无法正常运行,则可能无法正常工作,因为在这种情况下,可能同时(当您只希望其中一个要隐藏)隐藏/显示多个点。
为便于使用,您可以将UIView子类化,并覆盖其draw和init方法,例如添加行。
INIT:
init(frame: CGRect, path: [UIBezierPath], strokeColor: [UIColor], fillColor: [UIColor], lineWidth: [CGFloat]) {
// Initialize the view
super.init(frame: frame)
self.paths = path
self.strokeColors = strokeColor
self.fillColors = fillColor
self.lineWidths = lineWidth
self.backgroundColor = UIColor.clear // Background will always be clear by default
}
DRAW:
override func draw(_ rect: CGRect) {
super.draw(rect)
guard paths.count == strokeColors.count && strokeColors.count == fillColors.count && fillColors.count == lineWidths.count else {
print("ERROR: ARRAYS DON'T MATCH") // Stronger error handling recommended
return
}
for psfl in 0..<paths.count {
// Fill path if appropriate
self.fillColors[psfl].setFill()
self.paths[psfl].fill()
self.strokeColors[psfl].setStroke()
self.paths[psfl].lineWidth = self.lineWidths[psfl]
self.paths[psfl].stroke()
}
}
然后,您可以像这样使遮罩层动画化
动画:
func animate(startingRect: CGRect, duration: Double, animationKey: String) {
// Create a path based on the starting rect
let maskPath = UIBezierPath(rect: startingRect)
// Create a path based on the final rect
let finalPath = UIBezierPath(rect: self.frame)
// Create a shapelayer for the animation block
let maskLayer = CAShapeLayer()
maskLayer.frame = startingRect
// Add the mask layer to the custom view
self.layer.mask = maskLayer
// Animation block
let animation = CABasicAnimation(keyPath: "path")
animation.delegate = self // (Optionaly) set the delegate so we can remove the mask when the animation completes
animation.fromValue = maskLayer.path
animation.toValue = finalPath.cgPath
animation.duration = duration
maskLayer.add(animation, forKey: animationKey) // Add the animation to the mask layer
// Necessary for the animation to work properly
CATransaction.begin()
CATransaction.setDisableActions(true)
maskLayer.path = maskPath.cgPath
CATransaction.commit()
}
有了子类,实现起来很容易。对其进行初始化,将其添加为所需的子视图,然后在希望动画开始时调用animate()。如果希望隐藏图形开始,还可以摆弄其他东西,例如在初始化和动画过程中使用Alpha。