// // Transform.swift // lottie-swift // // Created by Brandon Withrow on 1/7/19. // import Foundation /// The animatable transform for a layer. Controls position, rotation, scale, and opacity. final class Transform: Codable { /// The anchor point of the transform. let anchorPoint: KeyframeGroup /// The position of the transform. This is nil if the position data was split. let position: KeyframeGroup? /// The positionX of the transform. This is nil if the position property is set. let positionX: KeyframeGroup? /// The positionY of the transform. This is nil if the position property is set. let positionY: KeyframeGroup? /// The scale of the transform let scale: KeyframeGroup /// The rotation of the transform. Note: This is single dimensional rotation. let rotation: KeyframeGroup /// The opacity of the transform. let opacity: KeyframeGroup /// Should always be nil. let rotationZ: KeyframeGroup? enum CodingKeys : String, CodingKey { case anchorPoint = "a" case position = "p" case positionX = "px" case positionY = "py" case scale = "s" case rotation = "r" case rotationZ = "rz" case opacity = "o" } enum PositionCodingKeys : String, CodingKey { case split = "s" case positionX = "x" case positionY = "y" } required init(from decoder: Decoder) throws { /** This manual override of decode is required because we want to throw an error in the case that there is not position data. */ let container = try decoder.container(keyedBy: Transform.CodingKeys.self) // AnchorPoint self.anchorPoint = try container.decodeIfPresent(KeyframeGroup.self, forKey: .anchorPoint) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) // Position if container.contains(.positionX), container.contains(.positionY) { // Position dimensions are split into two keyframe groups self.positionX = try container.decode(KeyframeGroup.self, forKey: .positionX) self.positionY = try container.decode(KeyframeGroup.self, forKey: .positionY) self.position = nil } else if let positionKeyframes = try? container.decode(KeyframeGroup.self, forKey: .position) { // Position dimensions are a single keyframe group. self.position = positionKeyframes self.positionX = nil self.positionY = nil } else if let positionContainer = try? container.nestedContainer(keyedBy: PositionCodingKeys.self, forKey: .position), let positionX = try? positionContainer.decode(KeyframeGroup.self, forKey: .positionX), let positionY = try? positionContainer.decode(KeyframeGroup.self, forKey: .positionY) { /// Position keyframes are split and nested. self.positionX = positionX self.positionY = positionY self.position = nil } else { /// Default value. self.position = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0)) self.positionX = nil self.positionY = nil } // Scale self.scale = try container.decodeIfPresent(KeyframeGroup.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100)) // Rotation if let rotationZ = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotationZ) { self.rotation = rotationZ } else { self.rotation = try container.decodeIfPresent(KeyframeGroup.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0)) } self.rotationZ = nil // Opacity self.opacity = try container.decodeIfPresent(KeyframeGroup.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100)) } }