【发布时间】:2019-01-08 18:33:38
【问题描述】:
好的,这个问题可能在 SO 上被问了 1000 次。我一定已经尝试了 500 个答案。没有任何效果,这让我觉得这可能是不可能的。
在我开始之前,我不想要一个可动画的自定义属性,我只想利用边界变化。大多数答案只处理前者。但是,如果我的内容仅取决于图层的大小而仅取决于其他内容怎么办?
这真的不可能吗? 我试过了:
override class func needsDisplay(forKey key: String) -> BoolneedsDisplayOnBoundsChange- 覆盖各种
action(for:)或animation(for:)方法 contentMode = .redraw- 大量的技巧,例如覆盖
setBounds并添加隐式动画(顺便说一句,这是无用的,因为图层有动画,它只是不会在调整大小时重绘) - 也许我忘记了其他几件事,花了很长时间来解决这个问题
我达到的最远:内容在动画开始时绘制,然后在结束时绘制。从大到小当然看起来很糟糕(也像素化)。
不一定重要,但这里是绘制方法供参考:
override func draw(in ctx: CGContext) {
UIGraphicsPushContext(ctx)
paths = [Dimension: [UIBezierPath]]()
cuts = [Dimension: [UIBezierPath]]()
let centerTranslate = CGAffineTransform.init(translationX: frame.width * 0.5,
y: frame.height * 0.5)
let dimRotateAndTranslate = centerTranslate.rotated(by: CGFloat.pi / 2)
let needlePath = UIBezierPath(rect: CGRect(x: 0,
y: 0,
width: circularSpacing,
height: frame.height * 0.5 - centerRadius))
let piProgress = CGFloat(progress * 2 * Float.pi)
var needleRotateAndTranslate = CGAffineTransform(rotationAngle: piProgress)
needleRotateAndTranslate = needleRotateAndTranslate.translatedBy(x: -needlePath.bounds.width * 0.5,
y: -needlePath.bounds.height - centerRadius)
needlePath.apply(needleRotateAndTranslate.concatenating(centerTranslate))
let sortedDims = states.sorted(by: {$0.key < $1.key})
for (offset: i, element: (key: dim, value: beats)) in sortedDims.enumerated() {
var pathsAtDim = paths[dim] ?? []
var cutAtDim = cuts[dim] ?? []
let thickness = (frame.width * 0.5 - centerRadius - circularSpacing * CGFloat(states.count - 1)) / CGFloat(states.count)
let xSpacing = thickness + circularSpacing
let radius = centerRadius + thickness * 0.5 + CGFloat(i) * xSpacing
for (offset: j, beat) in beats.enumerated() {
let length = CGFloat.pi * 2 / CGFloat(beats.count) * CGFloat(beat.relativeDuration)
var path = UIBezierPath(arcCenter: .zero,
radius: radius,
startAngle: CGFloat(j) * length,
endAngle: CGFloat(j + 1) * length,
clockwise: true)
path = UIBezierPath(cgPath: path.cgPath.copy(strokingWithWidth: thickness,
lineCap: .butt,
lineJoin: .miter,
miterLimit: 0,
transform: dimRotateAndTranslate))
func cutAtIndex(index: Int, offset: CGFloat) -> UIBezierPath {
let cut = UIBezierPath(rect: CGRect(x: 0,
y: 0,
width: thickness,
height: circularSpacing))
let offsetRadians = offset / radius
let x = (radius - thickness / 2) * cos(CGFloat(index) * length + offsetRadians)
let y = (radius - thickness / 2) * sin(CGFloat(index) * length + offsetRadians)
let translate = CGAffineTransform.init(translationX: x,
y: y)
let rotate = CGAffineTransform.init(rotationAngle: CGFloat(index) * length)
cut.apply(rotate
.concatenating(translate)
.concatenating(dimRotateAndTranslate))
return cut
}
cutAtDim.append(cutAtIndex(index: j, offset: -circularSpacing * 0.5))
pathsAtDim.append(path)
}
paths[dim] = pathsAtDim
cuts[dim] = cutAtDim
}
let clippingMask = UIBezierPath.init(rect: bounds)
for (dim, _) in states {
for cut in cuts[dim] ?? [] {
clippingMask.append(cut.reversing())
}
}
clippingMask.addClip()
for (dim, states) in states {
for i in 0..<states.count {
let state = states[i]
if let path = paths[dim]?[i] {
let color = dimensionsColors[dim]?[state.state] ?? .darkGray
color.setFill()
path.fill()
}
}
}
ctx.resetClip()
needleColor.setFill()
needlePath.fill()
UIGraphicsPopContext()
}
【问题讨论】:
-
为什么边界会改变?
标签: ios core-graphics core-animation