Merge pull request #3 from material-foundation/develop

Merged with latest MDC changes and bumped version to 3.0.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1fdb87d..cb7b46d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 3.0.0
+
+##### Breaking
+
+* Pulled in latest changes from [MDC](https://github.com/material-components/material-components-ios/commits/develop/components/SpritedAnimationView) 
+up to and including [sha 2d654](https://github.com/material-components/material-components-ios/commit/2d654)
+
 ## 2.0.0
 
 ##### Breaking
diff --git a/MDFSpritedAnimationView.podspec b/MDFSpritedAnimationView.podspec
index 4ce24f7..0e00b0a 100644
--- a/MDFSpritedAnimationView.podspec
+++ b/MDFSpritedAnimationView.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name         = "MDFSpritedAnimationView"
-  s.version      = "2.0.0"
+  s.version      = "3.0.0"
   s.authors      = { 'Chris Cox' => 'cjcox@google.com' }
   s.summary      = "A control providing an alternative to animating an array of images with an UIImageView."
   s.homepage     = "https://github.com/material-foundation/MDFSpritedAnimationView"
diff --git a/README.md b/README.md
index be940d0..266d093 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,18 @@
 # MDFSpritedAnimationView
 [![Build Status](https://travis-ci.org/google/MDFSpritedAnimationView.svg?branch=master)](https://travis-ci.org/google/MDFSpritedAnimationView)
 
+![SpritedAnimationView](docs/assets/sprited_animation_view.png)
+<!--{: .ios-screenshot .right }-->
+
 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`.
+<!--{: .intro }-->
 
-## Installation
+## Requirements
 
-### Requirements
-
-- Xcode 7.0 or higher.
-- iOS SDK version 7.0 or higher.
-
-### Installation with CocoaPods
-
-To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`:
-
-~~~ bash
-pod 'MDFScrollViewDelegateMultiplexer'
-~~~
-
-Then, run the following command:
-
-~~~ bash
-$ pod install
-~~~
-
-- - -
+- Xcode 7.0 or higher
+- iOS SDK version 7.0 or higher
 
 ## Create a sprite sheet asset
 
@@ -58,13 +44,13 @@
 
 | List Sprite Sheet | Grid Sprite Sheet |
 | :---------------------------: | :---------------------------: |
-| ![List Icon](examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid.png) | ![Grid Icon](examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list.png) |
+| ![List Icon](examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid.png) | ![Grid Icon](examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list.png) |
 
 #### Two-state example
 
 ```objectivec
 // Animate the sprited view.
-[_animationView startAnimatingWithCompletion:^{
+[_animationView startAnimatingWithCompletion:^(BOOL finished) {
 
   // When animation completes, toggle image.
   _toggle = !_toggle;
@@ -76,10 +62,27 @@
 
 ## Usage
 
+### Importing
+
+Before using Sprited Animation View, you'll need to import it:
+
+<!--<div class="material-code-render" markdown="1">-->
+#### Objective-C
+
+~~~ objc
+#import "MaterialSpritedAnimationView.h"
+~~~
+
+#### Swift
+~~~ swift
+import MaterialComponents
+~~~
+<!--</div>-->
+
 Integrating the `spritedAnimationView` is somewhat similar to adding an `UIImageView` to a view.
 
 ```objectivec
-#import "MDFSpritedAnimationView.h"
+#import "MaterialSpritedAnimationView.h"
 
 // Create a Sprited Animation View.
 UIImage *spriteSheet = [UIImage imageNamed:@"myImage"];
@@ -89,7 +92,7 @@
 [self.view addSubview:animationView];
 
 // To Animate.
-[animationView startAnimatingWithCompletion:^{
+[animationView startAnimatingWithCompletion:^(BOOL finished) {
     NSLog(@"Done animating.");
 }];
 ```
diff --git a/docs/assets/sprited_animation_view.png b/docs/assets/sprited_animation_view.png
new file mode 100644
index 0000000..ab30e55
--- /dev/null
+++ b/docs/assets/sprited_animation_view.png
Binary files differ
diff --git a/examples/SpritedAnimationViewTypicalUseViewController.m b/examples/SpritedAnimationViewTypicalUseViewController.m
index b37e574..a683386 100644
--- a/examples/SpritedAnimationViewTypicalUseViewController.m
+++ b/examples/SpritedAnimationViewTypicalUseViewController.m
@@ -14,25 +14,30 @@
  limitations under the License.
  */
 
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
 #import "SpritedAnimationViewTypicalUseViewController.h"
 
-#import "MDFSpritedAnimationView.h"
+#import "MaterialSpritedAnimationView.h"
 
-static NSString *const kSpriteList = @"gos_sprite_list__grid";
-static NSString *const kSpriteGrid = @"gos_sprite_grid__list";
+static NSString *const kSpriteList = @"sprite_list__grid";
+static NSString *const kSpriteGrid = @"sprite_grid__list";
 
 @implementation SpritedAnimationViewTypicalUseViewController {
   MDFSpritedAnimationView *_animationView;
   BOOL _toggle;
 }
 
-+ (NSArray *)catalogHierarchy {
-  // Support for catalog by convention.
-  return @[ @"Sprited Animation View", @"Typical use" ];
+// TODO: Support other categorizational methods.
++ (NSArray *)catalogBreadcrumbs {
+  return @[ @"Sprited Animation View", @"Sprited Animation View" ];
+}
+
++ (NSString *)catalogDescription {
+  return @"This control provides an alternative to animating an array of images with an"
+          " UIImageView.";
+}
+
++ (BOOL)catalogIsPrimaryDemo {
+  return YES;
 }
 
 - (void)viewDidLoad {
@@ -41,26 +46,103 @@
   self.view.backgroundColor = [UIColor whiteColor];
 
   NSBundle *bundle = [NSBundle bundleForClass:[self class]];
-  UIImage *spriteImage = [UIImage imageNamed:kSpriteList
-                                    inBundle:bundle
-               compatibleWithTraitCollection:nil];
+  UIImage *spriteImage =
+      [UIImage imageNamed:kSpriteList inBundle:bundle compatibleWithTraitCollection:nil];
   _animationView = [[MDFSpritedAnimationView alloc] initWithSpriteSheetImage:spriteImage];
   _animationView.frame = CGRectMake(0, 0, 30, 30);
   _animationView.center = self.view.center;
-  _animationView.tintColor = [UIColor blueColor];
+  _animationView.translatesAutoresizingMaskIntoConstraints = NO;
+  UIColor *blueColor = [UIColor colorWithRed:0.012 green:0.663 blue:0.957 alpha:1];
+  _animationView.tintColor = blueColor;
   [self.view addSubview:_animationView];
 
+  // AnimationView Layout Constraints
+  NSLayoutConstraint *animationViewWidthConstraint =
+      [NSLayoutConstraint constraintWithItem:_animationView
+                                   attribute:NSLayoutAttributeWidth
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:nil
+                                   attribute:NSLayoutAttributeNotAnAttribute
+                                  multiplier:1.0
+                                    constant:30];
+  animationViewWidthConstraint.active = true;
+  NSLayoutConstraint *animationViewHeightConstraint =
+      [NSLayoutConstraint constraintWithItem:_animationView
+                                   attribute:NSLayoutAttributeHeight
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:nil
+                                   attribute:NSLayoutAttributeNotAnAttribute
+                                  multiplier:1.0
+                                    constant:30];
+  animationViewHeightConstraint.active = true;
+  NSLayoutConstraint *animationViewXConstraint =
+      [NSLayoutConstraint constraintWithItem:self.view
+                                   attribute:NSLayoutAttributeCenterX
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:_animationView
+                                   attribute:NSLayoutAttributeCenterX
+                                  multiplier:1.0
+                                    constant:0.0];
+  animationViewXConstraint.active = true;
+  NSLayoutConstraint *animationViewYConstraint =
+      [NSLayoutConstraint constraintWithItem:self.view
+                                   attribute:NSLayoutAttributeCenterY
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:_animationView
+                                   attribute:NSLayoutAttributeCenterY
+                                  multiplier:1.0
+                                    constant:0.0];
+  animationViewYConstraint.active = true;
+
   // Add label with tap instructions.
   UILabel *label = [[UILabel alloc] initWithFrame:CGRectOffset(self.view.bounds, 0, 30)];
+  label.translatesAutoresizingMaskIntoConstraints = NO;
   label.text = @"Tap anywhere to animate icon.";
   label.textColor = [UIColor colorWithWhite:0 alpha:0.8];
   label.textAlignment = NSTextAlignmentCenter;
   [self.view addSubview:label];
 
+  // Label Layout Constraints
+  NSLayoutConstraint *labelWidthConstraint =
+      [NSLayoutConstraint constraintWithItem:label
+                                   attribute:NSLayoutAttributeWidth
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:self.view
+                                   attribute:NSLayoutAttributeWidth
+                                  multiplier:1.0
+                                    constant:0.0];
+  labelWidthConstraint.active = true;
+  NSLayoutConstraint *labelHeightConstraint =
+      [NSLayoutConstraint constraintWithItem:label
+                                   attribute:NSLayoutAttributeHeight
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:self.view
+                                   attribute:NSLayoutAttributeHeight
+                                  multiplier:1.0
+                                    constant:0.0];
+  labelHeightConstraint.active = true;
+  NSLayoutConstraint *labelXConstraint =
+      [NSLayoutConstraint constraintWithItem:label
+                                   attribute:NSLayoutAttributeCenterX
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:self.view
+                                   attribute:NSLayoutAttributeCenterX
+                                  multiplier:1.0
+                                    constant:0.0];
+  labelXConstraint.active = true;
+  NSLayoutConstraint *labelYConstraint =
+      [NSLayoutConstraint constraintWithItem:label
+                                   attribute:NSLayoutAttributeCenterY
+                                   relatedBy:NSLayoutRelationEqual
+                                      toItem:self.view
+                                   attribute:NSLayoutAttributeCenterY
+                                  multiplier:1.0
+                                    constant:30.0];
+  labelYConstraint.active = true;
+
   // Add tap gesture to view.
   UITapGestureRecognizer *tapGesture =
-      [[UITapGestureRecognizer alloc] initWithTarget:self
-                                              action:@selector(didTap:)];
+      [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)];
   [self.view addGestureRecognizer:tapGesture];
 }
 
@@ -68,7 +150,7 @@
   recognizer.enabled = NO;
 
   // Animate the sprited view.
-  [_animationView startAnimatingWithCompletion:^{
+  [_animationView startAnimatingWithCompletion:^(BOOL finished) {
 
     // When animation completes, toggle image.
     _toggle = !_toggle;
diff --git a/examples/app/Podfile.lock b/examples/app/Podfile.lock
index 0daec6f..bdac9e5 100644
--- a/examples/app/Podfile.lock
+++ b/examples/app/Podfile.lock
@@ -1,5 +1,5 @@
 PODS:
-  - MDFSpritedAnimationView (2.0.0)
+  - MDFSpritedAnimationView (3.0.0)
 
 DEPENDENCIES:
   - MDFSpritedAnimationView (from `../../`)
@@ -9,7 +9,7 @@
     :path: "../../"
 
 SPEC CHECKSUMS:
-  MDFSpritedAnimationView: 91e3a415e50ec36ea19c0fcb46905eaaff2f16fc
+  MDFSpritedAnimationView: 75dead8462287f526818520af1e377226c3bb671
 
 PODFILE CHECKSUM: 80195f268febc54d39de014ff5b21653bbb11547
 
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/Contents.json b/examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/Contents.json
deleted file mode 100644
index e2a76ca..0000000
--- a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "filename": "gos_sprite_grid__list.png", 
-            "idiom": "universal", 
-            "scale": "1x"
-        }, 
-        {
-            "filename": "gos_sprite_grid__list_2x.png", 
-            "idiom": "universal", 
-            "scale": "2x"
-        }, 
-        {
-            "filename": "gos_sprite_grid__list_3x.png", 
-            "idiom": "universal", 
-            "scale": "3x"
-        }
-    ], 
-    "info": {
-        "author": "xcode", 
-        "version": 1
-    }
-}
\ No newline at end of file
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/Contents.json b/examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/Contents.json
deleted file mode 100644
index 66faadd..0000000
--- a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "filename": "gos_sprite_list__grid.png", 
-            "idiom": "universal", 
-            "scale": "1x"
-        }, 
-        {
-            "filename": "gos_sprite_list__grid_2x.png", 
-            "idiom": "universal", 
-            "scale": "2x"
-        }, 
-        {
-            "filename": "gos_sprite_list__grid_3x.png", 
-            "idiom": "universal", 
-            "scale": "3x"
-        }
-    ], 
-    "info": {
-        "author": "xcode", 
-        "version": 1
-    }
-}
\ No newline at end of file
diff --git a/examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/Contents.json b/examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/Contents.json
new file mode 100644
index 0000000..eb09a34
--- /dev/null
+++ b/examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "sprite_grid__list.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "sprite_grid__list@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "sprite_grid__list@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list.png b/examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list.png
similarity index 100%
rename from examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list.png
rename to examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list.png
Binary files differ
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list_2x.png b/examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list@2x.png
similarity index 100%
rename from examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list_2x.png
rename to examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list@2x.png
Binary files differ
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list_3x.png b/examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list@3x.png
similarity index 100%
rename from examples/resources/SpritedAnimationView.xcassets/gos_sprite_grid__list.imageset/gos_sprite_grid__list_3x.png
rename to examples/resources/SpritedAnimationView.xcassets/sprite_grid__list.imageset/sprite_grid__list@3x.png
Binary files differ
diff --git a/examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/Contents.json b/examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/Contents.json
new file mode 100644
index 0000000..9580d40
--- /dev/null
+++ b/examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "sprite_list__grid.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "sprite_list__grid@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "sprite_list__grid@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid.png b/examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid.png
similarity index 100%
rename from examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid.png
rename to examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid.png
Binary files differ
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid_2x.png b/examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid@2x.png
similarity index 100%
rename from examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid_2x.png
rename to examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid@2x.png
Binary files differ
diff --git a/examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid_3x.png b/examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid@3x.png
similarity index 100%
rename from examples/resources/SpritedAnimationView.xcassets/gos_sprite_list__grid.imageset/gos_sprite_list__grid_3x.png
rename to examples/resources/SpritedAnimationView.xcassets/sprite_list__grid.imageset/sprite_list__grid@3x.png
Binary files differ
diff --git a/src/MDFSpritedAnimationView.h b/src/MDFSpritedAnimationView.h
index ae73047..a368ed9 100644
--- a/src/MDFSpritedAnimationView.h
+++ b/src/MDFSpritedAnimationView.h
@@ -69,8 +69,19 @@
  @param spriteSheetImage A vertical sprite sheet of square images.
  @return Initialized sprited animation view.
  */
-- (nonnull instancetype)initWithSpriteSheetImage:
-        (nullable UIImage *)spriteSheetImage NS_DESIGNATED_INITIALIZER;
+- (nonnull instancetype)initWithSpriteSheetImage:(nullable UIImage *)spriteSheetImage
+    NS_DESIGNATED_INITIALIZER;
+
+/**
+ Creates an animated sprite view. Use this initializer if your images are non-square.
+
+ @param spriteSheetImage A vertical sprite sheet of images.
+ @param numberOfFrames The number of frames in the sprite sheet image. Used for calculating
+      the size of each frame.
+ @return Initialized sprited animation view.
+ */
+- (nonnull instancetype)initWithSpriteSheetImage:(nullable UIImage *)spriteSheetImage
+                                  numberOfFrames:(NSInteger)numberOfFrames;
 
 /** Please use initWithSpriteSheetImage:. */
 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder NS_UNAVAILABLE;
@@ -82,7 +93,7 @@
 
  @param completion Block called when transition completes.
  */
-- (void)startAnimatingWithCompletion:(nullable void (^)())completion;
+- (void)startAnimatingWithCompletion:(nullable void (^)(BOOL finished))completion;
 
 /** Stop the animation. */
 - (void)stop;
diff --git a/src/MDFSpritedAnimationView.m b/src/MDFSpritedAnimationView.m
index 02c4d8e..42f5414 100644
--- a/src/MDFSpritedAnimationView.m
+++ b/src/MDFSpritedAnimationView.m
@@ -14,14 +14,20 @@
  limitations under the License.
  */
 
-#import "MDFSpritedAnimationView.h"
+#import "MaterialSpritedAnimationView.h"
 
 #import <QuartzCore/QuartzCore.h>
 
 static NSString *const kSpriteAnimationKey = @"spriteAnimate";
 static const NSInteger kSpriteFrameRateDefault = 60;
 
+#if defined(__IPHONE_10_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)
+@interface MDFSpritedAnimationView () <CAAnimationDelegate>
+@end
+#endif
+
 @interface MDFSpritedAnimationView ()
+@property(nonatomic, copy) void (^pendingCompletionBlock)(BOOL finished);
 @property(nonatomic, assign) NSInteger numberOfFrames;
 @property(nonatomic, assign) CGFloat singleFrameWidthInPercent;  // 1 / numberOfFrames
 @property(nonatomic, strong) CALayer *spriteLayer;
@@ -61,6 +67,24 @@
   return self;
 }
 
+- (instancetype)initWithSpriteSheetImage:(UIImage *)spriteSheetImage
+                          numberOfFrames:(NSInteger)numberOfFrames {
+  MDFSpritedAnimationView *animationView = [self initWithSpriteSheetImage:spriteSheetImage];
+  animationView.numberOfFrames = numberOfFrames;
+  animationView.singleFrameWidthInPercent = 1.0f / numberOfFrames;
+  [animationView updateSpriteAnimationLayer];
+
+  return animationView;
+}
+
+- (CGSize)intrinsicContentSize {
+  if (_spriteSheetImage) {
+    CGFloat width = _spriteSheetImage.size.width;
+    return CGSizeMake(width, width);
+  }
+  return [super intrinsicContentSize];
+}
+
 - (void)layoutSubviews {
   [super layoutSubviews];
 
@@ -69,12 +93,13 @@
   self.spriteLayer.bounds = self.bounds;
 }
 
-- (void)startAnimatingWithCompletion:(void (^)())completion {
-  [CATransaction begin];
-  [CATransaction setCompletionBlock:completion];
+- (void)startAnimatingWithCompletion:(void (^)(BOOL finished))completion {
+  [self stop];
 
-  NSMutableArray *linearValues = [NSMutableArray array];
-  NSMutableArray *keyTimes = [NSMutableArray array];
+  self.pendingCompletionBlock = completion;
+
+  NSMutableArray<NSValue *> *linearValues = [NSMutableArray array];
+  NSMutableArray<NSNumber *> *keyTimes = [NSMutableArray array];
   for (NSInteger i = 0; i < _numberOfFrames; i++) {
     CGRect contentsRect =
         CGRectMake(0, i * _singleFrameWidthInPercent, 1, _singleFrameWidthInPercent);
@@ -83,6 +108,7 @@
   }
 
   CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
+  animation.delegate = self;
   animation.duration = (NSTimeInterval)_numberOfFrames / (NSTimeInterval)_frameRate;
   animation.values = linearValues;
   animation.keyTimes = keyTimes;
@@ -95,22 +121,37 @@
   }
 
   [self.spriteLayer addAnimation:animation forKey:kSpriteAnimationKey];
-  [CATransaction commit];
 }
 
 - (void)stop {
-  // Removing the animation will cause the completion block to be also called.
+  // Removing the animation will trigger |animationDidStop| and therefore the completion block, but
+  // there is no guarantee it happens atomically so to ensure predictable call-order we manually
+  // trigger the completion block here.
+  void (^block)(BOOL cancelled) = self.pendingCompletionBlock;
+  self.pendingCompletionBlock = nil;
+
+  if (block) {
+    block(NO);
+  }
   [self.spriteLayer removeAnimationForKey:kSpriteAnimationKey];
 }
 
 - (void)seekToBeginning {
+  [self stop];
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
   self.spriteLayer.contentsRect = CGRectMake(0, 0, 1, _singleFrameWidthInPercent);
+  [CATransaction commit];
   [self.spriteLayer setNeedsDisplay];
 }
 
 - (void)seekToEnd {
+  [self stop];
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
   self.spriteLayer.contentsRect =
       CGRectMake(0, 1.0f - _singleFrameWidthInPercent, 1, _singleFrameWidthInPercent);
+  [CATransaction commit];
   [self.layer setNeedsDisplay];
 }
 
@@ -158,13 +199,31 @@
   CGSize spriteSheetSize = [_spriteSheetImage size];
   CGFloat singleFrameWidth = spriteSheetSize.width;
 
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
+  // Disable implicit animations for these assignments
   CALayer *layer = self.spriteLayer;
   layer.contents = (id)_spriteSheetImage.CGImage;
   layer.bounds = CGRectMake(0, 0, singleFrameWidth, singleFrameWidth);
   layer.contentsRect = CGRectMake(0, 0, 1, _singleFrameWidthInPercent);
+  [CATransaction commit];
 }
 
-#pragma mark Setters
+#pragma mark - CAAnimationDelegate
+
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)finished {
+  if (anim != [self.spriteLayer animationForKey:kSpriteAnimationKey]) {
+    return;
+  }
+  void (^block)(BOOL cancelled) = self.pendingCompletionBlock;
+  self.pendingCompletionBlock = nil;
+
+  if (block) {
+    block(finished);
+  }
+}
+
+#pragma mark - Setters
 
 - (void)setTintColor:(UIColor *)tintColor {
   if (_tintColor == tintColor) {
@@ -178,6 +237,7 @@
 }
 
 - (void)setSpriteSheetImage:(UIImage *)spriteSheetImage {
+  [self stop];
   if (!spriteSheetImage) {
     _spriteSheetImage = spriteSheetImage;
     return;
@@ -188,9 +248,8 @@
   _numberOfFrames = (NSInteger)floor(spriteSheetSize.height / singleFrameWidth);
   _singleFrameWidthInPercent = 1.0f / _numberOfFrames;
   _spriteSheetImage = [self colorizedSpriteSheet:spriteSheetImage];
-
+  [self invalidateIntrinsicContentSize];
   [self updateSpriteAnimationLayer];
-  [self.spriteLayer removeAllAnimations];
 }
 
 @end
diff --git a/src/MaterialSpritedAnimationView.h b/src/MaterialSpritedAnimationView.h
new file mode 100644
index 0000000..5df1f1c
--- /dev/null
+++ b/src/MaterialSpritedAnimationView.h
@@ -0,0 +1,17 @@
+/*
+ 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 "MDFSpritedAnimationView.h"
diff --git a/tests/unit/MDFSpritedAnimationViewTests.m b/tests/unit/MDFSpritedAnimationViewTests.m
index 65960e3..37b6628 100644
--- a/tests/unit/MDFSpritedAnimationViewTests.m
+++ b/tests/unit/MDFSpritedAnimationViewTests.m
@@ -16,9 +16,9 @@
 
 #import <XCTest/XCTest.h>
 
-#import "MDFSpritedAnimationView.h"
+#import "MaterialSpritedAnimationView.h"
 
-static NSString *const kSpriteList = @"gos_sprite_list__grid";
+static NSString *const kSpriteList = @"mdc_sprite_list__grid";
 static NSString *const kExpectationDescription = @"animatingWithCompletion";
 
 @interface SpritedAnimationViewTests : XCTestCase
@@ -45,7 +45,7 @@
   XCTestExpectation *expectation = [self expectationWithDescription:kExpectationDescription];
 
   // Fulfill expectation after completion of animation.
-  [animationView startAnimatingWithCompletion:^{
+  [animationView startAnimatingWithCompletion:^(BOOL completion) {
     [expectation fulfill];
   }];
 
@@ -71,8 +71,8 @@
                            XCTestExpectation *expectation = [self expectationWithDescription:kExpectationDescription];
 
                            // Fulfill expectation after completion of animation.
-                           [animationView startAnimatingWithCompletion:^{
-                             [expectation fulfill];
+                           [animationView startAnimatingWithCompletion:^(BOOL finished) {
+                               [expectation fulfill];
                            }];
 
                            [self waitForExpectationsWithTimeout:1.0