曾 伟 5 år sedan
förälder
incheckning
4f183c2275
100 ändrade filer med 9905 tillägg och 0 borttagningar
  1. 18 0
      Podfile
  2. 16 0
      Podfile.lock
  3. 16 0
      Pods/Manifest.lock
  4. 1260 0
      Pods/Pods.xcodeproj/project.pbxproj
  5. 26 0
      Pods/Target Support Files/Pods-share/Pods-share-Info.plist
  6. 3 0
      Pods/Target Support Files/Pods-share/Pods-share-acknowledgements.markdown
  7. 29 0
      Pods/Target Support Files/Pods-share/Pods-share-acknowledgements.plist
  8. 5 0
      Pods/Target Support Files/Pods-share/Pods-share-dummy.m
  9. 16 0
      Pods/Target Support Files/Pods-share/Pods-share-umbrella.h
  10. 6 0
      Pods/Target Support Files/Pods-share/Pods-share.debug.xcconfig
  11. 6 0
      Pods/Target Support Files/Pods-share/Pods-share.modulemap
  12. 6 0
      Pods/Target Support Files/Pods-share/Pods-share.release.xcconfig
  13. 26 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-Info.plist
  14. 208 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-acknowledgements.markdown
  15. 240 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-acknowledgements.plist
  16. 5 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-dummy.m
  17. 207 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-frameworks.sh
  18. 16 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-umbrella.h
  19. 10 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im.debug.xcconfig
  20. 6 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im.modulemap
  21. 10 0
      Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im.release.xcconfig
  22. 26 0
      Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist
  23. 5 0
      Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m
  24. 12 0
      Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch
  25. 30 0
      Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h
  26. 10 0
      Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig
  27. 6 0
      Pods/Target Support Files/lottie-ios/lottie-ios.modulemap
  28. 10 0
      Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig
  29. 201 0
      Pods/lottie-ios/LICENSE
  30. 631 0
      Pods/lottie-ios/README.md
  31. 46 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTCompositionContainer.h
  32. 239 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTCompositionContainer.m
  33. 37 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTLayerContainer.h
  34. 329 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTLayerContainer.m
  35. 18 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTMaskContainer.h
  36. 107 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTMaskContainer.m
  37. 27 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTBezierData.h
  38. 100 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTBezierData.m
  39. 49 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTKeyframe.h
  40. 242 0
      Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTKeyframe.m
  41. 98 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.h
  42. 480 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.m
  43. 54 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTBezierPath.h
  44. 471 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTBezierPath.m
  45. 26 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTHelpers.h
  46. 20 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTRadialGradientLayer.h
  47. 89 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTRadialGradientLayer.m
  48. 51 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/UIColor+Expanded.h
  49. 480 0
      Pods/lottie-ios/lottie-ios/Classes/Extensions/UIColor+Expanded.m
  50. 18 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/CALayer+Compat.h
  51. 18 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/CALayer+Compat.m
  52. 37 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/LOTPlatformCompat.h
  53. 22 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/NSValue+Compat.h
  54. 35 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/NSValue+Compat.m
  55. 80 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIBezierPath.h
  56. 312 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIBezierPath.m
  57. 44 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIColor.h
  58. 158 0
      Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIColor.m
  59. 38 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTAsset.h
  60. 59 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTAsset.m
  61. 28 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTAssetGroup.h
  62. 70 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTAssetGroup.m
  63. 76 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayer.h
  64. 183 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayer.m
  65. 30 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayerGroup.h
  66. 60 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayerGroup.m
  67. 29 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTMask.h
  68. 59 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTMask.m
  69. 28 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTModels.h
  70. 25 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeCircle.h
  71. 40 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeCircle.m
  72. 26 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeFill.h
  73. 52 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeFill.m
  74. 34 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGradientFill.h
  75. 67 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGradientFill.m
  76. 21 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGroup.h
  77. 102 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGroup.m
  78. 21 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapePath.h
  79. 35 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapePath.m
  80. 22 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRectangle.h
  81. 45 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRectangle.m
  82. 30 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRepeater.h
  83. 83 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRepeater.m
  84. 35 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStar.h
  85. 66 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStar.m
  86. 39 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStroke.h
  87. 73 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStroke.m
  88. 25 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTransform.h
  89. 78 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTransform.m
  90. 21 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTrimPath.h
  91. 43 0
      Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTrimPath.m
  92. 140 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimatedControl.m
  93. 199 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimatedSwitch.m
  94. 73 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationCache.m
  95. 127 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationTransitionController.m
  96. 845 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationView.m
  97. 22 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationView_Internal.h
  98. 80 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTBlockCallback.m
  99. 23 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTCacheProvider.m
  100. 0 0
      Pods/lottie-ios/lottie-ios/Classes/Private/LOTComposition.m

+ 18 - 0
Podfile

@@ -0,0 +1,18 @@
+# Uncomment the next line to define a global platform for your project
+# platform :ios, '9.0'
+
+target 'share' do
+  # Comment the next line if you don't want to use dynamic frameworks
+  use_frameworks!
+
+  # Pods for share
+
+end
+
+target 'shiku_im' do
+  # Comment the next line if you don't want to use dynamic frameworks
+  use_frameworks!
+
+  # Pods for shiku_im
+  pod 'lottie-ios'
+end

+ 16 - 0
Podfile.lock

@@ -0,0 +1,16 @@
+PODS:
+  - lottie-ios (2.5.3)
+
+DEPENDENCIES:
+  - lottie-ios
+
+SPEC REPOS:
+  trunk:
+    - lottie-ios
+
+SPEC CHECKSUMS:
+  lottie-ios: a50d5c0160425cd4b01b852bb9578963e6d92d31
+
+PODFILE CHECKSUM: 2639d7d7e520d42757d92332abce0e5c27f19735
+
+COCOAPODS: 1.9.1

+ 16 - 0
Pods/Manifest.lock

@@ -0,0 +1,16 @@
+PODS:
+  - lottie-ios (2.5.3)
+
+DEPENDENCIES:
+  - lottie-ios
+
+SPEC REPOS:
+  trunk:
+    - lottie-ios
+
+SPEC CHECKSUMS:
+  lottie-ios: a50d5c0160425cd4b01b852bb9578963e6d92d31
+
+PODFILE CHECKSUM: 2639d7d7e520d42757d92332abce0e5c27f19735
+
+COCOAPODS: 1.9.1

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1260 - 0
Pods/Pods.xcodeproj/project.pbxproj


+ 26 - 0
Pods/Target Support Files/Pods-share/Pods-share-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 3 - 0
Pods/Target Support Files/Pods-share/Pods-share-acknowledgements.markdown

@@ -0,0 +1,3 @@
+# Acknowledgements
+This application makes use of the following third party libraries:
+Generated by CocoaPods - https://cocoapods.org

+ 29 - 0
Pods/Target Support Files/Pods-share/Pods-share-acknowledgements.plist

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreferenceSpecifiers</key>
+	<array>
+		<dict>
+			<key>FooterText</key>
+			<string>This application makes use of the following third party libraries:</string>
+			<key>Title</key>
+			<string>Acknowledgements</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>Generated by CocoaPods - https://cocoapods.org</string>
+			<key>Title</key>
+			<string></string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+	</array>
+	<key>StringsTable</key>
+	<string>Acknowledgements</string>
+	<key>Title</key>
+	<string>Acknowledgements</string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/Pods-share/Pods-share-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Pods_share : NSObject
+@end
+@implementation PodsDummy_Pods_share
+@end

+ 16 - 0
Pods/Target Support Files/Pods-share/Pods-share-umbrella.h

@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double Pods_shareVersionNumber;
+FOUNDATION_EXPORT const unsigned char Pods_shareVersionString[];
+

+ 6 - 0
Pods/Target Support Files/Pods-share/Pods-share.debug.xcconfig

@@ -0,0 +1,6 @@
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Pods/Target Support Files/Pods-share/Pods-share.modulemap

@@ -0,0 +1,6 @@
+framework module Pods_share {
+  umbrella header "Pods-share-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 6 - 0
Pods/Target Support Files/Pods-share/Pods-share.release.xcconfig

@@ -0,0 +1,6 @@
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 26 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 208 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-acknowledgements.markdown

@@ -0,0 +1,208 @@
+# Acknowledgements
+This application makes use of the following third party libraries:
+
+## lottie-ios
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2018 Airbnb, Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+Generated by CocoaPods - https://cocoapods.org

+ 240 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-acknowledgements.plist

@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreferenceSpecifiers</key>
+	<array>
+		<dict>
+			<key>FooterText</key>
+			<string>This application makes use of the following third party libraries:</string>
+			<key>Title</key>
+			<string>Acknowledgements</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2018 Airbnb, Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+			<key>License</key>
+			<string>Apache</string>
+			<key>Title</key>
+			<string>lottie-ios</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>Generated by CocoaPods - https://cocoapods.org</string>
+			<key>Title</key>
+			<string></string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+	</array>
+	<key>StringsTable</key>
+	<string>Acknowledgements</string>
+	<key>Title</key>
+	<string>Acknowledgements</string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Pods_shiku_im : NSObject
+@end
+@implementation PodsDummy_Pods_shiku_im
+@end

+ 207 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-frameworks.sh

@@ -0,0 +1,207 @@
+#!/bin/sh
+set -e
+set -u
+set -o pipefail
+
+function on_error {
+  echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
+}
+trap 'on_error $LINENO' ERR
+
+if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
+  # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
+  # frameworks to, so exit 0 (signalling the script phase was successful).
+  exit 0
+fi
+
+echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+
+COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
+SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
+
+# Used as a return value for each invocation of `strip_invalid_archs` function.
+STRIP_BINARY_RETVAL=0
+
+# This protects against multiple targets copying the same framework dependency at the same time. The solution
+# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
+RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
+
+# Copies and strips a vendored framework
+install_framework()
+{
+  if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
+    local source="${BUILT_PRODUCTS_DIR}/$1"
+  elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
+    local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
+  elif [ -r "$1" ]; then
+    local source="$1"
+  fi
+
+  local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+
+  if [ -L "${source}" ]; then
+    echo "Symlinked..."
+    source="$(readlink "${source}")"
+  fi
+
+  # Use filter instead of exclude so missing patterns don't throw errors.
+  echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
+  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
+
+  local basename
+  basename="$(basename -s .framework "$1")"
+  binary="${destination}/${basename}.framework/${basename}"
+
+  if ! [ -r "$binary" ]; then
+    binary="${destination}/${basename}"
+  elif [ -L "${binary}" ]; then
+    echo "Destination binary is symlinked..."
+    dirname="$(dirname "${binary}")"
+    binary="${dirname}/$(readlink "${binary}")"
+  fi
+
+  # Strip invalid architectures so "fat" simulator / device frameworks work on device
+  if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
+    strip_invalid_archs "$binary"
+  fi
+
+  # Resign the code if required by the build settings to avoid unstable apps
+  code_sign_if_enabled "${destination}/$(basename "$1")"
+
+  # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
+  if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
+    local swift_runtime_libs
+    swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
+    for lib in $swift_runtime_libs; do
+      echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
+      rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
+      code_sign_if_enabled "${destination}/${lib}"
+    done
+  fi
+}
+
+# Copies and strips a vendored dSYM
+install_dsym() {
+  local source="$1"
+  warn_missing_arch=${2:-true}
+  if [ -r "$source" ]; then
+    # Copy the dSYM into the targets temp dir.
+    echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
+    rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
+
+    local basename
+    basename="$(basename -s .dSYM "$source")"
+    binary_name="$(ls "$source/Contents/Resources/DWARF")"
+    binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
+
+    # Strip invalid architectures so "fat" simulator / device frameworks work on device
+    if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
+      strip_invalid_archs "$binary" "$warn_missing_arch"
+    fi
+
+    if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
+      # Move the stripped file into its final destination.
+      echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
+      rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
+    else
+      # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
+      touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
+    fi
+  fi
+}
+
+# Copies the bcsymbolmap files of a vendored framework
+install_bcsymbolmap() {
+    local bcsymbolmap_path="$1"
+    local destination="${BUILT_PRODUCTS_DIR}"
+    echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
+    rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
+}
+
+# Signs a framework with the provided identity
+code_sign_if_enabled() {
+  if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
+    # Use the current code_sign_identity
+    echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
+    local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
+
+    if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
+      code_sign_cmd="$code_sign_cmd &"
+    fi
+    echo "$code_sign_cmd"
+    eval "$code_sign_cmd"
+  fi
+}
+
+# Strip invalid architectures
+strip_invalid_archs() {
+  binary="$1"
+  warn_missing_arch=${2:-true}
+  # Get architectures for current target binary
+  binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
+  # Intersect them with the architectures we are building for
+  intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
+  # If there are no archs supported by this binary then warn the user
+  if [[ -z "$intersected_archs" ]]; then
+    if [[ "$warn_missing_arch" == "true" ]]; then
+      echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
+    fi
+    STRIP_BINARY_RETVAL=0
+    return
+  fi
+  stripped=""
+  for arch in $binary_archs; do
+    if ! [[ "${ARCHS}" == *"$arch"* ]]; then
+      # Strip non-valid architectures in-place
+      lipo -remove "$arch" -output "$binary" "$binary"
+      stripped="$stripped $arch"
+    fi
+  done
+  if [[ "$stripped" ]]; then
+    echo "Stripped $binary of architectures:$stripped"
+  fi
+  STRIP_BINARY_RETVAL=1
+}
+
+install_artifact() {
+  artifact="$1"
+  base="$(basename "$artifact")"
+  case $base in
+  *.framework)
+    install_framework "$artifact"
+    ;;
+  *.dSYM)
+    # Suppress arch warnings since XCFrameworks will include many dSYM files
+    install_dsym "$artifact" "false"
+    ;;
+  *.bcsymbolmap)
+    install_bcsymbolmap "$artifact"
+    ;;
+  *)
+    echo "error: Unrecognized artifact "$artifact""
+    ;;
+  esac
+}
+
+copy_artifacts() {
+  file_list="$1"
+  while read artifact; do
+    install_artifact "$artifact"
+  done <$file_list
+}
+
+ARTIFACT_LIST_FILE="${BUILT_PRODUCTS_DIR}/cocoapods-artifacts-${CONFIGURATION}.txt"
+if [ -r "${ARTIFACT_LIST_FILE}" ]; then
+  copy_artifacts "${ARTIFACT_LIST_FILE}"
+fi
+
+if [[ "$CONFIGURATION" == "Debug" ]]; then
+  install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
+fi
+if [[ "$CONFIGURATION" == "Release" ]]; then
+  install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
+fi
+if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
+  wait
+fi

+ 16 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im-umbrella.h

@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double Pods_shiku_imVersionNumber;
+FOUNDATION_EXPORT const unsigned char Pods_shiku_imVersionString[];
+

+ 10 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im.debug.xcconfig

@@ -0,0 +1,10 @@
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
+LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
+OTHER_LDFLAGS = $(inherited) -framework "Lottie" -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im.modulemap

