Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.

Commit 5a3d65e

Browse files
authored
Always commit the model layer value inside a transaction with actions disabled. (#54)
* Always commit the model layer value inside a CATransaction with actions disabled. If the timing's duration was 0 or the animation was instant we were setting the model layer value without disabling actions, which could potentially result in an animation occurring. The proper model value setting logic has now been moved to the top of the animate method and the duplicate logic has been removed. * Add tests.
1 parent 89abc77 commit 5a3d65e

File tree

3 files changed

+89
-8
lines changed

3 files changed

+89
-8
lines changed

examples/apps/Catalog/MotionAnimatorCatalog.xcodeproj/project.pbxproj

+6-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
66DD4BF51EEF0ECB00207119 /* CalendarCardExpansionExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 66DD4BF41EEF0ECB00207119 /* CalendarCardExpansionExample.m */; };
2424
66DD4BF81EEF1C4B00207119 /* CalendarChipMotionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 66DD4BF71EEF1C4B00207119 /* CalendarChipMotionSpec.m */; };
2525
66EF6F281FC33C4800C83A63 /* HeadlessLayerImplicitAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66EF6F271FC33C4800C83A63 /* HeadlessLayerImplicitAnimationTests.swift */; };
26+
66EF6F2A1FC48D6A00C83A63 /* InstantAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66EF6F291FC48D6A00C83A63 /* InstantAnimationTests.swift */; };
2627
66FD99FA1EE9FBBE00C53A82 /* MotionAnimatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 66FD99F91EE9FBBE00C53A82 /* MotionAnimatorTests.m */; };
2728
FCA09739CCE089F0D051AA87 /* Pods_MotionAnimatorCatalog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50D808A6F9E944D54276D32F /* Pods_MotionAnimatorCatalog.framework */; };
2829
/* End PBXBuildFile section */
@@ -71,6 +72,7 @@
7172
66DD4BF61EEF1C4B00207119 /* CalendarChipMotionSpec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CalendarChipMotionSpec.h; sourceTree = "<group>"; };
7273
66DD4BF71EEF1C4B00207119 /* CalendarChipMotionSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CalendarChipMotionSpec.m; sourceTree = "<group>"; };
7374
66EF6F271FC33C4800C83A63 /* HeadlessLayerImplicitAnimationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlessLayerImplicitAnimationTests.swift; sourceTree = "<group>"; };
75+
66EF6F291FC48D6A00C83A63 /* InstantAnimationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantAnimationTests.swift; sourceTree = "<group>"; };
7476
66FD99F91EE9FBBE00C53A82 /* MotionAnimatorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MotionAnimatorTests.m; sourceTree = "<group>"; };
7577
6EB4A3E0A8428C89A9BE95EE /* Pods-MotionAnimatorCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MotionAnimatorCatalog.release.xcconfig"; path = "../../../Pods/Target Support Files/Pods-MotionAnimatorCatalog/Pods-MotionAnimatorCatalog.release.xcconfig"; sourceTree = "<group>"; };
7678
9DE90426033EDF40C65232A9 /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "../../../Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -214,10 +216,11 @@
214216
children = (
215217
66A6A6671FBA158000DE54CB /* AnimationRemovalTests.swift */,
216218
66EF6F271FC33C4800C83A63 /* HeadlessLayerImplicitAnimationTests.swift */,
217-
66FD99F91EE9FBBE00C53A82 /* MotionAnimatorTests.m */,
218-
668726491EF04B4C00113675 /* MotionAnimatorTests.swift */,
219219
66BF5A8E1FB0E4CB00E864F6 /* ImplicitAnimationTests.swift */,
220220
6625876B1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift */,
221+
66EF6F291FC48D6A00C83A63 /* InstantAnimationTests.swift */,
222+
66FD99F91EE9FBBE00C53A82 /* MotionAnimatorTests.m */,
223+
668726491EF04B4C00113675 /* MotionAnimatorTests.swift */,
221224
660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */,
222225
);
223226
path = unit;
@@ -496,6 +499,7 @@
496499
files = (
497500
6625876C1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift in Sources */,
498501
66EF6F281FC33C4800C83A63 /* HeadlessLayerImplicitAnimationTests.swift in Sources */,
502+
66EF6F2A1FC48D6A00C83A63 /* InstantAnimationTests.swift in Sources */,
499503
660636021FACC24300C3DFB8 /* TimeScaleFactorTests.swift in Sources */,
500504
66BF5A8F1FB0E4CB00E864F6 /* ImplicitAnimationTests.swift in Sources */,
501505
66A6A6681FBA158000DE54CB /* AnimationRemovalTests.swift in Sources */,

src/MDMMotionAnimator.m

+5-6
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,12 @@ - (void)animateWithTiming:(MDMMotionTiming)timing
5959
}
6060
values = MDMCoerceUIKitValuesToCoreAnimationValues(values);
6161

