// // GradientFill.swift // lottie-swift // // Created by Brandon Withrow on 1/8/19. // import Foundation // MARK: - GradientType enum GradientType: Int, Codable { case none case linear case radial } // MARK: - GradientFill final class GradientFill: ShapeItem { // MARK: Lifecycle required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: GradientFill.CodingKeys.self) opacity = try container.decode(KeyframeGroup.self, forKey: .opacity) startPoint = try container.decode(KeyframeGroup.self, forKey: .startPoint) endPoint = try container.decode(KeyframeGroup.self, forKey: .endPoint) gradientType = try container.decode(GradientType.self, forKey: .gradientType) highlightLength = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightLength) highlightAngle = try container.decodeIfPresent(KeyframeGroup.self, forKey: .highlightAngle) fillRule = try container.decodeIfPresent(FillRule.self, forKey: .fillRule) ?? .nonZeroWinding let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors) numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors) try super.init(from: decoder) } required init(dictionary: [String: Any]) throws { let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity) opacity = try KeyframeGroup(dictionary: opacityDictionary) let startPointDictionary: [String: Any] = try dictionary.value(for: CodingKeys.startPoint) startPoint = try KeyframeGroup(dictionary: startPointDictionary) let endPointDictionary: [String: Any] = try dictionary.value(for: CodingKeys.endPoint) endPoint = try KeyframeGroup(dictionary: endPointDictionary) let gradientRawType: Int = try dictionary.value(for: CodingKeys.gradientType) guard let gradient = GradientType(rawValue: gradientRawType) else { throw InitializableError.invalidInput } gradientType = gradient if let highlightLengthDictionary = dictionary[CodingKeys.highlightLength.rawValue] as? [String: Any] { highlightLength = try? KeyframeGroup(dictionary: highlightLengthDictionary) } else { highlightLength = nil } if let highlightAngleDictionary = dictionary[CodingKeys.highlightAngle.rawValue] as? [String: Any] { highlightAngle = try? KeyframeGroup(dictionary: highlightAngleDictionary) } else { highlightAngle = nil } let colorsDictionary: [String: Any] = try dictionary.value(for: CodingKeys.colors) let nestedColorsDictionary: [String: Any] = try colorsDictionary.value(for: GradientDataKeys.colors) colors = try KeyframeGroup<[Double]>(dictionary: nestedColorsDictionary) numberOfColors = try colorsDictionary.value(for: GradientDataKeys.numberOfColors) if let fillRuleRawValue = dictionary[CodingKeys.fillRule.rawValue] as? Int, let fillRule = FillRule(rawValue: fillRuleRawValue) { self.fillRule = fillRule } else { fillRule = .nonZeroWinding } try super.init(dictionary: dictionary) } // MARK: Internal /// The opacity of the fill let opacity: KeyframeGroup /// The start of the gradient let startPoint: KeyframeGroup /// The end of the gradient let endPoint: KeyframeGroup /// The type of gradient let gradientType: GradientType /// Gradient Highlight Length. Only if type is Radial let highlightLength: KeyframeGroup? /// Highlight Angle. Only if type is Radial let highlightAngle: KeyframeGroup? /// The number of color points in the gradient let numberOfColors: Int /// The Colors of the gradient. let colors: KeyframeGroup<[Double]> /// The fill rule to use when filling a path let fillRule: FillRule override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(opacity, forKey: .opacity) try container.encode(startPoint, forKey: .startPoint) try container.encode(endPoint, forKey: .endPoint) try container.encode(gradientType, forKey: .gradientType) try container.encodeIfPresent(highlightLength, forKey: .highlightLength) try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle) try container.encodeIfPresent(fillRule, forKey: .fillRule) var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors) try colorsContainer.encode(numberOfColors, forKey: .numberOfColors) try colorsContainer.encode(colors, forKey: .colors) } // MARK: Private private enum CodingKeys: String, CodingKey { case opacity = "o" case startPoint = "s" case endPoint = "e" case gradientType = "t" case highlightLength = "h" case highlightAngle = "a" case colors = "g" case fillRule = "r" } private enum GradientDataKeys: String, CodingKey { case numberOfColors = "p" case colors = "k" } }