@@ -0,0 +1,6 @@
+framework module Pods_shiku_im {
+  umbrella header "Pods-shiku_im-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 10 - 0
Pods/Target Support Files/Pods-shiku_im/Pods-shiku_im.release.xcconfig

@@ -0,0 +1,10 @@
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
+LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
+OTHER_LDFLAGS = $(inherited) -framework "Lottie" -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 26 - 0
Pods/Target Support Files/lottie-ios/lottie-ios-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>2.5.3</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_lottie_ios : NSObject
+@end
+@implementation PodsDummy_lottie_ios
+@end

+ 12 - 0
Pods/Target Support Files/lottie-ios/lottie-ios-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 30 - 0
Pods/Target Support Files/lottie-ios/lottie-ios-umbrella.h

@@ -0,0 +1,30 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "LOTAnimatedControl.h"
+#import "LOTAnimatedSwitch.h"
+#import "LOTAnimationCache.h"
+#import "LOTAnimationTransitionController.h"
+#import "LOTAnimationView.h"
+#import "LOTAnimationView_Compat.h"
+#import "LOTBlockCallback.h"
+#import "LOTCacheProvider.h"
+#import "LOTComposition.h"
+#import "LOTInterpolatorCallback.h"
+#import "LOTKeypath.h"
+#import "Lottie.h"
+#import "LOTValueCallback.h"
+#import "LOTValueDelegate.h"
+
+FOUNDATION_EXPORT double LottieVersionNumber;
+FOUNDATION_EXPORT const unsigned char LottieVersionString[];
+

+ 10 - 0
Pods/Target Support Files/lottie-ios/lottie-ios.debug.xcconfig

@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/lottie-ios
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Pods/Target Support Files/lottie-ios/lottie-ios.modulemap

@@ -0,0 +1,6 @@
+framework module Lottie {
+  umbrella header "lottie-ios-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 10 - 0
Pods/Target Support Files/lottie-ios/lottie-ios.release.xcconfig

@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/lottie-ios
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 201 - 0
Pods/lottie-ios/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2018 Airbnb, Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 631 - 0
Pods/lottie-ios/README.md

@@ -0,0 +1,631 @@
+# Lottie for iOS, macOS (and [Android](https://github.com/airbnb/lottie-android) and [React Native](https://github.com/airbnb/lottie-react-native))
+
+### Table of Contents
+- [Introduction](#introduction)
+- [Installing Lottie](#installing-lottie)
+- [iOS Sample App](#ios-sample-app)
+- [macOS Sample App](#macos-sample-app)
+- [Objective C Examples](#objective-c-examples)
+- [Swift Examples](#swift-examples)
+- [Debugging Lottie](#debugging)
+- [iOS View Controller Transitioning](#ios-view-controller-transitioning)
+- [Changing Animations At Runtime](#changing-animations-at-runtime)
+- [Animated Controls and Switches](#animated-controls-and-switches)
+- [Adding Subviews to Animation](#adding-views-to-an-animation-at-runtime)
+- [Supported After Effects Features](#supported-after-effects-features)
+- [Currently Unsupported After Effects Features](#currently-unsupported-after-effects-features)
+- [Community Contributions](#community-contributions)
+- [Alternatives](#alternatives)
+- [Why is it called Lottie?](#why-is-it-called-lottie)
+- [Contributing](#contributing)
+- [Issues or feature requests?](#issues-or-feature-requests)
+
+
+## Introduction
+
+Lottie is a mobile library for Android and iOS that parses [Adobe After Effects](http://www.adobe.com/products/aftereffects.html) animations exported as json with [bodymovin](https://github.com/bodymovin/bodymovin) and renders the vector animations natively on mobile and through React Native!
+
+For the first time, designers can create **and ship** beautiful animations without an engineer painstakingly recreating it by hand.
+Since the animation is backed by JSON they are extremely small in size but can be large in complexity!
+Animations can be played, resized, looped, sped up, slowed down, reversed, and even interactively scrubbed.
+Lottie can play or loop just a portion of the animation as well, the possibilities are endless!
+Animations can even be ***changed at runtime*** in various ways! Change the color, position or any keyframable value!
+Lottie also supports native UIViewController Transitions out of the box!
+
+Here is just a small sampling of the power of Lottie
+
+![Example1](_Gifs/Examples1.gif)
+![Example2](_Gifs/Examples2.gif)
+
+<img src="_Gifs/Community 2_3.gif" />
+
+![Example3](_Gifs/Examples3.gif)
+
+![Abcs](_Gifs/Examples4.gif)
+
+## Installing Lottie
+
+### Github Repo
+You can pull the [Lottie Github Repo](https://github.com/airbnb/lottie-ios/) and include the Lottie.xcodeproj to build a dynamic or static library.
+
+### Cocoapods
+Get [Cocoapods](https://cocoapods.org/)
+Add the pod to your podfile
+```
+pod 'lottie-ios'
+```
+run
+```
+pod install
+```
+
+After installing the cocoapod into your project import Lottie with
+Objective C
+`#import <Lottie/Lottie.h>` 
+Swift
+`import Lottie`
+
+### Carthage
+Get [Carthage](https://github.com/Carthage/Carthage)
+
+Add Lottie to your Cartfile
+```
+github "airbnb/lottie-ios" "master"
+```
+run
+```
+carthage update
+```
+
+In your application targets “General” tab under the “Linked Frameworks and Libraries” section, drag and drop lottie-ios.framework from the Carthage/Build/iOS directory that `carthage update` produced.
+
+## iOS Sample App
+
+Clone this repo and try out [the Sample App](https://github.com/airbnb/lottie-ios/tree/master/Example)
+The repo can build a macOS Example and an iOS Example
+
+The iOS Example App demos several of the features of Lottie
+
+![Example 1](_Gifs/iosexample1.png)![Example 2](_Gifs/iosexample2.png)
+![Example 3](_Gifs/iosexample3.png)
+
+The animation Explorer allows you to scrub, play, loop, and resize animations.
+Animations can be loaded from the app bundle or from [Lottie Files](http://www.lottiefiles.com) using the built in QR Code reader.
+
+## macOS Sample App
+
+Clone this repo and try out [the Sample App](https://github.com/airbnb/lottie-ios/tree/master/Example)
+The repo can build a macOS Example and an iOS Example
+
+![Lottie Viewer](_Gifs/macexample.png)
+
+The Lottie Viewer for macOS allows you to drag and drop JSON files to open, play, scrub and loop animations. This app is backed by the same animation code as the iOS app, so you will get an accurate representation of Mac and iOS animations.
+
+
+## Objective C Examples
+
+
+Lottie animations can be loaded from bundled JSON or from a URL
+To bundle JSON just add it and any images that the animation requires to your target in xcode.
+
+```objective-c
+LOTAnimationView *animation = [LOTAnimationView animationNamed:@"Lottie"];
+[self.view addSubview:animation];
+[animation playWithCompletion:^(BOOL animationFinished) {
+  // Do Something
+}];
+```
+
+If you are working with multiple bundles you can use.
+
+```objective-c
+LOTAnimationView *animation = [LOTAnimationView animationNamed:@"Lottie" inBundle:[NSBundle YOUR_BUNDLE]];
+[self.view addSubview:animation];
+[animation playWithCompletion:^(BOOL animationFinished) {
+  // Do Something
+}];
+```
+
+Or you can load it programmatically from a NSURL
+```objective-c
+LOTAnimationView *animation = [[LOTAnimationView alloc] initWithContentsOfURL:[NSURL URLWithString:URL]];
+[self.view addSubview:animation];
+```
+
+Lottie supports the iOS `UIViewContentModes` aspectFit, aspectFill and scaleFill
+
+You can also set the animation progress interactively.
+```objective-c
+CGPoint translation = [gesture getTranslationInView:self.view];
+CGFloat progress = translation.y / self.view.bounds.size.height;
+animationView.animationProgress = progress;
+```
+
+Or you can play just a portion of the animation:
+```objective-c
+[lottieAnimation playFromProgress:0.25 toProgress:0.5 withCompletion:^(BOOL animationFinished) {
+  // Do Something
+}];
+```
+## Swift Examples
+
+Lottie animations can be loaded from bundled JSON or from a URL
+To bundle JSON just add it and any images that the animation requires to your target in xcode.
+
+```swift
+let animationView = LOTAnimationView(name: "LottieLogo")
+self.view.addSubview(animationView)
+animationView.play{ (finished) in
+  // Do Something
+}
+```
+
+If your animation is in another bundle you can use
+```swift
+let animationView = LOTAnimationView(name: "LottieLogo", bundle: yourBundle)
+self.view.addSubview(animationView)
+animationView.play()
+```
+
+Or you can load it asynchronously from a URL
+```swift
+let animationView = LOTAnimationView(contentsOf: WebURL)
+self.view.addSubview(animationView)
+animationView.play()
+```
+
+You can also set the animation progress interactively.
+```swift
+let translation = gesture.getTranslationInView(self.view)
+let progress = translation.y / self.view.bounds.size.height
+animationView.animationProgress = progress
+```
+
+Or you can play just a portion of the animation:
+```swift
+animationView.play(fromProgress: 0.25, toProgress: 0.5, withCompletion: nil)
+```
+
+## iOS View Controller Transitioning
+
+Lottie comes with a `UIViewController` animation-controller for making custom viewController transitions!
+
+![Transition1](_Gifs/transitionMasked.gif)
+![Transition2](_Gifs/transitionPosition.gif)
+
+Just become the delegate for a transition
+
+```objective-c
+- (void)_showTransitionA {
+  ToAnimationViewController *vc = [[ToAnimationViewController alloc] init];
+  vc.transitioningDelegate = self;
+  [self presentViewController:vc animated:YES completion:NULL];
+}
+```
+
+And implement the delegate methods with a `LOTAnimationTransitionController`
+
+```objective-c
+#pragma mark -- View Controller Transitioning
+
+- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
+  LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition1" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
+  return animationController;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
+  LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition2" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
+  return animationController;
+}
+
+```
+
+By setting `applyAnimationTransform` to YES you can make the Lottie animation move the from and to view controllers. They will be positioned at the origin of the layer. When set to NO Lottie just masks the view controller with the specified layer while respecting z order.
+
+## Debugging
+Lottie has a couple of debugging features to know about. 
+When an animation is loaded unsupported features are logged out in the console with their function names.
+
+If you checkout LOTHelpers.h you will see two debug flags. `ENABLE_DEBUG_LOGGING` and `ENABLE_DEBUG_SHAPES`. 
+`ENABLE_DEBUG_LOGGING` increases the verbosity of Lottie Logging. It logs anytime an animation node is set during animation. If your animation if not working, turn this on and play your animation. The console log might give you some clues as to whats going on.
+
+`ENABLE_DEBUG_SHAPES` Draws a colored square for the anchor-point of every layer and shape. This is helpful to see if anything is on screen.
+
+### Keypaths
+
+LOTAnimationView provides `- (void)logHierarchyKeypaths` which will recursively log all settable keypaths for the animation. This is helpful for changing animations at runtime.
+
+## Adding Views to an Animation at Runtime
+
+Not only can you [change animations at runtime](#changing-animations-at-runtime) with Lottie, you can also add custom UI to a LOTAnimation at runtime.
+The example below shows some advance uses of this to create a dynamic image loader.
+
+## A Dynamic Image Loading Spinner
+
+![Spinner](/_Gifs/spinner.gif)
+
+The example above shows a single LOTAnimationView that is set with a loading spinner animation. The loading spinner loops a portion of its animation while an image is downloaded asynchronously. When the download is complete, the image is added to the animation and the rest of the animation is played seamlessly. The image is cleanly animated in and a completion block is called.
+
+![Spinner_Alt](/_Gifs/spinner_Alternative.gif)
+
+Now, the animation has been changed by a designer and needs to be updated. All that is required is updating the JSON file in the bundle. No code change needed!
+
+![Spinner_Dark](/_Gifs/spinner_DarkMode.gif)
+
+Here, the design has decided to add a 'Dark Mode' to the app. Just a few lines of code change the color of the animation at runtime.
+
+
+Pretty powerful eh?
+
+Check out the code below for an example!
+
+```swift
+
+import UIKit
+import Lottie
+
+class ViewController: UIViewController {
+  
+  var animationView: LOTAnimationView = LOTAnimationView(name: "SpinnerSpin");
+  
+  override func viewDidLoad() {
+    super.viewDidLoad()
+    
+    // Setup our animation view
+    animationView.contentMode = .scaleAspectFill
+    animationView.frame = CGRect(x: 20, y: 20, width: 200, height: 200)
+
+    self.view.addSubview(animationView)
+    // Lets change some of the properties of the animation
+    // We aren't going to use the MaskLayer, so let's just hide it
+    animationView.setValue(0, forKeypath: "MaskLayer.Ellipse 1.Transform.Opacity", atFrame: 0)
+    // All of the strokes and fills are white, lets make them DarkGrey
+    animationView.setValue(UIColor.darkGray, forKeypath: "OuterRing.Stroke.Color", atFrame: 0)
+    animationView.setValue(UIColor.darkGray, forKeypath: "InnerRing.Stroke.Color", atFrame: 0)
+    animationView.setValue(UIColor.darkGray, forKeypath: "InnerRing.Fill.Color", atFrame: 0)
+    
+    // Lets turn looping on, since we want it to repeat while the image is 'Downloading'
+    animationView.loopAnimation = true
+    // Now play from 0 to 0.5 progress and loop indefinitely.
+    animationView.play(fromProgress: 0, toProgress: 0.5, withCompletion: nil)
+    
+    // Lets simulate a download that finishes in 4 seconds.
+    let dispatchTime = DispatchTime.now() + 4.0
+    DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
+      self.simulateImageDownloaded()
+    }
+  }
+  
+  func simulateImageDownloaded() {
+    // Our downloaded image
+    let image = UIImage(named: "avatar.jpg")
+    let imageView = UIImageView(image: image)
+
+    // We want the image to show up centered in the animation view at 150Px150P
+    // Convert that rect to the animations coordinate space
+    // The origin is set to -75, -75 because the origin is centered in the animation view
+    let imageRect = animationView.convert(CGRect(x: -75, y: -75, width: 150, height: 150), toLayerNamed: nil)
+    
+    // Setup our image view with the rect and add rounded corners
+    imageView.frame = imageRect
+    imageView.layer.masksToBounds = true
+    imageView.layer.cornerRadius = imageRect.width / 2;
+    
+    // Now we set the completion block on the currently running animation
+    animationView.completionBlock = { (result: Bool) in ()
+      // Add the image view to the layer named "TransformLayer"
+      self.animationView.addSubview(imageView, toLayerNamed: "TransformLayer", applyTransform: true)
+      // Now play the last half of the animation
+      self.animationView.play(fromProgress: 0.5, toProgress: 1, withCompletion: { (complete: Bool) in
+        // Now the animation has finished and our image is displayed on screen
+        print("Image Downloaded and Displayed")
+      })
+    }
+    
+    // Turn looping off. Once the current loop finishes the animation will stop 
+    // and the completion block will be called.
+    animationView.loopAnimation = false
+  }
+  
+}
+
+```
+
+## Changing Animations At Runtime
+
+Lottie can do more than just play beautiful animations. Lottie allows you to **change** animations at runtime.
+
+### Say we want to create 4 toggle switches.
+![Toggle](_Gifs/switch_Normal.gif)
+Its easy to create the four switches and play them:
+
+```swift
+let animationView = LOTAnimationView(name: "toggle");
+self.view.addSubview(animationView)
+animationView.frame.origin.x = 40
+animationView.frame.origin.y = 20
+animationView.autoReverseAnimation = true
+animationView.loopAnimation = true
+animationView.play()
+
+let animationView2 = LOTAnimationView(name: "toggle");
+self.view.addSubview(animationView2)
+animationView2.frame.origin.x = 40
+animationView2.frame.origin.y = animationView.frame.maxY + 4
+animationView2.autoReverseAnimation = true
+animationView2.loopAnimation = true
+animationView2.play()
+
+let animationView3 = LOTAnimationView(name: "toggle");
+self.view.addSubview(animationView3)
+animationView3.frame.origin.x = 40
+animationView3.frame.origin.y = animationView2.frame.maxY + 4
+animationView3.autoReverseAnimation = true
+animationView3.loopAnimation = true
+animationView3.play()
+
+let animationView4 = LOTAnimationView(name: "toggle");
+self.view.addSubview(animationView4)
+animationView4.frame.origin.x = 40
+animationView4.frame.origin.y = animationView3.frame.maxY + 4
+animationView4.autoReverseAnimation = true
+animationView4.loopAnimation = true
+animationView4.play()
+
+```
+### Now lets change their colors
+![Recolored Toggle](_Gifs/switch_BgColors.gif)
+
+**NB**: `animationView.setValue(YOUR_COLOR, forKeypath: "YOUR_PATH.Color", atFrame: 0)` is now deprecated.
+
+```swift
+class GreenDelegate : NSObject, LOTColorValueDelegate {
+	func color(forFrame currentFrame: CGFloat, startKeyframe: CGFloat, endKeyframe: CGFloat, interpolatedProgress: CGFloat, start startColor: CGColor!, end endColor: CGColor!, currentColor interpolatedColor: CGColor!) -> Unmanaged<CGColor>! {
+		return  Unmanaged.passRetained(UIColor.green.cgColor)
+	}
+}
+animationView2.setValueDelegate(GreenDelegate(), for: LOTKeypath(string: "BG-On.Group 1.Fill 1.Color"))
+
+class RedDelegate : NSObject, LOTColorValueDelegate {
+	func color(forFrame currentFrame: CGFloat, startKeyframe: CGFloat, endKeyframe: CGFloat, interpolatedProgress: CGFloat, start startColor: CGColor!, end endColor: CGColor!, currentColor interpolatedColor: CGColor!) -> Unmanaged<CGColor>! {
+		return  Unmanaged.passRetained(UIColor.red.cgColor)
+	}
+}
+animationView3.setValueDelegate(RedDelegate(), for: LOTKeypath(string: "BG-On.Group 1.Fill 1.Color"))
+
+class OrangeDelegate : NSObject, LOTColorValueDelegate {
+	func color(forFrame currentFrame: CGFloat, startKeyframe: CGFloat, endKeyframe: CGFloat, interpolatedProgress: CGFloat, start startColor: CGColor!, end endColor: CGColor!, currentColor interpolatedColor: CGColor!) -> Unmanaged<CGColor>! {
+		return  Unmanaged.passRetained(UIColor.orange.cgColor)
+	}
+}
+animationView4.setValueDelegate(OrangeDelegate(), for: LOTKeypath(string: "BG-On.Group 1.Fill 1.Color"))
+```
+The keyPath is a dot separated path of layer and property names from After Effects.
+LOTAnimationView provides `- (void)logHierarchyKeypaths` which will recursively log all settable keypaths for the animation.
+![Key Path](_Gifs/aftereffectskeypath.png)
+"BG-On.Group 1.Fill 1.Color"
+
+### Now lets change a couple of properties
+![Multiple Colors](_Gifs/switch_MultipleBgs.gif)
+
+```swift
+animationView2.setValueDelegate(delegate, for: LOTKeypath(string: YOUR_PATH))
+```
+
+Lottie allows you to change **any** property that is animatable in After Effects. If a keyframe does not exist, a linear keyframe is created for you. If a keyframe does exist then just its data is replaced.
+
+For this you need to use a `LOTValueDelegate` there are many already available:
+* `LOTColorValueDelegate`
+* `LOTNumberValueDelegate`
+* `LOTPointValueDelegate`
+* `LOTSizeValueDelegate`
+* `LOTPathValueDelegate`
+
+## Animated Controls and Switches
+
+![Animated Buttons](_Gifs/switchTest.gif)
+
+Lottie also has a custom subclass of UIControl for creating custom animatable interactive controls.
+Currently Lottie has `LOTAnimatedSwitch` which is a toggle style switch control. Tapping on the switch plays either the On-Off or Off-On animation and sends out a UIControlStateValueChanged broadcast to all targets. It is used in the same way UISwitch is used with a few additions to setup the animation with Lottie.
+
+You initialize the switch either using the convenience method or by supplying the animation directly.
+
+```
+// Convenience
+LOTAnimatedSwitch *toggle1 = [LOTAnimatedSwitch switchNamed:@"Switch"];
+ 
+// Manually 
+LOTComposition *comp = [LOTComposition animationNamed:@"Switch"];
+LOTAnimatedSwitch *toggle1 = [[LOTAnimatedSwitch alloc] initWithFrame:CGRectZero];
+[toggle1 setAnimationComp:comp];
+```
+
+You can also specify a specific portion of the animation's timeline for the On and Off animation.
+By default `LOTAnimatedSwitch` will play the animation forward for On and backwards for off.
+
+Lets say that the supplied animation animates ON from 0.5-1 progress and OFF from 0-0.5:
+
+```
+/// On animation is 0.5 to 1 progress.
+[toggle1 setProgressRangeForOnState:0.5 toProgress:1];
+
+/// Off animation is 0 to 0.5 progress.
+[toggle1 setProgressRangeForOffState:0 toProgress:0.5];
+```
+
+Also, all LOTAnimatedControls add support for changing appearance for state changes. This requires some setup in After Effects. Lottie will switch visible animated layers based on the controls state. This can be used to have Disabled, selected, or Highlighted states. These states are associated with layer names in After Effects, and are dynamically displayed as the control changes states.
+
+Lets say we have a toggle switch with a Normal and Disabled state. In Effects we have a composition that contains Precomps of the regular "Button" and disabled "Disabled" states. They have different visual styles.
+
+![Regular](_Gifs/switch_enabled.png)
+![Disabled](_Gifs/switch_disabled.png)
+
+Now in code we can associate `UIControlState` with these layers
+
+```
+// Specify the layer names for different states
+[statefulSwitch setLayerName:@"Button" forState:UIControlStateNormal];
+[statefulSwitch setLayerName:@"Disabled" forState:UIControlStateDisabled];
+
+// Changes visual appearance by switching animation layer to "Disabled"
+statefulSwitch.enabled = NO;
+
+// Changes visual appearance by switching animation layer to "Button"
+statefulSwitch.enabled = YES;
+```
+
+## Supported After Effects Features
+
+### Keyframe Interpolation
+
+---
+
+* Linear Interpolation
+* Bezier Interpolation
+* Hold Interpolation
+* Rove Across Time
+* Spatial Bezier
+
+### Solids
+
+---
+
+* Transform Anchor Point
+* Transform Position
+* Transform Scale
+* Transform Rotation
+* Transform Opacity
+
+### Masks
+
+---
+
+* Path
+* Opacity
+* Multiple Masks (additive, subtractive and intersection)
+
+### Track Mattes
+
+---
+
+* Alpha Matte
+
+### Parenting
+
+---
+
+* Multiple Parenting
+* Nulls
+
+### Shape Layers
+
+---
+
+* Anchor Point
+* Position
+* Scale
+* Rotation
+* Opacity
+* Path
+* Group Transforms (Anchor point, position, scale etc)
+* Rectangle (All properties)
+* Eclipse (All properties)
+* Multiple paths in one group
+* Even-Odd winding paths
+* Reverse Fill Rule
+
+#### Stroke (shape layer)
+
+---
+
+* Stroke Color
+* Stroke Opacity
+* Stroke Width
+* Line Cap
+* Dashes (Now Animated!)
+
+#### Fill (shape layer)
+
+---
+
+* Fill Color
+* Fill Opacity
+
+#### Trim Paths (shape layer)
+
+---
+
+* Trim Paths Start
+* Trim Paths End
+* Trim Paths Offset
+
+### Repeaters
+
+---
+
+* Supports repeater transforms
+* Offset currently not supported.
+
+### Gradients
+
+---
+
+* Support for Linear Gradients
+* Support for Radial Gradients
+
+### Polystar and Polygon
+
+---
+
+* Supported! Theres a known bug if the roundness is greater than 100 percent.
+
+#### Layer Features
+
+---
+
+* Precomps
+* Image Layers
+* Shape Layers
+* Null Layers
+* Solid Layers
+* Parenting Layers
+* Alpha Matte Layers
+
+## Currently Unsupported After Effects Features
+
+* Merge Shapes
+* Alpha Inverted Masks
+* Trim Shapes Individually feature of Trim Paths
+* Expressions
+* 3d Layer support
+* Time remapping / Layer Reverse
+* Layer Blend Modes
+* Layer Effects
+
+
+## Community Contributions
+ * [Xamarin bindings](https://github.com/martijn00/LottieXamarin)
+ * [NativeScript bindings](https://github.com/bradmartin/nativescript-lottie)
+ * [Appcelerator Titanium bindings](https://github.com/m1ga/ti.animation)
+ * macOS Support added by [Alex Pawlowski](https://github.com/pawlowskialex)
+
+## Alternatives
+1. Build animations by hand. Building animations by hand is a huge time commitment for design and engineering across Android and iOS. It's often hard or even impossible to justify spending so much time to get an animation right.
+2. [Facebook Keyframes](https://github.com/facebookincubator/Keyframes). Keyframes is a wonderful new library from Facebook that they built for reactions. However, Keyframes doesn't support some of Lottie's features such as masks, mattes, trim paths, dash patterns, and more.
+2. Gifs. Gifs are more than double the size of a bodymovin JSON and are rendered at a fixed size that can't be scaled up to match large and high density screens.
+3. Png sequences. Png sequences are even worse than gifs in that their file sizes are often 30-50x the size of the bodymovin json and also can't be scaled up.
+
+## Why is it called Lottie?
+Lottie is named after a German film director and the foremost pioneer of silhouette animation. Her best known films are The Adventures of Prince Achmed (1926) – the oldest surviving feature-length animated film, preceding Walt Disney's feature-length Snow White and the Seven Dwarfs (1937) by over ten years
+[The art of Lotte Reineger](https://www.youtube.com/watch?v=LvU55CUw5Ck&feature=youtu.be)
+
+## Contributing
+Contributors are more than welcome. Just upload a PR with a description of your changes.
+
+If you would like to add more JSON files feel free to do so!
+
+## Issues or feature requests?
+File github issues for anything that is unexpectedly broken. If an After Effects file is not working, please attach it to your issue. Debugging without the original file is much more difficult. Lottie is developed and maintained by [Brandon Withrow](mailto:brandon@withrow.io). Feel free to reach out via email or [Twitter](https://twitter.com/theWithra)
+
+## Roadmap (In no particular order)
+- Add support for interactive animated transitions

+ 46 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTCompositionContainer.h

@@ -0,0 +1,46 @@
+//
+//  LOTCompositionContainer.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/18/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTLayerContainer.h"
+#import "LOTAssetGroup.h"
+
+@interface LOTCompositionContainer : LOTLayerContainer
+
+- (instancetype _Nonnull)initWithModel:(LOTLayer * _Nullable)layer
+                          inLayerGroup:(LOTLayerGroup * _Nullable)layerGroup
+                        withLayerGroup:(LOTLayerGroup * _Nullable)childLayerGroup
+                       withAssestGroup:(LOTAssetGroup * _Nullable)assetGroup;
+
+- (nullable NSArray *)keysForKeyPath:(nonnull LOTKeypath *)keypath;
+
+- (CGPoint)convertPoint:(CGPoint)point
+         toKeypathLayer:(nonnull LOTKeypath *)keypath
+        withParentLayer:(CALayer *_Nonnull)parent;
+
+- (CGRect)convertRect:(CGRect)rect
+       toKeypathLayer:(nonnull LOTKeypath *)keypath
+      withParentLayer:(CALayer *_Nonnull)parent;
+
+- (CGPoint)convertPoint:(CGPoint)point
+       fromKeypathLayer:(nonnull LOTKeypath *)keypath
+        withParentLayer:(CALayer *_Nonnull)parent;
+
+- (CGRect)convertRect:(CGRect)rect
+     fromKeypathLayer:(nonnull LOTKeypath *)keypath
+      withParentLayer:(CALayer *_Nonnull)parent;
+
+- (void)addSublayer:(nonnull CALayer *)subLayer
+    toKeypathLayer:(nonnull LOTKeypath *)keypath;
+
+- (void)maskSublayer:(nonnull CALayer *)subLayer
+     toKeypathLayer:(nonnull LOTKeypath *)keypath;
+
+@property (nonatomic, readonly, nonnull) NSArray<LOTLayerContainer *> *childLayers;
+@property (nonatomic, readonly, nonnull)  NSDictionary *childMap;
+
+@end

+ 239 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTCompositionContainer.m

@@ -0,0 +1,239 @@
+//
+//  LOTCompositionContainer.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/18/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTCompositionContainer.h"
+#import "LOTAsset.h"
+#import "CGGeometry+LOTAdditions.h"
+#import "LOTHelpers.h"
+#import "LOTValueInterpolator.h"
+#import "LOTAnimatorNode.h"
+#import "LOTRenderNode.h"
+#import "LOTRenderGroup.h"
+#import "LOTNumberInterpolator.h"
+
+@implementation LOTCompositionContainer {
+  NSNumber *_frameOffset;
+  CALayer *DEBUG_Center;
+  NSMutableDictionary *_keypathCache;
+  LOTNumberInterpolator *_timeInterpolator;
+}
+
+- (instancetype)initWithModel:(LOTLayer *)layer
+                 inLayerGroup:(LOTLayerGroup *)layerGroup
+               withLayerGroup:(LOTLayerGroup *)childLayerGroup
+              withAssestGroup:(LOTAssetGroup *)assetGroup {
+  self = [super initWithModel:layer inLayerGroup:layerGroup];
+  if (self) {
+    DEBUG_Center = [CALayer layer];
+    
+    DEBUG_Center.bounds = CGRectMake(0, 0, 20, 20);
+    DEBUG_Center.borderColor = [UIColor orangeColor].CGColor;
+    DEBUG_Center.borderWidth = 2;
+    DEBUG_Center.masksToBounds = YES;
+    if (ENABLE_DEBUG_SHAPES) {
+      [self.wrapperLayer addSublayer:DEBUG_Center];
+    }
+    if (layer.startFrame != nil) {
+      _frameOffset = layer.startFrame;
+    } else {
+      _frameOffset = @0;
+    }
+
+    if (layer.timeRemapping) {
+      _timeInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:layer.timeRemapping.keyframes];
+    }
+
+    [self initializeWithChildGroup:childLayerGroup withAssetGroup:assetGroup];
+  }
+  return self;
+}
+
+- (void)initializeWithChildGroup:(LOTLayerGroup *)childGroup
+                  withAssetGroup:(LOTAssetGroup *)assetGroup {
+  NSMutableDictionary *childMap = [NSMutableDictionary dictionary];
+  NSMutableArray *children = [NSMutableArray array];
+  NSArray *reversedItems = [[childGroup.layers reverseObjectEnumerator] allObjects];
+  
+  CALayer *maskedLayer = nil;
+  for (LOTLayer *layer in reversedItems) {
+    LOTAsset *asset;
+    if (layer.referenceID) {
+      // Get relevant Asset
+      asset = [assetGroup assetModelForID:layer.referenceID];
+    }
+    
+    LOTLayerContainer *child = nil;
+    if (asset.layerGroup) {
+      // Layer is a precomp
+      LOTCompositionContainer *compLayer = [[LOTCompositionContainer alloc] initWithModel:layer inLayerGroup:childGroup withLayerGroup:asset.layerGroup withAssestGroup:assetGroup];
+      child = compLayer;
+    } else {
+      child = [[LOTLayerContainer alloc] initWithModel:layer inLayerGroup:childGroup];
+    }
+    if (maskedLayer) {
+      maskedLayer.mask = child;
+      maskedLayer = nil;
+    } else {
+      if (layer.matteType == LOTMatteTypeAdd) {
+        maskedLayer = child;
+      }
+      [self.wrapperLayer addSublayer:child];
+    }
+    [children addObject:child];
+    if (child.layerName) {
+      [childMap setObject:child forKey:child.layerName];
+    }
+  }
+  _childMap = childMap;
+  _childLayers = children;
+}
+
+- (void)displayWithFrame:(NSNumber *)frame forceUpdate:(BOOL)forceUpdate {
+  if (ENABLE_DEBUG_LOGGING) NSLog(@"-------------------- Composition Displaying Frame %@ --------------------", frame);
+  [super displayWithFrame:frame forceUpdate:forceUpdate];
+  NSNumber *newFrame = @((frame.floatValue  - _frameOffset.floatValue) / self.timeStretchFactor.floatValue);
+  if (_timeInterpolator) {
+    newFrame = @([_timeInterpolator floatValueForFrame:newFrame]);
+  }
+  for (LOTLayerContainer *child in _childLayers) {
+    [child displayWithFrame:newFrame forceUpdate:forceUpdate];
+  }
+  if (ENABLE_DEBUG_LOGGING) NSLog(@"-------------------- ------------------------------- --------------------");
+  if (ENABLE_DEBUG_LOGGING) NSLog(@"-------------------- ------------------------------- --------------------");
+}
+
+- (void)setViewportBounds:(CGRect)viewportBounds {
+  [super setViewportBounds:viewportBounds];
+  for (LOTLayerContainer *layer in _childLayers) {
+    layer.viewportBounds = viewportBounds;
+  }
+}
+
+- (void)searchNodesForKeypath:(LOTKeypath * _Nonnull)keypath {
+  if (self.layerName != nil) {
+    [super searchNodesForKeypath:keypath];
+  }
+  if (self.layerName == nil ||
+      [keypath pushKey:self.layerName]) {
+    for (LOTLayerContainer *child in _childLayers) {
+      [child searchNodesForKeypath:keypath];
+    }
+    if (self.layerName != nil) {
+      [keypath popKey];
+    }
+  }
+}
+
+- (void)setValueDelegate:(id<LOTValueDelegate> _Nonnull)delegate
+              forKeypath:(LOTKeypath * _Nonnull)keypath {
+  if (self.layerName != nil) {
+    [super setValueDelegate:delegate forKeypath:keypath];
+  }
+  if (self.layerName == nil ||
+      [keypath pushKey:self.layerName]) {
+    for (LOTLayerContainer *child in _childLayers) {
+      [child setValueDelegate:delegate forKeypath:keypath];
+    }
+    if (self.layerName != nil) {
+      [keypath popKey];
+    }
+  }
+}
+
+- (nullable NSArray *)keysForKeyPath:(nonnull LOTKeypath *)keypath {
+  if (_keypathCache == nil) {
+    _keypathCache = [NSMutableDictionary dictionary];
+  }
+  [self searchNodesForKeypath:keypath];
+  [_keypathCache addEntriesFromDictionary:keypath.searchResults];
+  return keypath.searchResults.allKeys;
+}
+
+- (CALayer *)_layerForKeypath:(nonnull LOTKeypath *)keypath {
+  id node = _keypathCache[keypath.absoluteKeypath];
+  if (node == nil) {
+    [self keysForKeyPath:keypath];
+    node = _keypathCache[keypath.absoluteKeypath];
+  }
+  if (node == nil) {
+    NSLog(@"LOTComposition could not find layer for keypath:%@", keypath.absoluteKeypath);
+    return nil;
+  }
+  if ([node isKindOfClass:[CALayer class]]) {
+    return (CALayer *)node;
+  }
+  if (![node isKindOfClass:[LOTRenderNode class]]) {
+    NSLog(@"LOTComposition: Keypath return non-layer node:%@ ", keypath.absoluteKeypath);
+    return nil;
+  }
+  if ([node isKindOfClass:[LOTRenderGroup class]]) {
+    return [(LOTRenderGroup *)node containerLayer];
+  }
+  LOTRenderNode *renderNode = (LOTRenderNode *)node;
+  return renderNode.outputLayer;
+}
+
+- (CGPoint)convertPoint:(CGPoint)point
+         toKeypathLayer:(nonnull LOTKeypath *)keypath
+        withParentLayer:(CALayer *_Nonnull)parent{
+  CALayer *layer = [self _layerForKeypath:keypath];
+  if (!layer) {
+    return CGPointZero;
+  }
+  return [parent convertPoint:point toLayer:layer];
+}
+
+- (CGRect)convertRect:(CGRect)rect
+       toKeypathLayer:(nonnull LOTKeypath *)keypath
+      withParentLayer:(CALayer *_Nonnull)parent{
+  CALayer *layer = [self _layerForKeypath:keypath];
+  if (!layer) {
+    return CGRectZero;
+  }
+  return [parent convertRect:rect toLayer:layer];
+}
+
+- (CGPoint)convertPoint:(CGPoint)point
+       fromKeypathLayer:(nonnull LOTKeypath *)keypath
+        withParentLayer:(CALayer *_Nonnull)parent{
+  CALayer *layer = [self _layerForKeypath:keypath];
+  if (!layer) {
+    return CGPointZero;
+  }
+  return [parent convertPoint:point fromLayer:layer];
+}
+
+- (CGRect)convertRect:(CGRect)rect
+     fromKeypathLayer:(nonnull LOTKeypath *)keypath
+      withParentLayer:(CALayer *_Nonnull)parent{
+  CALayer *layer = [self _layerForKeypath:keypath];
+  if (!layer) {
+    return CGRectZero;
+  }
+  return [parent convertRect:rect fromLayer:layer];
+}
+
+- (void)addSublayer:(nonnull CALayer *)subLayer
+     toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  CALayer *layer = [self _layerForKeypath:keypath];
+  if (layer) {
+    [layer addSublayer:subLayer];
+  }
+}
+
+- (void)maskSublayer:(nonnull CALayer *)subLayer
+      toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  CALayer *layer = [self _layerForKeypath:keypath];
+  if (layer) {
+    [layer.superlayer addSublayer:subLayer];
+    [layer removeFromSuperlayer];
+    subLayer.mask = layer;
+  }
+}
+
+@end

+ 37 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTLayerContainer.h

@@ -0,0 +1,37 @@
+//
+//  LOTLayerContainer.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/18/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTPlatformCompat.h"
+#import "LOTLayer.h"
+#import "LOTLayerGroup.h"
+#import "LOTKeypath.h"
+#import "LOTValueDelegate.h"
+
+@class LOTValueCallback;
+
+@interface LOTLayerContainer : CALayer
+
+- (instancetype _Nonnull)initWithModel:(LOTLayer * _Nullable)layer
+                 inLayerGroup:(LOTLayerGroup * _Nullable)layerGroup;
+
+@property (nonatomic,  readonly, strong, nullable) NSString *layerName;
+@property (nonatomic, nullable) NSNumber *currentFrame;
+@property (nonatomic, readonly, nonnull) NSNumber *timeStretchFactor;
+@property (nonatomic, assign) CGRect viewportBounds;
+@property (nonatomic, readonly, nonnull) CALayer *wrapperLayer;
+@property (nonatomic, readonly, nonnull) NSDictionary *valueInterpolators;
+
+- (void)displayWithFrame:(NSNumber * _Nonnull)frame;
+- (void)displayWithFrame:(NSNumber * _Nonnull)frame forceUpdate:(BOOL)forceUpdate;
+
+- (void)searchNodesForKeypath:(LOTKeypath * _Nonnull)keypath;
+
+- (void)setValueDelegate:(id<LOTValueDelegate> _Nonnull)delegate
+              forKeypath:(LOTKeypath * _Nonnull)keypath;
+
+@end

+ 329 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTLayerContainer.m

@@ -0,0 +1,329 @@
+//
+//  LOTLayerContainer.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/18/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTLayerContainer.h"
+#import "LOTTransformInterpolator.h"
+#import "LOTNumberInterpolator.h"
+#import "CGGeometry+LOTAdditions.h"
+#import "LOTRenderGroup.h"
+#import "LOTHelpers.h"
+#import "LOTMaskContainer.h"
+#import "LOTAsset.h"
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+#import "LOTCacheProvider.h"
+#endif
+
+@implementation LOTLayerContainer {
+  LOTTransformInterpolator *_transformInterpolator;
+  LOTNumberInterpolator *_opacityInterpolator;
+  NSNumber *_inFrame;
+  NSNumber *_outFrame;
+  CALayer *DEBUG_Center;
+  LOTRenderGroup *_contentsGroup;
+  LOTMaskContainer *_maskLayer;
+}
+
+@dynamic currentFrame;
+
+- (instancetype)initWithModel:(LOTLayer *)layer
+                 inLayerGroup:(LOTLayerGroup *)layerGroup {
+  self = [super init];
+  if (self) {
+    _wrapperLayer = [CALayer new];
+    [self addSublayer:_wrapperLayer];
+    DEBUG_Center = [CALayer layer];
+    
+    DEBUG_Center.bounds = CGRectMake(0, 0, 20, 20);
+    DEBUG_Center.borderColor = [UIColor blueColor].CGColor;
+    DEBUG_Center.borderWidth = 2;
+    DEBUG_Center.masksToBounds = YES;
+    
+    if (ENABLE_DEBUG_SHAPES) {
+      [_wrapperLayer addSublayer:DEBUG_Center];
+    } 
+    self.actions = @{@"hidden" : [NSNull null], @"opacity" : [NSNull null], @"transform" : [NSNull null]};
+    _wrapperLayer.actions = [self.actions copy];
+    _timeStretchFactor = @1;
+    [self commonInitializeWith:layer inLayerGroup:layerGroup];
+  }
+  return self;
+}
+
+- (void)commonInitializeWith:(LOTLayer *)layer
+                inLayerGroup:(LOTLayerGroup *)layerGroup {
+  if (layer == nil) {
+    return;
+  }
+  _layerName = layer.layerName;
+  if (layer.layerType == LOTLayerTypeImage ||
+      layer.layerType == LOTLayerTypeSolid ||
+      layer.layerType == LOTLayerTypePrecomp) {
+    _wrapperLayer.bounds = CGRectMake(0, 0, layer.layerWidth.floatValue, layer.layerHeight.floatValue);
+    _wrapperLayer.anchorPoint = CGPointMake(0, 0);
+    _wrapperLayer.masksToBounds = YES;
+    DEBUG_Center.position = LOT_RectGetCenterPoint(self.bounds);
+  }
+  
+  if (layer.layerType == LOTLayerTypeImage) {
+    [self _setImageForAsset:layer.imageAsset];
+  }
+  
+  _inFrame = [layer.inFrame copy];
+  _outFrame = [layer.outFrame copy];
+
+  _timeStretchFactor = [layer.timeStretch copy];
+  _transformInterpolator = [LOTTransformInterpolator transformForLayer:layer];
+
+  if (layer.parentID != nil) {
+    NSNumber *parentID = layer.parentID;
+    LOTTransformInterpolator *childInterpolator = _transformInterpolator;
+    while (parentID != nil) {
+      LOTLayer *parentModel = [layerGroup layerModelForID:parentID];
+      LOTTransformInterpolator *interpolator = [LOTTransformInterpolator transformForLayer:parentModel];
+      childInterpolator.inputNode = interpolator;
+      childInterpolator = interpolator;
+      parentID = parentModel.parentID;
+    }
+  }
+  _opacityInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:layer.opacity.keyframes];
+  if (layer.layerType == LOTLayerTypeShape &&
+      layer.shapes.count) {
+    [self buildContents:layer.shapes];
+  }
+  if (layer.layerType == LOTLayerTypeSolid) {
+    _wrapperLayer.backgroundColor = layer.solidColor.CGColor;
+  }
+  if (layer.masks.count) {
+    _maskLayer = [[LOTMaskContainer alloc] initWithMasks:layer.masks];
+    _wrapperLayer.mask = _maskLayer;
+  }
+  
+  NSMutableDictionary *interpolators = [NSMutableDictionary dictionary];
+  interpolators[@"Opacity"] = _opacityInterpolator;
+  interpolators[@"Anchor Point"] = _transformInterpolator.anchorInterpolator;
+  interpolators[@"Scale"] = _transformInterpolator.scaleInterpolator;
+  interpolators[@"Rotation"] = _transformInterpolator.rotationInterpolator;
+  if (_transformInterpolator.positionXInterpolator &&
+      _transformInterpolator.positionYInterpolator) {
+    interpolators[@"X Position"] = _transformInterpolator.positionXInterpolator;
+    interpolators[@"Y Position"] = _transformInterpolator.positionYInterpolator;
+  } else if (_transformInterpolator.positionInterpolator) {
+    interpolators[@"Position"] = _transformInterpolator.positionInterpolator;
+  }
+
+  // Deprecated
+  interpolators[@"Transform.Opacity"] = _opacityInterpolator;
+  interpolators[@"Transform.Anchor Point"] = _transformInterpolator.anchorInterpolator;
+  interpolators[@"Transform.Scale"] = _transformInterpolator.scaleInterpolator;
+  interpolators[@"Transform.Rotation"] = _transformInterpolator.rotationInterpolator;
+  if (_transformInterpolator.positionXInterpolator &&
+      _transformInterpolator.positionYInterpolator) {
+    interpolators[@"Transform.X Position"] = _transformInterpolator.positionXInterpolator;
+    interpolators[@"Transform.Y Position"] = _transformInterpolator.positionYInterpolator;
+  } else if (_transformInterpolator.positionInterpolator) {
+    interpolators[@"Transform.Position"] = _transformInterpolator.positionInterpolator;
+  }
+  _valueInterpolators = interpolators;
+}
+
+- (void)buildContents:(NSArray *)contents {
+  _contentsGroup = [[LOTRenderGroup alloc] initWithInputNode:nil contents:contents keyname:_layerName];
+  [_wrapperLayer addSublayer:_contentsGroup.containerLayer];
+}
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+
+- (void)_setImageForAsset:(LOTAsset *)asset {
+  if (asset.imageName) {
+    UIImage *image;
+    if ([asset.imageName hasPrefix:@"data:"]) {
+      // Contents look like a data: URL. Ignore asset.imageDirectory and simply load the image directly.
+      NSURL *imageUrl = [NSURL URLWithString:asset.imageName];
+      NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
+      image = [UIImage imageWithData:imageData];
+    } else if (asset.rootDirectory.length > 0) {
+      NSString *rootDirectory  = asset.rootDirectory;
+      if (asset.imageDirectory.length > 0) {
+        rootDirectory = [rootDirectory stringByAppendingPathComponent:asset.imageDirectory];
+      }
+      NSString *imagePath = [rootDirectory stringByAppendingPathComponent:asset.imageName];
+        
+      id<LOTImageCache> imageCache = [LOTCacheProvider imageCache];
+      if (imageCache) {
+        image = [imageCache imageForKey:imagePath];
+        if (!image) {
+          image = [UIImage imageWithContentsOfFile:imagePath];
+          [imageCache setImage:image forKey:imagePath];
+        }
+      } else {
+        image = [UIImage imageWithContentsOfFile:imagePath];
+      }
+    } else {
+        NSString *imagePath = [asset.assetBundle pathForResource:asset.imageName ofType:nil];
+        image = [UIImage imageWithContentsOfFile:imagePath];
+    }
+
+    //try loading from asset catalogue instead if all else fails
+    if (!image) {
+      image = [UIImage imageNamed:asset.imageName inBundle: asset.assetBundle compatibleWithTraitCollection:nil];
+    }
+    
+    if (image) {
+      _wrapperLayer.contents = (__bridge id _Nullable)(image.CGImage);
+    } else {
+      NSLog(@"%s: Warn: image not found: %@", __PRETTY_FUNCTION__, asset.imageName);
+    }
+  }
+}
+
+#else
+
+- (void)_setImageForAsset:(LOTAsset *)asset {
+  if (asset.imageName) {
+    NSArray *components = [asset.imageName componentsSeparatedByString:@"."];
+    NSImage *image = [NSImage imageNamed:components.firstObject];
+    if (image == nil) {
+      if (asset.rootDirectory.length > 0 && asset.imageDirectory.length > 0) {
+        NSString *imagePath = [[asset.rootDirectory stringByAppendingPathComponent:asset.imageDirectory] stringByAppendingPathComponent:asset.imageName];
+        image = [[NSImage alloc] initWithContentsOfFile:imagePath];
+      }
+    }
+    if (image) {
+      NSWindow *window = [NSApp mainWindow];
+      CGFloat desiredScaleFactor = [window backingScaleFactor];
+      CGFloat actualScaleFactor = [image recommendedLayerContentsScale:desiredScaleFactor];
+      id layerContents = [image layerContentsForContentsScale:actualScaleFactor];
+      _wrapperLayer.contents = layerContents;
+    }
+  }
+  
+}
+
+#endif
+
+// MARK - Animation
+
++ (BOOL)needsDisplayForKey:(NSString *)key {
+  if ([key isEqualToString:@"currentFrame"]) {
+    return YES;
+  }
+  return [super needsDisplayForKey:key];
+}
+
+- (id<CAAction>)actionForKey:(NSString *)event {
+  if ([event isEqualToString:@"currentFrame"]) {
+    CABasicAnimation *theAnimation = [CABasicAnimation
+                                      animationWithKeyPath:event];
+    theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
+    theAnimation.fromValue = [[self presentationLayer] valueForKey:event];
+    return theAnimation;
+  }
+  return [super actionForKey:event];
+}
+
+- (id)initWithLayer:(id)layer {
+  if (self = [super initWithLayer:layer]) {
+    if ([layer isKindOfClass:[LOTLayerContainer class]]) {
+      LOTLayerContainer *other = (LOTLayerContainer *)layer;
+      self.currentFrame = [other.currentFrame copy];
+    }
+  }
+  return self;
+}
+
+- (void)display {
+  @synchronized(self) {
+    LOTLayerContainer *presentation = self;
+    if (self.animationKeys.count &&
+      self.presentationLayer) {
+        presentation = (LOTLayerContainer *)self.presentationLayer;
+    }
+    [self displayWithFrame:presentation.currentFrame];
+  }
+}
+
+- (void)displayWithFrame:(NSNumber *)frame {
+  [self displayWithFrame:frame forceUpdate:NO];
+}
+
+- (void)displayWithFrame:(NSNumber *)frame forceUpdate:(BOOL)forceUpdate {
+  NSNumber *newFrame = @(frame.floatValue / self.timeStretchFactor.floatValue);
+  if (ENABLE_DEBUG_LOGGING) NSLog(@"View %@ Displaying Frame %@, with local time %@", self, frame, newFrame);
+  BOOL hidden = NO;
+  if (_inFrame && _outFrame) {
+    hidden = (frame.floatValue < _inFrame.floatValue ||
+              frame.floatValue > _outFrame.floatValue);
+  }
+  self.hidden = hidden;
+  if (hidden) {
+    return;
+  }
+  if (_opacityInterpolator && [_opacityInterpolator hasUpdateForFrame:newFrame]) {
+    self.opacity = [_opacityInterpolator floatValueForFrame:newFrame];
+  }
+  if (_transformInterpolator && [_transformInterpolator hasUpdateForFrame:newFrame]) {
+    _wrapperLayer.transform = [_transformInterpolator transformForFrame:newFrame];
+  }
+  [_contentsGroup updateWithFrame:newFrame withModifierBlock:nil forceLocalUpdate:forceUpdate];
+  _maskLayer.currentFrame = newFrame;
+}
+
+- (void)setViewportBounds:(CGRect)viewportBounds {
+  _viewportBounds = viewportBounds;
+  if (_maskLayer) {
+    CGPoint center = LOT_RectGetCenterPoint(viewportBounds);
+    viewportBounds.origin = CGPointMake(-center.x, -center.y);
+    _maskLayer.bounds = viewportBounds;
+  }
+}
+
+- (void)searchNodesForKeypath:(LOTKeypath * _Nonnull)keypath {
+  if (_contentsGroup == nil && [keypath pushKey:self.layerName]) {
+    // Matches self.
+    if ([keypath pushKey:@"Transform"]) {
+      // Is a transform node, check interpolators
+      LOTValueInterpolator *interpolator = _valueInterpolators[keypath.currentKey];
+      if (interpolator) {
+        // We have a match!
+        [keypath pushKey:keypath.currentKey];
+        [keypath addSearchResultForCurrentPath:_wrapperLayer];
+        [keypath popKey];
+      }
+      if (keypath.endOfKeypath) {
+        [keypath addSearchResultForCurrentPath:_wrapperLayer];
+      }
+      [keypath popKey];
+    }
+    if (keypath.endOfKeypath) {
+      [keypath addSearchResultForCurrentPath:_wrapperLayer];
+    }
+    [keypath popKey];
+  }
+  [_contentsGroup searchNodesForKeypath:keypath];
+}
+
+- (void)setValueDelegate:(id<LOTValueDelegate> _Nonnull)delegate
+              forKeypath:(LOTKeypath * _Nonnull)keypath {
+  if ([keypath pushKey:self.layerName]) {
+    // Matches self.
+    if ([keypath pushKey:@"Transform"]) {
+      // Is a transform node, check interpolators
+      LOTValueInterpolator *interpolator = _valueInterpolators[keypath.currentKey];
+      if (interpolator) {
+        // We have a match!
+        [interpolator setValueDelegate:delegate];
+      }
+      [keypath popKey];
+    }
+    [keypath popKey];
+  }
+  [_contentsGroup setValueDelegate:delegate forKeypath:keypath];
+}
+
+@end

+ 18 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTMaskContainer.h

@@ -0,0 +1,18 @@
+//
+//  LOTMaskContainer.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/19/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import <QuartzCore/QuartzCore.h>
+#import "LOTMask.h"
+
+@interface LOTMaskContainer : CALayer
+
+- (instancetype _Nonnull)initWithMasks:(NSArray<LOTMask *> * _Nonnull)masks;
+
+@property (nonatomic, strong, nullable) NSNumber *currentFrame;
+
+@end

+ 107 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableLayers/LOTMaskContainer.m

@@ -0,0 +1,107 @@
+//
+//  LOTMaskContainer.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/19/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTMaskContainer.h"
+#import "LOTPathInterpolator.h"
+#import "LOTNumberInterpolator.h"
+
+@interface LOTMaskNodeLayer : CAShapeLayer
+
+@property (nonatomic, readonly) LOTMask *maskNode;
+
+- (instancetype)initWithMask:(LOTMask *)maskNode;
+- (BOOL)hasUpdateForFrame:(NSNumber *)frame;
+
+@end
+
+@implementation LOTMaskNodeLayer {
+  LOTPathInterpolator *_pathInterpolator;
+  LOTNumberInterpolator *_opacityInterpolator;
+  LOTNumberInterpolator *_expansionInterpolator;
+}
+
+- (instancetype)initWithMask:(LOTMask *)maskNode {
+  self = [super init];
+  if (self) {
+    _pathInterpolator = [[LOTPathInterpolator alloc] initWithKeyframes:maskNode.maskPath.keyframes];
+    _opacityInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:maskNode.opacity.keyframes];
+    _expansionInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:maskNode.expansion.keyframes];
+    _maskNode = maskNode;
+    self.fillColor = [UIColor blueColor].CGColor;
+  }
+  return self;
+}
+
+- (void)updateForFrame:(NSNumber *)frame withViewBounds:(CGRect)viewBounds {
+  if ([self hasUpdateForFrame:frame]) {
+    LOTBezierPath *path = [_pathInterpolator pathForFrame:frame cacheLengths:NO];
+    
+    if (self.maskNode.maskMode == LOTMaskModeSubtract) {
+      CGMutablePathRef pathRef = CGPathCreateMutable();
+      CGPathAddRect(pathRef, NULL, viewBounds);
+      CGPathAddPath(pathRef, NULL, path.CGPath);
+      self.path = pathRef;
+      self.fillRule = @"even-odd";
+      CGPathRelease(pathRef);
+    } else {
+      self.path = path.CGPath;
+    }
+    
+    self.opacity = [_opacityInterpolator floatValueForFrame:frame];
+  }
+}
+
+- (BOOL)hasUpdateForFrame:(NSNumber *)frame {
+  return ([_pathInterpolator hasUpdateForFrame:frame] ||
+          [_opacityInterpolator hasUpdateForFrame:frame]);
+}
+
+@end
+
+@implementation LOTMaskContainer {
+  NSArray<LOTMaskNodeLayer *> *_masks;
+}
+
+- (instancetype)initWithMasks:(NSArray<LOTMask *> *)masks {
+  self = [super init];
+  if (self) {
+    NSMutableArray *maskNodes = [NSMutableArray array];
+    CALayer *containerLayer = [CALayer layer];
+    
+    for (LOTMask *mask in masks) {
+      LOTMaskNodeLayer *node = [[LOTMaskNodeLayer alloc] initWithMask:mask];
+      [maskNodes addObject:node];
+      if (mask.maskMode == LOTMaskModeAdd ||
+          mask == masks.firstObject) {
+        [containerLayer addSublayer:node];
+      } else {
+        containerLayer.mask = node;
+        CALayer *newContainer = [CALayer layer];
+        [newContainer addSublayer:containerLayer];
+        containerLayer = newContainer;
+      }
+    }
+    [self addSublayer:containerLayer];
+    _masks = maskNodes;
+
+  }
+  return self;
+}
+
+- (void)setCurrentFrame:(NSNumber *)currentFrame {
+  if (_currentFrame == currentFrame) {
+    return;
+  }
+  _currentFrame = currentFrame;
+  
+  for (LOTMaskNodeLayer *nodeLayer in _masks) {
+    [nodeLayer updateForFrame:currentFrame withViewBounds:self.bounds];
+  }
+}
+
+@end

+ 27 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTBezierData.h

@@ -0,0 +1,27 @@
+//
+//  LOTBezierData.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/10/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTBezierData : NSObject
+
+- (instancetype)initWithData:(NSDictionary *)bezierData;
+
+@property (nonatomic, readonly) NSInteger count;
+@property (nonatomic, readonly) BOOL closed;
+
+- (CGPoint)vertexAtIndex:(NSInteger)index;
+- (CGPoint)inTangentAtIndex:(NSInteger)index;
+- (CGPoint)outTangentAtIndex:(NSInteger)index;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 100 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTBezierData.m

@@ -0,0 +1,100 @@
+//
+//  LOTBezierData.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/10/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTBezierData.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTBezierData {
+  CGPoint *_vertices;
+  CGPoint *_inTangents;
+  CGPoint *_outTangents;
+}
+
+- (instancetype)initWithData:(NSDictionary *)bezierData
+{
+  self = [super init];
+  if (self) {
+    [self initializeData:bezierData];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  free(_vertices);
+  free(_inTangents);
+  free(_outTangents);
+}
+
+- (CGPoint)vertexAtIndex:(NSInteger)index {
+  NSAssert((index < _count &&
+            index >= 0),
+           @"Lottie: Index out of bounds");
+  return _vertices[index];
+}
+
+- (CGPoint)inTangentAtIndex:(NSInteger)index {
+  NSAssert((index < _count &&
+            index >= 0),
+           @"Lottie: Index out of bounds");
+  return _inTangents[index];
+}
+
+- (CGPoint)outTangentAtIndex:(NSInteger)index {
+  NSAssert((index < _count &&
+            index >= 0),
+           @"Lottie: Index out of bounds");
+  return _outTangents[index];
+}
+
+- (void)initializeData:(NSDictionary *)bezierData {
+
+  NSArray *pointsArray = bezierData[@"v"];
+  NSArray *inTangents = bezierData[@"i"];
+  NSArray *outTangents = bezierData[@"o"];
+  
+  if (pointsArray.count == 0) {
+    NSLog(@"%s: Warning: shape has no vertices", __PRETTY_FUNCTION__);
+    return ;
+  }
+  
+  NSAssert((pointsArray.count == inTangents.count &&
+            pointsArray.count == outTangents.count),
+           @"Lottie: Incorrect number of points and tangents");
+  _count = pointsArray.count;
+  _vertices = (CGPoint *)malloc(sizeof(CGPoint) * pointsArray.count);
+  _inTangents = (CGPoint *)malloc(sizeof(CGPoint) * pointsArray.count);
+  _outTangents = (CGPoint *)malloc(sizeof(CGPoint) * pointsArray.count);
+  if (bezierData[@"c"]) {
+    _closed = [bezierData[@"c"] boolValue];
+  }
+  for (int i = 0; i < pointsArray.count; i ++) {
+    CGPoint vertex = [self _vertexAtIndex:i inArray:pointsArray];
+    CGPoint outTan = LOT_PointAddedToPoint(vertex, [self _vertexAtIndex:i inArray:outTangents]);
+    CGPoint inTan = LOT_PointAddedToPoint(vertex, [self _vertexAtIndex:i inArray:inTangents]);
+    // BW BUG Straight Lines - Test Later
+    // Bake fix for lines here
+    _vertices[i] = vertex;
+    _inTangents[i] = inTan;
+    _outTangents[i] = outTan;
+  }
+}
+
+- (CGPoint)_vertexAtIndex:(NSInteger)idx inArray:(NSArray *)points {
+  NSAssert((idx < points.count),
+           @"Lottie: Vertex Point out of bounds");
+  
+  NSArray *pointArray = points[idx];
+  
+  NSAssert((pointArray.count >= 2 &&
+            [pointArray.firstObject isKindOfClass:[NSNumber class]]),
+           @"Lottie: Point Data Malformed");
+  
+  return CGPointMake([(NSNumber *)pointArray[0] floatValue], [(NSNumber *)pointArray[1] floatValue]);
+}
+
+@end

+ 49 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTKeyframe.h

@@ -0,0 +1,49 @@
+//
+//  LOTKeyframe.h
+//  Pods
+//
+//  Created by brandon_withrow on 7/10/17.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import <QuartzCore/QuartzCore.h>
+#import "LOTPlatformCompat.h"
+#import "LOTBezierData.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTKeyframe : NSObject
+
+- (instancetype)initWithKeyframe:(NSDictionary *)keyframe;
+- (instancetype)initWithValue:(id)value;
+- (void)remapValueWithBlock:(CGFloat (^)(CGFloat inValue))remapBlock;
+- (LOTKeyframe *)copyWithData:(id)data;
+
+@property (nonatomic, readonly) NSNumber *keyframeTime;
+@property (nonatomic, readonly) BOOL isHold;
+@property (nonatomic, readonly) CGPoint inTangent;
+@property (nonatomic, readonly) CGPoint outTangent;
+@property (nonatomic, readonly) CGPoint spatialInTangent;
+@property (nonatomic, readonly) CGPoint spatialOutTangent;
+
+@property (nonatomic, readonly) CGFloat floatValue;
+@property (nonatomic, readonly) CGPoint pointValue;
+@property (nonatomic, readonly) CGSize sizeValue;
+@property (nonatomic, readonly) UIColor *colorValue;
+@property (nonatomic, readonly, nullable) LOTBezierData *pathData;
+@property (nonatomic, readonly) NSArray *arrayValue;
+
+@end
+
+@interface LOTKeyframeGroup : NSObject
+
+- (instancetype)initWithData:(id)data;
+
+- (void)remapKeyframesWithBlock:(CGFloat (^)(CGFloat inValue))remapBlock;
+
+@property (nonatomic, readonly) NSArray<LOTKeyframe *> *keyframes;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 242 - 0
Pods/lottie-ios/lottie-ios/Classes/AnimatableProperties/LOTKeyframe.m

@@ -0,0 +1,242 @@
+//
+//  LOTKeyframe.m
+//  Pods
+//
+//  Created by brandon_withrow on 7/10/17.
+//
+//
+
+#import "LOTKeyframe.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTKeyframe
+
+- (instancetype)initWithKeyframe:(NSDictionary *)keyframe {
+  self = [super init];
+  if (self) {
+    _keyframeTime = keyframe[@"t"];
+    _inTangent = CGPointZero;
+    _outTangent = CGPointZero;
+    _spatialInTangent = CGPointZero;
+    _spatialOutTangent = CGPointZero;
+    NSDictionary *timingOutTangent = keyframe[@"o"];
+    NSDictionary *timingInTangent = keyframe[@"i"];
+    if (timingInTangent) {
+      _inTangent = [self _pointFromValueDict:timingInTangent];
+    }
+    if (timingOutTangent) {
+      _outTangent = [self _pointFromValueDict:timingOutTangent];
+    }
+    if ([keyframe[@"h"] boolValue]) {
+      _isHold = YES;
+    }
+    if (keyframe[@"to"]) {
+      NSArray *to = keyframe[@"to"];
+      _spatialOutTangent = [self _pointFromValueArray:to];
+    }
+    if (keyframe[@"ti"]) {
+      NSArray *ti = keyframe[@"ti"];
+      _spatialInTangent =  [self _pointFromValueArray:ti];
+    }
+    id data = keyframe[@"s"];
+    if (data) {
+      [self setupOutputWithData:data];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValue:(id)value {
+  self = [super init];
+  if (self) {
+    _keyframeTime = @0;
+    _isHold = YES;
+    [self setupOutputWithData:value];
+  }
+  return self;
+}
+
+- (instancetype)initWithLOTKeyframe:(LOTKeyframe *)keyframe {
+  self = [super init];
+  if (self) {
+    _keyframeTime = [keyframe.keyframeTime copy];
+    _inTangent = keyframe.inTangent;
+    _outTangent = keyframe.outTangent;
+    _spatialInTangent = keyframe.spatialInTangent;
+    _spatialOutTangent = keyframe.spatialOutTangent;
+    _isHold = keyframe.isHold;
+  }
+  return self;
+}
+
+- (LOTKeyframe *)copyWithData:(id)data {
+  LOTKeyframe *newFrame = [[LOTKeyframe alloc] initWithLOTKeyframe:self];
+  [newFrame setData:data];
+  return newFrame;
+}
+
+- (void)setData:(id)data {
+  [self setupOutputWithData:data];
+}
+
+- (void)remapValueWithBlock:(CGFloat (^)(CGFloat inValue))remapBlock {
+  _floatValue = remapBlock(_floatValue);
+  _pointValue = CGPointMake(remapBlock(_pointValue.x), remapBlock(_pointValue.y));
+  _sizeValue = CGSizeMake(remapBlock(_sizeValue.width), remapBlock(_sizeValue.height));
+}
+
+- (void)setupOutputWithData:(id)data {
+  if ([data isKindOfClass:[NSNumber class]]) {
+    _floatValue = [(NSNumber *)data floatValue];
+  }
+  if ([data isKindOfClass:[NSArray class]] &&
+      [[(NSArray *)data firstObject] isKindOfClass:[NSNumber class]]) {
+    NSArray *numberArray = (NSArray *)data;
+    if (numberArray.count > 0) {
+      _floatValue = [(NSNumber *)numberArray[0] floatValue];
+    }
+    if (numberArray.count > 1) {
+      _pointValue = CGPointMake(_floatValue = [(NSNumber *)numberArray[0] floatValue],
+                                _floatValue = [(NSNumber *)numberArray[1] floatValue]);
+      _sizeValue = CGSizeMake(_pointValue.x, _pointValue.y);
+    }
+    if (numberArray.count > 3) {
+      _colorValue = [self _colorValueFromArray:numberArray];
+    }
+    _arrayValue = numberArray;
+  } else if ([data isKindOfClass:[NSArray class]] &&
+      [[(NSArray *)data firstObject] isKindOfClass:[NSDictionary class]]) {
+    _pathData = [[LOTBezierData alloc] initWithData:[(NSArray *)data firstObject]];
+  } else if ([data isKindOfClass:[NSDictionary class]]) {
+    _pathData = [[LOTBezierData alloc] initWithData:data];
+  }
+}
+
+- (CGPoint)_pointFromValueArray:(NSArray *)values {
+  CGPoint returnPoint = CGPointZero;
+  if (values.count > 1) {
+    returnPoint.x = [(NSNumber *)values[0] floatValue];
+    returnPoint.y = [(NSNumber *)values[1] floatValue];
+  }
+  return returnPoint;
+}
+
+- (CGPoint)_pointFromValueDict:(NSDictionary *)values {
+  NSNumber *xValue = @0, *yValue = @0;
+  if ([values[@"x"] isKindOfClass:[NSNumber class]]) {
+    xValue = values[@"x"];
+  } else if ([values[@"x"] isKindOfClass:[NSArray class]]) {
+    xValue = values[@"x"][0];
+  }
+  
+  if ([values[@"y"] isKindOfClass:[NSNumber class]]) {
+    yValue = values[@"y"];
+  } else if ([values[@"y"] isKindOfClass:[NSArray class]]) {
+    yValue = values[@"y"][0];
+  }
+  
+  return CGPointMake([xValue floatValue], [yValue floatValue]);
+}
+
+- (UIColor *)_colorValueFromArray:(NSArray<NSNumber *>  *)colorArray {
+  if (colorArray.count == 4) {
+    BOOL shouldUse255 = NO;
+    for (NSNumber *number in colorArray) {
+      if (number.floatValue > 1) {
+        shouldUse255 = YES;
+      }
+    }
+    return [UIColor colorWithRed:colorArray[0].floatValue / (shouldUse255 ? 255.f : 1.f)
+                           green:colorArray[1].floatValue / (shouldUse255 ? 255.f : 1.f)
+                            blue:colorArray[2].floatValue / (shouldUse255 ? 255.f : 1.f)
+                           alpha:colorArray[3].floatValue / (shouldUse255 ? 255.f : 1.f)];
+  }
+  return nil;
+}
+
+@end
+
+@implementation LOTKeyframeGroup
+
+- (instancetype)initWithData:(id)data {
+  self = [super init];
+  if (self) {
+    if ([data isKindOfClass:[NSDictionary class]] &&
+        [(NSDictionary *)data valueForKey:@"k"]) {
+      [self buildKeyframesFromData:[(NSDictionary *)data valueForKey:@"k"]];
+    } else {
+      [self buildKeyframesFromData:data];
+    }
+  }
+  return self;
+}
+
+- (void)buildKeyframesFromData:(id)data {
+  if ([data isKindOfClass:[NSArray class]] &&
+      [[(NSArray *)data firstObject] isKindOfClass:[NSDictionary class]] &&
+      [(NSArray *)data firstObject][@"t"]) {
+    // Array of Keyframes
+    NSArray *keyframes =  (NSArray *)data;
+    NSMutableArray *keys = [NSMutableArray array];
+    NSDictionary *previousFrame = nil;
+    for (NSDictionary *keyframe in keyframes) {
+      NSMutableDictionary *currentFrame = [NSMutableDictionary dictionary];
+      if (keyframe[@"t"]) {
+        // Set time
+        currentFrame[@"t"] = keyframe[@"t"];
+      }
+      if (keyframe[@"s"]) {
+        // Set Value for Keyframe
+        currentFrame[@"s"] = keyframe[@"s"];
+      } else if (previousFrame[@"e"]) {
+        // Set Value for Keyframe
+        currentFrame[@"s"] = previousFrame[@"e"];
+      }
+      if (keyframe[@"o"]) {
+        // Set out tangent
+        currentFrame[@"o"] = keyframe[@"o"];
+      }
+      if (previousFrame[@"i"]) {
+        currentFrame[@"i"] = previousFrame[@"i"];
+      }
+      if (keyframe[@"to"]) {
+        // Set out tangent
+        currentFrame[@"to"] = keyframe[@"to"];
+      }
+      if (previousFrame[@"ti"]) {
+        currentFrame[@"ti"] = previousFrame[@"ti"];
+      }
+      if (keyframe[@"h"]) {
+        currentFrame[@"h"] = keyframe[@"h"];
+      }
+      LOTKeyframe *key = [[LOTKeyframe alloc] initWithKeyframe:currentFrame];
+      [keys addObject:key];
+      previousFrame = keyframe;
+    }
+    _keyframes = keys;
+    
+  } else {
+    LOTKeyframe *key = [[LOTKeyframe alloc] initWithValue:data];
+    _keyframes = @[key];
+  }
+}
+
+- (void)remapKeyframesWithBlock:(CGFloat (^)(CGFloat))remapBlock {
+  for (LOTKeyframe *keyframe in _keyframes) {
+    [keyframe remapValueWithBlock:remapBlock];
+  }
+}
+
+@end
+/*
+ +KeyFrameObject has
+ +	i (PointObject)			// Timing curve in tangent
+ +	o (PointObject)			// Timing curve out tangent
+ +	n (array of string)		// String representation of timing curve
+ +	t (integer)				// Keyframe time for start of keyframe
+ +	s (float or array of float or PathObject)	// The key information
+ +	e (float or array of float or PathObject)	// The end key information
+ +	to (array of float)		// For spacial bezier path interpolation, the in tangent
+ +	ti (array of float)		// For spacial bezier path interpolation, the out tangent
+ +	h (integer)				// If the keyframe is a Hold keyframe or not
+*/

+ 98 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.h

@@ -0,0 +1,98 @@
+
+#import "LOTPlatformCompat.h"
+
+#import <CoreGraphics/CoreGraphics.h>
+
+//
+// Core Graphics Geometry Additions
+//
+
+extern const CGSize CGSizeMax;
+
+CGRect LOT_RectIntegral(CGRect rect);
+
+// Centering
+
+// Returns a rectangle of the given size, centered at a point
+CGRect LOT_RectCenteredAtPoint(CGPoint center, CGSize size, BOOL integral);
+
+// Returns the center point of a CGRect
+CGPoint LOT_RectGetCenterPoint(CGRect rect);
+
+// Insetting
+
+// Inset the rectangle on a single edge
+CGRect LOT_RectInsetLeft(CGRect rect, CGFloat inset);
+CGRect LOT_RectInsetRight(CGRect rect, CGFloat inset);
+CGRect LOT_RectInsetTop(CGRect rect, CGFloat inset);
+CGRect LOT_RectInsetBottom(CGRect rect, CGFloat inset);
+
+// Inset the rectangle on two edges
+CGRect LOT_RectInsetHorizontal(CGRect rect, CGFloat leftInset, CGFloat rightInset);
+CGRect LOT_RectInsetVertical(CGRect rect, CGFloat topInset, CGFloat bottomInset);
+
+// Inset the rectangle on all edges
+CGRect LOT_RectInsetAll(CGRect rect, CGFloat leftInset, CGFloat rightInset, CGFloat topInset, CGFloat bottomInset);
+
+// Framing
+
+// Returns a rectangle of size framed in the center of the given rectangle
+CGRect LOT_RectFramedCenteredInRect(CGRect rect, CGSize size, BOOL integral);
+
+// Returns a rectangle of size framed in the given rectangle and inset
+CGRect LOT_RectFramedLeftInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral);
+CGRect LOT_RectFramedRightInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral);
+CGRect LOT_RectFramedTopInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral);
+CGRect LOT_RectFramedBottomInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral);
+
+CGRect LOT_RectFramedTopLeftInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral);
+CGRect LOT_RectFramedTopRightInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral);
+CGRect LOT_RectFramedBottomLeftInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral);
+CGRect LOT_RectFramedBottomRightInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral);
+
+// Divides a rect into sections and returns the section at specified index
+
+CGRect LOT_RectDividedSection(CGRect rect, NSInteger sections, NSInteger index, CGRectEdge fromEdge);
+
+// Returns a rectangle of size attached to the given rectangle
+CGRect LOT_RectAttachedLeftToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral);
+CGRect LOT_RectAttachedRightToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral);
+CGRect LOT_RectAttachedTopToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral);
+CGRect LOT_RectAttachedBottomToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral);
+
+CGRect LOT_RectAttachedBottomLeftToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral);
+CGRect LOT_RectAttachedBottomRightToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral);
+CGRect LOT_RectAttachedTopRightToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral);
+CGRect LOT_RectAttachedTopLeftToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral);
+
+BOOL LOT_CGPointIsZero(CGPoint point);
+
+// Combining
+// Adds all values of the 2nd rect to the first rect
+CGRect LOT_RectAddRect(CGRect rect, CGRect other);
+CGRect LOT_RectAddPoint(CGRect rect, CGPoint point);
+CGRect LOT_RectAddSize(CGRect rect, CGSize size);
+CGRect LOT_RectBounded(CGRect rect);
+
+CGPoint LOT_PointAddedToPoint(CGPoint point1, CGPoint point2);
+
+CGRect LOT_RectSetHeight(CGRect rect, CGFloat height);
+
+CGFloat LOT_PointDistanceFromPoint(CGPoint point1, CGPoint point2);
+CGFloat LOT_DegreesToRadians(CGFloat degrees);
+
+CGFloat LOT_RemapValue(CGFloat value, CGFloat low1, CGFloat high1, CGFloat low2, CGFloat high2 );
+CGPoint LOT_PointByLerpingPoints(CGPoint point1, CGPoint point2, CGFloat value);
+
+CGPoint LOT_PointInLine(CGPoint A, CGPoint B, CGFloat T);
+CGPoint LOT_PointInCubicCurve(CGPoint start, CGPoint cp1, CGPoint cp2, CGPoint end, CGFloat T);
+
+CGFloat LOT_CubicBezierInterpolate(CGPoint P0, CGPoint P1, CGPoint P2, CGPoint P3, CGFloat x);
+CGFloat LOT_SolveCubic(CGFloat a, CGFloat b, CGFloat c, CGFloat d);
+CGFloat LOT_SolveQuadratic(CGFloat a, CGFloat b, CGFloat c);
+CGFloat LOT_Squared(CGFloat f);
+CGFloat LOT_Cubed(CGFloat f);
+CGFloat LOT_CubicRoot(CGFloat f);
+
+CGFloat LOT_CubicLength(CGPoint fromPoint, CGPoint toPoint, CGPoint controlPoint1, CGPoint controlPoint2);
+CGFloat LOT_CubicLengthWithPrecision(CGPoint fromPoint, CGPoint toPoint, CGPoint controlPoint1, CGPoint controlPoint2, CGFloat iterations);

+ 480 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.m

@@ -0,0 +1,480 @@
+
+#import "CGGeometry+LOTAdditions.h"
+
+const CGSize CGSizeMax = {CGFLOAT_MAX, CGFLOAT_MAX};
+//
+// Core Graphics Geometry Additions
+//
+
+// CGRectIntegral returns a rectangle with the smallest integer values for its origin and size that contains the source rectangle.
+// For a rect with .origin={5, 5.5}, .size=(10, 10), it will return .origin={5,5}, .size={10, 11};
+// LOT_RectIntegral will return {5,5}, {10, 10}.
+CGRect LOT_RectIntegral(CGRect rect) {
+  rect.origin = CGPointMake(rintf(rect.origin.x), rintf(rect.origin.y));
+  rect.size = CGSizeMake(ceilf(rect.size.width), ceil(rect.size.height));
+  return rect;
+}
+
+//
+// Centering
+
+// Returns a rectangle of the given size, centered at a point
+
+CGRect LOT_RectCenteredAtPoint(CGPoint center, CGSize size, BOOL integral) {
+  CGRect result;
+  result.origin.x = center.x - 0.5f * size.width;
+  result.origin.y = center.y - 0.5f * size.height;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+// Returns the center point of a CGRect
+CGPoint LOT_RectGetCenterPoint(CGRect rect) {
+	return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
+}
+
+//
+// Insetting
+
+// Inset the rectangle on a single edge
+
+CGRect LOT_RectInsetLeft(CGRect rect, CGFloat inset) {
+  rect.origin.x += inset;
+  rect.size.width -= inset;
+  return rect;
+}
+
+CGRect LOT_RectInsetRight(CGRect rect, CGFloat inset) {
+  rect.size.width -= inset;
+  return rect;
+}
+
+CGRect LOT_RectInsetTop(CGRect rect, CGFloat inset) {
+  rect.origin.y += inset;
+  rect.size.height -= inset;
+  return rect;
+}
+
+CGRect LOT_RectInsetBottom(CGRect rect, CGFloat inset) {
+  rect.size.height -= inset;
+  return rect;
+}
+
+// Inset the rectangle on two edges
+
+CGRect LOT_RectInsetHorizontal(CGRect rect, CGFloat leftInset, CGFloat rightInset) {
+  rect.origin.x += leftInset;
+  rect.size.width -= (leftInset + rightInset);
+  return rect;
+}
+
+CGRect LOT_RectInsetVertical(CGRect rect, CGFloat topInset, CGFloat bottomInset) {
+  rect.origin.y += topInset;
+  rect.size.height -= (topInset + bottomInset);
+  return rect;
+}
+
+// Inset the rectangle on all edges
+
+CGRect LOT_RectInsetAll(CGRect rect, CGFloat leftInset, CGFloat rightInset, CGFloat topInset, CGFloat bottomInset) {
+  rect.origin.x += leftInset;
+  rect.origin.y += topInset;
+  rect.size.width -= (leftInset + rightInset);
+  rect.size.height -= (topInset + bottomInset);
+  return rect;
+}
+
+//
+// Framing
+
+// Returns a rectangle of size framed in the center of the given rectangle
+
+CGRect LOT_RectFramedCenteredInRect(CGRect rect, CGSize size, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rintf(0.5f * (rect.size.width - size.width));
+  result.origin.y = rect.origin.y + rintf(0.5f * (rect.size.height - size.height));
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+// Returns a rectangle of size framed in the given rectangle and inset
+
+CGRect LOT_RectFramedLeftInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + inset;
+  result.origin.y = rect.origin.y + rintf(0.5f * (rect.size.height - size.height));
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedRightInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rect.size.width - size.width - inset;
+  result.origin.y = rect.origin.y + rintf(0.5f * (rect.size.height - size.height));
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedTopInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rintf(0.5f * (rect.size.width - size.width));
+  result.origin.y = rect.origin.y + inset;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedBottomInRect(CGRect rect, CGSize size, CGFloat inset, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rintf(0.5f * (rect.size.width - size.width));
+  result.origin.y = rect.origin.y + rect.size.height - size.height - inset;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedTopLeftInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + insetWidth;
+  result.origin.y = rect.origin.y + insetHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedTopRightInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rect.size.width - size.width - insetWidth;
+  result.origin.y = rect.origin.y + insetHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedBottomLeftInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + insetWidth;
+  result.origin.y = rect.origin.y + rect.size.height - size.height - insetHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectFramedBottomRightInRect(CGRect rect, CGSize size, CGFloat insetWidth, CGFloat insetHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rect.size.width - size.width - insetWidth;
+  result.origin.y = rect.origin.y + rect.size.height - size.height - insetHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+// Returns a rectangle of size attached to the given rectangle
+
+CGRect LOT_RectAttachedLeftToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x - size.width - margin;
+  result.origin.y = rect.origin.y + rintf(0.5f * (rect.size.height - size.height));
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedRightToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rect.size.width + margin;
+  result.origin.y = rect.origin.y + rintf(0.5f * (rect.size.height - size.height));
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedTopToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rintf(0.5f * (rect.size.width - size.width));
+  result.origin.y = rect.origin.y - size.height - margin;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedTopLeftToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + marginWidth;
+  result.origin.y = rect.origin.y - size.height - marginHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedTopRightToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rect.size.width - size.width - marginWidth;
+  result.origin.y = rect.origin.y - rect.size.height - marginHeight;
+  result.size = size;
+
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedBottomToRect(CGRect rect, CGSize size, CGFloat margin, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rintf(0.5f * (rect.size.width - size.width));
+  result.origin.y = rect.origin.y + rect.size.height + margin;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedBottomLeftToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + marginWidth;
+  result.origin.y = rect.origin.y + rect.size.height + marginHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+CGRect LOT_RectAttachedBottomRightToRect(CGRect rect, CGSize size, CGFloat marginWidth, CGFloat marginHeight, BOOL integral) {
+  CGRect result;
+  result.origin.x = rect.origin.x + rect.size.width - size.width - marginWidth;
+  result.origin.y = rect.origin.y + rect.size.height + marginHeight;
+  result.size = size;
+  
+  if (integral) { result = LOT_RectIntegral(result); }
+  return result;
+}
+
+// Divides a rect into sections and returns the section at specified index
+
+CGRect LOT_RectDividedSection(CGRect rect, NSInteger sections, NSInteger index, CGRectEdge fromEdge) {
+  if (sections == 0) {
+    return CGRectZero;
+  }
+  CGRect r = rect;
+  if (fromEdge == CGRectMaxXEdge || fromEdge == CGRectMinXEdge) {
+    r.size.width = rect.size.width / sections;
+    r.origin.x += r.size.width * index;
+  } else {
+    r.size.height = rect.size.height / sections;
+    r.origin.y += r.size.height * index;
+  }
+  return r;
+}
+
+
+CGRect LOT_RectAddRect(CGRect rect, CGRect other) {
+  return CGRectMake(rect.origin.x + other.origin.x, rect.origin.y + other.origin.y,
+                    rect.size.width + other.size.width, rect.size.height + other.size.height);
+}
+
+CGRect LOT_RectAddPoint(CGRect rect, CGPoint point) {
+  return CGRectMake(rect.origin.x + point.x, rect.origin.y + point.y,
+                    rect.size.width, rect.size.height);
+}
+
+CGRect LOT_RectAddSize(CGRect rect, CGSize size) {
+  return CGRectMake(rect.origin.x, rect.origin.y,
+                    rect.size.width + size.width, rect.size.height + size.height);
+}
+
+CGRect LOT_RectBounded(CGRect rect) {
+  CGRect returnRect = rect;
+  returnRect.origin = CGPointZero;
+  return returnRect;
+}
+
+CGPoint LOT_PointAddedToPoint(CGPoint point1, CGPoint point2) {
+  CGPoint returnPoint = point1;
+  returnPoint.x += point2.x;
+  returnPoint.y += point2.y;
+  return returnPoint;
+}
+
+CGRect LOT_RectSetHeight(CGRect rect, CGFloat height) {
+  return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, height);
+}
+
+CGFloat LOT_DegreesToRadians(CGFloat degrees) {
+  return degrees * M_PI / 180;
+}
+
+CGFloat LOT_PointDistanceFromPoint(CGPoint point1, CGPoint point2) {
+  CGFloat xDist = (point2.x - point1.x);
+  CGFloat yDist = (point2.y - point1.y);
+  CGFloat distance = sqrt((xDist * xDist) + (yDist * yDist));
+  return distance;
+}
+
+CGFloat LOT_RemapValue(CGFloat value, CGFloat low1, CGFloat high1, CGFloat low2, CGFloat high2 ) {
+  return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
+}
+
+CGPoint LOT_PointByLerpingPoints(CGPoint point1, CGPoint point2, CGFloat value) {
+  CGFloat xDiff = point2.x - point1.x;
+  CGFloat yDiff = point2.y - point1.y;
+  CGPoint transposed = CGPointMake(fabs(xDiff), fabs(yDiff));
+  CGPoint returnPoint;
+  if (xDiff == 0 || yDiff == 0) {
+    returnPoint.x = xDiff == 0 ? point1.x : LOT_RemapValue(value, 0, 1, point1.x, point2.x);
+    returnPoint.y = yDiff == 0 ? point1.y : LOT_RemapValue(value, 0, 1, point1.y, point2.y);
+  } else {
+    CGFloat rx = transposed.x / transposed.y;
+    CGFloat yLerp = LOT_RemapValue(value, 0, 1, 0, transposed.y);
+    CGFloat xLerp = yLerp * rx;
+    CGPoint interpolatedPoint = CGPointMake(point2.x < point1.x ? xLerp * -1 : xLerp,
+                                            point2.y < point1.y ? yLerp * -1 : yLerp);
+    returnPoint = LOT_PointAddedToPoint(point1, interpolatedPoint);
+  }
+  return returnPoint;
+}
+
+CGPoint LOT_PointInLine(CGPoint A, CGPoint B, CGFloat T) {
+  CGPoint C;
+  C.x = A.x - ((A.x - B.x) * T);
+  C.y = A.y - ((A.y - B.y) * T);
+  return C;
+}
+
+CGFloat LOT_CubicBezierGetY(CGPoint cp1, CGPoint cp2, CGFloat T) {
+//       (1-x)^3 * y0 + 3*(1-x)^2 * x * y1 + 3*(1-x) * x^2 * y2 + x^3 * y3
+  return 3 * powf(1.f - T, 2.f) * T * cp1.y + 3.f * (1.f - T) * powf(T, 2.f) * cp2.y + powf(T, 3.f);
+}
+
+CGPoint LOT_PointInCubicCurve(CGPoint start, CGPoint cp1, CGPoint cp2, CGPoint end, CGFloat T) {
+  CGPoint A = LOT_PointInLine(start, cp1, T);
+  CGPoint B = LOT_PointInLine(cp1, cp2, T);
+  CGPoint C = LOT_PointInLine(cp2, end, T);
+  CGPoint D = LOT_PointInLine(A, B, T);
+  CGPoint E = LOT_PointInLine(B, C, T);
+  CGPoint F = LOT_PointInLine(D, E, T);
+  return F;
+}
+
+CGFloat LOT_SolveCubic(CGFloat a, CGFloat b, CGFloat c, CGFloat d) {
+  if (a == 0) return LOT_SolveQuadratic(b, c, d);
+  if (d == 0) return 0;
+  
+  b /= a;
+  c /= a;
+  d /= a;
+  CGFloat q = (3.0 * c - LOT_Squared(b)) / 9.0;
+  CGFloat r = (-27.0 * d + b * (9.0 * c - 2.0 * LOT_Squared(b))) / 54.0;
+  CGFloat disc = LOT_Cubed(q) + LOT_Squared(r);
+  CGFloat term1 = b / 3.0;
+  
+  if (disc > 0) {
+    double s = r + sqrtf(disc);
+    s = (s < 0) ? - LOT_CubicRoot(-s) : LOT_CubicRoot(s);
+    double t = r - sqrtf(disc);
+    t = (t < 0) ? - LOT_CubicRoot(-t) : LOT_CubicRoot(t);
+    
+    double result = -term1 + s + t;
+    if (result >= 0 && result <= 1) return result;
+  } else if (disc == 0) {
+    double r13 = (r < 0) ? - LOT_CubicRoot(-r) : LOT_CubicRoot(r);
+    
+    double result = -term1 + 2.0 * r13;
+    if (result >= 0 && result <= 1) return result;
+    
+    result = -(r13 + term1);
+    if (result >= 0 && result <= 1) return result;
+  } else {
+    q = -q;
+    double dum1 = q * q * q;
+    dum1 = acosf(r / sqrtf(dum1));
+    double r13 = 2.0 * sqrtf(q);
+    
+    double result = -term1 + r13 * cos(dum1 / 3.0);
+    if (result >= 0 && result <= 1) return result;
+    
+    result = -term1 + r13 * cos((dum1 + 2.0 * M_PI) / 3.0);
+    if (result >= 0 && result <= 1) return result;
+    
+    result = -term1 + r13 * cos((dum1 + 4.0 * M_PI) / 3.0);
+    if (result >= 0 && result <= 1) return result;
+  }
+  
+  return -1;
+}
+
+CGFloat LOT_SolveQuadratic(CGFloat a, CGFloat b, CGFloat c) {
+  CGFloat result = (-b + sqrtf(LOT_Squared(b) - 4 * a * c)) / (2 * a);
+  if (result >= 0 && result <= 1) return result;
+  
+  result = (-b - sqrtf(LOT_Squared(b) - 4 * a * c)) / (2 * a);
+  if (result >= 0 && result <= 1) return result;
+  
+  return -1;
+}
+
+CGFloat LOT_Squared(CGFloat f) { return f * f; }
+
+CGFloat LOT_Cubed(CGFloat f) { return f * f * f; }
+
+CGFloat LOT_CubicRoot(CGFloat f) { return powf(f, 1.0 / 3.0); }
+
+CGFloat LOT_CubicBezierInterpolate(CGPoint P0, CGPoint P1, CGPoint P2, CGPoint P3, CGFloat x) {
+  CGFloat t;
+  if (x == P0.x) {
+    // Handle corner cases explicitly to prevent rounding errors
+    t = 0;
+  } else if (x == P3.x) {
+    t = 1;
+  } else {
+    // Calculate t
+    CGFloat a = -P0.x + 3 * P1.x - 3 * P2.x + P3.x;
+    CGFloat b = 3 * P0.x - 6 * P1.x + 3 * P2.x;
+    CGFloat c = -3 * P0.x + 3 * P1.x;
+    CGFloat d = P0.x - x;
+    CGFloat tTemp = LOT_SolveCubic(a, b, c, d);
+    if (tTemp == -1) return -1;
+    t = tTemp;
+  }
+  
+  // Calculate y from t
+  return LOT_Cubed(1 - t) * P0.y + 3 * t * LOT_Squared(1 - t) * P1.y + 3 * LOT_Squared(t) * (1 - t) * P2.y + LOT_Cubed(t) * P3.y;
+}
+
+CGFloat LOT_CubicLengthWithPrecision(CGPoint fromPoint, CGPoint toPoint, CGPoint controlPoint1, CGPoint controlPoint2, CGFloat iterations) {
+  CGFloat length = 0;
+  CGPoint previousPoint = fromPoint;
+  iterations = ceilf(iterations);
+  for (int i = 1; i <= iterations; ++i) {
+    float s = (float)i  / iterations;
+    
+    CGPoint p = LOT_PointInCubicCurve(fromPoint, controlPoint1, controlPoint2, toPoint, s);
+    
+    length += LOT_PointDistanceFromPoint(previousPoint, p);
+    previousPoint = p;
+  }
+  return length;
+}
+
+CGFloat LOT_CubicLength(CGPoint fromPoint, CGPoint toPoint, CGPoint controlPoint1, CGPoint controlPoint2) {
+  return LOT_CubicLengthWithPrecision(fromPoint, toPoint, controlPoint1, controlPoint2, 20);
+}
+
+BOOL LOT_CGPointIsZero(CGPoint point) {
+  return CGPointEqualToPoint(point, CGPointZero);
+}

+ 54 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTBezierPath.h

@@ -0,0 +1,54 @@
+//
+//  LOTBezierPath.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/20/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTPlatformCompat.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTBezierPath : NSObject
+
++ (instancetype)pathWithCGPath:(CGPathRef)path;
+
++ (instancetype)newPath;
+
+- (void)LOT_moveToPoint:(CGPoint)point;
+
+- (void)LOT_addLineToPoint:(CGPoint)point;
+
+- (void)LOT_addCurveToPoint:(CGPoint)point
+              controlPoint1:(CGPoint)cp1
+              controlPoint2:(CGPoint)cp2;
+
+- (void)LOT_closePath;
+
+- (void)LOT_removeAllPoints;
+
+- (void)LOT_appendPath:(LOTBezierPath *)bezierPath;
+
+- (void)trimPathFromT:(CGFloat)fromT toT:(CGFloat)toT offset:(CGFloat)offset;
+
+- (void)LOT_applyTransform:(CGAffineTransform)transform;
+
+@property (nonatomic, assign) BOOL cacheLengths;
+
+@property (nonatomic, readonly) CGFloat length;
+
+@property (nonatomic, readonly) CGPathRef CGPath;
+@property (nonatomic, readonly) CGPoint currentPoint;
+@property (nonatomic) CGFloat lineWidth;
+@property (nonatomic) CGLineCap lineCapStyle;
+@property (nonatomic) CGLineJoin lineJoinStyle;
+@property (nonatomic) CGFloat miterLimit;
+@property (nonatomic) CGFloat flatness;
+@property (nonatomic) BOOL usesEvenOddFillRule;
+@property (readonly, getter=isEmpty) BOOL empty;
+@property (nonatomic, readonly) CGRect bounds;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 471 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTBezierPath.m

@@ -0,0 +1,471 @@
+//
+//  LOTBezierPath.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/20/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTBezierPath.h"
+#import "CGGeometry+LOTAdditions.h"
+
+typedef struct LOT_Subpath LOT_Subpath;
+typedef void(^LOTBezierPathEnumerationHandler)(const CGPathElement *element);
+
+struct LOT_Subpath {
+  CGPathElementType type;
+  CGFloat length;
+  CGPoint endPoint;
+  CGPoint controlPoint1;
+  CGPoint controlPoint2;
+  LOT_Subpath *nextSubpath;
+};
+
+@interface LOTBezierPath ()
+@property (nonatomic, readonly) LOT_Subpath *headSubpath;
+@end
+
+@implementation LOTBezierPath {
+  LOT_Subpath *headSubpath_;
+  LOT_Subpath *tailSubpath_;
+  CGPoint subpathStartPoint_;
+  CGFloat *_lineDashPattern;
+  NSInteger _lineDashCount;
+  CGFloat _lineDashPhase;
+  CGMutablePathRef _path;
+}
+
+// MARK - Lifecycle
+
++ (instancetype)pathWithCGPath:(CGPathRef)path {
+  LOTBezierPath *returnPath = [LOTBezierPath newPath];
+  [returnPath setWithCGPath:path];
+  return returnPath;
+}
+
++ (instancetype)newPath {
+  return [[LOTBezierPath alloc] init];
+}
+
+- (instancetype)init
+{
+  self = [super init];
+  if (self) {
+    _length = 0;
+    headSubpath_ = NULL;
+    tailSubpath_ = NULL;
+    _path = CGPathCreateMutable();
+    _lineWidth = 1;
+    _lineCapStyle = kCGLineCapButt;
+    _lineJoinStyle = kCGLineJoinMiter;
+    _miterLimit = 10;
+    _flatness = 0.6;
+    _usesEvenOddFillRule = NO;
+    _lineDashPattern = NULL;
+    _lineDashCount = 0;
+    _lineDashPhase = 0;
+    _cacheLengths = NO;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self removeAllSubpaths];
+  if (_path) CGPathRelease(_path);
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+  LOTBezierPath *copy = [[self class] new];
+  
+  copy.cacheLengths = self.cacheLengths;
+  copy.lineWidth = self.lineWidth;
+  copy.lineCapStyle = self.lineCapStyle;
+  copy.lineJoinStyle = self.lineJoinStyle;
+  copy.miterLimit = self.miterLimit;
+  copy.flatness = self.flatness;
+  copy.usesEvenOddFillRule = self.usesEvenOddFillRule;
+  
+  [copy LOT_appendPath:self];
+  
+  return copy;
+}
+
+// MARK - Subpaths List
+
+- (void)removeAllSubpaths {
+  LOT_Subpath *node = headSubpath_;
+  while (node) {
+    LOT_Subpath *nextNode = node->nextSubpath;
+    node->nextSubpath = NULL;
+    free(node);
+    node = nextNode;
+  }
+  headSubpath_ = NULL;
+  tailSubpath_ = NULL;
+}
+
+- (void)addSubpathWithType:(CGPathElementType)type
+                    length:(CGFloat)length
+                  endPoint:(CGPoint)endPoint
+             controlPoint1:(CGPoint)controlPoint1
+             controlPoint1:(CGPoint)controlPoint2 {
+  LOT_Subpath *subPath = (LOT_Subpath *)malloc(sizeof(LOT_Subpath));
+  subPath->type = type;
+  subPath->length = length;
+  subPath->endPoint = endPoint;
+  subPath->controlPoint1 = controlPoint1;
+  subPath->controlPoint2 = controlPoint2;
+  subPath->nextSubpath = NULL;
+  if (tailSubpath_ == NULL) {
+    headSubpath_ = subPath;
+    tailSubpath_ = subPath;
+  } else {
+    tailSubpath_->nextSubpath = subPath;
+    tailSubpath_ = subPath;
+  }
+}
+
+// MARK Getters Setters
+
+- (CGPoint)currentPoint {
+  CGPoint previousPoint = tailSubpath_ ? tailSubpath_->endPoint : CGPointZero;
+  return previousPoint;
+}
+
+- (CGPathRef)CGPath {
+  return _path;
+}
+
+- (LOT_Subpath *)headSubpath {
+  return headSubpath_;
+}
+
+// MARK - External
+
+- (void)LOT_moveToPoint:(CGPoint)point {
+  subpathStartPoint_ = point;
+  [self addSubpathWithType:kCGPathElementMoveToPoint length:0 endPoint:point controlPoint1:CGPointZero controlPoint1:CGPointZero];
+  CGPathMoveToPoint(_path, NULL, point.x, point.y);
+}
+
+- (void)LOT_addLineToPoint:(CGPoint)point {
+  CGFloat length = 0;
+  if (_cacheLengths) {
+    length = LOT_PointDistanceFromPoint(self.currentPoint, point);
+    _length = _length + length;
+  }
+  [self addSubpathWithType:kCGPathElementAddLineToPoint length:length endPoint:point controlPoint1:CGPointZero controlPoint1:CGPointZero];
+  CGPathAddLineToPoint(_path, NULL, point.x, point.y);
+}
+
+- (void)LOT_addCurveToPoint:(CGPoint)point
+              controlPoint1:(CGPoint)cp1
+              controlPoint2:(CGPoint)cp2 {
+  CGFloat length = 0;
+  if (_cacheLengths) {
+    length = LOT_CubicLengthWithPrecision(self.currentPoint, point, cp1, cp2, 5);
+    _length = _length + length;
+  }
+  [self addSubpathWithType:kCGPathElementAddCurveToPoint length:length endPoint:point controlPoint1:cp1 controlPoint1:cp2];
+  CGPathAddCurveToPoint(_path, NULL, cp1.x, cp1.y, cp2.x, cp2.y, point.x, point.y);
+}
+
+- (void)LOT_closePath {
+  CGFloat length = 0;
+  if (_cacheLengths) {
+    length = LOT_PointDistanceFromPoint(self.currentPoint, subpathStartPoint_);
+    _length = _length + length;
+  }
+  [self addSubpathWithType:kCGPathElementCloseSubpath length:length endPoint:subpathStartPoint_ controlPoint1:CGPointZero controlPoint1:CGPointZero];
+  CGPathCloseSubpath(_path);
+}
+
+- (void)_clearPathData {
+  _length = 0;
+  subpathStartPoint_ = CGPointZero;
+  CGPathRelease(_path);
+  _path = CGPathCreateMutable();
+}
+
+- (void)LOT_removeAllPoints {
+  [self removeAllSubpaths];
+  [self _clearPathData];
+}
+
+- (BOOL)containsPoint:(CGPoint)point {
+  return CGPathContainsPoint(_path, NULL, point, _usesEvenOddFillRule);
+}
+
+- (BOOL)isEmpty {
+  return CGPathIsEmpty(_path);
+}
+
+- (CGRect)bounds {
+  return CGPathGetBoundingBox(_path);
+}
+
+- (void)LOT_applyTransform:(CGAffineTransform)transform {
+  CGMutablePathRef mutablePath = CGPathCreateMutable();
+  CGPathAddPath(mutablePath, &transform, _path);
+  CGPathRelease(_path);
+  _path = mutablePath;
+}
+
+- (void)LOT_appendPath:(LOTBezierPath *)bezierPath {
+  CGPathAddPath(_path, NULL, bezierPath.CGPath);
+  
+  LOT_Subpath *nextSubpath = bezierPath.headSubpath;
+  while (nextSubpath) {
+    CGFloat length = 0;
+    if (self.cacheLengths) {
+      if (bezierPath.cacheLengths) {
+        length = nextSubpath->length;
+      } else {
+        // No previous length data, measure.
+        if (nextSubpath->type == kCGPathElementAddLineToPoint) {
+          length = LOT_PointDistanceFromPoint(self.currentPoint, nextSubpath->endPoint);
+        } else if (nextSubpath->type == kCGPathElementAddCurveToPoint) {
+          length = LOT_CubicLengthWithPrecision(self.currentPoint, nextSubpath->endPoint, nextSubpath->controlPoint1, nextSubpath->controlPoint2, 5);
+        } else if (nextSubpath->type == kCGPathElementCloseSubpath) {
+          length = LOT_PointDistanceFromPoint(self.currentPoint, nextSubpath->endPoint);
+        }
+      }
+    }
+    _length = _length + length;
+    [self addSubpathWithType:nextSubpath->type
+                      length:length
+                    endPoint:nextSubpath->endPoint
+               controlPoint1:nextSubpath->controlPoint1
+               controlPoint1:nextSubpath->controlPoint2];
+    
+    nextSubpath = nextSubpath->nextSubpath;
+  }
+}
+
+- (void)trimPathFromT:(CGFloat)fromT toT:(CGFloat)toT offset:(CGFloat)offset {
+  fromT = MIN(MAX(0, fromT), 1);
+  toT = MIN(MAX(0, toT), 1);
+  if (fromT > toT) {
+    CGFloat to = fromT;
+    fromT = toT;
+    toT = to;
+  }
+  
+  offset = offset - floor(offset);
+  CGFloat fromLength = fromT + offset;
+  CGFloat toLength = toT + offset;
+  
+  if (toT - fromT == 1) {
+    // Do Nothing, Full Path returned.
+    return;
+  }
+  
+  if (fromLength == toLength) {
+    // Empty Path
+    [self LOT_removeAllPoints];
+    return;
+  }
+  
+  if (fromLength >= 1) {
+    fromLength = fromLength - floor(fromLength);
+  }
+  if (toLength > 1) {
+    toLength = toLength - floor(toLength);
+  }
+  
+  if (fromLength == 0 &&
+      toLength == 1) {
+    // Do Nothing. Full Path returned.
+    return;
+  }
+  
+  if (fromLength == toLength) {
+    // Empty Path
+    [self LOT_removeAllPoints];
+    return;
+  }
+  
+  CGFloat totalLength = _length;
+  
+  [self _clearPathData];
+
+  LOT_Subpath *subpath = headSubpath_;
+  headSubpath_ = NULL;
+  tailSubpath_ = NULL;
+  
+  fromLength = fromLength * totalLength;
+  toLength = toLength * totalLength;
+  
+  CGFloat currentStartLength = fromLength < toLength ? fromLength : 0;
+  CGFloat currentEndLength = toLength;
+
+  CGFloat subpathBeginningLength = 0;
+  CGPoint currentPoint = CGPointZero;
+
+  while (subpath) {
+    
+    CGFloat pathLength = subpath->length;
+    if (!_cacheLengths) {
+      if (subpath->type == kCGPathElementAddLineToPoint) {
+        pathLength = LOT_PointDistanceFromPoint(currentPoint, subpath->endPoint);
+      } else if (subpath->type == kCGPathElementAddCurveToPoint) {
+        pathLength = LOT_CubicLengthWithPrecision(currentPoint, subpath->endPoint, subpath->controlPoint1, subpath->controlPoint2, 5);
+      } else if (subpath->type == kCGPathElementCloseSubpath) {
+        pathLength = LOT_PointDistanceFromPoint(currentPoint, subpath->endPoint);
+      }
+    }
+    CGFloat subpathEndLength = subpathBeginningLength + pathLength;
+
+    if (subpath->type != kCGPathElementMoveToPoint &&
+        subpathEndLength > currentStartLength) {
+      // The end of this path overlaps the current drawing region
+      
+      // x                    x                                 x                          x
+      // ---------------ooooooooooooooooooooooooooooooooooooooooooooooooo-------------------
+      // Start          |currentStartLength             currentEndLength|                End
+      
+      CGFloat currentSpanStartT = LOT_RemapValue(currentStartLength, subpathBeginningLength, subpathEndLength, 0, 1);
+      CGFloat currentSpanEndT = LOT_RemapValue(currentEndLength, subpathBeginningLength, subpathEndLength, 0, 1);
+      
+      // At this point currentSpan start and end T can be less than 0 or greater than 1
+      
+      if (subpath->type == kCGPathElementAddLineToPoint) {
+        
+        if (currentSpanStartT >= 0) {
+          // The current drawable span either starts with this subpath or along this subpath.
+          // If this is the middle of a segment then currentSpanStartT would be less than 0
+          if (currentSpanStartT > 0) {
+            currentPoint = LOT_PointInLine(currentPoint, subpath->endPoint, currentSpanStartT);
+          }
+          [self LOT_moveToPoint:currentPoint];
+          // Now we are ready to draw a line
+        }
+        
+        CGPoint toPoint = subpath->endPoint;
+        if (currentSpanEndT < 1) {
+          // The end of the span is inside of the current subpath. Find it.
+          toPoint = LOT_PointInLine(currentPoint, subpath->endPoint, currentSpanEndT);
+        }
+        [self LOT_addLineToPoint:toPoint];
+        currentPoint = toPoint;
+      } else if (subpath->type == kCGPathElementAddCurveToPoint) {
+
+        CGPoint cp1, cp2, end;
+        cp1 = subpath->controlPoint1;
+        cp2 = subpath->controlPoint2;
+        end = subpath->endPoint;
+        
+        if (currentSpanStartT >= 0) {
+          // The current drawable span either starts with this subpath or along this subpath.
+          // If this is the middle of a segment then currentSpanStartT would be less than 0
+          // Beginning of a segment Move start point and calculate cp1 and 2 is necessary
+          if (currentSpanStartT > 0) {
+            CGPoint A = LOT_PointInLine(currentPoint, cp1, currentSpanStartT);
+            CGPoint B = LOT_PointInLine(cp1, cp2, currentSpanStartT);
+            CGPoint C = LOT_PointInLine(cp2, end, currentSpanStartT);
+            CGPoint D = LOT_PointInLine(A, B, currentSpanStartT);
+            CGPoint E = LOT_PointInLine(B, C, currentSpanStartT);
+            CGPoint F = LOT_PointInLine(D, E, currentSpanStartT);
+            currentPoint = F;
+            cp1 = E;
+            cp2 = C;
+            currentSpanEndT = LOT_RemapValue(currentSpanEndT, currentSpanStartT, 1, 0, 1);
+          }
+          [self LOT_moveToPoint:currentPoint];
+        }
+        
+        if (currentSpanEndT < 1) {
+          CGPoint A = LOT_PointInLine(currentPoint, cp1, currentSpanEndT);
+          CGPoint B = LOT_PointInLine(cp1, cp2, currentSpanEndT);
+          CGPoint C = LOT_PointInLine(cp2, end, currentSpanEndT);
+          CGPoint D = LOT_PointInLine(A, B, currentSpanEndT);
+          CGPoint E = LOT_PointInLine(B, C, currentSpanEndT);
+          CGPoint F = LOT_PointInLine(D, E, currentSpanEndT);
+          cp1 = A;
+          cp2 = D;
+          end = F;
+        }
+        [self LOT_addCurveToPoint:end controlPoint1:cp1 controlPoint2:cp2];
+      }
+
+      if (currentSpanEndT <= 1) {
+        // We have possibly reached the end.
+        // Current From and To will possibly need to be reset.
+        if (fromLength < toLength) {
+            while (subpath) {
+                LOT_Subpath *nextNode = subpath->nextSubpath;
+                subpath->nextSubpath = NULL;
+                free(subpath);
+                subpath = nextNode;
+            }
+            break;
+        } else {
+          currentStartLength = fromLength;
+          currentEndLength = totalLength;
+          if (fromLength < (subpathBeginningLength + pathLength) &&
+              fromLength > subpathBeginningLength &&
+              currentSpanEndT < 1) {
+            // Loop over this subpath one more time.
+            // In this case the path start and end trim fall within this subpath bounds
+            continue;
+          }
+        }
+      }
+    }
+    currentPoint = subpath->endPoint;
+    subpathBeginningLength = subpathEndLength;
+    
+    LOT_Subpath *nextNode = subpath->nextSubpath;
+    subpath->nextSubpath = NULL;
+    free(subpath);
+    subpath = nextNode;
+  }
+}
+
+#pragma mark - From CGPath
+
+- (void)setWithCGPath:(CGPathRef)path {
+  [self lot_enumeratePath:path elementsUsingBlock:^(const CGPathElement *element) {
+    switch (element->type) {
+      case kCGPathElementMoveToPoint: {
+        CGPoint point = element ->points[0];
+        [self LOT_moveToPoint:point];
+        break;
+      }
+      case kCGPathElementAddLineToPoint: {
+        CGPoint point = element ->points[0];
+        [self LOT_addLineToPoint:point];
+        break;
+      }
+      case kCGPathElementAddQuadCurveToPoint: {
+        break;
+      }
+      case kCGPathElementAddCurveToPoint: {
+        CGPoint point1 = element->points[0];
+        CGPoint point2 = element->points[1];
+        CGPoint point3 = element->points[2];
+        [self LOT_addCurveToPoint:point3 controlPoint1:point1 controlPoint2:point2];
+        break;
+      }
+      case kCGPathElementCloseSubpath: {
+        [self LOT_closePath];
+        break;
+      }
+    }
+  }];
+}
+
+- (void)lot_enumeratePath:(CGPathRef)cgPath elementsUsingBlock:(LOTBezierPathEnumerationHandler)handler {
+  void CGPathEnumerationCallback(void *info, const CGPathElement *element);
+  CGPathApply(cgPath, (__bridge void * _Nullable)(handler), CGPathEnumerationCallback);
+}
+
+@end
+
+void CGPathEnumerationCallback(void *info, const CGPathElement *element)
+{
+  LOTBezierPathEnumerationHandler handler = (__bridge  LOTBezierPathEnumerationHandler)(info);
+  if (handler) {
+    handler(element);
+  }
+}

+ 26 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTHelpers.h

@@ -0,0 +1,26 @@
+//
+//  LOTHelpers.h
+//  Lottie
+//
+//  Created by Brandon Withrow on 7/28/16.
+//  Copyright © 2016 Brandon Withrow. All rights reserved.
+//
+
+#ifndef LOTHelpers_h
+#define LOTHelpers_h
+
+#import "UIColor+Expanded.h"
+#import "CGGeometry+LOTAdditions.h"
+#import "LOTBezierPath.h"
+
+#define ENABLE_DEBUG_LOGGING NO
+#define ENABLE_DEBUG_SHAPES NO
+
+#endif /* LOTHelpers_h */
+
+// TODO Feature Phase
+/*
+ - Trim Path individually
+ - Image Cache Support
+ - Skew transform
+ */

+ 20 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTRadialGradientLayer.h

@@ -0,0 +1,20 @@
+//
+//  LOTAnimationView
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+#import <QuartzCore/QuartzCore.h>
+#import <Foundation/Foundation.h>
+
+@interface LOTRadialGradientLayer : CALayer
+
+@property CGPoint startPoint;
+@property CGPoint endPoint;
+
+@property (nonatomic, copy) NSArray *colors;
+@property (nonatomic, copy) NSArray<NSNumber *> *locations;
+@property (nonatomic, assign) BOOL isRadial;
+
+@end

+ 89 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/LOTRadialGradientLayer.m

@@ -0,0 +1,89 @@
+//
+//  LOTAnimationView
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTRadialGradientLayer.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTRadialGradientLayer
+
+@dynamic isRadial;
+@dynamic startPoint;
+@dynamic endPoint;
+@dynamic colors;
+@dynamic locations;
+
++ (BOOL)needsDisplayForKey:(NSString *)key {
+  if ([key isEqualToString:@"startPoint"] ||
+      [key isEqualToString:@"endPoint"] ||
+      [key isEqualToString:@"colors"] ||
+      [key isEqualToString:@"locations"] ||
+      [key isEqualToString:@"isRadial"]) {
+    return YES;
+  }
+  return [super needsDisplayForKey:key];
+}
+
+- (id)actionForKey:(NSString *)key {
+  id action = self.actions[key];
+  if (action) {
+    if (action == [NSNull null]) {
+      return nil;
+    }
+    return action;
+  }
+  
+  if ([key isEqualToString:@"startPoint"] ||
+      [key isEqualToString:@"endPoint"] ||
+      [key isEqualToString:@"colors"] ||
+      [key isEqualToString:@"locations"] ||
+      [key isEqualToString:@"isRadial"]) {
+    CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:key];
+    theAnimation.fromValue = [self.presentationLayer valueForKey:key];
+    return theAnimation;
+  }
+  return [super actionForKey:key];
+}
+
+- (void)drawInContext:(CGContextRef)ctx {
+  if (self.colors.count == 0) {
+    return;
+  }
+    
+  NSInteger numberOfLocations = self.locations.count;
+  CGColorRef colorRef = (__bridge CGColorRef)[self.colors objectAtIndex:0];
+  NSInteger numberOfComponents = CGColorGetNumberOfComponents(colorRef);
+  CGColorSpaceRef colorSpace = CGColorGetColorSpace(colorRef);
+  
+  CGPoint origin = self.startPoint;
+  CGFloat radius = LOT_PointDistanceFromPoint(self.startPoint, self.endPoint);
+  
+  CGFloat gradientLocations[numberOfLocations];
+  CGFloat gradientComponents[numberOfLocations * numberOfComponents];
+  
+  for (NSInteger locationIndex = 0; locationIndex < numberOfLocations; locationIndex++) {
+    
+    gradientLocations[locationIndex] = [self.locations[locationIndex] floatValue];
+    const CGFloat *colorComponents = CGColorGetComponents((__bridge CGColorRef)self.colors[locationIndex]);
+    
+    for (NSInteger componentIndex = 0; componentIndex < numberOfComponents; componentIndex++) {
+      gradientComponents[numberOfComponents * locationIndex + componentIndex] = colorComponents[componentIndex];
+    }
+  }
+  
+  CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientComponents, gradientLocations, numberOfLocations);
+  
+  if (self.isRadial) {
+    CGContextDrawRadialGradient(ctx, gradient, origin, 0, origin, radius, kCGGradientDrawsAfterEndLocation);
+  } else {
+    CGContextDrawLinearGradient(ctx, gradient, self.startPoint, self.endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
+  }
+  
+  CGGradientRelease(gradient);
+}
+
+@end

+ 51 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/UIColor+Expanded.h

@@ -0,0 +1,51 @@
+#import "LOTPlatformCompat.h"
+
+// From http://github.com/ars/uicolor-utilities
+#define CLAMP(val,min,max)    MIN(MAX(val,min),max)
+
+@interface UIColor (UIColor_Expanded)
+@property (nonatomic, readonly) CGColorSpaceModel colorSpaceModel;
+@property (nonatomic, readonly) BOOL canProvideRGBComponents;
+@property (nonatomic, readonly) CGFloat red; // Only valid if canProvideRGBComponents is YES
+@property (nonatomic, readonly) CGFloat green; // Only valid if canProvideRGBComponents is YES
+@property (nonatomic, readonly) CGFloat blue; // Only valid if canProvideRGBComponents is YES
+@property (nonatomic, readonly) CGFloat white; // Only valid if colorSpaceModel == kCGColorSpaceModelMonochrome
+@property (nonatomic, readonly) CGFloat alpha;
+@property (nonatomic, readonly) UInt32 rgbHex;
+
+- (NSString *)LOT_colorSpaceString;
+
+- (NSArray *)LOT_arrayFromRGBAComponents;
+
+- (BOOL)LOT_red:(CGFloat *)r green:(CGFloat *)g blue:(CGFloat *)b alpha:(CGFloat *)a;
+
+- (UIColor *)LOT_colorByLuminanceMapping;
+
+- (UIColor *)LOT_colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
+- (UIColor *)       LOT_colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
+- (UIColor *) LOT_colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
+- (UIColor *)  LOT_colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
+
+- (UIColor *)LOT_colorByMultiplyingBy:(CGFloat)f;
+- (UIColor *)       LOT_colorByAdding:(CGFloat)f;
+- (UIColor *) LOT_colorByLighteningTo:(CGFloat)f;
+- (UIColor *)  LOT_colorByDarkeningTo:(CGFloat)f;
+
+- (UIColor *)LOT_colorByMultiplyingByColor:(UIColor *)color;
+- (UIColor *)       LOT_colorByAddingColor:(UIColor *)color;
+- (UIColor *) LOT_colorByLighteningToColor:(UIColor *)color;
+- (UIColor *)  LOT_colorByDarkeningToColor:(UIColor *)color;
+
+- (NSString *)LOT_stringFromColor;
+- (NSString *)LOT_hexStringValue;
+
++ (UIColor *)LOT_randomColor;
++ (UIColor *)LOT_colorWithString:(NSString *)stringToConvert;
++ (UIColor *)LOT_colorWithRGBHex:(UInt32)hex;
++ (UIColor *)LOT_colorWithHexString:(NSString *)stringToConvert;
+
++ (UIColor *)LOT_colorWithName:(NSString *)cssColorName;
+
++ (UIColor *)LOT_colorByLerpingFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor amount:(CGFloat)amount;
+
+@end

+ 480 - 0
Pods/lottie-ios/lottie-ios/Classes/Extensions/UIColor+Expanded.m

@@ -0,0 +1,480 @@
+#import "UIColor+Expanded.h"
+
+/*
+ 
+ Thanks to Poltras, Millenomi, Eridius, Nownot, WhatAHam, jberry,
+ and everyone else who helped out but whose name is inadvertently omitted
+ 
+ */
+
+/*
+ Current outstanding request list:
+ 
+ - PolarBearFarm - color descriptions ([UIColor warmGrayWithHintOfBlueTouchOfRedAndSplashOfYellowColor])
+ - Crayola color set
+ - Eridius - UIColor needs a method that takes 2 colors and gives a third complementary one
+ - Consider UIMutableColor that can be adjusted (brighter, cooler, warmer, thicker-alpha, etc)
+ */
+
+/*
+ FOR REFERENCE: Color Space Models: enum CGColorSpaceModel {
+ kCGColorSpaceModelUnknown = -1,
+ kCGColorSpaceModelMonochrome,
+ kCGColorSpaceModelRGB,
+ kCGColorSpaceModelCMYK,
+ kCGColorSpaceModelLab,
+ kCGColorSpaceModelDeviceN,
+ kCGColorSpaceModelIndexed,
+ kCGColorSpaceModelPattern
+ };
+ */
+
+// Static cache of looked up color names. Used with +LOT_colorWithName:
+static NSMutableDictionary *colorNameCache = nil;
+
+@interface UIColor (UIColor_Expanded_Support)
++ (UIColor *)searchForColorByName:(NSString *)cssColorName;
+@end
+
+#pragma mark -
+
+@implementation UIColor (UIColor_Expanded)
+
+- (CGColorSpaceModel)colorSpaceModel {
+	return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor));
+}
+
+- (NSString *)LOT_colorSpaceString {
+	switch (self.colorSpaceModel) {
+		case kCGColorSpaceModelUnknown:
+			return @"kCGColorSpaceModelUnknown";
+		case kCGColorSpaceModelMonochrome:
+			return @"kCGColorSpaceModelMonochrome";
+		case kCGColorSpaceModelRGB:
+			return @"kCGColorSpaceModelRGB";
+		case kCGColorSpaceModelCMYK:
+			return @"kCGColorSpaceModelCMYK";
+		case kCGColorSpaceModelLab:
+			return @"kCGColorSpaceModelLab";
+		case kCGColorSpaceModelDeviceN:
+			return @"kCGColorSpaceModelDeviceN";
+		case kCGColorSpaceModelIndexed:
+			return @"kCGColorSpaceModelIndexed";
+		case kCGColorSpaceModelPattern:
+			return @"kCGColorSpaceModelPattern";
+		default:
+			return @"Not a valid color space";
+	}
+}
+
+- (BOOL)canProvideRGBComponents {
+	switch (self.colorSpaceModel) {
+		case kCGColorSpaceModelRGB:
+		case kCGColorSpaceModelMonochrome:
+			return YES;
+		default:
+			return NO;
+	}
+}
+
+- (NSArray *)LOT_arrayFromRGBAComponents {
+	NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -LOT_arrayFromRGBAComponents");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [NSArray arrayWithObjects:
+          [NSNumber numberWithFloat:r],
+          [NSNumber numberWithFloat:g],
+          [NSNumber numberWithFloat:b],
+          [NSNumber numberWithFloat:a],
+          nil];
+}
+
+- (BOOL)LOT_red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha {
+	const CGFloat *components = CGColorGetComponents(self.CGColor);
+  
+	CGFloat r,g,b,a;
+  
+	switch (self.colorSpaceModel) {
+		case kCGColorSpaceModelMonochrome:
+			r = g = b = components[0];
+			a = components[1];
+			break;
+		case kCGColorSpaceModelRGB:
+			r = components[0];
+			g = components[1];
+			b = components[2];
+			a = components[3];
+			break;
+		default:	// We don't know how to handle this model
+			return NO;
+	}
+  
+	if (red) *red = r;
+	if (green) *green = g;
+	if (blue) *blue = b;
+	if (alpha) *alpha = a;
+  
+	return YES;
+}
+
+- (CGFloat)red {
+	NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -red");
+	const CGFloat *c = CGColorGetComponents(self.CGColor);
+	return c[0];
+}
+
+- (CGFloat)green {
+	NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -green");
+	const CGFloat *c = CGColorGetComponents(self.CGColor);
+	if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0];
+	return c[1];
+}
+
+- (CGFloat)blue {
+	NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -blue");
+	const CGFloat *c = CGColorGetComponents(self.CGColor);
+	if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0];
+	return c[2];
+}
+
+- (CGFloat)white {
+	NSAssert(self.colorSpaceModel == kCGColorSpaceModelMonochrome, @"Must be a Monochrome color to use -white");
+	const CGFloat *c = CGColorGetComponents(self.CGColor);
+	return c[0];
+}
+
+- (CGFloat)alpha {
+	return CGColorGetAlpha(self.CGColor);
+}
+
+- (UInt32)rgbHex {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbHex");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return 0;
+  
+	r = MIN(MAX(self.red, 0.0f), 1.0f);
+	g = MIN(MAX(self.green, 0.0f), 1.0f);
+	b = MIN(MAX(self.blue, 0.0f), 1.0f);
+  
+	return (((int)roundf(r * 255)) << 16)
+  | (((int)roundf(g * 255)) << 8)
+  | (((int)roundf(b * 255)));
+}
+
+#pragma mark Arithmetic operations
+
+- (UIColor *)LOT_colorByLuminanceMapping {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	// http://en.wikipedia.org/wiki/Luma_(video)
+	// Y = 0.2126 R + 0.7152 G + 0.0722 B
+	return [UIColor colorWithWhite:r*0.2126f + g*0.7152f + b*0.0722f
+                           alpha:a];
+  
+}
+
+- (UIColor *)LOT_colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r * red))
+                         green:MAX(0.0, MIN(1.0, g * green))
+                          blue:MAX(0.0, MIN(1.0, b * blue))
+                         alpha:MAX(0.0, MIN(1.0, a * alpha))];
+}
+
+- (UIColor *)LOT_colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r + red))
+                         green:MAX(0.0, MIN(1.0, g + green))
+                          blue:MAX(0.0, MIN(1.0, b + blue))
+                         alpha:MAX(0.0, MIN(1.0, a + alpha))];
+}
+
+- (UIColor *)LOT_colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [UIColor colorWithRed:MAX(r, red)
+                         green:MAX(g, green)
+                          blue:MAX(b, blue)
+                         alpha:MAX(a, alpha)];
+}
+
+- (UIColor *)LOT_colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [UIColor colorWithRed:MIN(r, red)
+                         green:MIN(g, green)
+                          blue:MIN(b, blue)
+                         alpha:MIN(a, alpha)];
+}
+
+- (UIColor *)LOT_colorByMultiplyingBy:(CGFloat)f {
+	return [self LOT_colorByMultiplyingByRed:f green:f blue:f alpha:1.0f];
+}
+
+- (UIColor *)LOT_colorByAdding:(CGFloat)f {
+	return [self LOT_colorByMultiplyingByRed:f green:f blue:f alpha:0.0f];
+}
+
+- (UIColor *)LOT_colorByLighteningTo:(CGFloat)f {
+	return [self LOT_colorByLighteningToRed:f green:f blue:f alpha:0.0f];
+}
+
+- (UIColor *)LOT_colorByDarkeningTo:(CGFloat)f {
+	return [self LOT_colorByDarkeningToRed:f green:f blue:f alpha:1.0f];
+}
+
+- (UIColor *)LOT_colorByMultiplyingByColor:(UIColor *)color {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [self LOT_colorByMultiplyingByRed:r green:g blue:b alpha:1.0f];
+}
+
+- (UIColor *)LOT_colorByAddingColor:(UIColor *)color {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [self LOT_colorByAddingRed:r green:g blue:b alpha:0.0f];
+}
+
+- (UIColor *)LOT_colorByLighteningToColor:(UIColor *)color {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [self LOT_colorByLighteningToRed:r green:g blue:b alpha:0.0f];
+}
+
+- (UIColor *)LOT_colorByDarkeningToColor:(UIColor *)color {
+	NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
+  
+	CGFloat r,g,b,a;
+	if (![self LOT_red:&r green:&g blue:&b alpha:&a]) return nil;
+  
+	return [self LOT_colorByDarkeningToRed:r green:g blue:b alpha:1.0f];
+}
+
+#pragma mark String utilities
+
+- (NSString *)LOT_stringFromColor {
+	NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -LOT_stringFromColor");
+	NSString *result;
+	switch (self.colorSpaceModel) {
+		case kCGColorSpaceModelRGB:
+			result = [NSString stringWithFormat:@"{%0.3f, %0.3f, %0.3f, %0.3f}", self.red, self.green, self.blue, self.alpha];
+			break;
+		case kCGColorSpaceModelMonochrome:
+			result = [NSString stringWithFormat:@"{%0.3f, %0.3f}", self.white, self.alpha];
+			break;
+		default:
+			result = nil;
+	}
+	return result;
+}
+
+- (NSString *)LOT_hexStringValue {
+	return [NSString stringWithFormat:@"%0.6X", (unsigned int)self.rgbHex];
+}
+
++ (UIColor *)LOT_colorWithString:(NSString *)stringToConvert {
+	NSScanner *scanner = [NSScanner scannerWithString:stringToConvert];
+	if (![scanner scanString:@"{" intoString:NULL]) return nil;
+	const NSUInteger kMaxComponents = 4;
+	float c[kMaxComponents];
+	NSUInteger i = 0;
+	if (![scanner scanFloat:&c[i++]]) return nil;
+	while (1) {
+		if ([scanner scanString:@"}" intoString:NULL]) break;
+		if (i >= kMaxComponents) return nil;
+		if ([scanner scanString:@"," intoString:NULL]) {
+			if (![scanner scanFloat:&c[i++]]) return nil;
+		} else {
+			// either we're at the end of there's an unexpected character here
+			// both cases are error conditions
+			return nil;
+		}
+	}
+	if (![scanner isAtEnd]) return nil;
+	UIColor *color;
+	switch (i) {
+		case 2: // monochrome
+			color = [UIColor colorWithWhite:c[0] alpha:c[1]];
+			break;
+		case 4: // RGB
+			color = [UIColor colorWithRed:c[0] green:c[1] blue:c[2] alpha:c[3]];
+			break;
+		default:
+			color = nil;
+	}
+	return color;
+}
+
+#pragma mark Class methods
+
++ (UIColor *)LOT_randomColor {
+	return [UIColor colorWithRed:(CGFloat)random() / (CGFloat)RAND_MAX
+                         green:(CGFloat)random() / (CGFloat)RAND_MAX
+                          blue:(CGFloat)random() / (CGFloat)RAND_MAX
+                         alpha:1.0f];
+}
+
++ (UIColor *)LOT_colorWithRGBHex:(UInt32)hex {
+	int r = (hex >> 16) & 0xFF;
+	int g = (hex >> 8) & 0xFF;
+	int b = (hex) & 0xFF;
+  
+	return [UIColor colorWithRed:r / 255.0f
+                         green:g / 255.0f
+                          blue:b / 255.0f
+                         alpha:1.0f];
+}
+
+// Returns a UIColor by scanning the string for a hex number and passing that to +[UIColor LOT_colorWithRGBHex:]
+// Skips any leading whitespace and ignores any trailing characters
++ (UIColor *)LOT_colorWithHexString:(NSString *)stringToConvert {
+  NSString *strippedString = [stringToConvert stringByReplacingOccurrencesOfString:@"#" withString:@""];
+	NSScanner *scanner = [NSScanner scannerWithString:strippedString];
+	unsigned hexNum;
+	if (![scanner scanHexInt:&hexNum]) return nil;
+	return [UIColor LOT_colorWithRGBHex:hexNum];
+}
+
+// Lookup a color using css 3/svg color name
++ (UIColor *)LOT_colorWithName:(NSString *)cssColorName {
+	UIColor *color;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        colorNameCache = [[NSMutableDictionary alloc] init];
+    });
+    
+	@synchronized(colorNameCache) {
+		// Look for the color in the cache
+		color = [colorNameCache objectForKey:cssColorName];
+    
+		if ((id)color == [NSNull null]) {
+			// If it wasn't there previously, it's still not there now
+			color = nil;
+		} else if (!color) {
+			// Color not in cache, so search for it now
+			color = [self searchForColorByName:cssColorName];
+      
+			// Set the value in cache, storing NSNull on failure
+			[colorNameCache setObject:(color ?: (id)[NSNull null])
+                         forKey:cssColorName];
+		}
+	}
+  
+	return color;
+}
+
++ (UIColor *)LOT_colorByLerpingFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor amount:(CGFloat)amount {
+  NSAssert((toColor != nil && fromColor != nil), @"Passing Nil Color");
+  amount = CLAMP(amount, 0.f, 1.f);
+  const CGFloat *fromComponents = CGColorGetComponents(fromColor.CGColor);
+  const CGFloat *toComponents = CGColorGetComponents(toColor.CGColor);
+  float r = fromComponents[0] + ((toComponents[0] - fromComponents[0]) * amount);
+  float g = fromComponents[1] + ((toComponents[1] - fromComponents[1]) * amount);
+  float b = fromComponents[2] + ((toComponents[2] - fromComponents[2]) * amount);
+  float a = fromComponents[3] + ((toComponents[3] - fromComponents[3]) * amount);
+  return [UIColor colorWithRed:r green:g blue:b alpha:a];
+}
+
+@end
+
+#pragma mark -
+
+@implementation UIColor (UIColor_Expanded_Support)
+/*
+ * Database of color names and hex rgb values, derived
+ * from the css 3 color spec:
+ *	http://www.w3.org/TR/css3-color/
+ *
+ * We think this is a very compact way of storing
+ * this information, and relatively cheap to lookup.
+ *
+ * Note that we search for color names starting with ','
+ * and terminated by '#', so that we don't get false matches.
+ * For this reason, the database begins with ','.
+ */
+static const char *colorNameDB = ","
+"aliceblue#f0f8ff,antiquewhite#faebd7,aqua#00ffff,aquamarine#7fffd4,azure#f0ffff,"
+"beige#f5f5dc,bisque#ffe4c4,black#000000,blanchedalmond#ffebcd,blue#0000ff,"
+"blueviolet#8a2be2,brown#a52a2a,burlywood#deb887,cadetblue#5f9ea0,chartreuse#7fff00,"
+"chocolate#d2691e,coral#ff7f50,cornflowerblue#6495ed,cornsilk#fff8dc,crimson#dc143c,"
+"cyan#00ffff,darkblue#00008b,darkcyan#008b8b,darkgoldenrod#b8860b,darkgray#a9a9a9,"
+"darkgreen#006400,darkgrey#a9a9a9,darkkhaki#bdb76b,darkmagenta#8b008b,"
+"darkolivegreen#556b2f,darkorange#ff8c00,darkorchid#9932cc,darkred#8b0000,"
+"darksalmon#e9967a,darkseagreen#8fbc8f,darkslateblue#483d8b,darkslategray#2f4f4f,"
+"darkslategrey#2f4f4f,darkturquoise#00ced1,darkviolet#9400d3,deeppink#ff1493,"
+"deepskyblue#00bfff,dimgray#696969,dimgrey#696969,dodgerblue#1e90ff,"
+"firebrick#b22222,floralwhite#fffaf0,forestgreen#228b22,fuchsia#ff00ff,"
+"gainsboro#dcdcdc,ghostwhite#f8f8ff,gold#ffd700,goldenrod#daa520,gray#808080,"
+"green#008000,greenyellow#adff2f,grey#808080,honeydew#f0fff0,hotpink#ff69b4,"
+"indianred#cd5c5c,indigo#4b0082,ivory#fffff0,khaki#f0e68c,lavender#e6e6fa,"
+"lavenderblush#fff0f5,lawngreen#7cfc00,lemonchiffon#fffacd,lightblue#add8e6,"
+"lightcoral#f08080,lightcyan#e0ffff,lightgoldenrodyellow#fafad2,lightgray#d3d3d3,"
+"lightgreen#90ee90,lightgrey#d3d3d3,lightpink#ffb6c1,lightsalmon#ffa07a,"
+"lightseagreen#20b2aa,lightskyblue#87cefa,lightslategray#778899,"
+"lightslategrey#778899,lightsteelblue#b0c4de,lightyellow#ffffe0,lime#00ff00,"
+"limegreen#32cd32,linen#faf0e6,magenta#ff00ff,maroon#800000,mediumaquamarine#66cdaa,"
+"mediumblue#0000cd,mediumorchid#ba55d3,mediumpurple#9370db,mediumseagreen#3cb371,"
+"mediumslateblue#7b68ee,mediumspringgreen#00fa9a,mediumturquoise#48d1cc,"
+"mediumvioletred#c71585,midnightblue#191970,mintcream#f5fffa,mistyrose#ffe4e1,"
+"moccasin#ffe4b5,navajowhite#ffdead,navy#000080,oldlace#fdf5e6,olive#808000,"
+"olivedrab#6b8e23,orange#ffa500,orangered#ff4500,orchid#da70d6,palegoldenrod#eee8aa,"
+"palegreen#98fb98,paleturquoise#afeeee,palevioletred#db7093,papayawhip#ffefd5,"
+"peachpuff#ffdab9,peru#cd853f,pink#ffc0cb,plum#dda0dd,powderblue#b0e0e6,"
+"purple#800080,red#ff0000,rosybrown#bc8f8f,royalblue#4169e1,saddlebrown#8b4513,"
+"salmon#fa8072,sandybrown#f4a460,seagreen#2e8b57,seashell#fff5ee,sienna#a0522d,"
+"silver#c0c0c0,skyblue#87ceeb,slateblue#6a5acd,slategray#708090,slategrey#708090,"
+"snow#fffafa,springgreen#00ff7f,steelblue#4682b4,tan#d2b48c,teal#008080,"
+"thistle#d8bfd8,tomato#ff6347,turquoise#40e0d0,violet#ee82ee,wheat#f5deb3,"
+"white#ffffff,whitesmoke#f5f5f5,yellow#ffff00,yellowgreen#9acd32";
+
++ (UIColor *)searchForColorByName:(NSString *)cssColorName {
+	UIColor *result = nil;
+  
+	// Compile the string we'll use to search against the database
+	// We search for ",<colorname>#" to avoid false matches
+	const char *searchString = [[NSString stringWithFormat:@",%@#", cssColorName] UTF8String];
+  
+	// Search for the color name
+	const char *found = strstr(colorNameDB, searchString);
+  
+	// If found, step past the search string and grab the hex representation
+	if (found) {
+		const char *after = found + strlen(searchString);
+		int hex;
+		if (sscanf(after, "%x", &hex) == 1) {
+			result = [self LOT_colorWithRGBHex:hex];
+		}
+	}
+  
+	return result;
+}
+@end

+ 18 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/CALayer+Compat.h

@@ -0,0 +1,18 @@
+//
+// Created by Oleksii Pavlovskyi on 2/2/17.
+// Copyright (c) 2017 Airbnb. All rights reserved.
+//
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import <Foundation/Foundation.h>
+#import <QuartzCore/QuartzCore.h>
+
+@interface CALayer (Compat)
+
+@property (nonatomic, assign) BOOL allowsEdgeAntialiasing;
+
+@end
+
+#endif

+ 18 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/CALayer+Compat.m

@@ -0,0 +1,18 @@
+//
+// Created by Oleksii Pavlovskyi on 2/2/17.
+// Copyright (c) 2017 Airbnb. All rights reserved.
+//
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import "CALayer+Compat.h"
+
+@implementation CALayer (Compat)
+
+- (BOOL)allowsEdgeAntialiasing { return NO; }
+- (void)setAllowsEdgeAntialiasing:(BOOL)allowsEdgeAntialiasing { }
+
+@end
+
+#endif

+ 37 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/LOTPlatformCompat.h

@@ -0,0 +1,37 @@
+//
+//  LOTPlatformCompat.h
+//  Lottie
+//
+//  Created by Oleksii Pavlovskyi on 2/2/17.
+//  Copyright (c) 2017 Airbnb. All rights reserved.
+//
+
+#ifndef LOTPlatformCompat_h
+#define LOTPlatformCompat_h
+
+#include <TargetConditionals.h>
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+
+#import <UIKit/UIKit.h>
+
+#else
+
+#import <AppKit/AppKit.h>
+#import "UIColor.h"
+#import "CALayer+Compat.h"
+#import "NSValue+Compat.h"
+#import "UIBezierPath.h"
+
+NS_INLINE NSString *NSStringFromCGRect(CGRect rect) {
+    return NSStringFromRect(rect);
+}
+
+NS_INLINE NSString *NSStringFromCGPoint(CGPoint point) {
+    return NSStringFromPoint(point);
+}
+
+typedef NSEdgeInsets UIEdgeInsets;
+
+#endif
+#endif

+ 22 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/NSValue+Compat.h

@@ -0,0 +1,22 @@
+//
+// Created by Oleksii Pavlovskyi on 2/2/17.
+// Copyright (c) 2017 Airbnb. All rights reserved.
+//
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import <Foundation/Foundation.h>
+
+@interface NSValue (Compat)
+
++ (NSValue *)valueWithCGRect:(CGRect)rect;
++ (NSValue *)valueWithCGPoint:(CGPoint)point;
+
+@property (nonatomic, readonly) CGRect CGRectValue;
+@property(nonatomic, readonly) CGPoint CGPointValue;
+@property (nonatomic, readonly) CGSize CGSizeValue;
+
+@end
+
+#endif

+ 35 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/NSValue+Compat.m

@@ -0,0 +1,35 @@
+//
+// Created by Oleksii Pavlovskyi on 2/2/17.
+// Copyright (c) 2017 Airbnb. All rights reserved.
+//
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import "NSValue+Compat.h"
+
+@implementation NSValue (Compat)
+
++ (NSValue *)valueWithCGRect:(CGRect)rect {
+    return [self valueWithRect:rect];
+}
+
++ (NSValue *)valueWithCGPoint:(CGPoint)point {
+    return [self valueWithPoint:point];
+}
+
+- (CGRect)CGRectValue {
+    return self.rectValue;
+}
+
+- (CGPoint)CGPointValue {
+    return self.pointValue;
+}
+
+- (CGSize)CGSizeValue {
+  return self.sizeValue;
+}
+
+@end
+
+#endif

+ 80 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIBezierPath.h

@@ -0,0 +1,80 @@
+// Kindly stolen from https://github.com/BigZaphod/Chameleon
+/*
+ * Copyright (c) 2011, The Iconfactory. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of The Iconfactory nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE ICONFACTORY BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
+    UIRectCornerTopLeft     = 1 << 0,
+    UIRectCornerTopRight    = 1 << 1,
+    UIRectCornerBottomLeft  = 1 << 2,
+    UIRectCornerBottomRight = 1 << 3,
+    UIRectCornerAllCorners  = UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight
+};
+
+@interface UIBezierPath : NSObject <NSCopying>
+
++ (UIBezierPath *)bezierPath;
++ (UIBezierPath *)bezierPathWithRect:(CGRect)rect;
++ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect;
++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
++ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
++ (UIBezierPath *)bezierPathWithCGPath:(CGPathRef)CGPath;
+
+- (void)moveToPoint:(CGPoint)point;
+- (void)addLineToPoint:(CGPoint)point;
+- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
+- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
+- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
+- (void)closePath;
+- (void)removeAllPoints;
+- (void)appendPath:(UIBezierPath *)bezierPath;
+- (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;
+- (void)getLineDash:(CGFloat *)pattern count:(NSInteger *)count phase:(CGFloat *)phase;
+- (BOOL)containsPoint:(CGPoint)point;
+- (void)applyTransform:(CGAffineTransform)transform;
+
+@property (nonatomic) CGPathRef CGPath;
+@property (nonatomic, readonly) CGPoint currentPoint;
+@property (nonatomic) CGFloat lineWidth;
+@property (nonatomic) CGLineCap lineCapStyle;
+@property (nonatomic) CGLineJoin lineJoinStyle;
+@property (nonatomic) CGFloat miterLimit;
+@property (nonatomic) CGFloat flatness;
+@property (nonatomic) BOOL usesEvenOddFillRule;
+@property (readonly, getter=isEmpty) BOOL empty;
+@property (nonatomic, readonly) CGRect bounds;
+@end
+
+#endif

+ 312 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIBezierPath.m

@@ -0,0 +1,312 @@
+// Kindly stolen from https://github.com/BigZaphod/Chameleon
+/*
+ * Copyright (c) 2011, The Iconfactory. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of The Iconfactory nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE ICONFACTORY BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import "UIBezierPath.h"
+
+@implementation UIBezierPath {
+    CGFloat *_lineDashPattern;
+    NSInteger _lineDashCount;
+    CGFloat _lineDashPhase;
+}
+@synthesize CGPath = _path;
+
+- (id)init {
+    self = [super init];
+    if (self) {
+        _path = CGPathCreateMutable();
+        _lineWidth = 1;
+        _lineCapStyle = kCGLineCapButt;
+        _lineJoinStyle = kCGLineJoinMiter;
+        _miterLimit = 10;
+        _flatness = 0.6;
+        _usesEvenOddFillRule = NO;
+        _lineDashPattern = NULL;
+        _lineDashCount = 0;
+        _lineDashPhase = 0;
+    }
+    return self;
+}
+
+- (void)dealloc {
+    if (_path) CGPathRelease(_path);
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    UIBezierPath *copy = [[self class] new];
+
+    copy.CGPath = self.CGPath;
+    copy.lineWidth = self.lineWidth;
+    copy.lineCapStyle = self.lineCapStyle;
+    copy.lineJoinStyle = self.lineJoinStyle;
+    copy.miterLimit = self.miterLimit;
+    copy.flatness = self.flatness;
+    copy.usesEvenOddFillRule = self.usesEvenOddFillRule;
+
+    NSInteger lineDashCount = 0;
+    [self getLineDash:NULL count:&lineDashCount phase:NULL];
+
+    if (lineDashCount > 0) {
+        CGFloat *lineDashPattern = malloc(sizeof(CGFloat) * lineDashCount);
+        CGFloat lineDashPhase = 0;
+        [self getLineDash:lineDashPattern count:NULL phase:&lineDashPhase];
+        [copy setLineDash:lineDashPattern count:lineDashCount phase:lineDashPhase];
+        free(lineDashPattern);
+    }
+
+    return copy;
+}
+
++ (UIBezierPath *)bezierPathWithCGPath:(CGPathRef)CGPath {
+    NSAssert(CGPath != NULL, @"CGPath must not be NULL");
+    UIBezierPath *bezierPath = [[self alloc] init];
+    bezierPath.CGPath = CGPath;
+    return bezierPath;
+}
+
++ (UIBezierPath *)bezierPath {
+    UIBezierPath *bezierPath = [[self alloc] init];
+    return bezierPath;
+}
+
++ (UIBezierPath *)bezierPathWithRect:(CGRect)rect {
+    CGMutablePathRef path = CGPathCreateMutable();
+    CGPathAddRect(path, NULL, rect);
+
+    UIBezierPath *bezierPath = [[self alloc] init];
+    bezierPath->_path = path;
+    return bezierPath;
+}
+
++ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect {
+    CGMutablePathRef path = CGPathCreateMutable();
+    CGPathAddEllipseInRect(path, NULL, rect);
+
+    UIBezierPath *bezierPath = [[self alloc] init];
+    bezierPath->_path = path;
+    return bezierPath;
+}
+
++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect
+                               cornerRadius:(CGFloat)cornerRadius {
+    return [self bezierPathWithRoundedRect:rect
+                         byRoundingCorners:UIRectCornerAllCorners
+                               cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
+}
+
++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect
+                          byRoundingCorners:(UIRectCorner)corners
+                                cornerRadii:(CGSize)cornerRadii {
+
+    CGMutablePathRef path = CGPathCreateMutable();
+
+    const CGPoint topLeft = rect.origin;
+    const CGPoint topRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
+    const CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
+    const CGPoint bottomLeft = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
+
+    if (corners & UIRectCornerTopLeft) {
+        CGPathMoveToPoint(path, NULL, topLeft.x + cornerRadii.width, topLeft.y);
+    } else {
+        CGPathMoveToPoint(path, NULL, topLeft.x, topLeft.y);
+    }
+
+    if (corners & UIRectCornerTopRight) {
+        CGPathAddLineToPoint(path, NULL, topRight.x - cornerRadii.width, topRight.y);
+        CGPathAddCurveToPoint(path, NULL, topRight.x, topRight.y, topRight.x, topRight.y + cornerRadii.height, topRight.x, topRight.y + cornerRadii.height);
+    } else {
+        CGPathAddLineToPoint(path, NULL, topRight.x, topRight.y);
+    }
+
+    if (corners & UIRectCornerBottomRight) {
+        CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y - cornerRadii.height);
+        CGPathAddCurveToPoint(path, NULL, bottomRight.x, bottomRight.y, bottomRight.x - cornerRadii.width, bottomRight.y, bottomRight.x - cornerRadii.width, bottomRight.y);
+    } else {
+        CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y);
+    }
+
+    if (corners & UIRectCornerBottomLeft) {
+        CGPathAddLineToPoint(path, NULL, bottomLeft.x + cornerRadii.width, bottomLeft.y);
+        CGPathAddCurveToPoint(path, NULL, bottomLeft.x, bottomLeft.y, bottomLeft.x, bottomLeft.y - cornerRadii.height, bottomLeft.x, bottomLeft.y - cornerRadii.height);
+    } else {
+        CGPathAddLineToPoint(path, NULL, bottomLeft.x, bottomLeft.y);
+    }
+
+    if (corners & UIRectCornerTopLeft) {
+        CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y + cornerRadii.height);
+        CGPathAddCurveToPoint(path, NULL, topLeft.x, topLeft.y, topLeft.x + cornerRadii.width, topLeft.y, topLeft.x + cornerRadii.width, topLeft.y);
+    } else {
+        CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y);
+    }
+
+    CGPathCloseSubpath(path);
+
+    UIBezierPath *bezierPath = [[self alloc] init];
+    bezierPath->_path = path;
+    return bezierPath;
+}
+
++ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center
+                                   radius:(CGFloat)radius
+                               startAngle:(CGFloat)startAngle
+                                 endAngle:(CGFloat)endAngle
+                                clockwise:(BOOL)clockwise {
+
+    CGMutablePathRef path = CGPathCreateMutable();
+    CGPathAddArc(path, NULL, center.x, center.y, radius, startAngle, endAngle, clockwise);
+
+    UIBezierPath *bezierPath = [[self alloc] init];
+    bezierPath->_path = path;
+    return bezierPath;
+}
+
+- (void)moveToPoint:(CGPoint)point {
+    CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+    CGPathMoveToPoint(mutablePath, NULL, point.x, point.y);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)addLineToPoint:(CGPoint)point {
+    CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+    CGPathAddLineToPoint(mutablePath, NULL, point.x, point.y);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise {
+    CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+    CGPathAddArc(mutablePath, NULL, center.x, center.y, radius, startAngle, endAngle, clockwise);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2 {
+    CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+    CGPathAddCurveToPoint(mutablePath, NULL, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint {
+    CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+    CGPathAddQuadCurveToPoint(mutablePath, NULL, controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)closePath {
+    CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+    CGPathCloseSubpath(mutablePath);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)removeAllPoints {
+    CGMutablePathRef mutablePath = CGPathCreateMutable();
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+- (void)appendPath:(UIBezierPath *)bezierPath {
+    if (bezierPath) {
+        CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path);
+        CGPathAddPath(mutablePath, NULL, bezierPath.CGPath);
+        self.CGPath = mutablePath;
+        CGPathRelease(mutablePath);
+    }
+}
+
+- (void)setCGPath:(CGPathRef)path {
+    NSAssert(path != NULL, @"path must not be NULL");
+    if (path != _path) {
+        if (_path) CGPathRelease(_path);
+        _path = CGPathCreateCopy(path);
+    }
+}
+
+- (CGPoint)currentPoint {
+    return CGPathGetCurrentPoint(_path);
+}
+
+- (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase {
+    free(_lineDashPattern);
+
+    if (pattern && count > 0) {
+        const size_t size = sizeof(CGFloat) * count;
+        _lineDashPattern = malloc(size);
+        bcopy(pattern, _lineDashPattern, size);
+    } else {
+        _lineDashPattern = NULL;
+    }
+
+    _lineDashCount = count;
+    _lineDashPhase = phase;
+}
+
+- (void)getLineDash:(CGFloat *)pattern count:(NSInteger *)count phase:(CGFloat *)phase {
+    if (pattern && _lineDashPattern && _lineDashCount > 0) {
+        const size_t size = sizeof(CGFloat) * _lineDashCount;
+        bcopy(_lineDashPattern, pattern, size);
+    }
+
+    if (count) {
+        *count = _lineDashCount;
+    }
+
+    if (phase) {
+        *phase = _lineDashPhase;
+    }
+}
+
+- (BOOL)containsPoint:(CGPoint)point {
+    return CGPathContainsPoint(_path, NULL, point, _usesEvenOddFillRule);
+}
+
+- (BOOL)isEmpty {
+    return CGPathIsEmpty(_path);
+}
+
+- (CGRect)bounds {
+    return CGPathGetBoundingBox(_path);
+}
+
+- (void)applyTransform:(CGAffineTransform)transform {
+    CGMutablePathRef mutablePath = CGPathCreateMutable();
+    CGPathAddPath(mutablePath, &transform, _path);
+    self.CGPath = mutablePath;
+    CGPathRelease(mutablePath);
+}
+
+@end
+
+#endif

+ 44 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIColor.h

@@ -0,0 +1,44 @@
+//
+//  UIColor.h
+//  Lottie
+//
+//  Created by Oleksii Pavlovskyi on 2/2/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+@interface UIColor : NSObject <NSCopying>
+
++ (UIColor *)colorWithWhite:(CGFloat)white alpha:(CGFloat)alpha;
++ (UIColor *)colorWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha;
++ (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
++ (UIColor *)colorWithCGColor:(CGColorRef)cgColor;
+
++ (UIColor *)blackColor;     
++ (UIColor *)darkGrayColor;  
++ (UIColor *)lightGrayColor; 
++ (UIColor *)whiteColor;     
++ (UIColor *)grayColor;      
++ (UIColor *)redColor;       
++ (UIColor *)greenColor;     
++ (UIColor *)blueColor;      
++ (UIColor *)cyanColor;      
++ (UIColor *)yellowColor;    
++ (UIColor *)magentaColor;   
++ (UIColor *)orangeColor;    
++ (UIColor *)purpleColor;    
++ (UIColor *)brownColor;     
++ (UIColor *)clearColor;
+
+- (UIColor *)colorWithAlphaComponent:(CGFloat)alpha;
+
+@property (nonatomic, readonly) CGColorRef CGColor;
+
+@end
+
+#endif

+ 158 - 0
Pods/lottie-ios/lottie-ios/Classes/MacCompatibility/UIColor.m

@@ -0,0 +1,158 @@
+//
+//  UIColor.m
+//  Lottie
+//
+//  Created by Oleksii Pavlovskyi on 2/2/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import "UIColor.h"
+#import <AppKit/AppKit.h>
+
+#define StaticColor(staticColor) \
+static UIColor *color = nil; \
+static dispatch_once_t onceToken; \
+dispatch_once(&onceToken, ^{ \
+    color = NSColor.staticColor.UIColor; \
+}); \
+return color; \
+
+@interface UIColor ()
+
+@property (nonatomic, strong) NSColor *color;
+
+- (instancetype)initWithNSColor:(NSColor *)color;
+
+@end
+
+@interface NSColor (UIColor)
+
+@property (nonatomic, readonly) UIColor *UIColor;
+
+@end
+
+@implementation UIColor
+
+- (instancetype)initWithNSColor:(NSColor *)color {
+    self = [super init];
+    if (self) {
+        self.color = color;
+    }
+    return self;
+}
+
++ (UIColor *)colorWithNSColor:(NSColor *)color {
+    return [[self alloc] initWithNSColor:color];
+}
+
++ (UIColor *)colorWithWhite:(CGFloat)white alpha:(CGFloat)alpha {
+    return [[NSColor colorWithWhite:white alpha:alpha] UIColor];
+}
+
++ (UIColor *)colorWithHue:(CGFloat)hue
+               saturation:(CGFloat)saturation
+               brightness:(CGFloat)brightness
+                    alpha:(CGFloat)alpha {
+    return [[NSColor colorWithHue:hue
+                       saturation:saturation
+                       brightness:brightness
+                            alpha:alpha] UIColor];
+}
+
++ (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+    return [[NSColor colorWithRed:red
+                            green:green
+                             blue:blue
+                            alpha:alpha] UIColor];
+}
+
++ (UIColor *)colorWithCGColor:(CGColorRef)cgColor {
+    return [[NSColor colorWithCGColor:cgColor] UIColor];
+}
+
++ (UIColor *)blackColor {
+    StaticColor(blackColor)
+}
+
++ (UIColor *)darkGrayColor {
+    StaticColor(darkGrayColor)
+}
+
++ (UIColor *)lightGrayColor {
+    StaticColor(lightGrayColor)
+}
+
++ (UIColor *)whiteColor {
+    StaticColor(whiteColor)
+}
+
++ (UIColor *)grayColor {
+    StaticColor(grayColor)
+}
+
++ (UIColor *)redColor {
+    StaticColor(redColor)
+}
+
++ (UIColor *)greenColor {
+    StaticColor(greenColor)
+}
+
++ (UIColor *)blueColor {
+    StaticColor(blueColor)
+}
+
++ (UIColor *)cyanColor {
+    StaticColor(cyanColor)
+}
+
++ (UIColor *)yellowColor {
+    StaticColor(yellowColor)
+}
+
++ (UIColor *)magentaColor {
+    StaticColor(magentaColor)
+}
+
++ (UIColor *)orangeColor {
+    StaticColor(orangeColor)
+}
+
++ (UIColor *)purpleColor {
+    StaticColor(purpleColor)
+}
+
++ (UIColor *)brownColor {
+    StaticColor(brownColor)
+}
+
++ (UIColor *)clearColor {
+    StaticColor(clearColor)
+}
+
+- (CGColorRef)CGColor {
+    return self.color.CGColor;
+}
+
+- (UIColor *)colorWithAlphaComponent:(CGFloat)alpha {
+    return [self.color colorWithAlphaComponent:alpha].UIColor;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    return [[self.color copyWithZone:zone] UIColor];
+}
+
+@end
+
+@implementation NSColor (UIColor)
+
+- (UIColor *)UIColor {
+    return [UIColor colorWithNSColor:self];
+}
+
+@end
+
+#endif

+ 38 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTAsset.h

@@ -0,0 +1,38 @@
+//
+//  LOTAsset.h
+//  Pods
+//
+//  Created by Brandon Withrow on 2/16/17.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class LOTLayerGroup;
+@class LOTLayer;
+@class LOTAssetGroup;
+
+@interface LOTAsset : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary
+              withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+             withAssetBundle:(NSBundle *_Nonnull)bundle
+               withFramerate:(NSNumber *)framerate;
+
+@property (nonatomic, readonly, nullable) NSString *referenceID;
+@property (nonatomic, readonly, nullable) NSNumber *assetWidth;
+@property (nonatomic, readonly, nullable) NSNumber *assetHeight;
+
+@property (nonatomic, readonly, nullable) NSString *imageName;
+@property (nonatomic, readonly, nullable) NSString *imageDirectory;
+
+@property (nonatomic, readonly, nullable) LOTLayerGroup *layerGroup;
+
+@property (nonatomic, readwrite) NSString *rootDirectory;
+@property (nonatomic, readonly) NSBundle *assetBundle;
+@end
+
+NS_ASSUME_NONNULL_END

+ 59 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTAsset.m

@@ -0,0 +1,59 @@
+//
+//  LOTAsset.m
+//  Pods
+//
+//  Created by Brandon Withrow on 2/16/17.
+//
+//
+
+#import "LOTAsset.h"
+#import "LOTLayer.h"
+#import "LOTLayerGroup.h"
+#import "LOTAssetGroup.h"
+
+@implementation LOTAsset
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary
+              withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+             withAssetBundle:(NSBundle *_Nonnull)bundle
+               withFramerate:(NSNumber *)framerate {
+  self = [super init];
+  if (self) {
+    _assetBundle = bundle;
+    [self _mapFromJSON:jsonDictionary
+        withAssetGroup:assetGroup
+     withFramerate:framerate];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary
+      withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+       withFramerate:(NSNumber *)framerate {
+  _referenceID = [jsonDictionary[@"id"] copy];
+  
+  if (jsonDictionary[@"w"]) {
+    _assetWidth = [jsonDictionary[@"w"] copy];
+  }
+  
+  if (jsonDictionary[@"h"]) {
+    _assetHeight = [jsonDictionary[@"h"] copy];
+  }
+  
+  if (jsonDictionary[@"u"]) {
+    _imageDirectory = [jsonDictionary[@"u"] copy];
+  }
+  
+  if (jsonDictionary[@"p"]) {
+    _imageName = [jsonDictionary[@"p"] copy];
+  }
+
+  NSArray *layersJSON = jsonDictionary[@"layers"];
+  if (layersJSON) {
+    _layerGroup = [[LOTLayerGroup alloc] initWithLayerJSON:layersJSON
+                                            withAssetGroup:assetGroup
+                                             withFramerate:framerate];
+  }
+}
+
+@end

+ 28 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTAssetGroup.h

@@ -0,0 +1,28 @@
+//
+//  LOTAssetGroup.h
+//  Pods
+//
+//  Created by Brandon Withrow on 2/17/17.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+@class LOTAsset;
+@class LOTLayerGroup;
+@interface LOTAssetGroup : NSObject
+@property (nonatomic, readwrite) NSString * _Nullable rootDirectory;
+@property (nonatomic, readonly, nullable) NSBundle *assetBundle;
+
+- (instancetype _Nonnull)initWithJSON:(NSArray * _Nonnull)jsonArray
+                      withAssetBundle:(NSBundle *_Nullable)bundle
+                        withFramerate:(NSNumber * _Nonnull)framerate;
+
+- (void)buildAssetNamed:(NSString * _Nonnull)refID withFramerate:(NSNumber * _Nonnull)framerate;
+
+- (void)finalizeInitializationWithFramerate:(NSNumber * _Nonnull)framerate;
+
+- (LOTAsset * _Nullable)assetModelForID:(NSString * _Nonnull)assetID;
+
+@end

+ 70 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTAssetGroup.m

@@ -0,0 +1,70 @@
+//
+//  LOTAssetGroup.m
+//  Pods
+//
+//  Created by Brandon Withrow on 2/17/17.
+//
+//
+
+#import "LOTAssetGroup.h"
+#import "LOTAsset.h"
+
+@implementation LOTAssetGroup {
+  NSMutableDictionary<NSString *, LOTAsset *> *_assetMap;
+  NSDictionary<NSString *, NSDictionary *> *_assetJSONMap;
+}
+
+- (instancetype _Nonnull)initWithJSON:(NSArray * _Nonnull)jsonArray
+                      withAssetBundle:(NSBundle * _Nullable)bundle
+                        withFramerate:(NSNumber * _Nonnull)framerate {
+  self = [super init];
+  if (self) {
+    _assetBundle = bundle;
+    _assetMap = [NSMutableDictionary dictionary];
+    NSMutableDictionary *assetJSONMap = [NSMutableDictionary dictionary];
+    for (NSDictionary<NSString *, NSString *> *assetDictionary in jsonArray) {
+      NSString *referenceID = assetDictionary[@"id"];
+      if (referenceID) {
+        assetJSONMap[referenceID] = assetDictionary;
+      }
+    }
+    _assetJSONMap = assetJSONMap;
+  }
+  return self;
+}
+
+- (void)buildAssetNamed:(NSString *)refID
+          withFramerate:(NSNumber * _Nonnull)framerate {
+  
+  if ([self assetModelForID:refID]) {
+    return;
+  }
+  
+  NSDictionary *assetDictionary = _assetJSONMap[refID];
+  if (assetDictionary) {
+    LOTAsset *asset = [[LOTAsset alloc] initWithJSON:assetDictionary
+                                      withAssetGroup:self
+                                     withAssetBundle:_assetBundle
+                                       withFramerate:framerate];
+    _assetMap[refID] = asset;
+  }
+}
+
+- (void)finalizeInitializationWithFramerate:(NSNumber * _Nonnull)framerate {
+  for (NSString *refID in _assetJSONMap.allKeys) {
+    [self buildAssetNamed:refID withFramerate:framerate];
+  }
+  _assetJSONMap = nil;
+}
+
+- (LOTAsset *)assetModelForID:(NSString *)assetID {
+  return _assetMap[assetID];
+}
+
+- (void)setRootDirectory:(NSString *)rootDirectory {
+    _rootDirectory = rootDirectory;
+    [_assetMap enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, LOTAsset * _Nonnull obj, BOOL * _Nonnull stop) {
+        obj.rootDirectory = rootDirectory;
+    }];
+}
+@end

+ 76 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayer.h

@@ -0,0 +1,76 @@
+//
+//  LOTLayer.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTPlatformCompat.h"
+#import "LOTKeyframe.h"
+
+@class LOTShapeGroup;
+@class LOTMask;
+@class LOTAsset;
+@class LOTAssetGroup;
+
+typedef enum : NSInteger {
+  LOTLayerTypePrecomp,
+  LOTLayerTypeSolid,
+  LOTLayerTypeImage,
+  LOTLayerTypeNull,
+  LOTLayerTypeShape,
+  LOTLayerTypeUnknown
+} LOTLayerType;
+
+typedef enum : NSInteger {
+  LOTMatteTypeNone,
+  LOTMatteTypeAdd,
+  LOTMatteTypeInvert,
+  LOTMatteTypeUnknown
+} LOTMatteType;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTLayer : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary
+              withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+               withFramerate:(NSNumber *)framerate;
+
+@property (nonatomic, readonly) NSString *layerName;
+@property (nonatomic, readonly, nullable) NSString *referenceID;
+@property (nonatomic, readonly) NSNumber *layerID;
+@property (nonatomic, readonly) LOTLayerType layerType;
+@property (nonatomic, readonly, nullable) NSNumber *parentID;
+@property (nonatomic, readonly) NSNumber *startFrame;
+@property (nonatomic, readonly) NSNumber *inFrame;
+@property (nonatomic, readonly) NSNumber *outFrame;
+@property (nonatomic, readonly) NSNumber *timeStretch;
+@property (nonatomic, readonly) CGRect layerBounds;
+
+@property (nonatomic, readonly, nullable) NSArray<LOTShapeGroup *> *shapes;
+@property (nonatomic, readonly, nullable) NSArray<LOTMask *> *masks;
+
+@property (nonatomic, readonly, nullable) NSNumber *layerWidth;
+@property (nonatomic, readonly, nullable) NSNumber *layerHeight;
+@property (nonatomic, readonly, nullable) UIColor *solidColor;
+@property (nonatomic, readonly, nullable) LOTAsset *imageAsset;
+
+@property (nonatomic, readonly) LOTKeyframeGroup *opacity;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *timeRemapping;
+@property (nonatomic, readonly) LOTKeyframeGroup *rotation;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *position;
+
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *positionX;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *positionY;
+
+@property (nonatomic, readonly) LOTKeyframeGroup *anchor;
+@property (nonatomic, readonly) LOTKeyframeGroup *scale;
+
+@property (nonatomic, readonly) LOTMatteType matteType;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 183 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayer.m

@@ -0,0 +1,183 @@
+//
+//  LOTLayer.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTLayer.h"
+#import "LOTAsset.h"
+#import "LOTAssetGroup.h"
+#import "LOTShapeGroup.h"
+#import "LOTComposition.h"
+#import "LOTHelpers.h"
+#import "LOTMask.h"
+#import "LOTHelpers.h"
+
+@implementation LOTLayer
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary
+              withAssetGroup:(LOTAssetGroup *)assetGroup
+               withFramerate:(NSNumber *)framerate {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary
+     withAssetGroup:assetGroup
+     withFramerate:framerate];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary
+      withAssetGroup:(LOTAssetGroup *)assetGroup
+       withFramerate:(NSNumber *)framerate {
+
+  _layerName = [jsonDictionary[@"nm"] copy];
+  _layerID = [jsonDictionary[@"ind"] copy];
+  
+  NSNumber *layerType = jsonDictionary[@"ty"];
+  _layerType = layerType.integerValue;
+  
+  if (jsonDictionary[@"refId"]) {
+    _referenceID = [jsonDictionary[@"refId"] copy];
+  }
+  
+  _parentID = [jsonDictionary[@"parent"] copy];
+  
+  if (jsonDictionary[@"st"]) {
+    _startFrame = [jsonDictionary[@"st"] copy];
+  }
+  _inFrame = [jsonDictionary[@"ip"] copy];
+  _outFrame = [jsonDictionary[@"op"] copy];
+
+  if (jsonDictionary[@"sr"]) {
+    _timeStretch = [jsonDictionary[@"sr"] copy];
+  } else {
+    _timeStretch = @1;
+  }
+
+  if (_layerType == LOTLayerTypePrecomp) {
+    _layerHeight = [jsonDictionary[@"h"] copy];
+    _layerWidth = [jsonDictionary[@"w"] copy];
+    [assetGroup buildAssetNamed:_referenceID withFramerate:framerate];
+  } else if (_layerType == LOTLayerTypeImage) {
+    [assetGroup buildAssetNamed:_referenceID withFramerate:framerate];
+    _imageAsset = [assetGroup assetModelForID:_referenceID];
+    _layerWidth = [_imageAsset.assetWidth copy];
+    _layerHeight = [_imageAsset.assetHeight copy];
+  } else if (_layerType == LOTLayerTypeSolid) {
+    _layerWidth = jsonDictionary[@"sw"];
+    _layerHeight = jsonDictionary[@"sh"];
+    NSString *solidColor = jsonDictionary[@"sc"];
+    _solidColor = [UIColor LOT_colorWithHexString:solidColor];
+  }
+  
+  _layerBounds = CGRectMake(0, 0, _layerWidth.floatValue, _layerHeight.floatValue);
+  
+  NSDictionary *ks = jsonDictionary[@"ks"];
+  
+  NSDictionary *opacity = ks[@"o"];
+  if (opacity) {
+    _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
+    [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+
+  NSDictionary *timeRemap = jsonDictionary[@"tm"];
+  if (timeRemap) {
+    _timeRemapping = [[LOTKeyframeGroup alloc] initWithData:timeRemap];
+    [_timeRemapping remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return inValue * framerate.doubleValue;
+    }];
+  }
+  
+  NSDictionary *rotation = ks[@"r"];
+  if (rotation == nil) {
+    rotation = ks[@"rz"];
+  }
+  if (rotation) {
+    _rotation = [[LOTKeyframeGroup alloc] initWithData:rotation];
+    [_rotation remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_DegreesToRadians(inValue);
+    }];
+  }
+  
+  NSDictionary *position = ks[@"p"];
+  if ([position[@"s"] boolValue]) {
+    // Separate dimensions
+    _positionX = [[LOTKeyframeGroup alloc] initWithData:position[@"x"]];
+    _positionY = [[LOTKeyframeGroup alloc] initWithData:position[@"y"]];
+  } else {
+    _position = [[LOTKeyframeGroup alloc] initWithData:position ];
+  }
+  
+  NSDictionary *anchor = ks[@"a"];
+  if (anchor) {
+    _anchor = [[LOTKeyframeGroup alloc] initWithData:anchor];
+  }
+  
+  NSDictionary *scale = ks[@"s"];
+  if (scale) {
+    _scale = [[LOTKeyframeGroup alloc] initWithData:scale];
+    [_scale remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, -100, 100, -1, 1);
+    }];
+  }
+  
+  _matteType = [jsonDictionary[@"tt"] integerValue];
+  
+  
+  NSMutableArray *masks = [NSMutableArray array];
+  for (NSDictionary *maskJSON in jsonDictionary[@"masksProperties"]) {
+    LOTMask *mask = [[LOTMask alloc] initWithJSON:maskJSON];
+    [masks addObject:mask];
+  }
+  _masks = masks.count ? masks : nil;
+  
+  NSMutableArray *shapes = [NSMutableArray array];
+  for (NSDictionary *shapeJSON in jsonDictionary[@"shapes"]) {
+    id shapeItem = [LOTShapeGroup shapeItemWithJSON:shapeJSON];
+    if (shapeItem) {
+      [shapes addObject:shapeItem];
+    }
+  }
+  _shapes = shapes;
+    
+  NSArray *effects = jsonDictionary[@"ef"];
+  if (effects.count > 0) {
+    
+    NSDictionary *effectNames = @{ @0: @"slider",
+                                   @1: @"angle",
+                                   @2: @"color",
+                                   @3: @"point",
+                                   @4: @"checkbox",
+                                   @5: @"group",
+                                   @6: @"noValue",
+                                   @7: @"dropDown",
+                                   @9: @"customValue",
+                                   @10: @"layerIndex",
+                                   @20: @"tint",
+                                   @21: @"fill" };
+                             
+    for (NSDictionary *effect in effects) {
+      NSNumber *typeNumber = effect[@"ty"];
+      NSString *name = effect[@"nm"];
+      NSString *internalName = effect[@"mn"];
+      NSString *typeString = effectNames[typeNumber];
+      if (typeString) {
+        NSLog(@"%s: Warning: %@ effect not supported: %@ / %@", __PRETTY_FUNCTION__, typeString, internalName, name);
+      }
+    }
+  }
+}
+
+- (NSString *)description {
+    NSMutableString *text = [[super description] mutableCopy];
+    [text appendFormat:@" %@ id: %d pid: %d frames: %d-%d", _layerName, (int)_layerID.integerValue, (int)_parentID.integerValue,
+     (int)_inFrame.integerValue, (int)_outFrame.integerValue];
+    return text;
+}
+
+@end

+ 30 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayerGroup.h

@@ -0,0 +1,30 @@
+//
+//  LOTLayerGroup.h
+//  Pods
+//
+//  Created by Brandon Withrow on 2/16/17.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class LOTLayer;
+@class LOTAssetGroup;
+
+@interface LOTLayerGroup : NSObject
+
+- (instancetype)initWithLayerJSON:(NSArray *)layersJSON
+                   withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+                    withFramerate:(NSNumber *)framerate;
+
+@property (nonatomic, readonly) NSArray <LOTLayer *> *layers;
+
+- (LOTLayer *)layerModelForID:(NSNumber *)layerID;
+- (LOTLayer *)layerForReferenceID:(NSString *)referenceID;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 60 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTLayerGroup.m

@@ -0,0 +1,60 @@
+//
+//  LOTLayerGroup.m
+//  Pods
+//
+//  Created by Brandon Withrow on 2/16/17.
+//
+//
+
+#import "LOTLayerGroup.h"
+#import "LOTLayer.h"
+#import "LOTAssetGroup.h"
+
+@implementation LOTLayerGroup {
+  NSDictionary *_modelMap;
+  NSDictionary *_referenceIDMap;
+}
+
+- (instancetype)initWithLayerJSON:(NSArray *)layersJSON
+                   withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+                    withFramerate:(NSNumber *)framerate {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:layersJSON withAssetGroup:assetGroup withFramerate:framerate];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSArray *)layersJSON
+      withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
+       withFramerate:(NSNumber *)framerate {
+  
+  NSMutableArray *layers = [NSMutableArray array];
+  NSMutableDictionary *modelMap = [NSMutableDictionary dictionary];
+  NSMutableDictionary *referenceMap = [NSMutableDictionary dictionary];
+  
+  for (NSDictionary *layerJSON in layersJSON) {
+    LOTLayer *layer = [[LOTLayer alloc] initWithJSON:layerJSON
+                                      withAssetGroup:assetGroup
+                                       withFramerate:framerate];
+    [layers addObject:layer];
+    modelMap[layer.layerID] = layer;
+    if (layer.referenceID) {
+      referenceMap[layer.referenceID] = layer;
+    }
+  }
+  
+  _referenceIDMap = referenceMap;
+  _modelMap = modelMap;
+  _layers = layers;
+}
+
+- (LOTLayer *)layerModelForID:(NSNumber *)layerID {
+  return _modelMap[layerID];
+}
+
+- (LOTLayer *)layerForReferenceID:(NSString *)referenceID {
+  return _referenceIDMap[referenceID];
+}
+
+@end

+ 29 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTMask.h

@@ -0,0 +1,29 @@
+//
+//  LOTMask.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+typedef enum : NSUInteger {
+  LOTMaskModeAdd,
+  LOTMaskModeSubtract,
+  LOTMaskModeIntersect,
+  LOTMaskModeUnknown
+} LOTMaskMode;
+
+@interface LOTMask : NSObject
+
+- (instancetype _Nonnull)initWithJSON:(NSDictionary * _Nonnull)jsonDictionary;
+
+@property (nonatomic, readonly) BOOL closed;
+@property (nonatomic, readonly) BOOL inverted;
+@property (nonatomic, readonly) LOTMaskMode maskMode;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *maskPath;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *opacity;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *expansion;
+@end

+ 59 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTMask.m

@@ -0,0 +1,59 @@
+//
+//  LOTMask.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTMask.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTMask
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  NSNumber *closed = jsonDictionary[@"cl"];
+  _closed = closed.boolValue;
+  
+  NSNumber *inverted = jsonDictionary[@"inv"];
+  _inverted = inverted.boolValue;
+  
+  NSString *mode = jsonDictionary[@"mode"];
+  if ([mode isEqualToString:@"a"]) {
+    _maskMode = LOTMaskModeAdd;
+  } else if ([mode isEqualToString:@"s"]) {
+    _maskMode = LOTMaskModeSubtract;
+  } else if ([mode isEqualToString:@"i"]) {
+    _maskMode = LOTMaskModeIntersect;
+  } else {
+    _maskMode = LOTMaskModeUnknown;
+  }
+  
+  NSDictionary *maskshape = jsonDictionary[@"pt"];
+  if (maskshape) {
+    _maskPath = [[LOTKeyframeGroup alloc] initWithData:maskshape];
+  }
+  
+  NSDictionary *opacity = jsonDictionary[@"o"];
+  if (opacity) {
+    _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
+    [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  NSDictionary *expansion = jsonDictionary[@"x"];
+  if (expansion) {
+    _expansion = [[LOTKeyframeGroup alloc] initWithData:expansion];
+  }
+}
+
+@end

+ 28 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTModels.h

@@ -0,0 +1,28 @@
+//
+//  LOTModels.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#ifndef LOTModels_h
+#define LOTModels_h
+
+#import "LOTKeyframe.h"
+#import "LOTComposition.h"
+#import "LOTLayer.h"
+#import "LOTMask.h"
+#import "LOTShapeCircle.h"
+#import "LOTShapeFill.h"
+#import "LOTShapeGroup.h"
+#import "LOTShapePath.h"
+#import "LOTShapeRectangle.h"
+#import "LOTShapeStroke.h"
+#import "LOTShapeTransform.h"
+#import "LOTShapeTrimPath.h"
+#import "LOTLayerGroup.h"
+#import "LOTAsset.h"
+#import "LOTShapeGradientFill.h"
+
+#endif /* LOTModels_h */

+ 25 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeCircle.h

@@ -0,0 +1,25 @@
+//
+//  LOTShapeCircle.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTShapeCircle : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) LOTKeyframeGroup *position;
+@property (nonatomic, readonly) LOTKeyframeGroup *size;
+@property (nonatomic, readonly) BOOL reversed;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 40 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeCircle.m

@@ -0,0 +1,40 @@
+//
+//  LOTShapeCircle.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeCircle.h"
+
+@implementation LOTShapeCircle
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *position = jsonDictionary[@"p"];
+  if (position) {
+    _position = [[LOTKeyframeGroup alloc] initWithData:position];
+  }
+  
+  NSDictionary *size= jsonDictionary[@"s"];
+  if (size) {
+    _size = [[LOTKeyframeGroup alloc] initWithData:size];
+  }
+  NSNumber *reversed = jsonDictionary[@"d"];
+  _reversed = (reversed.integerValue == 3);
+}
+
+@end

+ 26 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeFill.h

@@ -0,0 +1,26 @@
+//
+//  LOTShapeFill.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTShapeFill : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) BOOL fillEnabled;
+@property (nonatomic, readonly) LOTKeyframeGroup *color;
+@property (nonatomic, readonly) LOTKeyframeGroup *opacity;
+@property (nonatomic, readonly) BOOL evenOddFillRule;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 52 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeFill.m

@@ -0,0 +1,52 @@
+//
+//  LOTShapeFill.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeFill.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTShapeFill
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *color = jsonDictionary[@"c"];
+  if (color) {
+    _color = [[LOTKeyframeGroup alloc] initWithData:color];
+  }
+  
+  NSDictionary *opacity = jsonDictionary[@"o"];
+  if (opacity) {
+    _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
+    [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  NSNumber *evenOdd = jsonDictionary[@"r"];
+  if (evenOdd.integerValue == 2) {
+    _evenOddFillRule = YES;
+  } else {
+    _evenOddFillRule = NO;
+  }
+  
+  NSNumber *fillEnabled = jsonDictionary[@"fillEnabled"];
+  _fillEnabled = fillEnabled.boolValue;
+}
+
+@end

+ 34 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGradientFill.h

@@ -0,0 +1,34 @@
+//
+//  LOTShapeGradientFill.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/26/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef enum : NSUInteger {
+  LOTGradientTypeLinear,
+  LOTGradientTypeRadial
+} LOTGradientType;
+
+@interface LOTShapeGradientFill : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) NSNumber *numberOfColors;
+@property (nonatomic, readonly) LOTKeyframeGroup *startPoint;
+@property (nonatomic, readonly) LOTKeyframeGroup *endPoint;
+@property (nonatomic, readonly) LOTKeyframeGroup *gradient;
+@property (nonatomic, readonly) LOTKeyframeGroup *opacity;
+@property (nonatomic, readonly) BOOL evenOddFillRule;
+@property (nonatomic, readonly) LOTGradientType type;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 67 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGradientFill.m