62+
[CATransaction begin];
63+
[CATransaction setDisableActions:YES];
64+
[layer setValue:[values lastObject] forKeyPath:keyPath];
65+
[CATransaction commit];
66+
6267
if (timing.duration == 0 || timing.curve.type == MDMMotionCurveTypeInstant) {
63-
[layer setValue:[values lastObject] forKeyPath:keyPath];
6468
if (completion) {
6569
completion();
6670
}
@@ -104,11 +108,6 @@ - (void)animateWithTiming:(MDMMotionTiming)timing
104108
}
105109
}
106110
}
107-
108-
[CATransaction begin];
109-
[CATransaction setDisableActions:YES];
110-
[layer setValue:[values lastObject] forKeyPath:keyPath];
111-
[CATransaction commit];
112111
}
113112

114113
- (void)animateWithTiming:(MDMMotionTiming)timing animations:(void (^)(void))animations {
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Copyright 2017-present The Material Motion Authors. All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import XCTest
18+
#if IS_BAZEL_BUILD
19+
import _MotionAnimator
20+
#else
21+
import MotionAnimator
22+
#endif
23+
24+
class InstantAnimationTests: XCTestCase {
25+
26+
var animator: MotionAnimator!
27+
var timing: MotionTiming!
28+
var view: UIView!
29+
var addedAnimations: [CAAnimation]!
30+
31+
override func setUp() {
32+
super.setUp()
33+
34+
animator = MotionAnimator()
35+
36+
timing = MotionTiming(delay: 0,
37+
duration: 0,
38+
curve: .init(type: .instant, data: (0, 0, 0, 0)),
39+
repetition: .init(type: .none, amount: 0, autoreverses: false))
40+
41+
let window = UIWindow()
42+
window.makeKeyAndVisible()
43+
view = UIView() // Need to animate a view's layer to get implicit animations.
44+
window.addSubview(view)
45+
46+
addedAnimations = []
47+
animator.addCoreAnimationTracer { (_, animation) in
48+
self.addedAnimations.append(animation)
49+
}
50+
}
51+
52+
override func tearDown() {
53+
animator = nil
54+
view = nil
55+
addedAnimations = nil
56+
57+
super.tearDown()
58+
}
59+
60+
func testDoesNotGenerateImplicitAnimations() {
61+
animator.animate(with: timing, to: view.layer, withValues: [1, 0.5], keyPath: .opacity)
62+
63+
XCTAssertNil(view.layer.animationKeys())
64+
XCTAssertEqual(addedAnimations.count, 0)
65+
}
66+
67+
func testDoesNotGenerateImplicitAnimationsInUIViewAnimationBlock() {
68+
UIView.animate(withDuration: 0.5) {
69+
self.animator.animate(with: self.timing,
70+
to: self.view.layer,
71+
withValues: [1, 0.5],
72+
keyPath: .opacity)
73+
}
74+
75+
XCTAssertNil(view.layer.animationKeys())
76+
XCTAssertEqual(addedAnimations.count, 0)
77+
}
78+
}

0 commit comments

Comments
 (0)