UIView Layer Mask Анимировать
Я пытаюсь анимировать слой маски на UIView.
В основном этот код отображает изображение ниже:
let bounds: CGRect = self.manualWBMaskView!.bounds
let maskLayer: CAShapeLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.fillColor = UIColor.blackColor().CGColor
let screenWith : CGFloat = UIScreen.mainScreen().bounds.width
let roundedRectFrame : CGRect = CGRectMake(self.manualWBMaskView!.bounds.midX - (screenWith/4), self.manualWBMaskView!.bounds.midY - (screenWith/4), screenWith/2, screenWith/2)
let path: UIBezierPath = UIBezierPath(roundedRect:roundedRectFrame, cornerRadius:10 )
path.appendPath(UIBezierPath(rect: bounds))
maskLayer.path = path.CGPath
maskLayer.fillRule = kCAFillRuleEvenOdd
self.manualWBMaskView!.layer.mask = maskLayer
Я хочу, чтобы он анимировал эту позицию выреза из полноэкранного в верхнее положение:
Я попробовал анимацию UIView, и не повезло. Так как у меня уже есть CAShapeLayer, я должен быть в состоянии оживить это?
РЕДАКТИРОВАТЬ**
Вот код анимации, который я пробовал, который не работал:
//Before Animation Make the Mask fill the whole view:
self.manualWBMaskView!.layer.mask = self.manualWBMaskView!.bounds
var duration: NSTimeInterval = 0.8
CATransaction.begin()
CATransaction[kCATransactionAnimationDuration] = Int(duration)
self.manualWBMaskView!.layer.mask = CGRectMake(self.manualWBMaskView!.bounds.midX - (screenWith/4), self.manualWBMaskView!.bounds.midY - (screenWith/4), screenWith/2, screenWith/2)
CATransaction.commit()
Согласно ответу 'originaluser2 ниже, у меня есть это:
func presentMaskScreenWithAnimation () {
let bounds: CGRect = self.manualWBMaskView!.bounds
let maskLayer: CAShapeLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.fillColor = UIColor.blackColor().CGColor
let screenWith : CGFloat = UIScreen.mainScreen().bounds.width
let roundedRectFrame : CGRect = self.manualWBMaskView.bounds
let path: UIBezierPath = UIBezierPath(roundedRect:roundedRectFrame, cornerRadius:0 )
path.appendPath(UIBezierPath(rect: bounds))
maskLayer.path = path.CGPath
maskLayer.fillRule = kCAFillRuleEvenOdd
self.manualWBMaskView!.layer.mask = maskLayer
// define your new path to animate the mask layer to
let screenWith2 : CGFloat = UIScreen.mainScreen().bounds.width
let roundedRectFrame2 : CGRect = CGRectMake(self.manualWBMaskView!.bounds.midX - (screenWith2/4), self.manualWBMaskView!.bounds.midY - (screenWith2/4), screenWith2/2, screenWith2/2)
let path2 = UIBezierPath(roundedRect:roundedRectFrame2, cornerRadius:10 )
// create new animation
let anim = CABasicAnimation(keyPath: "path")
// from value is the current mask path
anim.fromValue = maskLayer.path
// to value is the new path
anim.toValue = path2.CGPath
// duration of your animation
anim.duration = 5.0
// custom timing function to make it look smooth
anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// add animation
maskLayer.addAnimation(anim, forKey: nil)
// update the path property on the mask layer, using a CATransaction to prevent an implicit animation
CATransaction.begin()
CATransaction.setDisableActions(true)
maskLayer.path = path2.CGPath
maskLayer.fillRule = kCAFillRuleEvenOdd
CATransaction.commit()
}
Делает анимацию; Тем не менее, у меня есть две проблемы: 1. Форма отключена во время анимации, но все же в конце анимации. (Скриншот ниже)
- Маска перевернута. Она видна внутри фигуры, но прозрачна вокруг нее. Мне нужно наоборот. Внутри форма прозрачная.
1 ответ
Вы хотите использовать CABasicAnimation
на path
свойство вашего слоя маски
Это позволит вам анимировать вашу маску с данного пути на другой данный путь. Базовая анимация автоматически интерполирует промежуточные шаги для вас.
Нечто подобное должно сработать:
// define your new path to animate the mask layer to
let path = UIBezierPath(roundedRect: CGRectInset(view.bounds, 100, 100), cornerRadius: 20.0)
path.appendPath(UIBezierPath(rect: view.bounds))
// create new animation
let anim = CABasicAnimation(keyPath: "path")
// from value is the current mask path
anim.fromValue = maskLayer.path
// to value is the new path
anim.toValue = path.CGPath
// duration of your animation
anim.duration = 5.0
// custom timing function to make it look smooth
anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// add animation
maskLayer.addAnimation(anim, forKey: nil)
// update the path property on the mask layer, using a CATransaction to prevent an implicit animation
CATransaction.begin()
CATransaction.setDisableActions(true)
maskLayer.path = path.CGPath
CATransaction.commit()
Результат:
Стоит отметить, что в Core Animation есть ошибка, из-за которой невозможно правильно анимировать путь, включающий прямоугольник, в путь, включающий скругленный прямоугольник с радиусом угла. Я подал отчет об ошибке в Apple для этого.
Для этого есть простое исправление, заключающееся в использовании закругленного прямоугольника с угловым радиусом пренебрежимо малого значения на исходном пути (для меня работает 0,0001). Немного хакерский - но вы не можете заметить разницу при использовании приложения.
Сообщение об ошибке от Apple
К сожалению, Apple считает это "ожидаемым поведением". Они сказали следующее:
Чтобы анимация отображалась правильно, анимированные пути должны иметь одинаковое количество элементов / сегментов (в идеале, одного типа). В противном случае, нет разумного способа интерполировать между ними.
Например, если первый путь имеет четыре элемента, а второй путь имеет шесть элементов, мы можем легко создать первые четыре точки, усредняя первые четыре начальные и конечные точки с весом для текущего времени, но невозможно придумать универсальный способ расчета промежуточных позиций для пятого и шестого баллов. Вот почему анимация прекрасно работает для углового радиуса 0,001 (такое же количество элементов), но не работает для простого прямоугольного пути (различное количество элементов).
Лично я не согласен, если вы укажете скругленный прямоугольник с радиусом угла 0, а затем анимируете с закругленным прямоугольником с радиусом угла> 0 - неправильная анимация все равно получается, даже если оба пути должны иметь "одинаковое количество элементов". Я подозреваю, что причина, по которой это не работает, заключается в том, что внутренняя реализация проверяет радиус 0 и пытается оптимизировать, используя вместо этого прямоугольный путь с 4 элементами, поэтому он не работает.
Я предоставлю еще одно сообщение об ошибке, объясняющее это, когда у меня будет время.
Пара других вещей...
Ты силой разворачиваешь свой manualWBMaskView
немного. Не. Вам нужно научиться правильно обращаться с опциями. Каждый раз, когда вы заставляете что-то развернуть, ваше приложение может зависнуть.
У вас также есть несколько строк, как это:
let maskLayer: CAShapeLayer = CAShapeLayer()
: CAShapeLayer
здесь не только нет необходимости, но и идет вразрез с хорошей практикой. Вы должны всегда позволять Swift выводить тип значения, где это возможно.