@@ -0,0 +1,67 @@
+//
+//  LOTShapeGradientFill.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/26/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTShapeGradientFill.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTShapeGradientFill
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSNumber *type = jsonDictionary[@"t"];
+  
+  if (type.integerValue != 1) {
+    _type = LOTGradientTypeRadial;
+  } else {
+    _type = LOTGradientTypeLinear;
+  }
+  
+  NSDictionary *start = jsonDictionary[@"s"];
+  if (start) {
+    _startPoint = [[LOTKeyframeGroup alloc] initWithData:start];
+  }
+  
+  NSDictionary *end = jsonDictionary[@"e"];
+  if (end) {
+    _endPoint = [[LOTKeyframeGroup alloc] initWithData:end];
+  }
+  
+  NSDictionary *gradient = jsonDictionary[@"g"];
+  if (gradient) {
+    NSDictionary *unwrappedGradient = gradient[@"k"];
+    _numberOfColors = gradient[@"p"];
+    _gradient = [[LOTKeyframeGroup alloc] initWithData:unwrappedGradient];
+  }
+  
+  NSDictionary *opacity = jsonDictionary[@"o"];
+  if (opacity) {
+    _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
+    [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  NSNumber *evenOdd = jsonDictionary[@"r"];
+  if (evenOdd.integerValue == 2) {
+    _evenOddFillRule = YES;
+  } else {
+    _evenOddFillRule = NO;
+  }
+}
+@end

+ 21 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGroup.h

@@ -0,0 +1,21 @@
+//
+//  LOTShape.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+@interface LOTShapeGroup : NSObject
+
+- (instancetype _Nonnull)initWithJSON:(NSDictionary *_Nonnull)jsonDictionary;
+
+@property (nonatomic, readonly, nonnull) NSString *keyname;
+@property (nonatomic, readonly, nonnull) NSArray *items;
+
++ (id _Nullable)shapeItemWithJSON:(NSDictionary * _Nonnull)itemJSON;
+
+@end

+ 102 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeGroup.m

@@ -0,0 +1,102 @@
+//
+//  LOTShape.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeGroup.h"
+#import "LOTShapeFill.h"
+#import "LOTShapePath.h"
+#import "LOTShapeCircle.h"
+#import "LOTShapeStroke.h"
+#import "LOTShapeTransform.h"
+#import "LOTShapeRectangle.h"
+#import "LOTShapeTrimPath.h"
+#import "LOTShapeGradientFill.h"
+#import "LOTShapeStar.h"
+#import "LOTShapeRepeater.h"
+
+@implementation LOTShapeGroup
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSArray *itemsJSON = jsonDictionary[@"it"];
+  NSMutableArray *items = [NSMutableArray array];
+  for (NSDictionary *itemJSON in itemsJSON) {
+    id newItem = [LOTShapeGroup shapeItemWithJSON:itemJSON];
+    if (newItem) {
+      [items addObject:newItem];
+    }
+  }
+  _items = items;
+}
+
++ (id)shapeItemWithJSON:(NSDictionary *)itemJSON {
+  NSString *type = itemJSON[@"ty"];
+  if ([type isEqualToString:@"gr"]) {
+    LOTShapeGroup *group = [[LOTShapeGroup alloc] initWithJSON:itemJSON];
+    return group;
+  } else if ([type isEqualToString:@"st"]) {
+    LOTShapeStroke *stroke = [[LOTShapeStroke alloc] initWithJSON:itemJSON];
+    return stroke;
+  } else if ([type isEqualToString:@"fl"]) {
+    LOTShapeFill *fill = [[LOTShapeFill alloc] initWithJSON:itemJSON];
+    return fill;
+  } else if ([type isEqualToString:@"tr"]) {
+    LOTShapeTransform *transform = [[LOTShapeTransform alloc] initWithJSON:itemJSON];
+    return transform;
+  } else if ([type isEqualToString:@"sh"]) {
+    LOTShapePath *path = [[LOTShapePath alloc] initWithJSON:itemJSON];
+    return path;
+  } else if ([type isEqualToString:@"el"]) {
+    LOTShapeCircle *circle = [[LOTShapeCircle alloc] initWithJSON:itemJSON];
+    return circle;
+  } else if ([type isEqualToString:@"rc"]) {
+    LOTShapeRectangle *rectangle = [[LOTShapeRectangle alloc] initWithJSON:itemJSON];
+    return rectangle;
+  } else if ([type isEqualToString:@"tm"]) {
+    LOTShapeTrimPath *trim = [[LOTShapeTrimPath alloc] initWithJSON:itemJSON];
+    return trim;
+  } else  if ([type isEqualToString:@"gs"]) {
+    NSLog(@"%s: Warning: gradient strokes are not supported", __PRETTY_FUNCTION__);
+  } else  if ([type isEqualToString:@"gf"]) {
+    LOTShapeGradientFill *gradientFill = [[LOTShapeGradientFill alloc] initWithJSON:itemJSON];
+    return gradientFill;
+  } else if ([type isEqualToString:@"sr"]) {
+    LOTShapeStar *star = [[LOTShapeStar alloc] initWithJSON:itemJSON];
+    return star;
+  } else if ([type isEqualToString:@"mm"]) {
+    NSString *name = itemJSON[@"nm"];
+    NSLog(@"%s: Warning: merge shape is not supported. name: %@", __PRETTY_FUNCTION__, name);
+  } else if ([type isEqualToString:@"rp"]) {
+    LOTShapeRepeater *repeater = [[LOTShapeRepeater alloc] initWithJSON:itemJSON];
+    return repeater;
+  } else {
+    NSString *name = itemJSON[@"nm"];
+    NSLog(@"%s: Unsupported shape: %@ name: %@", __PRETTY_FUNCTION__, type, name);
+  }
+  
+  return nil;
+}
+
+- (NSString *)description {
+    NSMutableString *text = [[super description] mutableCopy];
+    [text appendFormat:@" items: %@", self.items];
+    return text;
+}
+
+@end

