Initial commit.
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..365d737
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakBeforeMultilineStrings: false
+BinPackParameters: false
+ColumnLimit: 0
+ObjCSpaceBeforeProtocolList: true
+PointerBindsToType: false
+IndentWrappedFunctionNames: true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68851ec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,61 @@
+# GOS
+
+/docs
+build_tests/CocoapodsObjCApp/CocoapodsObjCApp.xcworkspace
+build_tests/CocoapodsSwiftApp/CocoapodsSwiftApp.xcworkspace
+
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+
+## Other
+*.xccheckout
+*.moved-aside
+*.xcuserstate
+*.xcscmblueprint
+
+## OS X System files
+.DS_Store
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+Pods/
+demos/**/Pods/
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 
+# screenshots whenever they are needed.
+
+fastlane/report.xml
+fastlane/screenshots
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..c057941
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,44 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+## Pull requests
+
+Pull requests can be hard to review if they try to tackle too many things
+at once. Phabricator's "[Writing Reviewable Code](https://secure.phabricator.com/book/phabflavor/article/writing_reviewable_code/)"
+provides a set of guidelines that help increase the likelihood of your
+pull request getting merged.
+
+In short (slightly modified from the original article):
+
+- A pull request should be as small as possible, but no smaller.
+- The smallest a pull request can be is a single cohesive idea: don't
+  make pull requests so small that they are meaningless on their own.
+- Turn large pull requests into small pull requests by dividing large
+  problems into smaller problems and solving the small problems one at
+  a time.
+- Write sensible pull request descriptions.
+
+### Before you contribute
+
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+
+All submissions, including submissions by project members, require review. We
+allow pull requests to be filed, but we perform code reviews on codereview.cc.
+
+### The small print
+
+Contributions made by corporations are covered by a different agreement than
+the one above, the [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 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 [yyyy] [name of copyright owner]
+
+   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.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e1b0e49
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+# SpritedAnimationView
+
+This control provides an alternative to animating an array of images with an `UIImageView`. Only a
+single image composed of individual sprite frames is used, and animation simply consists of
+updating the layer `contentsRect`.
+
+## Requirements
+
+- Xcode 7.0 or higher
+- iOS SDK version 7.0 or higher
+
+## Create a sprite sheet asset
+
+A sprite sheet consists of a single image composed of a single column of individual sprite frames.
+The individual sprite frames must be sized and spaced evenly across the overall image bounds. A
+typical use case is to generate three sprite sheet images (1x, 2x, and 3x) and add these to an
+`.xcassets` file for use in the app by the `spritedAnimationView`.
+
+## Animating the sprite sheet
+
+Once a sprite sheet is added to a `spritedAnimationView` (either at `init` or adding later), the
+`spritedAnimationView` can animate the image from the first sprite frame to the last sprite frame.
+Properties are available to set the frame rate, which defaults to 60 fps (frames per second). The
+animation can happen once or be looped via the `animationRepeatCount` property. To start the
+animation, use the `-startAnimationWithCompletion:` method. A completion block gets called once
+the animation is finished. However, if the `animationRepeatCount` is set to loop infinitely (with
+a 0 setting), this block will not get called. Additional methods are provided to reset the
+`spritedAnimationView` to the beginning or end without animation.
+
+## Achieving two-state animation
+
+It is enough to provide a single sprite sheet, animate the image, and simply reset to the beginning
+once finished. However, in some cases, a nice user experience can be achieved by providing two
+separate sprite sheets. One showing an animation from state A to state B, then another sprite sheet
+showing state B going back to state A. This way, the sprite sheet can be swapped out after the
+animation completes for each state, and be replaced with the other sprite image.
+
+#### Two sample sprite sheets (showing Hide and Show states)
+
+| Checkmark Sprite Sheet (Hide) | Checkmark Sprite Sheet (Show) |
+| :---------------------------: | :---------------------------: |
+| ![Checkmark Hide](examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide.png) | ![Checkmark Show](examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show.png) |
+
+#### Two-state example
+
+```objectivec
+// Animate the sprited view.
+[_animationView startAnimatingWithCompletion:^{
+
+  // When animation completes, toggle image.
+  _checked = !_checked;
+  UIImage *spriteImage =
+      [UIImage imageNamed:_checked ? kSpriteCheckedImage : kSpriteUncheckedImage];
+  _animationView.spriteSheetImage = spriteImage;
+}];
+```
+
+## Usage
+
+Integrating the `spritedAnimationView` is somewhat similar to adding an `UIImageView` to a view.
+
+```objectivec
+#import "GOSSpritedAnimationView.h"
+
+// Create a Sprited Animation View.
+UIImage *spriteSheet = [UIImage imageNamed:@"myImage"];
+GOSSpritedAnimationView *animationView =
+    [[GOSSpritedAnimationView alloc] initWithSpriteSheetImage:spriteSheet];
+animationView.tintColor = [UIColor blueColor];
+[self.view addSubview:animationView];
+
+// To Animate.
+[animationView startAnimatingWithCompletion:^{
+    NSLog(@"Done animating.");
+}];
+```
diff --git a/examples/SpritedAnimationViewExample.xcodeproj/project.pbxproj b/examples/SpritedAnimationViewExample.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..840be8f
--- /dev/null
+++ b/examples/SpritedAnimationViewExample.xcodeproj/project.pbxproj
@@ -0,0 +1,446 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		5D3AFE951BED1598003EA2B2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D3AFE941BED1598003EA2B2 /* main.m */; };
+		5D3AFE981BED1598003EA2B2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D3AFE971BED1598003EA2B2 /* AppDelegate.m */; };
+		5D3AFE9B1BED1598003EA2B2 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D3AFE9A1BED1598003EA2B2 /* ViewController.m */; };
+		5D3AFEA01BED1598003EA2B2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D3AFE9F1BED1598003EA2B2 /* Assets.xcassets */; };
+		5D3AFEA31BED1598003EA2B2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D3AFEA11BED1598003EA2B2 /* LaunchScreen.storyboard */; };
+		5D971D6C1C90F975004729A7 /* SpritedAnimationViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D971D6A1C90F95B004729A7 /* SpritedAnimationViewTests.m */; };
+		5D971D701C90F9C9004729A7 /* GOSSpritedAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D971D6F1C90F9C9004729A7 /* GOSSpritedAnimationView.m */; };
+		5D971DB41C90FBED004729A7 /* GOSSpritedAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D971D6F1C90F9C9004729A7 /* GOSSpritedAnimationView.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		5D3AFEAA1BED1598003EA2B2 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 5D3AFE881BED1598003EA2B2 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 5D3AFE8F1BED1598003EA2B2;
+			remoteInfo = SpritedAnimationView;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		5D3AFE901BED1598003EA2B2 /* SpritedAnimationViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpritedAnimationViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		5D3AFE941BED1598003EA2B2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		5D3AFE961BED1598003EA2B2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		5D3AFE971BED1598003EA2B2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		5D3AFE991BED1598003EA2B2 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ViewController.h; path = SpritedAnimationViewExample/ViewController.h; sourceTree = "<group>"; };
+		5D3AFE9A1BED1598003EA2B2 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ViewController.m; path = SpritedAnimationViewExample/ViewController.m; sourceTree = "<group>"; };
+		5D3AFE9F1BED1598003EA2B2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		5D3AFEA21BED1598003EA2B2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		5D3AFEA41BED1598003EA2B2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5D3AFEA91BED1598003EA2B2 /* SpritedAnimationViewExample.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SpritedAnimationViewExample.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		5D63B0A91BF05CF800B646E3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = SpritedAnimationViewExample/README.md; sourceTree = "<group>"; };
+		5D971D6A1C90F95B004729A7 /* SpritedAnimationViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpritedAnimationViewTests.m; sourceTree = "<group>"; };
+		5D971D6E1C90F9C9004729A7 /* GOSSpritedAnimationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GOSSpritedAnimationView.h; sourceTree = "<group>"; };
+		5D971D6F1C90F9C9004729A7 /* GOSSpritedAnimationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GOSSpritedAnimationView.m; sourceTree = "<group>"; };
+		5D971DB31C90FB72004729A7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		5D3AFE8D1BED1598003EA2B2 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5D3AFEA61BED1598003EA2B2 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		5D3AFE871BED1598003EA2B2 = {
+			isa = PBXGroup;
+			children = (
+				5D63B0A91BF05CF800B646E3 /* README.md */,
+				5D63B0A71BF0163800B646E3 /* App */,
+				5D3AFE921BED1598003EA2B2 /* Supporting Files */,
+				5D3AFEC61BED1613003EA2B2 /* Tests */,
+				5D3AFE911BED1598003EA2B2 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		5D3AFE911BED1598003EA2B2 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				5D3AFE901BED1598003EA2B2 /* SpritedAnimationViewExample.app */,
+				5D3AFEA91BED1598003EA2B2 /* SpritedAnimationViewExample.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		5D3AFE921BED1598003EA2B2 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				5D3AFE931BED1598003EA2B2 /* Scaffolding */,
+				5D3AFE9F1BED1598003EA2B2 /* Assets.xcassets */,
+				5D3AFEA11BED1598003EA2B2 /* LaunchScreen.storyboard */,
+				5D3AFEA41BED1598003EA2B2 /* Info.plist */,
+				5D3AFE941BED1598003EA2B2 /* main.m */,
+			);
+			name = "Supporting Files";
+			path = SpritedAnimationViewExample;
+			sourceTree = "<group>";
+		};
+		5D3AFE931BED1598003EA2B2 /* Scaffolding */ = {
+			isa = PBXGroup;
+			children = (
+				5D3AFE961BED1598003EA2B2 /* AppDelegate.h */,
+				5D3AFE971BED1598003EA2B2 /* AppDelegate.m */,
+			);
+			name = Scaffolding;
+			sourceTree = "<group>";
+		};
+		5D3AFEC61BED1613003EA2B2 /* Tests */ = {
+			isa = PBXGroup;
+			children = (
+				5D971D691C90F95B004729A7 /* unit */,
+			);
+			name = Tests;
+			path = ../tests;
+			sourceTree = "<group>";
+		};
+		5D63B0A71BF0163800B646E3 /* App */ = {
+			isa = PBXGroup;
+			children = (
+				5D3AFE991BED1598003EA2B2 /* ViewController.h */,
+				5D3AFE9A1BED1598003EA2B2 /* ViewController.m */,
+				5D971D6D1C90F9C9004729A7 /* src */,
+			);
+			name = App;
+			sourceTree = "<group>";
+		};
+		5D971D691C90F95B004729A7 /* unit */ = {
+			isa = PBXGroup;
+			children = (
+				5D971DB31C90FB72004729A7 /* Info.plist */,
+				5D971D6A1C90F95B004729A7 /* SpritedAnimationViewTests.m */,
+			);
+			path = unit;
+			sourceTree = "<group>";
+		};
+		5D971D6D1C90F9C9004729A7 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				5D971D6E1C90F9C9004729A7 /* GOSSpritedAnimationView.h */,
+				5D971D6F1C90F9C9004729A7 /* GOSSpritedAnimationView.m */,
+			);
+			name = src;
+			path = ../src;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		5D3AFE8F1BED1598003EA2B2 /* SpritedAnimationViewExample */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5D3AFEBD1BED1598003EA2B2 /* Build configuration list for PBXNativeTarget "SpritedAnimationViewExample" */;
+			buildPhases = (
+				5D3AFE8C1BED1598003EA2B2 /* Sources */,
+				5D3AFE8D1BED1598003EA2B2 /* Frameworks */,
+				5D3AFE8E1BED1598003EA2B2 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = SpritedAnimationViewExample;
+			productName = SpritedAnimationView;
+			productReference = 5D3AFE901BED1598003EA2B2 /* SpritedAnimationViewExample.app */;
+			productType = "com.apple.product-type.application";
+		};
+		5D3AFEA81BED1598003EA2B2 /* SpritedAnimationViewExampleTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5D3AFEC01BED1598003EA2B2 /* Build configuration list for PBXNativeTarget "SpritedAnimationViewExampleTests" */;
+			buildPhases = (
+				5D3AFEA51BED1598003EA2B2 /* Sources */,
+				5D3AFEA61BED1598003EA2B2 /* Frameworks */,
+				5D3AFEA71BED1598003EA2B2 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				5D3AFEAB1BED1598003EA2B2 /* PBXTargetDependency */,
+			);
+			name = SpritedAnimationViewExampleTests;
+			productName = SpritedAnimationViewTests;
+			productReference = 5D3AFEA91BED1598003EA2B2 /* SpritedAnimationViewExample.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		5D3AFE881BED1598003EA2B2 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0710;
+				ORGANIZATIONNAME = "Google Inc";
+				TargetAttributes = {
+					5D3AFE8F1BED1598003EA2B2 = {
+						CreatedOnToolsVersion = 7.1;
+					};
+					5D3AFEA81BED1598003EA2B2 = {
+						CreatedOnToolsVersion = 7.1;
+						TestTargetID = 5D3AFE8F1BED1598003EA2B2;
+					};
+				};
+			};
+			buildConfigurationList = 5D3AFE8B1BED1598003EA2B2 /* Build configuration list for PBXProject "SpritedAnimationViewExample" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 5D3AFE871BED1598003EA2B2;
+			productRefGroup = 5D3AFE911BED1598003EA2B2 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				5D3AFE8F1BED1598003EA2B2 /* SpritedAnimationViewExample */,
+				5D3AFEA81BED1598003EA2B2 /* SpritedAnimationViewExampleTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		5D3AFE8E1BED1598003EA2B2 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D3AFEA31BED1598003EA2B2 /* LaunchScreen.storyboard in Resources */,
+				5D3AFEA01BED1598003EA2B2 /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5D3AFEA71BED1598003EA2B2 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		5D3AFE8C1BED1598003EA2B2 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D3AFE9B1BED1598003EA2B2 /* ViewController.m in Sources */,
+				5D3AFE981BED1598003EA2B2 /* AppDelegate.m in Sources */,
+				5D3AFE951BED1598003EA2B2 /* main.m in Sources */,
+				5D971D701C90F9C9004729A7 /* GOSSpritedAnimationView.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5D3AFEA51BED1598003EA2B2 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D971D6C1C90F975004729A7 /* SpritedAnimationViewTests.m in Sources */,
+				5D971DB41C90FBED004729A7 /* GOSSpritedAnimationView.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		5D3AFEAB1BED1598003EA2B2 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 5D3AFE8F1BED1598003EA2B2 /* SpritedAnimationViewExample */;
+			targetProxy = 5D3AFEAA1BED1598003EA2B2 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		5D3AFEA11BED1598003EA2B2 /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				5D3AFEA21BED1598003EA2B2 /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		5D3AFEBB1BED1598003EA2B2 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		5D3AFEBC1BED1598003EA2B2 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		5D3AFEBE1BED1598003EA2B2 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				INFOPLIST_FILE = SpritedAnimationViewExample/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.gos.SpritedAnimationView;
+				PRODUCT_NAME = SpritedAnimationViewExample;
+			};
+			name = Debug;
+		};
+		5D3AFEBF1BED1598003EA2B2 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				INFOPLIST_FILE = SpritedAnimationViewExample/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.gos.SpritedAnimationView;
+				PRODUCT_NAME = SpritedAnimationViewExample;
+			};
+			name = Release;
+		};
+		5D3AFEC11BED1598003EA2B2 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				INFOPLIST_FILE = ../tests/unit/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.gos.SpritedAnimationViewTests;
+				PRODUCT_NAME = SpritedAnimationViewExample;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpritedAnimationViewExample.app/SpritedAnimationViewExample";
+			};
+			name = Debug;
+		};
+		5D3AFEC21BED1598003EA2B2 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				INFOPLIST_FILE = ../tests/unit/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.gos.SpritedAnimationViewTests;
+				PRODUCT_NAME = SpritedAnimationViewExample;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SpritedAnimationViewExample.app/SpritedAnimationViewExample";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		5D3AFE8B1BED1598003EA2B2 /* Build configuration list for PBXProject "SpritedAnimationViewExample" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5D3AFEBB1BED1598003EA2B2 /* Debug */,
+				5D3AFEBC1BED1598003EA2B2 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5D3AFEBD1BED1598003EA2B2 /* Build configuration list for PBXNativeTarget "SpritedAnimationViewExample" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5D3AFEBE1BED1598003EA2B2 /* Debug */,
+				5D3AFEBF1BED1598003EA2B2 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5D3AFEC01BED1598003EA2B2 /* Build configuration list for PBXNativeTarget "SpritedAnimationViewExampleTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5D3AFEC11BED1598003EA2B2 /* Debug */,
+				5D3AFEC21BED1598003EA2B2 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 5D3AFE881BED1598003EA2B2 /* Project object */;
+}
diff --git a/examples/SpritedAnimationViewExample/AppDelegate.h b/examples/SpritedAnimationViewExample/AppDelegate.h
new file mode 100644
index 0000000..77a9933
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/AppDelegate.h
@@ -0,0 +1,23 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property(strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/examples/SpritedAnimationViewExample/AppDelegate.m b/examples/SpritedAnimationViewExample/AppDelegate.m
new file mode 100644
index 0000000..2ac2c33
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/AppDelegate.m
@@ -0,0 +1,31 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import "AppDelegate.h"
+
+#import "ViewController.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application
+    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
+  self.window.rootViewController = [[ViewController alloc] init];
+  [self.window makeKeyAndVisible];
+  return YES;
+}
+
+@end
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/SpritedAnimationViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..85257ca
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,73 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "83.5x83.5",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/Contents.json b/examples/SpritedAnimationViewExample/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/Contents.json b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/Contents.json
new file mode 100644
index 0000000..efdc0bc
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "filename": "gos_sprite_check__hide.png",
+            "idiom": "universal",
+            "scale": "1x"
+        },
+        {
+            "filename": "gos_sprite_check__hide_2x.png",
+            "idiom": "universal",
+            "scale": "2x"
+        },
+        {
+            "filename": "gos_sprite_check__hide_3x.png",
+            "idiom": "universal",
+            "scale": "3x"
+        }
+    ],
+    "info": {
+        "author": "xcode",
+        "version": 1
+    }
+}
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide.png b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide.png
new file mode 100644
index 0000000..e800ee9
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide.png
Binary files differ
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide_2x.png b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide_2x.png
new file mode 100644
index 0000000..6ade10c
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide_2x.png
Binary files differ
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide_3x.png b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide_3x.png
new file mode 100644
index 0000000..88e4eed
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__hide.imageset/gos_sprite_check__hide_3x.png
Binary files differ
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/Contents.json b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/Contents.json
new file mode 100644
index 0000000..60c0662
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "filename": "gos_sprite_check__show.png",
+            "idiom": "universal",
+            "scale": "1x"
+        },
+        {
+            "filename": "gos_sprite_check__show_2x.png",
+            "idiom": "universal",
+            "scale": "2x"
+        },
+        {
+            "filename": "gos_sprite_check__show_3x.png",
+            "idiom": "universal",
+            "scale": "3x"
+        }
+    ],
+    "info": {
+        "author": "xcode",
+        "version": 1
+    }
+}
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show.png b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show.png
new file mode 100644
index 0000000..15f3b67
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show.png
Binary files differ
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show_2x.png b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show_2x.png
new file mode 100644
index 0000000..528b497
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show_2x.png
Binary files differ
diff --git a/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show_3x.png b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show_3x.png
new file mode 100644
index 0000000..298907d
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Assets.xcassets/gos_sprite_check__show.imageset/gos_sprite_check__show_3x.png
Binary files differ
diff --git a/examples/SpritedAnimationViewExample/Base.lproj/LaunchScreen.storyboard b/examples/SpritedAnimationViewExample/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..90d6157
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/examples/SpritedAnimationViewExample/Base.lproj/Main.storyboard b/examples/SpritedAnimationViewExample/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..fa9923d
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/examples/SpritedAnimationViewExample/Info.plist b/examples/SpritedAnimationViewExample/Info.plist
new file mode 100644
index 0000000..eabb3ae
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/Info.plist
@@ -0,0 +1,45 @@
+<?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>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/examples/SpritedAnimationViewExample/README.md b/examples/SpritedAnimationViewExample/README.md
new file mode 100644
index 0000000..45c43bd
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/README.md
@@ -0,0 +1,17 @@
+# SpritedAnimationView example app
+
+This app demonstrates how to create a sprited animation view using the
+GOSSpritedAnimationView.
+
+## Requirements
+
+Xcode 7.0 or higher.
+
+## Where is the code?
+
+The relevant code may be found in the 'App' directory.
+
+## Code structure
+
+This example shows how one might use a sprited animation view. A tap gesture is added to
+the view. When tapped, the animation starts. The control toggles between two sprite images.
diff --git a/examples/SpritedAnimationViewExample/ViewController.h b/examples/SpritedAnimationViewExample/ViewController.h
new file mode 100644
index 0000000..07d2f49
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/ViewController.h
@@ -0,0 +1,21 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+
+@end
diff --git a/examples/SpritedAnimationViewExample/ViewController.m b/examples/SpritedAnimationViewExample/ViewController.m
new file mode 100644
index 0000000..fbb0f6d
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/ViewController.m
@@ -0,0 +1,72 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import "ViewController.h"
+
+#import "GOSSpritedAnimationView.h"
+
+static NSString *const kSpriteChecked = @"gos_sprite_check__hide";
+static NSString *const kSpriteUnchecked = @"gos_sprite_check__show";
+
+@implementation ViewController {
+  GOSSpritedAnimationView *_animationView;
+  BOOL _checked;
+}
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+
+  _checked = YES;
+  self.view.backgroundColor = [UIColor whiteColor];
+
+  // Sprited animation view.
+  UIImage *spriteImage = [UIImage imageNamed:kSpriteChecked];
+  _animationView = [[GOSSpritedAnimationView alloc] initWithSpriteSheetImage:spriteImage];
+  _animationView.frame = CGRectMake(0, 0, 30, 30);
+  _animationView.center = self.view.center;
+  _animationView.tintColor = [UIColor blueColor];
+  [self.view addSubview:_animationView];
+
+  // Add label with tap instructions.
+  UILabel *label = [[UILabel alloc] initWithFrame:CGRectOffset(self.view.bounds, 0, 30)];
+  label.text = @"Tap anywhere to animate checkmark.";
+  label.textColor = [UIColor colorWithWhite:0 alpha:0.8];
+  label.textAlignment = NSTextAlignmentCenter;
+  [self.view addSubview:label];
+
+  // Add tap gesture to view.
+  UITapGestureRecognizer *tapGesture =
+      [[UITapGestureRecognizer alloc] initWithTarget:self
+                                              action:@selector(didTap:)];
+  [self.view addGestureRecognizer:tapGesture];
+}
+
+- (void)didTap:(UITapGestureRecognizer *)recognizer {
+  recognizer.enabled = NO;
+
+  // Animate the sprited view.
+  [_animationView startAnimatingWithCompletion:^{
+
+    // When animation completes, toggle image.
+    _checked = !_checked;
+    UIImage *spriteImage = [UIImage imageNamed:_checked ? kSpriteChecked : kSpriteUnchecked];
+    _animationView.spriteSheetImage = spriteImage;
+
+    recognizer.enabled = YES;
+  }];
+}
+
+@end
diff --git a/examples/SpritedAnimationViewExample/main.m b/examples/SpritedAnimationViewExample/main.m
new file mode 100644
index 0000000..d47e1c6
--- /dev/null
+++ b/examples/SpritedAnimationViewExample/main.m
@@ -0,0 +1,24 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char *argv[]) {
+  @autoreleasepool {
+    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+  }
+}
diff --git a/src/GOSSpritedAnimationView.h b/src/GOSSpritedAnimationView.h
new file mode 100644
index 0000000..b5e7676
--- /dev/null
+++ b/src/GOSSpritedAnimationView.h
@@ -0,0 +1,98 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import <UIKit/UIKit.h>
+
+/**
+ * Animation view for loading in sprites.
+ *
+ * This control provides an alternative to animating an array of images with a UIImageView. Only
+ * a single image composed of individual sprite frames is used, and animation simply consists of
+ * updating the layer contentsRect.
+ *
+ * @ingroup GOSSpritedAnimationView
+ */
+@interface GOSSpritedAnimationView : UIView
+
+/**
+ * Optional tint color of the icon.
+ *
+ * If set to non-nil, the sprite sheet will be treated as an alpha mask and the color will be
+ * applied.
+ */
+@property(nonatomic, strong, nullable) UIColor *tintColor;
+
+/**
+ * An image composed of a single column of individual sprite frames.
+ *
+ * Each sprite frame is assumed to be square.
+ */
+@property(nonatomic, strong, nullable) UIImage *spriteSheetImage;
+
+/**
+ * Framerate of the sprite playback in number of frames per second.
+ *
+ * Default is 60 frames per second.
+ */
+@property(nonatomic, assign) NSInteger frameRate;
+
+/**
+ * Number of times to repeat the animation.
+ *
+ * Setting to 0 means infinite. Default is 1.
+ */
+@property(nonatomic, assign) NSInteger animationRepeatCount;
+
+/**
+ * Returns whether the animation is playing.
+ *
+ * @return YES if the the sprite animation is playing.
+ */
+@property(nonatomic, readonly, getter=isAnimating) BOOL animating;
+
+/**
+ * Creates an animated sprite view.
+ *
+ * Designated initializer.
+ *
+ * @param spriteSheetImage A vertical sprite sheet of square images.
+ * @return Initialized sprited animation view.
+ */
+- (nonnull instancetype)initWithSpriteSheetImage:
+    (nullable UIImage *)spriteSheetImage NS_DESIGNATED_INITIALIZER;
+
+/** Please use initWithSpriteSheetImage:. */
+- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder NS_UNAVAILABLE;
+
+/**
+ * Start the animation.
+ *
+ * Does nothing if the animation is already playing.
+ *
+ * @param completion Block called when transition completes.
+ */
+- (void)startAnimatingWithCompletion:(nullable void (^)())completion;
+
+/** Stop the animation. */
+- (void)stop;
+
+/** Reset playing state to the first frame. */
+- (void)seekToBeginning;
+
+/** Reset playing state to the last frame. */
+- (void)seekToEnd;
+
+@end
diff --git a/src/GOSSpritedAnimationView.m b/src/GOSSpritedAnimationView.m
new file mode 100644
index 0000000..2caa8b9
--- /dev/null
+++ b/src/GOSSpritedAnimationView.m
@@ -0,0 +1,196 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import "GOSSpritedAnimationView.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+static NSString *const kGOSSpriteAnimationKey = @"spriteAnimate";
+static const NSInteger kGOSSpriteFrameRateDefault = 60;
+
+@interface GOSSpritedAnimationView ()
+@property(nonatomic, assign) NSInteger numberOfFrames;
+@property(nonatomic, assign) CGFloat singleFrameWidthInPercent;  // 1 / numberOfFrames
+@property(nonatomic, strong) CALayer *spriteLayer;
+@end
+
+@implementation GOSSpritedAnimationView
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+  [self doesNotRecognizeSelector:_cmd];
+  return nil;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [self initWithSpriteSheetImage:nil];
+  if (self) {
+    self.frame = frame;
+  }
+  return self;
+}
+
+- (instancetype)initWithSpriteSheetImage:(UIImage *)spriteSheetImage {
+  self = [super initWithFrame:CGRectZero];
+  if (self) {
+    self.backgroundColor = [UIColor clearColor];
+    self.opaque = NO;
+    self.userInteractionEnabled = NO;
+
+    _spriteLayer = [CALayer layer];
+    _spriteLayer.bounds = self.layer.bounds;
+    [self.layer addSublayer:_spriteLayer];
+
+    _frameRate = kGOSSpriteFrameRateDefault;
+    _singleFrameWidthInPercent = 1;
+    _animationRepeatCount = 1;
+    [self setSpriteSheetImage:spriteSheetImage];
+  }
+  return self;
+}
+
+- (void)layoutSubviews {
+  [super layoutSubviews];
+
+  CGRect bounds = self.bounds;
+  self.spriteLayer.position = CGPointMake(bounds.size.width / 2.f, bounds.size.height / 2.f);
+  self.spriteLayer.bounds = self.bounds;
+}
+
+- (void)startAnimatingWithCompletion:(void (^)())completion {
+  [CATransaction begin];
+  [CATransaction setCompletionBlock:completion];
+
+  NSMutableArray *linearValues = [NSMutableArray array];
+  NSMutableArray *keyTimes = [NSMutableArray array];
+  for (NSInteger i = 0; i < _numberOfFrames; i++) {
+    CGRect contentsRect =
+        CGRectMake(0, i * _singleFrameWidthInPercent, 1, _singleFrameWidthInPercent);
+    [linearValues addObject:[NSValue valueWithCGRect:contentsRect]];
+    [keyTimes addObject:@(i * _singleFrameWidthInPercent)];
+  }
+
+  CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
+  animation.duration = (NSTimeInterval)_numberOfFrames / (NSTimeInterval)_frameRate;
+  animation.values = linearValues;
+  animation.keyTimes = keyTimes;
+  animation.keyPath = NSStringFromSelector(@selector(contentsRect));
+  animation.calculationMode = kCAAnimationDiscrete;
+  animation.repeatCount = (_animationRepeatCount == 0) ? HUGE_VALF : _animationRepeatCount;
+  if (_animationRepeatCount == 1) {
+    animation.fillMode = kCAFillModeForwards;
+    animation.removedOnCompletion = NO;
+  }
+
+  [self.spriteLayer addAnimation:animation forKey:kGOSSpriteAnimationKey];
+  [CATransaction commit];
+}
+
+- (void)stop {
+  // Removing the animation will cause the completion block to be also called.
+  [self.spriteLayer removeAnimationForKey:kGOSSpriteAnimationKey];
+}
+
+- (void)seekToBeginning {
+  self.spriteLayer.contentsRect = CGRectMake(0, 0, 1, _singleFrameWidthInPercent);
+  [self.spriteLayer setNeedsDisplay];
+}
+
+- (void)seekToEnd {
+  self.spriteLayer.contentsRect =
+      CGRectMake(0, 1.0f - _singleFrameWidthInPercent, 1, _singleFrameWidthInPercent);
+  [self.layer setNeedsDisplay];
+}
+
+- (BOOL)isAnimating {
+  return ([self.spriteLayer animationForKey:kGOSSpriteAnimationKey] != nil);
+}
+
+#pragma mark - Mask Color Handling
+
+- (UIImage *)colorizedSpriteSheet:(UIImage *)spriteSheet {
+  if (!_tintColor) {
+    return spriteSheet;
+  }
+
+  if (!spriteSheet) {
+    return spriteSheet;
+  }
+
+  CGSize spritesSize = [spriteSheet size];
+
+  UIGraphicsBeginImageContextWithOptions(spritesSize, NO, 0);
+  CGContextRef context = UIGraphicsGetCurrentContext();
+
+  // Flip context for CGImageRef drawing.
+  CGContextTranslateCTM(context, 0, spritesSize.height);
+  CGContextScaleCTM(context, 1.0, -1.0);
+
+  CGRect rect = CGRectMake(0, 0, spritesSize.width, spritesSize.height);
+
+  // Draw original image.
+  CGContextSetBlendMode(context, kCGBlendModeNormal);
+  CGContextDrawImage(context, rect, spriteSheet.CGImage);
+
+  // Overlay the foreground color based on the alpha of the pixels in the context.
+  CGContextSetBlendMode(context, kCGBlendModeSourceIn);
+  [_tintColor setFill];
+  CGContextFillRect(context, rect);
+
+  UIImage *colorizedSpriteSheet = UIGraphicsGetImageFromCurrentImageContext();
+  UIGraphicsEndImageContext();
+  return colorizedSpriteSheet;
+}
+
+- (void)updateSpriteAnimationLayer {
+  CGSize spriteSheetSize = [_spriteSheetImage size];
+  CGFloat singleFrameWidth = spriteSheetSize.width;
+
+  CALayer *layer = self.spriteLayer;
+  layer.contents = (id)_spriteSheetImage.CGImage;
+  layer.bounds = CGRectMake(0, 0, singleFrameWidth, singleFrameWidth);
+  layer.contentsRect = CGRectMake(0, 0, 1, _singleFrameWidthInPercent);
+}
+
+#pragma mark Setters
+
+- (void)setTintColor:(UIColor *)tintColor {
+  if (_tintColor == tintColor) {
+    return;
+  }
+
+  _tintColor = tintColor;
+  _spriteSheetImage = [self colorizedSpriteSheet:_spriteSheetImage];
+
+  [self updateSpriteAnimationLayer];
+}
+
+- (void)setSpriteSheetImage:(UIImage *)spriteSheetImage {
+  if (!spriteSheetImage) {
+    _spriteSheetImage = spriteSheetImage;
+    return;
+  }
+
+  CGSize spriteSheetSize = [spriteSheetImage size];
+  CGFloat singleFrameWidth = spriteSheetSize.width;
+  _numberOfFrames = (NSInteger)floor(spriteSheetSize.height / singleFrameWidth);
+  _singleFrameWidthInPercent = 1.0f / _numberOfFrames;
+  _spriteSheetImage = [self colorizedSpriteSheet:spriteSheetImage];
+
+  [self updateSpriteAnimationLayer];
+  [self.spriteLayer removeAllAnimations];
+}
+
+@end
diff --git a/tests/unit/Info.plist b/tests/unit/Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/tests/unit/Info.plist
@@ -0,0 +1,24 @@
+<?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>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/tests/unit/SpritedAnimationViewTests.m b/tests/unit/SpritedAnimationViewTests.m
new file mode 100644
index 0000000..3cc09d6
--- /dev/null
+++ b/tests/unit/SpritedAnimationViewTests.m
@@ -0,0 +1,85 @@
+/*
+ Copyright 2015-present Google Inc. All Rights Reserved.
+
+ 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.
+ */
+
+#import <XCTest/XCTest.h>
+
+#import "GOSSpritedAnimationView.h"
+
+static NSString *const kSpriteChecked = @"gos_sprite_check__hide";
+static NSString *const kExpectationDescription = @"animatingWithCompletion";
+
+@interface SpritedAnimationViewTests : XCTestCase
+
+@end
+
+@implementation SpritedAnimationViewTests
+
+- (void)setUp {
+  [super setUp];
+}
+
+- (void)tearDown {
+  [super tearDown];
+}
+
+- (void)testAnimationCompletion {
+  // Sprited animation view.
+  UIImage *spriteImage = [UIImage imageNamed:kSpriteChecked];
+  GOSSpritedAnimationView *animationView =
+      [[GOSSpritedAnimationView alloc] initWithSpriteSheetImage:spriteImage];
+
+  // Create expectation.
+  XCTestExpectation *expectation = [self expectationWithDescription:kExpectationDescription];
+
+  // Fulfill expectation after completion of animation.
+  [animationView startAnimatingWithCompletion:^{
+    [expectation fulfill];
+  }];
+
+  [self waitForExpectationsWithTimeout:1.0
+                               handler:^(NSError *error) {
+                                 XCTAssertEqual(error, nil);
+                               }];
+}
+
+- (void)testAnimationPerformance {
+  NSArray *metrics = [[self class] defaultPerformanceMetrics];
+  [self measureMetrics:metrics
+      automaticallyStartMeasuring:NO
+                         forBlock:^{
+                           [self startMeasuring];
+
+                           // Sprited animation view.
+                           UIImage *spriteImage = [UIImage imageNamed:kSpriteChecked];
+                           GOSSpritedAnimationView *animationView =
+                               [[GOSSpritedAnimationView alloc] initWithSpriteSheetImage:spriteImage];
+
+                           // Create expectation.
+                           XCTestExpectation *expectation = [self expectationWithDescription:kExpectationDescription];
+
+                           // Fulfill expectation after completion of animation.
+                           [animationView startAnimatingWithCompletion:^{
+                             [expectation fulfill];
+                           }];
+
+                           [self waitForExpectationsWithTimeout:1.0
+                                                        handler:^(NSError *error) {
+                                                          [self stopMeasuring];
+                                                        }];
+                         }];
+}
+
+@end