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

Commit a91ac69

Browse files
authored
Add tests verifying the UIKit beginFromCurrentState option behavior. (#84)
1 parent ab856f6 commit a91ac69

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

tests/unit/UIKitBehavioralTests.swift

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,184 @@ class UIKitBehavioralTests: XCTestCase {
176176
"Expected \(keyPath.rawValue) not to generate any animations.")
177177
}
178178
}
179+
180+
// MARK: .beginFromCurrentState option behavior
181+
//
182+
// The following tests indicate that UIKit treats .beginFromCurrentState differently depending
183+
// on the key path being animated. This difference is in line with whether or not a key path is
184+
// animated additively or not.
185+
//
186+
// > See testSomePropertiesImplicitlyAnimateAdditively and
187+
// > testSomePropertiesImplicitlyAnimateButNotAdditively for a list of which key paths are
188+
// > animated which way.
189+
//
190+
// Notably, ONLY non-additive key paths are affected by the beginFromCurrentState option. This
191+
// likely became the case starting in iOS 8 when additive animations were enabled by default.
192+
// Additive animations will always animate additively regardless of whether or not you provide
193+
// this flag.
194+
195+
func testDefaultsAnimatesOpacityNonAdditivelyFromItsModelLayerState() {
196+
UIView.animate(withDuration: 0.1) {
197+
self.view.alpha = 0.5
198+
}
199+
200+
RunLoop.main.run(until: .init(timeIntervalSinceNow: 0.01))
201+
202+
let initialValue = self.view.layer.opacity
203+
204+
UIView.animate(withDuration: 0.1) {
205+
self.view.alpha = 0.2
206+
}
207+
208+
XCTAssertNotNil(view.layer.animationKeys(),
209+
"Expected an animation to be added, but none were found.")
210+
guard let animationKeys = view.layer.animationKeys() else {
211+
return
212+
}
213+
XCTAssertEqual(animationKeys.count, 1,
214+
"Expected only one animation to be added, but the following were found: "
215+
+ "\(animationKeys).")
216+
guard let key = animationKeys.first,
217+
let animation = view.layer.animation(forKey: key) as? CABasicAnimation else {
218+
return
219+
}
220+
221+
XCTAssertFalse(animation.isAdditive, "Expected the animation not to be additive, but it was.")
222+
223+
XCTAssertTrue(animation.fromValue is Float,
224+
"The animation's from value was not a number type: "
225+
+ String(describing: animation.fromValue))
226+
guard let fromValue = animation.fromValue as? Float else {
227+
return
228+
}
229+
230+
XCTAssertEqualWithAccuracy(fromValue, initialValue, accuracy: 0.0001,
231+
"Expected the animation to start from \(initialValue), "
232+
+ "but it did not.")
233+
}
234+
235+
func testBeginFromCurrentStateAnimatesOpacityNonAdditivelyFromItsPresentationLayerState() {
236+
UIView.animate(withDuration: 0.1) {
237+
self.view.alpha = 0.5
238+
}
239+
240+
RunLoop.main.run(until: .init(timeIntervalSinceNow: 0.01))
241+
242+
let initialValue = self.view.layer.presentation()!.opacity
243+
244+
UIView.animate(withDuration: 0.1, delay: 0, options: .beginFromCurrentState, animations: {
245+
self.view.alpha = 0.2
246+
}, completion: nil)
247+
248+
XCTAssertNotNil(view.layer.animationKeys(),
249+
"Expected an animation to be added, but none were found.")
250+
guard let animationKeys = view.layer.animationKeys() else {
251+
return
252+
}
253+
XCTAssertEqual(animationKeys.count, 1,
254+
"Expected only one animation to be added, but the following were found: "
255+
+ "\(animationKeys).")
256+
guard let key = animationKeys.first,
257+
let animation = view.layer.animation(forKey: key) as? CABasicAnimation else {
258+
return
259+
}
260+
261+
XCTAssertFalse(animation.isAdditive, "Expected the animation not to be additive, but it was.")
262+
263+
XCTAssertTrue(animation.fromValue is Float,
264+
"The animation's from value was not a number type: "
265+
+ String(describing: animation.fromValue))
266+
guard let fromValue = animation.fromValue as? Float else {
267+
return
268+
}
269+
270+
XCTAssertEqualWithAccuracy(fromValue, initialValue, accuracy: 0.0001,
271+
"Expected the animation to start from \(initialValue), "
272+
+ "but it did not.")
273+
}
274+
275+
func testDefaultsAnimatesPositionAdditivelyFromItsModelLayerState() {
276+
UIView.animate(withDuration: 0.1) {
277+
self.view.layer.position = CGPoint(x: 100, y: self.view.layer.position.y)
278+
}
279+
280+
RunLoop.main.run(until: .init(timeIntervalSinceNow: 0.01))
281+
282+
let initialValue = self.view.layer.position
283+
284+
UIView.animate(withDuration: 0.1) {
285+
self.view.layer.position = CGPoint(x: 20, y: self.view.layer.position.y)
286+
}
287+
288+
let displacement = initialValue.x - self.view.layer.position.x
289+
290+
XCTAssertNotNil(view.layer.animationKeys(),
291+
"Expected an animation to be added, but none were found.")
292+
guard let animationKeys = view.layer.animationKeys() else {
293+
return
294+
}
295+
XCTAssertEqual(animationKeys.count, 2,
296+
"Expected two animations to be added, but the following were found: "
297+
+ "\(animationKeys).")
298+
guard let key = animationKeys.first(where: { $0 != "position" }),
299+
let animation = view.layer.animation(forKey: key) as? CABasicAnimation else {
300+
return
301+
}
302+
303+
XCTAssertTrue(animation.isAdditive, "Expected the animation to be additive, but it wasn't.")
304+
305+
XCTAssertTrue(animation.fromValue is CGPoint,
306+
"The animation's from value was not a point type: "
307+
+ String(describing: animation.fromValue))
308+
guard let fromValue = animation.fromValue as? CGPoint else {
309+
return
310+
}
311+
312+
XCTAssertEqualWithAccuracy(fromValue.x, displacement, accuracy: 0.0001,
313+
"Expected the animation to have a delta of \(displacement), "
314+
+ "but it did not.")
315+
}
316+
317+
func testBeginFromCurrentStateAnimatesPositionAdditivelyFromItsModelLayerState() {
318+
UIView.animate(withDuration: 0.1) {
319+
self.view.layer.position = CGPoint(x: 100, y: self.view.layer.position.y)
320+
}
321+
322+
RunLoop.main.run(until: .init(timeIntervalSinceNow: 0.01))
323+
324+
let initialValue = self.view.layer.position
325+
326+
UIView.animate(withDuration: 0.1, delay: 0, options: .beginFromCurrentState, animations: {
327+
self.view.layer.position = CGPoint(x: 20, y: self.view.layer.position.y)
328+
}, completion: nil)
329+
330+
let displacement = initialValue.x - self.view.layer.position.x
331+
332+
XCTAssertNotNil(view.layer.animationKeys(),
333+
"Expected an animation to be added, but none were found.")
334+
guard let animationKeys = view.layer.animationKeys() else {
335+
return
336+
}
337+
XCTAssertEqual(animationKeys.count, 2,
338+
"Expected two animations to be added, but the following were found: "
339+
+ "\(animationKeys).")
340+
guard let key = animationKeys.first(where: { $0 != "position" }),
341+
let animation = view.layer.animation(forKey: key) as? CABasicAnimation else {
342+
return
343+
}
344+
345+
XCTAssertTrue(animation.isAdditive, "Expected the animation to be additive, but it wasn't.")
346+
347+
XCTAssertTrue(animation.fromValue is CGPoint,
348+
"The animation's from value was not a point type: "
349+
+ String(describing: animation.fromValue))
350+
guard let fromValue = animation.fromValue as? CGPoint else {
351+
return
352+
}
353+
354+
XCTAssertEqualWithAccuracy(fromValue.x, displacement, accuracy: 0.0001,
355+
"Expected the animation to have a delta of \(displacement), "
356+
+ "but it did not.")
357+
}
358+
179359
}

0 commit comments

Comments
 (0)