+ 21 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapePath.h

@@ -0,0 +1,21 @@
+//
+//  LOTShapePath.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+@interface LOTShapePath : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) BOOL closed;
+@property (nonatomic, readonly) NSNumber *index;
+@property (nonatomic, readonly) LOTKeyframeGroup *shapePath;
+
+@end

+ 35 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapePath.m

@@ -0,0 +1,35 @@
+//
+//  LOTShapePath.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapePath.h"
+
+@implementation LOTShapePath
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  _index = jsonDictionary[@"ind"];
+  _closed = [jsonDictionary[@"closed"] boolValue];
+  NSDictionary *shape = jsonDictionary[@"ks"];
+  if (shape) {
+    _shapePath = [[LOTKeyframeGroup alloc] initWithData:shape];
+  }
+}
+
+@end

+ 22 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRectangle.h

@@ -0,0 +1,22 @@
+//
+//  LOTShapeRectangle.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+@interface LOTShapeRectangle : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) LOTKeyframeGroup *position;
+@property (nonatomic, readonly) LOTKeyframeGroup *size;
+@property (nonatomic, readonly) LOTKeyframeGroup *cornerRadius;
+@property (nonatomic, readonly) BOOL reversed;
+
+@end

+ 45 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRectangle.m

@@ -0,0 +1,45 @@
+//
+//  LOTShapeRectangle.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeRectangle.h"
+
+@implementation LOTShapeRectangle
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *position = jsonDictionary[@"p"];
+  if (position) {
+    _position = [[LOTKeyframeGroup alloc] initWithData:position];
+  }
+  
+  NSDictionary *cornerRadius = jsonDictionary[@"r"];
+  if (cornerRadius) {
+    _cornerRadius = [[LOTKeyframeGroup alloc] initWithData:cornerRadius];
+  }
+  
+  NSDictionary *size = jsonDictionary[@"s"];
+  if (size) {
+    _size = [[LOTKeyframeGroup alloc] initWithData:size];
+  }
+  NSNumber *reversed = jsonDictionary[@"d"];
+  _reversed = (reversed.integerValue == 3);
+}
+
+@end

