Я создал собственный круговой выбор диапазона, аналогичный тому, который используется в приложении «Будильник/перед сном» для iOS. Для этого я использую два UIButton
больших пальца с прикрепленными UIPanGestureRecognizer
и CAShapeLayer
+ UIBezierPath
, указывающими временной диапазон между ними. Это работает фантастически, когда я использую сенсорный ввод как для движения по кругу, так и для изменения размера самой дуги.
Простите мое грубое изображение кругов, но вот суть того, как работает сборщик:
Проблема заключается в том, что мне нужно переключаться между определенными предустановками временного диапазона с помощью UISegmentedControl
, и я действительно хотел бы анимировать это изменение. Мне удалось красиво анимировать оба больших пальца (зеленые) по кругу на все 360 градусов с помощью CAKeyframeAnimation(keyPath: "position")
, но я не смог сделать это с моим временным диапазоном (красный). CAShapeLayer
. Лучшее, что мне удалось, это анимировать слой диапазона с помощью CABasicAnimation(keyPath: "path")
, но он исказился во время анимации и не следовал за дугой, а вместо этого проходил по кругу по прямому вектору.
Для краткости я не буду публиковать содержимое getStuff()
функций, так как они не имеют отношения к проблеме.
// This works
let startAngle: CGFloat = getAngle(for: start)
let startPath: UIBezierPath = getThumbTravelPath(fromAngle: startThumbAngle, toAngle: startAngle)
let startAnimation = CAKeyframeAnimation(keyPath: "position")
startAnimation.path = startPath.cgPath
startAnimation.isRemovedOnCompletion = false
startAnimation.calculationMode = .cubicPaced
startAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
startAnimation.duration = 0.2
startThumbView.layer.add(startAnimation, forKey: nil)
let endAngle: CGFloat = getAngle(for: end)
let endPath: UIBezierPath = getThumbTravelPath(fromAngle: endThumbAngle, toAngle: endAngle)
let endAnimation = CAKeyframeAnimation(keyPath: "position")
endAnimation.path = endPath.cgPath
endAnimation.isRemovedOnCompletion = false
endAnimation.calculationMode = .cubicPaced
endAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
endAnimation.duration = 0.2
endThumbView.layer.add(endAnimation, forKey: nil)
// This doesn't work
let path: UIBezierPath = getRangePathFor(startAngle: startAngle, endAngle: endAngle)
let pathAnimation = CABasicAnimation(keyPath: "path")
pathAnimation.fromValue = rangePath.cgPath
pathAnimation.toValue = path.cgPath
pathAnimation.duration = 0.2
pathAnimation.isRemovedOnCompletion = false
pathAnimation.isAdditive = true
rangeLayer.path = path.cgPath
rangeLayer.add(pathAnimation, forKey: nil)
Я видел, как некоторые люди предлагали использовать подход CABasicAnimation(keyPath: "startStroke")
+ CABasicAnimation(keyPath: "endStroke")
, но, насколько я могу судить, это не сработает, потому что я не могу иметь отрицательное значение для startStroke
, поэтому я не могу иметь дугу, простирающуюся, скажем, от от 270° до 45°.
startStroke
иendStroke
варьируются только от 0,0 до 1,0, то есть я не могу перейти от 0,75 до 1,125. - person KLD   schedule 15.09.2020