Анимация маски UIView (от большего круга CAShapeLayer к меньшему) - PullRequest
0 голосов
/ 27 октября 2018

У меня есть класс UIView, который я добавляю в свой основной вид, который блокирует все, кроме формы круга.Сейчас я пытаюсь оживить круг.Я решил использовать CABasicAnimation для этого, но круг остается большим и не анимируется.Я не уверен, что я что-то упускаю или что я пытаюсь сделать, невозможно.

   class SpotlightOverlay: UIView {

        let overlayView = UIView()

        override init(frame: CGRect) {
            super.init(frame: frame)
            self.backgroundColor = UIColor.clear
        }

        init(frame: CGRect,
             xOffset: CGFloat,
             yOffset: CGFloat,
             radius: CGFloat) {

            super.init(frame: frame)

            overlayView.frame = frame
            overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.7)

            let path = CGMutablePath()
            path.addArc(center: CGPoint(x: xOffset, y: yOffset),
                        radius: radius*5,
                        startAngle: 0.0,
                        endAngle: 2.0 * .pi,
                        clockwise: false)
            path.addRect(CGRect(origin: .zero, size: overlayView.frame.size))

            let path2 = CGMutablePath()
            path2.addArc(center: CGPoint(x: xOffset, y: yOffset),
                        radius: radius,
                        startAngle: 0.0,
                        endAngle: 2.0 * .pi,
                        clockwise: false)
            path2.addRect(CGRect(origin: .zero, size: overlayView.frame.size))

            let anim = CABasicAnimation(keyPath: "path")

            anim.fromValue = path

            anim.toValue = path2

            anim.duration = 6.0

            anim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

            let maskLayer = CAShapeLayer()
            maskLayer.backgroundColor = UIColor.black.cgColor

            maskLayer.add(anim, forKey: nil)

            CATransaction.begin()
            CATransaction.setDisableActions(true)
            maskLayer.path = path
            CATransaction.commit()

            maskLayer.fillRule = CAShapeLayerFillRule.evenOdd


            overlayView.layer.mask = maskLayer

            overlayView.clipsToBounds = true

            self.addSubview(overlayView)

        }

        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

    }

1 Ответ

0 голосов
/ 30 октября 2018

Вместо использования CGMutablePath я достиг того, что хотел, с помощью UIBezierPath.Более конкретно, это выглядит как дыра в представлении, которое может анимировать.

import UIKit

class SpotlightOverlay: UIView {

    let overlayView = UIView()
    let maskLayer = CAShapeLayer()
    var initXOffset:CGFloat = 0
    var initYOffset:CGFloat = 0
    var initDiameter:CGFloat = 0

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.clear
    }

    init(frame: CGRect,
         xOffset: CGFloat,
         yOffset: CGFloat,
         startDiameter: CGFloat,
         endDiameter:CGFloat,
         duration:Double) {

        super.init(frame: frame)

        self.isUserInteractionEnabled = false
        self.overlayView.isUserInteractionEnabled = false

        overlayView.frame = frame
        overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.7)

        let padding:CGFloat = 50

        let startDiameter = startDiameter + padding
        let endDiameter = endDiameter + padding

        initXOffset = xOffset
        initYOffset = yOffset
        initDiameter = endDiameter

        let ovalFrame1 = CGRect(x:xOffset-(startDiameter/2),
                                y:yOffset-(startDiameter/2),
                                width:startDiameter,
                                height:startDiameter)

        let ovalFrame2 = CGRect(x:xOffset-(endDiameter/2),
                                y:yOffset-(endDiameter/2),
                                width:endDiameter,
                                height:endDiameter)


        let path = UIBezierPath(ovalIn: ovalFrame1)
        let path2 = UIBezierPath(ovalIn: ovalFrame2)

        maskLayer.backgroundColor = UIColor.black.cgColor

        path.append(UIBezierPath(rect: self.bounds))
        path2.append(UIBezierPath(rect: self.bounds))

        maskLayer.fillRule = CAShapeLayerFillRule.evenOdd

        CATransaction.begin()
        CATransaction.setDisableActions(true)
        maskLayer.path = path.cgPath
        CATransaction.commit()

        let anim = CABasicAnimation(keyPath: "path")
        anim.fromValue = path.cgPath
        anim.toValue = path2.cgPath
        anim.duration = duration
        anim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        anim.fillMode = CAMediaTimingFillMode.forwards
        anim.isRemovedOnCompletion = false
        maskLayer.add(anim, forKey: nil)

        overlayView.layer.mask = maskLayer
        overlayView.clipsToBounds = true
        self.addSubview(overlayView)

    }

    func animateTo(xOffset: CGFloat,
                   yOffset: CGFloat,
                   endDiameter:CGFloat,
                   duration:Double) {

        let padding:CGFloat = 50

        let startDiameter = initDiameter
        let endDiameter = endDiameter + padding

        let ovalFrame1 = CGRect(x:initXOffset-(startDiameter/2),
                                y:initYOffset-(startDiameter/2),
                                width:startDiameter,
                                height:startDiameter)

        let ovalFrame2 = CGRect(x:xOffset-(endDiameter/2),
                                y:yOffset-(endDiameter/2),
                                width:endDiameter,
                                height:endDiameter)

        initXOffset = xOffset
        initYOffset = yOffset

        let path = UIBezierPath(ovalIn: ovalFrame1)
        let path2 = UIBezierPath(ovalIn: ovalFrame2)

        path.append(UIBezierPath(rect: self.bounds))
        path2.append(UIBezierPath(rect: self.bounds))

        CATransaction.begin()
        CATransaction.setDisableActions(true)
        maskLayer.path = path.cgPath
        CATransaction.commit()

        let anim = CABasicAnimation(keyPath: "path")
        anim.fromValue = path.cgPath
        anim.toValue = path2.cgPath
        anim.duration = duration
        anim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        anim.fillMode = CAMediaTimingFillMode.forwards
        anim.isRemovedOnCompletion = false
        maskLayer.add(anim, forKey: nil)

        initDiameter = endDiameter

    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

Сначала инициализируйте класс, а затем вызывайте функцию animateTo в любое время, когда вы хотите анимировать перемещение дыры.

...