+ 30 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRepeater.h

@@ -0,0 +1,30 @@
+//
+//  LOTShapeRepeater.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/28/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LOTShapeRepeater : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *copies;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *offset;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *anchorPoint;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *scale;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *position;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *rotation;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *startOpacity;
+@property (nonatomic, readonly, nullable) LOTKeyframeGroup *endOpacity;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 83 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeRepeater.m

@@ -0,0 +1,83 @@
+//
+//  LOTShapeRepeater.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/28/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTShapeRepeater.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTShapeRepeater
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary  {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *copies = jsonDictionary[@"c"];
+  if (copies) {
+    _copies = [[LOTKeyframeGroup alloc] initWithData:copies];
+  }
+  
+  NSDictionary *offset = jsonDictionary[@"o"];
+  if (offset) {
+    _offset = [[LOTKeyframeGroup alloc] initWithData:offset];
+  }
+  
+  NSDictionary *transform = jsonDictionary[@"tr"];
+  
+  NSDictionary *rotation = transform[@"r"];
+  if (rotation) {
+    _rotation = [[LOTKeyframeGroup alloc] initWithData:rotation];
+    [_rotation remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_DegreesToRadians(inValue);
+    }];
+  }
+  
+  NSDictionary *startOpacity = transform[@"so"];
+  if (startOpacity) {
+    _startOpacity = [[LOTKeyframeGroup alloc] initWithData:startOpacity];
+    [_startOpacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  NSDictionary *endOpacity = transform[@"eo"];
+  if (endOpacity) {
+    _endOpacity = [[LOTKeyframeGroup alloc] initWithData:endOpacity];
+    [_endOpacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  NSDictionary *anchorPoint = transform[@"a"];
+  if (anchorPoint) {
+    _anchorPoint = [[LOTKeyframeGroup alloc] initWithData:anchorPoint];
+  }
+  
+  NSDictionary *position = transform[@"p"];
+  if (position) {
+    _position = [[LOTKeyframeGroup alloc] initWithData:position];
+  }
+  
+  NSDictionary *scale = transform[@"s"];
+  if (scale) {
+    _scale = [[LOTKeyframeGroup alloc] initWithData:scale];
+    [_scale remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, -100, 100, -1, 1);
+    }];
+  }
+}
+
+@end

+ 35 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStar.h

@@ -0,0 +1,35 @@
+//
+//  LOTShapeStar.h
+//  Lottie
+//
+//  Created by brandon_withrow on 7/27/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+typedef enum : NSUInteger {
+  LOTPolystarShapeNone,
+  LOTPolystarShapeStar,
+  LOTPolystarShapePolygon
+} LOTPolystarShape;
+
+@interface LOTShapeStar : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) LOTKeyframeGroup *outerRadius;
+@property (nonatomic, readonly) LOTKeyframeGroup *outerRoundness;
+
+@property (nonatomic, readonly) LOTKeyframeGroup *innerRadius;
+@property (nonatomic, readonly) LOTKeyframeGroup *innerRoundness;
+
+@property (nonatomic, readonly) LOTKeyframeGroup *position;
+@property (nonatomic, readonly) LOTKeyframeGroup *numberOfPoints;
+@property (nonatomic, readonly) LOTKeyframeGroup *rotation;
+
+@property (nonatomic, readonly) LOTPolystarShape type;
+
+@end

