     /// Layer which holds the overlay.
     fileprivate var overlayLayer: CALayer?
 
-    /**
-     Determines whether a view can be animated.
-     - Parameter view: A UIView.
-     - Parameter state: A MotionTargetState.
-     - Parameter isAppearing: A boolean indicating whether or not the
-     view is going to appear.
-     */
+    override func clean() {
+        super.clean()
+        overlayLayer = nil
+    }
+    
     override class func canAnimate(view: UIView, state: MotionTargetState, isAppearing: Bool) -> Bool {
         return  nil != state.position           ||
                 nil != state.size               ||

    }
    
    /**
     Retrieves the current value for a given key. 
     - Parameter for key: A String.
     - Returns: An optional Any value.
     */
    func currentValue(for key: String) -> Any? {
        if let key = overlayKey(for: key) {
            return overlayLayer?.value(forKeyPath: key)
        }

     - Parameter toValue: A optional Any value.
     - Returns: A TimeInterval.
     */
    @discardableResult
    fileprivate func animate(key: String, beginTime: TimeInterval, fromValue: Any?, toValue: Any?) -> TimeInterval {
        let anim = getAnimation(key: key, beginTime:beginTime, fromValue: fromValue, toValue: toValue)
        
        if let overlayKey = overlayKey(for: key) {
            getOverlayLayer().add(anim, forKey: overlayKey)
            
        } else {
            snapshot.layer.add(anim, forKey: key)
            
            switch key {
            case "cornerRadius", "contentsRect", "contentsScale":
                contentLayer?.add(anim, forKey: key)
                overlayLayer?.add(anim, forKey: key)
                
            case "bounds.size":
                guard let fs = (fromValue as? NSValue)?.cgSizeValue else {
                    return 0
                }
                
                guard let ts = (toValue as? NSValue)?.cgSizeValue else {
                    return 0
                }
                
                // for the snapshotView(UIReplicantView): there is a
                // subview(UIReplicantContentView) that is hosting the real snapshot image.
                // because we are using CAAnimations and not UIView animations,
                // The snapshotView will not layout during animations.
                // we have to add two more animations to manually layout the content view.
                let fpn = NSValue(cgPoint: fs.center)
                let tpn = NSValue(cgPoint: ts.center)
                
                let a = getAnimation(key: "position", beginTime: 0, fromValue: fpn, toValue: tpn, ignoreArc: true)
                a.beginTime = anim.beginTime
                a.timingFunction = anim.timingFunction
                a.duration = anim.duration
                
                contentLayer?.add(a, forKey: "position")
                contentLayer?.add(anim, forKey: key)
                
                overlayLayer?.add(a, forKey: "position")
                overlayLayer?.add(anim, forKey: key)
                
            default: break
            }
        }
        
        return anim.duration + anim.beginTime - beginTime
    }
    
    /**
     Animates the contentLayer and overlayLayer with a given delay.
     - Parameter delay: A TimeInterval.
     */
    fileprivate func animate(delay: TimeInterval) {
        if let v = targetState.timingFunction {
            timingFunction = v
        }
        
        if let v = targetState.duration {
            duration = v
        }
        
        let beginTime = currentTime + delay
        
        var finalDuration: TimeInterval = duration
        
        for (key, (fromValue, toValue)) in transitionStates {
            let neededTime = animate(key: key, beginTime: beginTime, fromValue: fromValue, toValue: toValue)
            finalDuration = max(finalDuration, neededTime + delay)
        }
        
        duration = finalDuration
    }
    
    /**
     Constructs a map of key paths to animation values.
     - Parameter targetState state: A MotionTargetState.
     - Returns: A map of key paths to animation values.
     */
    fileprivate func viewState(targetState ts: MotionTargetState) -> [String: Any?] {
        var ts = ts
        var values = [String: Any?]()
        
        if let size = ts.size {
            if ts.useScaleBasedSizeChange ?? targetState.useScaleBasedSizeChange ?? false {
                let currentSize = snapshot.bounds.size
                ts.append(.scale(x: size.width / currentSize.width, y: size.height / currentSize.height))
                
            } else {
                values["bounds.size"] = NSValue(cgSize:size)
            }
        }
        
        if let position = ts.position {
            values["position"] = NSValue(cgPoint:position)
        }
        
        if let opacity = ts.opacity, !(snapshot is UIVisualEffectView) {
            values["opacity"] = NSNumber(value: opacity)
        }
        
        if let cornerRadius = ts.cornerRadius {
            values["cornerRadius"] = NSNumber(value: cornerRadius.native)
        }
        
        if let backgroundColor = ts.backgroundColor {
            values["backgroundColor"] = backgroundColor
        }
        
        if let zPosition = ts.zPosition {
            values["zPosition"] = NSNumber(value: zPosition.native)
        }
        
        if let borderWidth = ts.borderWidth {
            values["borderWidth"] = NSNumber(value: borderWidth.native)
        }
        
        if let borderColor = ts.borderColor {
            values["borderColor"] = borderColor
        }
        
        if let masksToBounds = ts.masksToBounds {
            values["masksToBounds"] = masksToBounds
        }
        
        if ts.displayShadow {
            if let shadowColor = ts.shadowColor {
                values["shadowColor"] = shadowColor
            }
            
            if let shadowRadius = ts.shadowRadius {
                values["shadowRadius"] = NSNumber(value: shadowRadius.native)
            }
            
            if let shadowOpacity = ts.shadowOpacity {
                values["shadowOpacity"] = NSNumber(value: shadowOpacity)
            }
            
            if let shadowPath = ts.shadowPath {
                values["shadowPath"] = shadowPath
            }
            
            if let shadowOffset = ts.shadowOffset {
                values["shadowOffset"] = NSValue(cgSize: shadowOffset)
            }
        }
        
        if let contentsRect = ts.contentsRect {
            values["contentsRect"] = NSValue(cgRect: contentsRect)
        }
        
        if let contentsScale = ts.contentsScale {
            values["contentsScale"] = NSNumber(value: contentsScale.native)
        }
        
        if let transform = ts.transform {
            values["transform"] = NSValue(caTransform3D: transform)
        }
        
        if let (color, opacity) = ts.overlay {
            values["overlay.backgroundColor"] = color
            values["overlay.opacity"] = NSNumber(value: opacity.native)
        }
        
        return values
    }
    
    fileprivate func seek(layer: CALayer, elapsedTime: TimeInterval) {
        let timeOffset = elapsedTime - targetState.delay
        for (key, anim) in layer.animations {
            anim.speed = 0
            anim.timeOffset = max(0, min(anim.duration - 0.01, timeOffset))
            layer.removeAnimation(forKey: key)
            layer.add(anim, forKey: key)
        }
    }
}