+ 66 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStar.m

@@ -0,0 +1,66 @@
+//
+//  LOTShapeStar.m
+//  Lottie
+//
+//  Created by brandon_withrow on 7/27/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTShapeStar.h"
+
+@implementation LOTShapeStar
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *outerRadius = jsonDictionary[@"or"];
+  if (outerRadius) {
+    _outerRadius = [[LOTKeyframeGroup alloc] initWithData:outerRadius];
+  }
+  
+  NSDictionary *outerRoundness = jsonDictionary[@"os"];
+  if (outerRoundness) {
+    _outerRoundness = [[LOTKeyframeGroup alloc] initWithData:outerRoundness];
+  }
+  
+  NSDictionary *innerRadius = jsonDictionary[@"ir"];
+  if (innerRadius) {
+    _innerRadius = [[LOTKeyframeGroup alloc] initWithData:innerRadius];
+  }
+  
+  NSDictionary *innerRoundness = jsonDictionary[@"is"];
+  if (innerRoundness) {
+    _innerRoundness = [[LOTKeyframeGroup alloc] initWithData:innerRoundness];
+  }
+  
+  NSDictionary *position = jsonDictionary[@"p"];
+  if (position) {
+    _position = [[LOTKeyframeGroup alloc] initWithData:position];
+  }
+  
+  NSDictionary *numberOfPoints = jsonDictionary[@"pt"];
+  if (numberOfPoints) {
+    _numberOfPoints = [[LOTKeyframeGroup alloc] initWithData:numberOfPoints];
+  }
+  
+  NSDictionary *rotation = jsonDictionary[@"r"];
+  if (rotation) {
+    _rotation = [[LOTKeyframeGroup alloc] initWithData:rotation];
+  }
+  
+  NSNumber *type = jsonDictionary[@"sy"];
+  _type = type.integerValue;
+}
+
+@end

+ 39 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStroke.h

@@ -0,0 +1,39 @@
+//
+//  LOTShapeStroke.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+typedef enum : NSUInteger {
+  LOTLineCapTypeButt,
+  LOTLineCapTypeRound,
+  LOTLineCapTypeUnknown
+} LOTLineCapType;
+
+typedef enum : NSUInteger {
+  LOTLineJoinTypeMiter,
+  LOTLineJoinTypeRound,
+  LOTLineJoinTypeBevel
+} LOTLineJoinType;
+
+@interface LOTShapeStroke : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) BOOL fillEnabled;
+@property (nonatomic, readonly) LOTKeyframeGroup *color;
+@property (nonatomic, readonly) LOTKeyframeGroup *opacity;
+@property (nonatomic, readonly) LOTKeyframeGroup *width;
+@property (nonatomic, readonly) LOTKeyframeGroup *dashOffset;
+@property (nonatomic, readonly) LOTLineCapType capType;
+@property (nonatomic, readonly) LOTLineJoinType joinType;
+
+@property (nonatomic, readonly) NSArray *lineDashPattern;
+
+@end

+ 73 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeStroke.m

@@ -0,0 +1,73 @@
+//
+//  LOTShapeStroke.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeStroke.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTShapeStroke
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *color = jsonDictionary[@"c"];
+  if (color) {
+    _color = [[LOTKeyframeGroup alloc] initWithData:color];
+  }
+  
+  NSDictionary *width = jsonDictionary[@"w"];
+  if (width) {
+    _width = [[LOTKeyframeGroup alloc] initWithData:width];
+  }
+  
+  NSDictionary *opacity = jsonDictionary[@"o"];
+  if (opacity) {
+    _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
+    [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  _capType = [jsonDictionary[@"lc"] integerValue] - 1;
+  _joinType = [jsonDictionary[@"lj"] integerValue] - 1;
+  
+  NSNumber *fillEnabled = jsonDictionary[@"fillEnabled"];
+  _fillEnabled = fillEnabled.boolValue;
+  
+  NSDictionary *dashOffset = nil;
+  NSArray *dashes = jsonDictionary[@"d"];
+  if (dashes) {
+    NSMutableArray *dashPattern = [NSMutableArray array];
+    for (NSDictionary *dash in dashes) {
+      if ([dash[@"n"] isEqualToString:@"o"]) {
+        dashOffset = dash[@"v"];
+        continue;
+      }
+      // TODO DASH PATTERNS
+      NSDictionary *value = dash[@"v"];
+      LOTKeyframeGroup *keyframeGroup = [[LOTKeyframeGroup alloc] initWithData:value];
+      [dashPattern addObject:keyframeGroup];
+    }
+    _lineDashPattern = dashPattern;
+  }
+  if (dashOffset) {
+    _dashOffset = [[LOTKeyframeGroup alloc] initWithData:dashOffset];
+  }
+}
+
+@end

+ 25 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTransform.h

@@ -0,0 +1,25 @@
+//
+//  LOTShapeTransform.h
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+#import <QuartzCore/QuartzCore.h>
+#import "LOTKeyframe.h"
+
+@interface LOTShapeTransform : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) LOTKeyframeGroup *position;
+@property (nonatomic, readonly) LOTKeyframeGroup *anchor;
+@property (nonatomic, readonly) LOTKeyframeGroup *scale;
+@property (nonatomic, readonly) LOTKeyframeGroup *rotation;
+@property (nonatomic, readonly) LOTKeyframeGroup *opacity;
+
+@end

+ 78 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTransform.m

@@ -0,0 +1,78 @@
+//
+//  LOTShapeTransform.m
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/15/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeTransform.h"
+#import "LOTHelpers.h"
+
+@implementation LOTShapeTransform
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *position = jsonDictionary[@"p"];
+  if (position) {
+    _position = [[LOTKeyframeGroup alloc] initWithData:position];
+  }
+  
+  NSDictionary *anchor = jsonDictionary[@"a"];
+  if (anchor) {
+    _anchor = [[LOTKeyframeGroup alloc] initWithData:anchor];
+  }
+  
+  NSDictionary *scale = jsonDictionary[@"s"];
+  if (scale) {
+    _scale = [[LOTKeyframeGroup alloc] initWithData:scale];
+    [_scale remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, -100, 100, -1, 1);
+    }];
+  }
+  
+  NSDictionary *rotation = jsonDictionary[@"r"];
+  if (rotation) {
+    _rotation = [[LOTKeyframeGroup alloc] initWithData:rotation];
+    [_rotation remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_DegreesToRadians(inValue);
+    }];
+  }
+  
+  NSDictionary *opacity = jsonDictionary[@"o"];
+  if (opacity) {
+    _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
+    [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
+      return LOT_RemapValue(inValue, 0, 100, 0, 1);
+    }];
+  }
+  
+  NSString *name = jsonDictionary[@"nm"];
+  
+  NSDictionary *skew = jsonDictionary[@"sk"];
+  BOOL hasSkew = (skew && [skew[@"k"] isEqual:@0] == NO);
+  NSDictionary *skewAxis = jsonDictionary[@"sa"];
+  BOOL hasSkewAxis = (skewAxis && [skewAxis[@"k"] isEqual:@0] == NO);
+  
+  if (hasSkew || hasSkewAxis) {
+    NSLog(@"%s: Warning: skew is not supported: %@", __PRETTY_FUNCTION__, name);
+  }
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"LOTShapeTransform \"Position: %@ Anchor: %@ Scale: %@ Rotation: %@ Opacity: %@\"", _position.description, _anchor.description, _scale.description, _rotation.description, _opacity.description];
+}
+
+@end

+ 21 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTrimPath.h

@@ -0,0 +1,21 @@
+//
+//  LOTShapeTrimPath.h
+//  LottieAnimator
+//
+//  Created by brandon_withrow on 7/26/16.
+//  Copyright © 2016 Brandon Withrow. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "LOTKeyframe.h"
+
+@interface LOTShapeTrimPath : NSObject
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
+
+@property (nonatomic, readonly) NSString *keyname;
+@property (nonatomic, readonly) LOTKeyframeGroup *start;
+@property (nonatomic, readonly) LOTKeyframeGroup *end;
+@property (nonatomic, readonly) LOTKeyframeGroup *offset;
+
+@end

+ 43 - 0
Pods/lottie-ios/lottie-ios/Classes/Models/LOTShapeTrimPath.m

@@ -0,0 +1,43 @@
+//
+//  LOTShapeTrimPath.m
+//  LottieAnimator
+//
+//  Created by brandon_withrow on 7/26/16.
+//  Copyright © 2016 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTShapeTrimPath.h"
+
+@implementation LOTShapeTrimPath
+
+- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
+  self = [super init];
+  if (self) {
+    [self _mapFromJSON:jsonDictionary];
+  }
+  return self;
+}
+
+- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
+  
+  if (jsonDictionary[@"nm"] ) {
+    _keyname = [jsonDictionary[@"nm"] copy];
+  }
+  
+  NSDictionary *start = jsonDictionary[@"s"];
+  if (start) {
+    _start = [[LOTKeyframeGroup alloc] initWithData:start];
+  }
+  
+  NSDictionary *end = jsonDictionary[@"e"];
+  if (end) {
+    _end = [[LOTKeyframeGroup alloc] initWithData:end];
+  }
+  
+  NSDictionary *offset = jsonDictionary[@"o"];
+  if (offset) {
+    _offset = [[LOTKeyframeGroup alloc] initWithData:offset];
+  }
+}
+
+@end

+ 140 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimatedControl.m

@@ -0,0 +1,140 @@
+//
+//  LOTAnimatedControl.m
+//  Lottie
+//
+//  Created by brandon_withrow on 8/25/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTAnimatedControl.h"
+#import "LOTAnimationView_Internal.h"
+
+@implementation LOTAnimatedControl {
+  UIControlState  _priorState;
+  NSMutableDictionary *_layerMap;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    [self _commonInit];
+  }
+  return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+  self = [super initWithCoder:aDecoder];
+  if (self) {
+    [self _commonInit];
+  }
+  return self;
+}
+
+- (void)_commonInit {
+  _animationView = [[LOTAnimationView alloc] init];
+  _animationView.contentMode = UIViewContentModeScaleAspectFit;
+  _animationView.userInteractionEnabled = NO;
+  [self addSubview:_animationView];
+  _layerMap = [NSMutableDictionary dictionary];
+}
+
+- (LOTComposition *)animationComp {
+  return _animationView.sceneModel;
+}
+
+- (void)setAnimationComp:(LOTComposition *)animationComp {
+  [_animationView setSceneModel:animationComp];
+  [self checkStateChangedAndUpdate:YES];
+}
+
+- (void)setLayerName:(NSString * _Nonnull)layerName forState:(UIControlState)state {
+  _layerMap[@(state)] = layerName;
+  [self checkStateChangedAndUpdate:YES];
+}
+
+#pragma mark - Setter Overrides
+
+- (void)setEnabled:(BOOL)enabled {
+  _priorState = self.state;
+  [super setEnabled:enabled];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (void)setSelected:(BOOL)selected {
+  _priorState = self.state;
+  [super setSelected:selected];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (void)setHighlighted:(BOOL)highlighted {
+  _priorState = self.state;
+  [super setHighlighted:highlighted];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+  _priorState = self.state;
+  [super touchesBegan:touches withEvent:event];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+  _priorState = self.state;
+  [super touchesMoved:touches withEvent:event];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+  _priorState = self.state;
+  [super touchesEnded:touches withEvent:event];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+  _priorState = self.state;
+  [super touchesCancelled:touches withEvent:event];
+  [self checkStateChangedAndUpdate:NO];
+}
+
+- (CGSize)intrinsicContentSize {
+  return _animationView.intrinsicContentSize;
+}
+
+- (void)layoutSubviews {
+  [super layoutSubviews];
+  _animationView.frame = self.bounds;
+}
+
+- (UIAccessibilityTraits)accessibilityTraits {
+  return UIAccessibilityTraitButton;
+}
+
+- (BOOL)isAccessibilityElement
+{
+  return YES;
+}
+
+#pragma mark - Private interface implementation
+
+- (void)checkStateChangedAndUpdate:(BOOL)forceUpdate {
+  if (self.state == _priorState && !forceUpdate) {
+    return;
+  }
+  _priorState = self.state;
+  
+  NSString *name = _layerMap[@(self.state)];
+  if (!name) {
+    return;
+  }
+  CALayer *layer = [_animationView layerForKey:name];
+  if (!layer) {
+    return;
+  }
+  
+  for (CALayer *child in [_animationView compositionLayers]) {
+    child.hidden = YES;
+  }
+  layer.hidden = NO;
+}
+
+@end

+ 199 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimatedSwitch.m

@@ -0,0 +1,199 @@
+//
+//  LOTAnimatedSwitch.m
+//  Lottie
+//
+//  Created by brandon_withrow on 8/25/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTAnimatedSwitch.h"
+#import "LOTAnimationView.h"
+#import "CGGeometry+LOTAdditions.h"
+
+@implementation LOTAnimatedSwitch {
+  CGFloat _onStartProgress;
+  CGFloat _onEndProgress;
+  CGFloat _offStartProgress;
+  CGFloat _offEndProgress;
+  CGPoint _touchTrackingStart;
+  BOOL _on;
+  BOOL _suppressToggle;
+  BOOL _toggleToState;
+}
+
+/// Convenience method to initialize a control from the Main Bundle by name
++ (instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName {
+  return [self switchNamed:toggleName inBundle:[NSBundle mainBundle]];
+}
+
+/// Convenience method to initialize a control from the specified bundle by name
++ (instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName inBundle:(NSBundle * _Nonnull)bundle {
+  LOTComposition *composition = [LOTComposition animationNamed:toggleName inBundle:bundle];
+  LOTAnimatedSwitch *animatedControl = [[self alloc] initWithFrame:CGRectZero];
+  if (composition) {
+    [animatedControl setAnimationComp:composition];
+    animatedControl.bounds = composition.compBounds;
+  }
+  return animatedControl;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    self.accessibilityHint = NSLocalizedString(@"Double tap to toggle setting.", @"Double tap to toggle setting.");
+    _onStartProgress = 0;
+    _onEndProgress = 1;
+    _offStartProgress = 1;
+    _offEndProgress = 0;
+    _on = NO;
+    [self addTarget:self action:@selector(_toggle) forControlEvents:UIControlEventTouchUpInside];
+  }
+  return self;
+}
+
+- (void)setAnimationComp:(LOTComposition *)animationComp {
+  [super setAnimationComp:animationComp];
+  [self setOn:_on animated:NO];
+}
+
+#pragma mark - External Methods
+
+- (void)setProgressRangeForOnState:(CGFloat)fromProgress toProgress:(CGFloat)toProgress {
+  _onStartProgress = fromProgress;
+  _onEndProgress = toProgress;
+  [self setOn:_on animated:NO];
+}
+
+- (void)setProgressRangeForOffState:(CGFloat)fromProgress toProgress:(CGFloat)toProgress {
+  _offStartProgress = fromProgress;
+  _offEndProgress = toProgress;
+  [self setOn:_on animated:NO];
+}
+
+- (void)setOn:(BOOL)on {
+  [self setOn:on animated:NO];
+}
+
+- (void)setOn:(BOOL)on animated:(BOOL)animated {
+  _on = on;
+  
+  CGFloat startProgress = on ? _onStartProgress : _offStartProgress;
+  CGFloat endProgress = on ? _onEndProgress : _offEndProgress;
+  CGFloat finalProgress = endProgress;
+  if (self.animationView.animationProgress < MIN(startProgress, endProgress) ||
+      self.animationView.animationProgress > MAX(startProgress, endProgress)) {
+    if (self.animationView.animationProgress != (!_on ? _onEndProgress : _offEndProgress)) {
+      // Current progress is in the wrong timeline. Switch.
+      endProgress = on ? _offStartProgress : _onStartProgress;
+      startProgress = on ? _offEndProgress : _onEndProgress;
+    }
+  }
+  
+  if (finalProgress == self.animationView.animationProgress) {
+    return;
+  }
+  
+  if (animated) {
+    [self.animationView pause];
+    [self.animationView playFromProgress:startProgress toProgress:endProgress withCompletion:^(BOOL animationFinished) {
+      if (animationFinished) {
+        self.animationView.animationProgress = finalProgress;
+      }
+    }];
+  } else {
+    self.animationView.animationProgress = endProgress;
+  }
+}
+
+- (NSString *)accessibilityValue {
+  return self.isOn ? NSLocalizedString(@"On", @"On")  : NSLocalizedString(@"Off", @"Off");
+}
+
+#pragma mark - Internal Methods
+
+- (void)_toggle {
+  if (!_suppressToggle) {
+    [self _toggleAndSendActions];
+  }
+}
+
+- (void)_toggleAndSendActions {
+  if (self.isEnabled) {
+    #ifndef TARGET_OS_TV
+    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
+      UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
+      [generator impactOccurred];
+    }
+    #endif
+    [self setOn:!_on animated:YES];
+    [self sendActionsForControlEvents:UIControlEventValueChanged];
+  }
+}
+
+- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
+  [super beginTrackingWithTouch:touch withEvent:event];
+  _suppressToggle = NO;
+  _touchTrackingStart = [touch locationInView:self];
+  return YES;
+}
+
+- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
+  BOOL superContinue = [super continueTrackingWithTouch:touch withEvent:event];
+  if (!_interactiveGesture) {
+    return superContinue;
+  }
+  CGPoint location = [touch locationInView:self];
+  CGFloat diff = location.x - _touchTrackingStart.x;
+  if (LOT_PointDistanceFromPoint(_touchTrackingStart, location) > self.bounds.size.width * 0.25) {
+    // The touch has moved enough to register as its own gesture. Suppress the touch up toggle.
+    _suppressToggle = YES;
+  }
+#ifdef __IPHONE_11_0
+  // Xcode 9+
+  if (@available(iOS 9.0, *)) {
+#else
+    // Xcode 8-
+    if ([UIView respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute:)]) {
+#endif
+      if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
+          diff = diff * -1;
+      }
+  }
+  if (_on) {
+    diff = diff * -1;
+    if (diff <= 0) {
+      self.animationView.animationProgress = _onEndProgress;
+      _toggleToState = YES;
+    } else {
+      diff = MAX(MIN(self.bounds.size.width, diff), 0);
+      self.animationView.animationProgress = LOT_RemapValue(diff, 0, self.bounds.size.width, _offStartProgress, _offEndProgress);
+      _toggleToState = (diff / self.bounds.size.width) > 0.5 ? NO : YES;
+    }
+  } else {
+    if (diff <= 0) {
+      self.animationView.animationProgress = _offEndProgress;
+      _toggleToState = NO;
+    } else {
+      diff = MAX(MIN(self.bounds.size.width, diff), 0);
+      self.animationView.animationProgress = LOT_RemapValue(diff, 0, self.bounds.size.width, _onStartProgress, _onEndProgress);
+      _toggleToState = (diff / self.bounds.size.width) > 0.5 ? YES : NO;
+    }
+  }
+  return YES;
+}
+
+- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
+  [super endTrackingWithTouch:touch withEvent:event];
+  if (!_interactiveGesture) {
+    return;
+  }
+  if (_suppressToggle) {
+    if (_toggleToState != _on) {
+      [self _toggleAndSendActions];
+    } else {
+      [self setOn:_toggleToState animated:YES];
+    }
+  }
+}
+
+@end

+ 73 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationCache.m

@@ -0,0 +1,73 @@
+//
+//  LOTAnimationCache.m
+//  Lottie
+//
+//  Created by Brandon Withrow on 1/9/17.
+//  Copyright © 2017 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTAnimationCache.h"
+
+const NSInteger kLOTCacheSize = 50;
+
+@implementation LOTAnimationCache {
+  NSMutableDictionary *animationsCache_;
+  NSMutableArray *lruOrderArray_;
+}
+
++ (instancetype)sharedCache {
+  static LOTAnimationCache *sharedCache = nil;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    sharedCache = [[self alloc] init];
+  });
+  return sharedCache;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    animationsCache_ = [[NSMutableDictionary alloc] init];
+    lruOrderArray_ = [[NSMutableArray alloc] init];
+  }
+  return self;
+}
+
+- (void)addAnimation:(LOTComposition *)animation forKey:(NSString *)key {
+  if (lruOrderArray_.count >= kLOTCacheSize) {
+    NSString *oldKey = lruOrderArray_[0];
+    [animationsCache_ removeObjectForKey:oldKey];
+    [lruOrderArray_ removeObject:oldKey];
+  }
+  [lruOrderArray_ removeObject:key];
+  [lruOrderArray_ addObject:key];
+  [animationsCache_ setObject:animation forKey:key];
+}
+
+- (LOTComposition *)animationForKey:(NSString *)key {
+  if (!key) {
+    return nil;
+  }
+  LOTComposition *animation = [animationsCache_ objectForKey:key];
+  [lruOrderArray_ removeObject:key];
+  [lruOrderArray_ addObject:key];
+  return animation;
+}
+
+- (void)clearCache {
+  [animationsCache_ removeAllObjects];
+  [lruOrderArray_ removeAllObjects];
+}
+
+- (void)removeAnimationForKey:(NSString *)key {
+  [lruOrderArray_ removeObject:key];
+  [animationsCache_ removeObjectForKey:key];
+}
+
+- (void)disableCaching {
+  [self clearCache];
+  animationsCache_ = nil;
+  lruOrderArray_ = nil;
+}
+
+@end

+ 127 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationTransitionController.m

@@ -0,0 +1,127 @@
+//
+//  LOTAnimationTransitionController.m
+//  Lottie
+//
+//  Created by Brandon Withrow on 1/18/17.
+//  Copyright © 2017 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTAnimationTransitionController.h"
+#import "LOTAnimationView.h"
+
+@implementation LOTAnimationTransitionController {
+  LOTAnimationView *transitionAnimationView_;
+  NSString *fromLayerName_;
+  NSString *toLayerName_;
+  NSBundle *inBundle_;
+  BOOL _applyTransform;
+}
+
+- (nonnull instancetype)initWithAnimationNamed:(nonnull NSString *)animation
+                                fromLayerNamed:(nullable NSString *)fromLayer
+                                  toLayerNamed:(nullable NSString *)toLayer
+                       applyAnimationTransform:(BOOL)applyAnimationTransform {
+  
+  return [self initWithAnimationNamed:animation
+                       fromLayerNamed:fromLayer
+                         toLayerNamed:toLayer
+              applyAnimationTransform:applyAnimationTransform
+                             inBundle:[NSBundle mainBundle]];
+}
+
+- (instancetype)initWithAnimationNamed:(NSString *)animation
+                        fromLayerNamed:(NSString *)fromLayer
+                          toLayerNamed:(NSString *)toLayer
+               applyAnimationTransform:(BOOL)applyAnimationTransform
+                              inBundle:(NSBundle *)bundle {
+  self = [super init];
+  if (self) {
+    transitionAnimationView_ = [LOTAnimationView animationNamed:animation inBundle:bundle];
+    fromLayerName_ = fromLayer;
+    toLayerName_ = toLayer;
+    _applyTransform = applyAnimationTransform;
+  }
+  return self;
+}
+
+- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
+  return transitionAnimationView_.animationDuration;
+}
+
+- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
+  UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
+  UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
+  UIView *containerView = transitionContext.containerView;
+  
+  UIView *toSnapshot = [toVC.view resizableSnapshotViewFromRect:containerView.bounds
+                                             afterScreenUpdates:YES
+                                                  withCapInsets:UIEdgeInsetsZero];
+  toSnapshot.frame = containerView.bounds;
+  
+  UIView *fromSnapshot = [fromVC.view resizableSnapshotViewFromRect:containerView.bounds
+                                                 afterScreenUpdates:NO
+                                                      withCapInsets:UIEdgeInsetsZero];
+  fromSnapshot.frame = containerView.bounds;
+  
+  transitionAnimationView_.frame = containerView.bounds;
+  transitionAnimationView_.contentMode = UIViewContentModeScaleAspectFill;
+  [containerView addSubview:transitionAnimationView_];
+  
+  BOOL crossFadeViews = NO;
+  
+  if (toLayerName_.length) {
+    LOTKeypath *toKeypath = [LOTKeypath keypathWithString:toLayerName_];
+    CGRect convertedBounds = [transitionAnimationView_ convertRect:containerView.bounds toKeypathLayer:toKeypath];
+    toSnapshot.frame = convertedBounds;
+    if (_applyTransform) {
+      [transitionAnimationView_ addSubview:toSnapshot toKeypathLayer:toKeypath];
+    } else {
+      [transitionAnimationView_ maskSubview:toSnapshot toKeypathLayer:toKeypath];
+    }
+  } else {
+    [containerView addSubview:toSnapshot];
+    [containerView sendSubviewToBack:toSnapshot];
+    toSnapshot.alpha = 0;
+    crossFadeViews = YES;
+  }
+  
+  if (fromLayerName_.length) {
+    LOTKeypath *fromKeypath = [LOTKeypath keypathWithString:fromLayerName_];
+    CGRect convertedBounds = [transitionAnimationView_ convertRect:containerView.bounds fromKeypathLayer:fromKeypath];
+    fromSnapshot.frame = convertedBounds;
+    if (_applyTransform) {
+      [transitionAnimationView_ addSubview:fromSnapshot toKeypathLayer:fromKeypath];
+    } else {
+      [transitionAnimationView_ maskSubview:fromSnapshot toKeypathLayer:fromKeypath];
+    }
+  } else {
+    [containerView addSubview:fromSnapshot];
+    [containerView sendSubviewToBack:fromSnapshot];
+  }
+  
+  [containerView addSubview:toVC.view];
+  toVC.view.hidden = YES;
+  
+  if (crossFadeViews) {
+    CGFloat duration = transitionAnimationView_.animationDuration * 0.25;
+    CGFloat delay = (transitionAnimationView_.animationDuration - duration) / 2.f;
+    
+    [UIView animateWithDuration:duration
+                          delay:delay
+                        options:(UIViewAnimationOptionCurveEaseInOut)
+                     animations:^{
+                       toSnapshot.alpha = 1;
+                     } completion:^(BOOL finished) {
+                       
+                     }];
+  }
+  
+  [transitionAnimationView_ playWithCompletion:^(BOOL animationFinished) {
+    toVC.view.hidden = false;
+    [self->transitionAnimationView_ removeFromSuperview];
+    [transitionContext completeTransition:animationFinished];
+  }];
+}
+
+@end
+

+ 845 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationView.m

@@ -0,0 +1,845 @@
+//
+//  LOTAnimationView
+//  LottieAnimator
+//
+//  Created by Brandon Withrow on 12/14/15.
+//  Copyright © 2015 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTAnimationView.h"
+#import "LOTPlatformCompat.h"
+#import "LOTModels.h"
+#import "LOTHelpers.h"
+#import "LOTAnimationView_Internal.h"
+#import "LOTAnimationCache.h"
+#import "LOTCompositionContainer.h"
+
+static NSString * const kCompContainerAnimationKey = @"play";
+
+@implementation LOTAnimationView {
+  LOTCompositionContainer *_compContainer;
+  NSNumber *_playRangeStartFrame;
+  NSNumber *_playRangeEndFrame;
+  CGFloat _playRangeStartProgress;
+  CGFloat _playRangeEndProgress;
+  NSBundle *_bundle;
+  CGFloat _animationProgress;
+  // Properties for tracking automatic restoration of animation.
+  BOOL _shouldRestoreStateWhenAttachedToWindow;
+  LOTAnimationCompletionBlock _completionBlockToRestoreWhenAttachedToWindow;
+}
+
+# pragma mark - Convenience Initializers
+
++ (nonnull instancetype)animationNamed:(nonnull NSString *)animationName {
+  return [self animationNamed:animationName inBundle:[NSBundle mainBundle]];
+}
+
++ (nonnull instancetype)animationNamed:(nonnull NSString *)animationName inBundle:(nonnull NSBundle *)bundle {
+  LOTComposition *comp = [LOTComposition animationNamed:animationName inBundle:bundle];
+  return [[self alloc] initWithModel:comp inBundle:bundle];
+}
+
++ (nonnull instancetype)animationFromJSON:(nonnull NSDictionary *)animationJSON {
+    return [self animationFromJSON:animationJSON inBundle:[NSBundle mainBundle]];
+}
+
++ (nonnull instancetype)animationFromJSON:(nullable NSDictionary *)animationJSON inBundle:(nullable NSBundle *)bundle {
+  LOTComposition *comp = [LOTComposition animationFromJSON:animationJSON inBundle:bundle];
+  return [[self alloc] initWithModel:comp inBundle:bundle];
+}
+
++ (nonnull instancetype)animationWithFilePath:(nonnull NSString *)filePath {
+  LOTComposition *comp = [LOTComposition animationWithFilePath:filePath];
+  return [[self alloc] initWithModel:comp inBundle:[NSBundle mainBundle]];
+}
+
+# pragma mark - Initializers
+
+- (instancetype)initWithContentsOfURL:(NSURL *)url {
+  self = [self initWithFrame:CGRectZero];
+  if (self) {
+    LOTComposition *laScene = [[LOTAnimationCache sharedCache] animationForKey:url.absoluteString];
+    if (laScene) {
+      laScene.cacheKey = url.absoluteString;
+      [self _initializeAnimationContainer];
+      [self _setupWithSceneModel:laScene];
+    } else {
+      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+        NSData *animationData = [NSData dataWithContentsOfURL:url];
+        if (!animationData) {
+          return;
+        }
+        NSError *error;
+        NSDictionary  *animationJSON = [NSJSONSerialization JSONObjectWithData:animationData
+                                                                       options:0 error:&error];
+        if (error || !animationJSON) {
+          return;
+        }
+        
+        LOTComposition *laScene = [[LOTComposition alloc] initWithJSON:animationJSON withAssetBundle:[NSBundle mainBundle]];
+        dispatch_async(dispatch_get_main_queue(), ^(void) {
+          [[LOTAnimationCache sharedCache] addAnimation:laScene forKey:url.absoluteString];
+          laScene.cacheKey = url.absoluteString;
+          [self _initializeAnimationContainer];
+          [self _setupWithSceneModel:laScene];
+        });
+      });
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithModel:(LOTComposition *)model inBundle:(NSBundle *)bundle {
+  self = [self initWithFrame:model.compBounds];
+  if (self) {
+    _bundle = bundle;
+    [self _initializeAnimationContainer];
+    [self _setupWithSceneModel:model];
+  }
+  return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    [self _commonInit];
+  }
+  return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+  self = [super initWithCoder:coder];
+  if (self) {
+    [self _commonInit];
+  }
+  return self;
+}
+
+# pragma mark - Inspectables
+
+- (void)setAnimation:(NSString *)animationName {
+    
+    _animation = animationName;
+    
+    [self setAnimationNamed:animationName];
+    
+}
+
+# pragma mark - Internal Methods
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+
+- (void)_initializeAnimationContainer {
+  self.clipsToBounds = YES;
+}
+
+- (void)_commonInit {
+  _animationSpeed = 1;
+  _animationProgress = 0;
+  _loopAnimation = NO;
+  _autoReverseAnimation = NO;
+  _playRangeEndFrame = nil;
+  _playRangeStartFrame = nil;
+  _playRangeEndProgress = 0;
+  _playRangeStartProgress = 0;
+  _shouldRasterizeWhenIdle = NO;
+  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_handleWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
+  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_handleWillEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
+}
+
+#else
+
+- (void)_initializeAnimationContainer {
+  self.wantsLayer = YES;
+}
+
+- (void)_commonInit {
+  _animationSpeed = 1;
+  _animationProgress = 0;
+  _loopAnimation = NO;
+  _autoReverseAnimation = NO;
+  _playRangeEndFrame = nil;
+  _playRangeStartFrame = nil;
+  _playRangeEndProgress = 0;
+  _playRangeStartProgress = 0;
+  _shouldRasterizeWhenIdle = NO;
+}
+
+#endif
+
+
+
+- (void)dealloc {
+  [NSNotificationCenter.defaultCenter removeObserver:self];
+}
+
+- (void)_setupWithSceneModel:(LOTComposition *)model {
+  if (_sceneModel) {
+    [self _removeCurrentAnimationIfNecessary];
+    [self _callCompletionIfNecessary:NO];
+    [_compContainer removeFromSuperlayer];
+    _compContainer = nil;
+    _sceneModel = nil;
+    [self _commonInit];
+  }
+  
+  _sceneModel = model;
+  _compContainer = [[LOTCompositionContainer alloc] initWithModel:nil inLayerGroup:nil withLayerGroup:_sceneModel.layerGroup withAssestGroup:_sceneModel.assetGroup];
+  [self.layer addSublayer:_compContainer];
+  [self _restoreState];
+  [self setNeedsLayout];
+}
+
+- (void)_restoreState {
+  if (_isAnimationPlaying) {
+    _isAnimationPlaying = NO;
+    if (_playRangeStartFrame && _playRangeEndFrame) {
+      [self playFromFrame:_playRangeStartFrame toFrame:_playRangeEndFrame withCompletion:self.completionBlock];
+    } else if (_playRangeEndProgress != _playRangeStartProgress) {
+      [self playFromProgress:_playRangeStartProgress toProgress:_playRangeEndProgress withCompletion:self.completionBlock];
+    } else {
+      [self playWithCompletion:self.completionBlock];
+    }
+  } else {
+    self.animationProgress = _animationProgress;
+  }
+}
+
+- (void)_removeCurrentAnimationIfNecessary {
+  _isAnimationPlaying = NO;
+  [_compContainer removeAllAnimations];
+  _compContainer.shouldRasterize = _shouldRasterizeWhenIdle;
+}
+
+- (CGFloat)_progressForFrame:(NSNumber *)frame {
+  if (!_sceneModel) {
+    return 0;
+  }
+  return ((frame.floatValue - _sceneModel.startFrame.floatValue) / (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue));
+}
+
+- (NSNumber *)_frameForProgress:(CGFloat)progress {
+  if (!_sceneModel) {
+    return @0;
+  }
+  return @(((_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue) * progress) + _sceneModel.startFrame.floatValue);
+}
+
+- (BOOL)_isSpeedNegative {
+  // If the animation speed is negative, then we're moving backwards.
+  return _animationSpeed >= 0;
+}
+
+- (void)_handleWindowChanges:(BOOL)hasNewWindow
+{
+  // When this view or its superview is leaving the screen, e.g. a modal is presented or another
+  // screen is pushed, this method will get called with newWindow value set to nil - indicating that
+  // this view will be detached from the visible window.
+  // When a view is detached, animations will stop - but will not automatically resumed when it's
+  // re-attached back to window, e.g. when the presented modal is dismissed or another screen is
+  // pop.
+  if (hasNewWindow) {
+    // The view is being re-attached, resume animation if needed.
+    if (_shouldRestoreStateWhenAttachedToWindow) {
+      _shouldRestoreStateWhenAttachedToWindow = NO;
+      
+      _isAnimationPlaying = YES;
+      _completionBlock = _completionBlockToRestoreWhenAttachedToWindow;
+      _completionBlockToRestoreWhenAttachedToWindow = nil;
+      
+      [self performSelector:@selector(_restoreState) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+    }
+  } else {
+    // The view is being detached, capture information that need to be restored later.
+    if (_isAnimationPlaying) {
+      LOTAnimationCompletionBlock completion = _completionBlock;
+      [self pause];
+      _shouldRestoreStateWhenAttachedToWindow = YES;
+      _completionBlockToRestoreWhenAttachedToWindow = completion;
+      _completionBlock = nil;
+    }
+  }
+}
+
+- (void)_handleWillEnterBackground {
+  [self _handleWindowChanges: false];
+}
+
+- (void)_handleWillEnterForeground {
+  [self _handleWindowChanges: (self.window != nil)];
+}
+
+# pragma mark - Completion Block
+
+- (void)_callCompletionIfNecessary:(BOOL)complete {
+  if (self.completionBlock) {
+    LOTAnimationCompletionBlock completion = self.completionBlock;
+    self.completionBlock = nil;
+    completion(complete);
+  }
+}
+
+# pragma mark - External Methods
+
+- (void)setAnimationNamed:(nonnull NSString *)animationName {
+  LOTComposition *comp = [LOTComposition animationNamed:animationName];
+
+  [self _initializeAnimationContainer];
+  [self _setupWithSceneModel:comp];
+}
+  
+- (void)setAnimationNamed:(NSString *)animationName inBundle:(NSBundle *)bundle {
+  LOTComposition *comp = [LOTComposition animationNamed:animationName inBundle:bundle];
+  
+  [self _initializeAnimationContainer];
+  [self _setupWithSceneModel:comp];
+}
+
+- (void)setAnimationFromJSON:(nonnull NSDictionary *)animationJSON {
+  LOTComposition *comp = [LOTComposition animationFromJSON:animationJSON];
+
+  [self _initializeAnimationContainer];
+  [self _setupWithSceneModel:comp];
+}
+  
+- (void)setAnimationFromJSON:(NSDictionary *)animationJSON inBundle:(NSBundle *)bundle {
+  LOTComposition *comp = [LOTComposition animationFromJSON:animationJSON inBundle:bundle];
+  
+  [self _initializeAnimationContainer];
+  [self _setupWithSceneModel:comp];
+}
+
+# pragma mark - External Methods - Model
+
+- (void)setSceneModel:(LOTComposition *)sceneModel {
+  [self _setupWithSceneModel:sceneModel];
+}
+
+# pragma mark - External Methods - Play Control
+
+- (void)play {
+  if (!_sceneModel) {
+    _isAnimationPlaying = YES;
+    return;
+  }
+  [self playFromFrame:_sceneModel.startFrame toFrame:_sceneModel.endFrame withCompletion:nil];
+}
+
+- (void)playWithCompletion:(LOTAnimationCompletionBlock)completion {
+  if (!_sceneModel) {
+    _isAnimationPlaying = YES;
+    self.completionBlock = completion;
+    return;
+  }
+  [self playFromFrame:_sceneModel.startFrame toFrame:_sceneModel.endFrame withCompletion:completion];
+}
+
+- (void)playToProgress:(CGFloat)progress withCompletion:(nullable LOTAnimationCompletionBlock)completion {
+  [self playFromProgress:0 toProgress:progress withCompletion:completion];
+}
+
+- (void)playFromProgress:(CGFloat)fromStartProgress
+              toProgress:(CGFloat)toEndProgress
+          withCompletion:(nullable LOTAnimationCompletionBlock)completion {
+  if (!_sceneModel) {
+    _isAnimationPlaying = YES;
+    self.completionBlock = completion;
+    _playRangeStartProgress = fromStartProgress;
+    _playRangeEndProgress = toEndProgress;
+    return;
+  }
+  [self playFromFrame:[self _frameForProgress:fromStartProgress]
+              toFrame:[self _frameForProgress:toEndProgress]
+       withCompletion:completion];
+}
+
+- (void)playToFrame:(nonnull NSNumber *)toFrame
+     withCompletion:(nullable LOTAnimationCompletionBlock)completion {
+  [self playFromFrame:_sceneModel.startFrame toFrame:toFrame withCompletion:completion];
+}
+
+- (void)playFromFrame:(nonnull NSNumber *)fromStartFrame
+              toFrame:(nonnull NSNumber *)toEndFrame
+       withCompletion:(nullable LOTAnimationCompletionBlock)completion {
+  if (_isAnimationPlaying) {
+    return;
+  }
+  _playRangeStartFrame = fromStartFrame;
+  _playRangeEndFrame = toEndFrame;
+  if (completion) {
+    self.completionBlock = completion;
+  }
+  if (!_sceneModel) {
+    _isAnimationPlaying = YES;
+    return;
+  }
+
+  BOOL playingForward = ((_animationSpeed > 0) && (toEndFrame.floatValue > fromStartFrame.floatValue))
+    || ((_animationSpeed < 0) && (fromStartFrame.floatValue > toEndFrame.floatValue));
+
+  CGFloat leftFrameValue = MIN(fromStartFrame.floatValue, toEndFrame.floatValue);
+  CGFloat rightFrameValue = MAX(fromStartFrame.floatValue, toEndFrame.floatValue);
+
+  NSNumber *currentFrame = [self _frameForProgress:_animationProgress];
+
+  currentFrame = @(MAX(MIN(currentFrame.floatValue, rightFrameValue), leftFrameValue));
+
+  if (currentFrame.floatValue == rightFrameValue && playingForward) {
+    currentFrame = @(leftFrameValue);
+  } else if (currentFrame.floatValue == leftFrameValue && !playingForward) {
+    currentFrame = @(rightFrameValue);
+  }
+  _animationProgress = [self _progressForFrame:currentFrame];
+  
+  CGFloat currentProgress = _animationProgress * (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue);
+  CGFloat skipProgress;
+  if (playingForward) {
+    skipProgress = currentProgress - leftFrameValue;
+  } else {
+    skipProgress = rightFrameValue - currentProgress;
+  }
+  NSTimeInterval offset = MAX(0, skipProgress) / _sceneModel.framerate.floatValue;
+  if (!self.window) {
+    _shouldRestoreStateWhenAttachedToWindow = YES;
+    _completionBlockToRestoreWhenAttachedToWindow = self.completionBlock;
+    self.completionBlock = nil;
+  } else {
+    NSTimeInterval duration = (ABS(toEndFrame.floatValue - fromStartFrame.floatValue) / _sceneModel.framerate.floatValue);
+    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"currentFrame"];
+    animation.speed = _animationSpeed;
+    animation.fromValue = fromStartFrame;
+    animation.toValue = toEndFrame;
+    animation.duration = duration;
+    animation.fillMode = kCAFillModeBoth;
+    animation.repeatCount = _loopAnimation ? HUGE_VALF : 1;
+    animation.autoreverses = _autoReverseAnimation;
+    animation.delegate = self;
+    animation.removedOnCompletion = NO;
+    if (offset != 0) {
+      CFTimeInterval currentTime = CACurrentMediaTime();
+      CFTimeInterval currentLayerTime = [self.layer convertTime:currentTime fromLayer:nil];
+      animation.beginTime = currentLayerTime - (offset * 1 / _animationSpeed);
+    }
+    [_compContainer addAnimation:animation forKey:kCompContainerAnimationKey];
+    _compContainer.shouldRasterize = NO;
+  }
+  _isAnimationPlaying = YES;
+}
+
+#pragma mark - Other Time Controls
+
+- (void)stop {
+  _isAnimationPlaying = NO;
+  if (_sceneModel) {
+    [self setProgressWithFrame:_sceneModel.startFrame callCompletionIfNecessary:YES];
+  }
+}
+
+- (void)pause {
+  if (!_sceneModel ||
+      !_isAnimationPlaying) {
+    _isAnimationPlaying = NO;
+    return;
+  }
+  NSNumber *frame = [_compContainer.presentationLayer.currentFrame copy];
+  [self setProgressWithFrame:frame callCompletionIfNecessary:YES];
+}
+
+- (void)setAnimationProgress:(CGFloat)animationProgress {
+  if (!_sceneModel) {
+    _animationProgress = animationProgress;
+    return;
+  }
+  [self setProgressWithFrame:[self _frameForProgress:animationProgress] callCompletionIfNecessary:YES];
+}
+
+- (void)setProgressWithFrame:(nonnull NSNumber *)currentFrame {
+  [self setProgressWithFrame:currentFrame callCompletionIfNecessary:YES];
+}
+
+- (void)setProgressWithFrame:(nonnull NSNumber *)currentFrame callCompletionIfNecessary:(BOOL)callCompletion {
+  [self _removeCurrentAnimationIfNecessary];
+
+  if (_shouldRestoreStateWhenAttachedToWindow) {
+    _shouldRestoreStateWhenAttachedToWindow = NO;
+
+    self.completionBlock = _completionBlockToRestoreWhenAttachedToWindow;
+    _completionBlockToRestoreWhenAttachedToWindow = nil;
+  }
+
+  _animationProgress = [self _progressForFrame:currentFrame];
+
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
+  _compContainer.currentFrame = currentFrame;
+  [_compContainer setNeedsDisplay];
+  [CATransaction commit];
+  if (callCompletion) {
+    [self _callCompletionIfNecessary:NO];
+  }
+}
+
+- (void)setLoopAnimation:(BOOL)loopAnimation {
+  _loopAnimation = loopAnimation;
+  if (_isAnimationPlaying && _sceneModel) {
+    NSNumber *frame = [_compContainer.presentationLayer.currentFrame copy];
+    [self setProgressWithFrame:frame callCompletionIfNecessary:NO];
+    [self playFromFrame:_playRangeStartFrame toFrame:_playRangeEndFrame withCompletion:self.completionBlock];
+  }
+}
+
+- (void)setAnimationSpeed:(CGFloat)animationSpeed {
+  _animationSpeed = animationSpeed;
+  if (_isAnimationPlaying && _sceneModel) {
+    NSNumber *frame = [_compContainer.presentationLayer.currentFrame copy];
+    [self setProgressWithFrame:frame callCompletionIfNecessary:NO];
+    [self playFromFrame:_playRangeStartFrame toFrame:_playRangeEndFrame withCompletion:self.completionBlock];
+  }
+}
+
+- (void)forceDrawingUpdate {
+  [self _layoutAndForceUpdate];
+}
+
+# pragma mark - External Methods - Idle Rasterization
+
+- (void)setShouldRasterizeWhenIdle:(BOOL)shouldRasterize {
+  _shouldRasterizeWhenIdle = shouldRasterize;
+  if (!_isAnimationPlaying) {
+    _compContainer.shouldRasterize = _shouldRasterizeWhenIdle;
+  }
+}
+
+# pragma mark - External Methods - Cache
+
+- (void)setCacheEnable:(BOOL)cacheEnable {
+  _cacheEnable = cacheEnable;
+  if (!self.sceneModel.cacheKey) {
+    return;
+  }
+  if (cacheEnable) {
+    [[LOTAnimationCache sharedCache] addAnimation:_sceneModel forKey:self.sceneModel.cacheKey];
+  } else {
+    [[LOTAnimationCache sharedCache] removeAnimationForKey:self.sceneModel.cacheKey];
+  }
+}
+
+# pragma mark - External Methods - Interactive Controls
+
+- (void)setValueDelegate:(id<LOTValueDelegate> _Nonnull)delegate
+              forKeypath:(LOTKeypath * _Nonnull)keypath {
+  [_compContainer setValueDelegate:delegate forKeypath:keypath];
+  [self _layoutAndForceUpdate];
+}
+
+- (nullable NSArray *)keysForKeyPath:(nonnull LOTKeypath *)keypath {
+  return [_compContainer keysForKeyPath:keypath];
+}
+
+- (CGPoint)convertPoint:(CGPoint)point
+         toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layoutAndForceUpdate];
+  return [_compContainer convertPoint:point toKeypathLayer:keypath withParentLayer:self.layer];
+}
+
+- (CGRect)convertRect:(CGRect)rect
+       toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layoutAndForceUpdate];
+  return [_compContainer convertRect:rect toKeypathLayer:keypath withParentLayer:self.layer];
+}
+
+- (CGPoint)convertPoint:(CGPoint)point
+       fromKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layoutAndForceUpdate];
+  return [_compContainer convertPoint:point fromKeypathLayer:keypath withParentLayer:self.layer];
+}
+
+- (CGRect)convertRect:(CGRect)rect
+     fromKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layoutAndForceUpdate];
+  return [_compContainer convertRect:rect fromKeypathLayer:keypath withParentLayer:self.layer];
+}
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+
+- (void)addSubview:(nonnull LOTView *)view
+    toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layoutAndForceUpdate];
+  CGRect viewRect = view.frame;
+  LOTView *wrapperView = [[LOTView alloc] initWithFrame:viewRect];
+  view.frame = view.bounds;
+  view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+  [wrapperView addSubview:view];
+  [self addSubview:wrapperView];
+  [_compContainer addSublayer:wrapperView.layer toKeypathLayer:keypath];
+}
+
+- (void)maskSubview:(nonnull LOTView *)view
+     toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layoutAndForceUpdate];
+  CGRect viewRect = view.frame;
+  LOTView *wrapperView = [[LOTView alloc] initWithFrame:viewRect];
+  view.frame = view.bounds;
+  view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+  [wrapperView addSubview:view];
+  [self addSubview:wrapperView];
+  [_compContainer maskSublayer:wrapperView.layer toKeypathLayer:keypath];
+}
+
+
+#else
+
+- (void)addSubview:(nonnull LOTView *)view
+    toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layout];
+  CGRect viewRect = view.frame;
+  LOTView *wrapperView = [[LOTView alloc] initWithFrame:viewRect];
+  view.frame = view.bounds;
+  view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+  [wrapperView addSubview:view];
+  [self addSubview:wrapperView];
+  [_compContainer addSublayer:wrapperView.layer toKeypathLayer:keypath];
+}
+
+- (void)maskSubview:(nonnull LOTView *)view
+     toKeypathLayer:(nonnull LOTKeypath *)keypath {
+  [self _layout];
+  CGRect viewRect = view.frame;
+  LOTView *wrapperView = [[LOTView alloc] initWithFrame:viewRect];
+  view.frame = view.bounds;
+  view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+  [wrapperView addSubview:view];
+  [self addSubview:wrapperView];
+  [_compContainer maskSublayer:wrapperView.layer toKeypathLayer:keypath];
+}
+
+#endif
+
+# pragma mark - Semi-Private Methods
+
+- (CALayer * _Nullable)layerForKey:(NSString * _Nonnull)keyname {
+  return _compContainer.childMap[keyname];
+}
+
+- (NSArray * _Nonnull)compositionLayers {
+  return _compContainer.childLayers;
+}
+
+# pragma mark - Getters and Setters
+
+- (CGFloat)animationDuration {
+  if (!_sceneModel) {
+    return 0;
+  }
+  CAAnimation *play = [_compContainer animationForKey:kCompContainerAnimationKey];
+  if (play) {
+    return play.duration;
+  }
+  return (_sceneModel.endFrame.floatValue - _sceneModel.startFrame.floatValue) / _sceneModel.framerate.floatValue;
+}
+
+- (CGFloat)animationProgress {
+  if (_isAnimationPlaying &&
+      _compContainer.presentationLayer) {
+    CGFloat activeProgress = [self _progressForFrame:[(LOTCompositionContainer *)_compContainer.presentationLayer currentFrame]];
+    return activeProgress;
+  }
+  return _animationProgress;
+}
+
+# pragma mark - Overrides
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+
+#define LOTViewContentMode UIViewContentMode
+#define LOTViewContentModeScaleToFill UIViewContentModeScaleToFill
+#define LOTViewContentModeScaleAspectFit UIViewContentModeScaleAspectFit
+#define LOTViewContentModeScaleAspectFill UIViewContentModeScaleAspectFill
+#define LOTViewContentModeRedraw UIViewContentModeRedraw
+#define LOTViewContentModeCenter UIViewContentModeCenter
+#define LOTViewContentModeTop UIViewContentModeTop
+#define LOTViewContentModeBottom UIViewContentModeBottom
+#define LOTViewContentModeLeft UIViewContentModeLeft
+#define LOTViewContentModeRight UIViewContentModeRight
+#define LOTViewContentModeTopLeft UIViewContentModeTopLeft
+#define LOTViewContentModeTopRight UIViewContentModeTopRight
+#define LOTViewContentModeBottomLeft UIViewContentModeBottomLeft
+#define LOTViewContentModeBottomRight UIViewContentModeBottomRight
+
+- (CGSize)intrinsicContentSize {
+  if (!_sceneModel) {
+    return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
+  }
+  return _sceneModel.compBounds.size;
+}
+
+- (void)didMoveToSuperview {
+  [super didMoveToSuperview];
+  if (self.superview == nil) {
+    [self _callCompletionIfNecessary:NO];
+  }
+}
+
+- (void)willMoveToWindow:(UIWindow *)newWindow {
+  [self _handleWindowChanges:(newWindow != nil)];
+}
+
+- (void)didMoveToWindow {
+    _compContainer.rasterizationScale = self.window.screen.scale;
+}
+
+- (void)setContentMode:(LOTViewContentMode)contentMode {
+  [super setContentMode:contentMode];
+  [self setNeedsLayout];
+}
+
+- (void)layoutSubviews {
+  [super layoutSubviews];
+  [self _layout];
+}
+
+#else
+
+- (void)viewWillMoveToWindow:(NSWindow *)newWindow {
+  [self _handleWindowChanges:(newWindow != nil)];
+}
+
+- (void)viewDidMoveToWindow {
+    _compContainer.rasterizationScale = self.window.screen.backingScaleFactor;
+}
+    
+- (void)setCompletionBlock:(LOTAnimationCompletionBlock)completionBlock {
+    if (completionBlock) {
+      _completionBlock = ^(BOOL finished) {
+        dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(finished); });
+      };
+    }
+    else {
+      _completionBlock = nil;
+    }
+}
+
+- (void)setContentMode:(LOTViewContentMode)contentMode {
+  _contentMode = contentMode;
+  [self setNeedsLayout];
+}
+
+- (void)setNeedsLayout {
+  self.needsLayout = YES;
+}
+
+- (BOOL)isFlipped {
+  return YES;
+}
+
+- (BOOL)wantsUpdateLayer {
+  return YES;
+}
+
+- (void)layout {
+  [super layout];
+  [self _layout];
+}
+
+#endif
+
+- (void)_layoutAndForceUpdate {
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
+  [self _layout];
+  [_compContainer displayWithFrame:_compContainer.currentFrame forceUpdate:YES];
+  [CATransaction commit];
+}
+
+- (void)_layout {
+  CGPoint centerPoint = LOT_RectGetCenterPoint(self.bounds);
+  CATransform3D xform;
+
+  if (self.contentMode == LOTViewContentModeScaleToFill) {
+    CGSize scaleSize = CGSizeMake(self.bounds.size.width / self.sceneModel.compBounds.size.width,
+            self.bounds.size.height / self.sceneModel.compBounds.size.height);
+    xform = CATransform3DMakeScale(scaleSize.width, scaleSize.height, 1);
+  } else if (self.contentMode == LOTViewContentModeScaleAspectFit) {
+    CGFloat compAspect = self.sceneModel.compBounds.size.width / self.sceneModel.compBounds.size.height;
+    CGFloat viewAspect = self.bounds.size.width / self.bounds.size.height;
+    BOOL scaleWidth = compAspect > viewAspect;
+    CGFloat dominantDimension = scaleWidth ? self.bounds.size.width : self.bounds.size.height;
+    CGFloat compDimension = scaleWidth ? self.sceneModel.compBounds.size.width : self.sceneModel.compBounds.size.height;
+    CGFloat scale = dominantDimension / compDimension;
+    xform = CATransform3DMakeScale(scale, scale, 1);
+  } else if (self.contentMode == LOTViewContentModeScaleAspectFill) {
+    CGFloat compAspect = self.sceneModel.compBounds.size.width / self.sceneModel.compBounds.size.height;
+    CGFloat viewAspect = self.bounds.size.width / self.bounds.size.height;
+    BOOL scaleWidth = compAspect < viewAspect;
+    CGFloat dominantDimension = scaleWidth ? self.bounds.size.width : self.bounds.size.height;
+    CGFloat compDimension = scaleWidth ? self.sceneModel.compBounds.size.width : self.sceneModel.compBounds.size.height;
+    CGFloat scale = dominantDimension / compDimension;
+    xform = CATransform3DMakeScale(scale, scale, 1);
+  } else {
+    xform = CATransform3DIdentity;
+  }
+
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
+  _compContainer.transform = CATransform3DIdentity;
+  _compContainer.bounds = _sceneModel.compBounds;
+  _compContainer.viewportBounds = _sceneModel.compBounds;
+  _compContainer.transform = xform;
+  _compContainer.position = centerPoint;
+  [CATransaction commit];
+}
+
+# pragma mark - CAAnimationDelegate
+
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)complete {
+  if ([_compContainer animationForKey:kCompContainerAnimationKey] == anim &&
+      [anim isKindOfClass:[CABasicAnimation class]]) {
+    CABasicAnimation *playAnimation = (CABasicAnimation *)anim;
+    NSNumber *frame = _compContainer.presentationLayer.currentFrame;
+    if (complete) {
+      // Set the final frame based on the animation to/from values. If playing forward, use the
+      // toValue otherwise we want to end on the fromValue.
+      frame = [self _isSpeedNegative] ? (NSNumber *)playAnimation.toValue : (NSNumber *)playAnimation.fromValue;
+    }
+    [self _removeCurrentAnimationIfNecessary];
+    [self setProgressWithFrame:frame callCompletionIfNecessary:NO];
+    [self _callCompletionIfNecessary:complete];
+  }
+}
+
+# pragma mark - DEPRECATED
+
+- (void)addSubview:(nonnull LOTView *)view
+      toLayerNamed:(nonnull NSString *)layer
+    applyTransform:(BOOL)applyTransform {
+  NSLog(@"%s: Function is DEPRECATED. Please use addSubview:forKeypathLayer:", __PRETTY_FUNCTION__);
+  LOTKeypath *keypath = [LOTKeypath keypathWithString:layer];
+  if (applyTransform) {
+    [self addSubview:view toKeypathLayer:keypath];
+  } else {
+    [self maskSubview:view toKeypathLayer:keypath];
+  }
+}
+
+- (CGRect)convertRect:(CGRect)rect
+         toLayerNamed:(NSString *_Nullable)layerName {
+  NSLog(@"%s: Function is DEPRECATED. Please use convertRect:forKeypathLayer:", __PRETTY_FUNCTION__);
+  LOTKeypath *keypath = [LOTKeypath keypathWithString:layerName];
+  return [self convertRect:rect toKeypathLayer:keypath];
+}
+
+- (void)setValue:(nonnull id)value
+      forKeypath:(nonnull NSString *)keypath
+         atFrame:(nullable NSNumber *)frame {
+  NSLog(@"%s: Function is DEPRECATED and no longer functional. Please use setValueCallback:forKeypath:", __PRETTY_FUNCTION__);
+}
+
+- (void)logHierarchyKeypaths {
+  NSArray *keypaths = [self keysForKeyPath:[LOTKeypath keypathWithString:@"**"]];
+  for (NSString *keypath in keypaths) {
+    NSLog(@"%@", keypath);
+  }
+}
+
+@end

+ 22 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTAnimationView_Internal.h

@@ -0,0 +1,22 @@
+//
+//  LOTAnimationView_Internal.h
+//  Lottie
+//
+//  Created by Brandon Withrow on 12/7/16.
+//  Copyright © 2016 Brandon Withrow. All rights reserved.
+//
+
+#import "LOTAnimationView.h"
+
+typedef enum : NSUInteger {
+  LOTConstraintTypeAlignToBounds,
+  LOTConstraintTypeAlignToLayer,
+  LOTConstraintTypeNone
+} LOTConstraintType;
+
+@interface LOTAnimationView () <CAAnimationDelegate>
+
+- (CALayer * _Nullable)layerForKey:(NSString * _Nonnull)keyname;
+- (NSArray * _Nonnull)compositionLayers;
+
+@end

+ 80 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTBlockCallback.m

@@ -0,0 +1,80 @@
+//
+//  LOTBlockCallback.m
+//  Lottie
+//
+//  Created by brandon_withrow on 12/15/17.
+//  Copyright © 2017 Airbnb. All rights reserved.
+//
+
+#import "LOTBlockCallback.h"
+
+@implementation LOTColorBlockCallback
+
++ (instancetype)withBlock:(LOTColorValueCallbackBlock)block {
+  LOTColorBlockCallback *colorCallback = [[self alloc] init];
+  colorCallback.callback = block;
+  return colorCallback;
+}
+
+- (CGColorRef)colorForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor currentColor:(CGColorRef)interpolatedColor {
+  return self.callback(currentFrame, startKeyframe, endKeyframe, interpolatedProgress, startColor, endColor, interpolatedColor);
+}
+
+@end
+
+@implementation LOTNumberBlockCallback
+
++ (instancetype)withBlock:(LOTNumberValueCallbackBlock)block {
+  LOTNumberBlockCallback *numberCallback = [[self alloc] init];
+  numberCallback.callback = block;
+  return numberCallback;
+}
+
+- (CGFloat)floatValueForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startValue:(CGFloat)startValue endValue:(CGFloat)endValue currentValue:(CGFloat)interpolatedValue {
+  return self.callback(currentFrame, startKeyframe, endKeyframe, interpolatedProgress, startValue, endValue, interpolatedValue);
+}
+
+@end
+
+@implementation LOTPointBlockCallback
+
++ (instancetype)withBlock:(LOTPointValueCallbackBlock)block {
+  LOTPointBlockCallback *callback = [[self alloc] init];
+  callback.callback = block;
+  return callback;
+}
+
+- (CGPoint)pointForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint currentPoint:(CGPoint)interpolatedPoint {
+  return self.callback(currentFrame, startKeyframe, endKeyframe, interpolatedProgress, startPoint, endPoint, interpolatedPoint);
+}
+
+@end
+
+@implementation LOTSizeBlockCallback
+
++ (instancetype)withBlock:(LOTSizeValueCallbackBlock)block {
+  LOTSizeBlockCallback *callback = [[self alloc] init];
+  callback.callback = block;
+  return callback;
+}
+
+- (CGSize)sizeForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress startSize:(CGSize)startSize endSize:(CGSize)endSize currentSize:(CGSize)interpolatedSize {
+  return self.callback(currentFrame, startKeyframe, endKeyframe, interpolatedProgress, startSize, endSize, interpolatedSize);
+}
+
+@end
+
+@implementation LOTPathBlockCallback
+
++ (instancetype)withBlock:(LOTPathValueCallbackBlock)block {
+  LOTPathBlockCallback *callback = [[self alloc] init];
+  callback.callback = block;
+  return callback;
+}
+
+- (CGPathRef)pathForFrame:(CGFloat)currentFrame startKeyframe:(CGFloat)startKeyframe endKeyframe:(CGFloat)endKeyframe interpolatedProgress:(CGFloat)interpolatedProgress {
+  return self.callback(currentFrame, startKeyframe, endKeyframe, interpolatedProgress);
+}
+
+@end
+

+ 23 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTCacheProvider.m

@@ -0,0 +1,23 @@
+//
+//  LOTCacheProvider.m
+//  Lottie
+//
+//  Created by punmy on 2017/7/8.
+//
+//
+
+#import "LOTCacheProvider.h"
+
+@implementation LOTCacheProvider
+
+static id<LOTImageCache> _imageCache;
+
++ (id<LOTImageCache>)imageCache {
+    return _imageCache;
+}
+
++ (void)setImageCache:(id<LOTImageCache>)cache {
+    _imageCache = cache;
+}
+
+@end

+ 0 - 0
Pods/lottie-ios/lottie-ios/Classes/Private/LOTComposition.m


Vissa filer visades inte eftersom för många filer har ändrats