├── .swift-version ├── chain-example-loop.gif ├── ChainedAnimation.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── ChainedAnimation.xcscheme └── project.pbxproj ├── .travis.yml ├── ChainedAnimation ├── Types.swift ├── ChainedAnimation.h ├── Animator.swift ├── Info.plist ├── AnimationProvider.swift ├── AnimationConfiguration.swift └── AnimationChain.swift ├── ChainedAnimationTests ├── Info.plist ├── ChainedAnimationTestCase.swift ├── MockAnimationProvider.swift ├── ChainedAnimationTests.swift └── AnimationConfigurationTests.swift ├── ChainedAnimation.podspec ├── ChainedAnimationExample ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── UIView+Helper.swift ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── AppDelegate.swift └── ViewController.swift ├── .gitignore ├── LICENSE └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /chain-example-loop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daehn/ChainedAnimation/HEAD/chain-example-loop.gif -------------------------------------------------------------------------------- /ChainedAnimation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode10 3 | 4 | script: 5 | - set -o pipefail && xcodebuild clean build test -scheme ChainedAnimation -destination "OS=12.0,name=iPhone XS" | xcpretty 6 | after_script: 7 | - bash <(curl -s https://codecov.io/bash) 8 | -------------------------------------------------------------------------------- /ChainedAnimation/Types.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Types.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | public typealias Animation = () -> Void 10 | public typealias Completion = (Bool) -> Void 11 | -------------------------------------------------------------------------------- /ChainedAnimation/ChainedAnimation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAnimation.h 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ChainedAnimation. 12 | FOUNDATION_EXPORT double ChainedAnimationVersionNumber; 13 | 14 | //! Project version string for ChainedAnimation. 15 | FOUNDATION_EXPORT const unsigned char ChainedAnimationVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ChainedAnimation/Animator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Animator.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | struct Animator { 11 | 12 | static var animationProvider: AnimationProvider = UIViewAnimationProvider() 13 | 14 | static func perform(configuration: AnimationConfiguration) { 15 | animationProvider.animate( 16 | withDuration: configuration.duration, 17 | delay: configuration.delay, 18 | options: configuration.options, 19 | animations: configuration.animation, 20 | completion: configuration.completion 21 | ) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /ChainedAnimationTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ChainedAnimation.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "ChainedAnimation" 4 | s.version = "3.0.1" 5 | s.summary = "ChainedAnimation is a small Swift library to chain multiple animations with different delays." 6 | s.homepage = "https://github.com/daehn/ChainedAnimation" 7 | s.license = 'MIT' 8 | s.author = { "Silvan Dähn" => "silvandaehn@me.com" } 9 | s.source = { :git => "https://github.com/daehn/ChainedAnimation.git", :tag => s.version.to_s } 10 | s.social_media_url = 'https://twitter.com/silvandaehn' 11 | 12 | s.platform = :ios, '8.0' 13 | s.requires_arc = true 14 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4' } 15 | 16 | s.source_files = "ChainedAnimation", "ChainedAnimation/**/*.{h,m,swift}" 17 | end 18 | -------------------------------------------------------------------------------- /ChainedAnimationExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | profile 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # CocoaPods 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Pods/ 31 | 32 | # Carthage 33 | # 34 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 35 | # Carthage/Checkouts 36 | 37 | Carthage/Build 38 | -------------------------------------------------------------------------------- /ChainedAnimation/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ChainedAnimation/AnimationProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationProvider.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | public protocol AnimationProvider { 11 | 12 | func animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIView.AnimationOptions, animations: @escaping Animation, completion: Completion?) 13 | 14 | } 15 | 16 | 17 | struct UIViewAnimationProvider: AnimationProvider { 18 | 19 | func animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIView.AnimationOptions, animations: @escaping Animation, completion: Completion?) { 20 | UIView.animate( 21 | withDuration: duration, 22 | delay: delay, 23 | options: options, 24 | animations: animations, 25 | completion: completion 26 | ) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /ChainedAnimationTests/ChainedAnimationTestCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAnimationBaseTest.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | import XCTest 11 | @testable import ChainedAnimation 12 | 13 | 14 | class ChainedAnimationTestCase: XCTestCase { 15 | 16 | var animationProvider: MockAnimationProvider! 17 | 18 | override func setUp() { 19 | super.setUp() 20 | animationProvider = MockAnimationProvider() 21 | Animator.animationProvider = animationProvider 22 | } 23 | 24 | // MARK: - Helper 25 | 26 | func testConfiguration(_ animation: @escaping Animation, completion: Completion? = nil) -> AnimationConfiguration { 27 | return AnimationConfiguration( 28 | animation, 29 | duration: 0.5, 30 | delay: 0.6, 31 | options: .curveEaseIn, 32 | completion: completion 33 | ) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ChainedAnimation/AnimationConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationConfiguration.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | struct AnimationConfiguration { 11 | 12 | let animation: Animation 13 | let duration: TimeInterval 14 | let delay: TimeInterval 15 | let options: UIView.AnimationOptions 16 | var completion : Completion? 17 | 18 | init(_ animation: @escaping Animation, duration: TimeInterval, delay: TimeInterval, options: UIView.AnimationOptions = [], completion: Completion? = nil) { 19 | self.animation = animation 20 | self.duration = duration 21 | self.delay = delay 22 | self.options = options 23 | self.completion = completion 24 | } 25 | 26 | mutating func add(completion: @escaping Completion) { 27 | let currentCompletion = self.completion 28 | self.completion = { 29 | currentCompletion?($0) 30 | completion($0) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Silvan Daehn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ChainedAnimationExample/UIView+Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Helper.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | 12 | 13 | extension UIView { 14 | 15 | func outline(_ color: UIColor = .black, borderWidth: CGFloat = 1, cornerRadius: CGFloat = 0) { 16 | backgroundColor = .clear 17 | layer.borderColor = color.cgColor 18 | layer.borderWidth = borderWidth 19 | layer.cornerRadius = cornerRadius 20 | } 21 | 22 | } 23 | 24 | 25 | extension UIView { 26 | 27 | var y: CGFloat { 28 | get { return frame.origin.y } 29 | set(value) { frame.origin.y = value } 30 | } 31 | 32 | var x: CGFloat { 33 | get { return frame.origin.x } 34 | set(value) { frame.origin.x = value } 35 | } 36 | 37 | var width: CGFloat { 38 | get { return frame.width } 39 | set(value) { frame.size.width = value } 40 | } 41 | 42 | var height: CGFloat { 43 | get { return frame.height } 44 | set(value) { frame.size.height = value } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ChainedAnimationExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ChainedAnimationTests/MockAnimationProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockAnimationProvider.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ChainedAnimation 11 | 12 | typealias AnimationParameters = ( 13 | duration: TimeInterval, 14 | delay: TimeInterval, 15 | options: UIView.AnimationOptions, 16 | animations: Animation, 17 | completion: Completion? 18 | ) 19 | 20 | class TestAnimation { 21 | 22 | var callCount = 0 23 | var closure: Animation! = nil 24 | 25 | init() { 26 | closure = { self.callCount += 1 } 27 | } 28 | 29 | } 30 | 31 | class TestCompletion { 32 | 33 | var callCount = 0 34 | var closure: Completion! = nil 35 | 36 | init() { 37 | closure = { _ in self.callCount += 1 } 38 | } 39 | 40 | } 41 | 42 | class MockAnimationProvider: AnimationProvider { 43 | 44 | var callClosure: ((AnimationParameters) -> Void)? 45 | var callParameters: AnimationParameters? 46 | var callCount = 0 47 | 48 | var callPassedInClosures = false 49 | 50 | public func animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIView.AnimationOptions, animations: @escaping Animation, completion: Completion?) { 51 | callCount += 1 52 | let parameters = (duration: duration, delay: delay, options: options, animations: animations, completion: completion) 53 | callParameters = parameters 54 | callClosure?(parameters) 55 | 56 | guard callPassedInClosures else { return } 57 | animations() 58 | completion?(true) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ChainedAnimationExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ChainedAnimationExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ChainedAnimationTests/ChainedAnimationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAnimationTests.swift 3 | // ChainedAnimationTests 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ChainedAnimation 11 | 12 | 13 | class ChainedAnimationTests: ChainedAnimationTestCase { 14 | 15 | func testAnimationChain() { 16 | let animationClosure: Animation = {} 17 | let firstBoxedAnimation = testConfiguration(animationClosure) 18 | let firstChain = AnimationChain(options: [.curveEaseIn, .autoreverse], animations: [[firstBoxedAnimation]]) 19 | let secondChain = firstChain.thenAfterStart(1.0, animation: animationClosure) 20 | 21 | XCTAssert(firstChain.currentOffset == 0) 22 | XCTAssert(firstChain.animations.joined().count == 1) 23 | XCTAssert(secondChain.currentOffset == 1.0) 24 | XCTAssert(secondChain.animations.joined().count == 2) 25 | XCTAssert(secondChain.options == firstChain.options) 26 | 27 | let completionChain = secondChain.chainAfterCompletion(0.5, delay: 0, options: .curveEaseInOut, animations: animationClosure) 28 | XCTAssert(completionChain.animations.joined().count == 3) 29 | XCTAssert(completionChain.animations.count == 2) 30 | XCTAssert(completionChain.currentOffset == 0) 31 | XCTAssert(completionChain.options == .curveEaseInOut) 32 | } 33 | 34 | func testAnimationCalled() { 35 | UIView.beginAnimationChain(0) { 36 | XCTAssert(true) 37 | }.animate() 38 | } 39 | 40 | func testCompletionCalled() { 41 | UIView.beginAnimationChain(2, delay: 0, options: UIView.AnimationOptions.curveEaseInOut) { 42 | }.completion { success in 43 | XCTAssert(success) 44 | }.animate() 45 | } 46 | 47 | func testThenAfterCalled() { 48 | UIView.beginAnimationChain(0.2, delay: 0) { 49 | }.thenAfterStart(0.2) { 50 | XCTAssert(true) 51 | }.animate() 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ChainedAnimationExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ChainedAnimationExample 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChainedAnimation 2 | 3 | [![Build Status](https://travis-ci.org/daehn/ChainedAnimation.svg?branch=develop)](https://travis-ci.org/daehn/ChainedAnimation) ![Swift 4.2](https://img.shields.io/badge/Swift-4.2-orange.svg?style=flat) [![codecov](https://codecov.io/gh/daehn/ChainedAnimation/branch/develop/graph/badge.svg)](https://codecov.io/gh/daehn/ChainedAnimation) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods](https://img.shields.io/cocoapods/v/ChainedAnimation.svg?style=flat)](https://cocoapods.org/pods/ChainedAnimation) 4 | 5 | ## Usage 6 | 7 | To run the example project, clone the repo, and run the Example application target. 8 | 9 | Chain multiple animations with different delays. Chained animations will be started with a delay after the last one started executing. 10 | 11 | 12 | 13 | You can create a chained animation with the `-beginAnimationChain` function on the UIView class. 14 | 15 | ```swift 16 | UIView.beginAnimationChain(0.5) { 17 | view.frame.origin.y = 170 18 | }.animate() 19 | ``` 20 | 21 | You can provide an optional delay an `UIViewAnimationOptions`. 22 | 23 | ```swift 24 | UIView.beginAnimationChain(0.5, delay: 0.2, options: .CurveEaseInOut) { 25 | view.frame.origin.y = 170 26 | }.animate() 27 | ``` 28 | To add an offset animation, use the `-thenAfterStart:` function and append an 29 | animation closure that will be executed as soon as the last animation started, delayed by the provided offset. Add a completion 30 | closure to the animation chain and it will be executed when the previous animation in the chain completed. 31 | 32 | ```swift 33 | UIView.beginAnimationChain(0.5, options: .CurveEaseInOut) { 34 | view.frame.origin.y = 170 35 | }.thenAfterStart(0.1) { 36 | // This animation will be started 0.1 seconds 37 | // after the previous one started 38 | otherView.tranform = awesomeTransform 39 | }.thenAfterStart(0.15) { 40 | // More shiny animations 41 | }.completion { bool in 42 | // Do something nice on completion. Or don't. 43 | }.animate() 44 | ``` 45 | 46 | It is also possible to add a new chain after a previous one completed. 47 | To add a new chain call `-chainAfterCompletion:` and start adding animation blocks with their offset. 48 | 49 | ```swift 50 | UIView.beginAnimationChain(0.33, options: .CurveEaseInOut) { 51 | view.frame.origin.y = 170 52 | }.thenAfterStart(0.15) { 53 | // Start animations with a delayed start 54 | }.completion { bool in 55 | // Do something nice on the completion of the first chain. 56 | }.chainAfterCompletion(1.2, options: .CurveEaseIn) { 57 | // This animation will be started after the last 58 | // animation in the first chain completed 59 | }.thenAfterStart(0.1) { 60 | // This animation will be started 0.1 seconds after 61 | // the first animation in the second chain started 62 | }.completion { _ in 63 | // Do something nice on the completion of the second chain. 64 | }.animate() 65 | ``` 66 | 67 | ## Author 68 | 69 | Silvan Dähn, @silvandaehn 70 | 71 | ## License 72 | 73 | Chain is available under the MIT license. See the LICENSE file for more info. 74 | -------------------------------------------------------------------------------- /ChainedAnimationTests/AnimationConfigurationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationConfigurationTests.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | import XCTest 11 | @testable import ChainedAnimation 12 | 13 | 14 | class AnimationConfigurationTests: ChainedAnimationTestCase { 15 | 16 | func testThatItCallsTheAnimationProviderWithTheGivenParametersWhenPerformIsCalled() { 17 | // Given 18 | let sut = AnimationConfiguration( 19 | {}, 20 | duration: 1.2, 21 | delay: 0.5, 22 | options: .curveEaseIn, 23 | completion: nil 24 | ) 25 | 26 | // when 27 | Animator.perform(configuration: sut) 28 | 29 | // then 30 | let params = animationProvider.callParameters 31 | XCTAssertEqual(params?.duration, 1.2) 32 | XCTAssertEqual(params?.delay, 0.5) 33 | XCTAssertEqual(params?.options, .curveEaseIn) 34 | } 35 | 36 | func testThatItCallsTheProviderWithTheAnimationAndCompletionClosure() { 37 | // Given 38 | animationProvider.callPassedInClosures = true 39 | let completionExpectation = expectation(description: "Completion should be called") 40 | var animationCalled = false 41 | let animation: Animation = { animationCalled = true } 42 | let completion: Completion = { _ in completionExpectation.fulfill() } 43 | let sut = testConfiguration(animation, completion: completion) 44 | 45 | // When 46 | Animator.perform(configuration: sut) 47 | 48 | // Then 49 | XCTAssertNotNil(animationProvider.callParameters) 50 | XCTAssert(animationCalled) 51 | waitForExpectations(timeout: 1, handler: nil) 52 | } 53 | 54 | func testThatAnAdditionalCompletionCanBeAddedToAnAnimationConfiguration() { 55 | // Given 56 | animationProvider.callPassedInClosures = true 57 | let animation = TestAnimation() 58 | let completion = TestCompletion() 59 | var sut = testConfiguration(animation.closure, completion: completion.closure) 60 | 61 | // When 62 | let secondCompletion = TestCompletion() 63 | sut.add(completion: secondCompletion.closure) 64 | 65 | Animator.perform(configuration: sut) 66 | 67 | // Then 68 | XCTAssertEqual(animation.callCount, 1) 69 | XCTAssertEqual(completion.callCount, 1) 70 | XCTAssertEqual(secondCompletion.callCount, 1) 71 | } 72 | 73 | func testThatAdditionalCompletionsCanBeAddedToAnAnimationConfiguration() { 74 | // Given 75 | animationProvider.callPassedInClosures = true 76 | let animation = TestAnimation() 77 | let completion = TestCompletion() 78 | var sut = testConfiguration(animation.closure, completion: completion.closure) 79 | 80 | // When 81 | let secondCompletion = TestCompletion() 82 | let thirdCompletion = TestCompletion() 83 | sut.add(completion: secondCompletion.closure) 84 | sut.add(completion: thirdCompletion.closure) 85 | 86 | Animator.perform(configuration: sut) 87 | 88 | // Then 89 | XCTAssertEqual(animation.callCount, 1) 90 | XCTAssertEqual(completion.callCount, 1) 91 | XCTAssertEqual(secondCompletion.callCount, 1) 92 | XCTAssertEqual(thirdCompletion.callCount, 1) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /ChainedAnimation.xcodeproj/xcshareddata/xcschemes/ChainedAnimation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ChainedAnimationExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 06/21/2015. 6 | // Copyright (c) 06/21/2015 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | import ChainedAnimation 12 | 13 | 14 | class ViewController: UIViewController { 15 | 16 | var displayImageView = UIImageView() 17 | var phone = UIView() 18 | var subheader = UILabel() 19 | var headline = UILabel() 20 | var firstSection = UIView() 21 | var secondSection = UIView() 22 | var thirdSection = UIView() 23 | var fourthSection = UIView() 24 | let sectionOffset: CGFloat = 40 25 | 26 | override func viewDidAppear(_ animated: Bool) { 27 | super.viewDidAppear(animated) 28 | let delay = DispatchTime.now() + Double(Int64(1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) 29 | DispatchQueue.main.asyncAfter(deadline: delay) { 30 | self.startAnimation() 31 | } 32 | } 33 | 34 | // MARK: - Layout 35 | 36 | override func viewDidLayoutSubviews() { 37 | layoutPhoneAndDisplay() 38 | layoutSections() 39 | layoutSubheader() 40 | layoutHeadline() 41 | super.viewDidLayoutSubviews() 42 | } 43 | 44 | func layoutPhoneAndDisplay() { 45 | phone.frame = view.bounds.insetBy(dx: 60, dy: 60) 46 | phone.outline(cornerRadius: 40) 47 | phone.y = view.height 48 | 49 | displayImageView.frame = phone.bounds.insetBy(dx: 10, dy: 60) 50 | displayImageView.outline() 51 | displayImageView.backgroundColor = .groupTableViewBackground 52 | phone.addSubview(displayImageView) 53 | view.addSubview(phone) 54 | } 55 | 56 | func layoutSections() { 57 | let sectionPadding: CGFloat = 15.0 58 | let sectionHeight = (displayImageView.height - sectionPadding * 5) / 4.0 59 | let sectionFrame = displayImageView.bounds.insetBy(dx: sectionPadding, dy: displayImageView.height / 2.5) 60 | var currentY = sectionPadding 61 | 62 | for view in [firstSection, secondSection, thirdSection, fourthSection] { 63 | view.frame = sectionFrame 64 | view.y = currentY + sectionOffset 65 | view.backgroundColor = .white 66 | currentY += sectionHeight + sectionPadding 67 | view.alpha = 0 68 | displayImageView.addSubview(view) 69 | } 70 | } 71 | 72 | func layoutSubheader() { 73 | subheader.text = "This is an example application." 74 | subheader.font = UIFont(name: "Avenir Next", size: 18) 75 | subheader.textColor = .black 76 | subheader.sizeToFit() 77 | subheader.center = view.center 78 | subheader.y = 0 79 | subheader.alpha = 0 80 | view.addSubview(subheader) 81 | } 82 | 83 | func layoutHeadline() { 84 | headline.text = "Welcome!" 85 | headline.font = UIFont(name: "AvenirNext-UltraLight", size: 42) 86 | headline.textColor = .black 87 | headline.sizeToFit() 88 | headline.center = view.center 89 | headline.y = 0 90 | headline.alpha = 0 91 | view.addSubview(headline) 92 | } 93 | 94 | // MARK: - Animation 95 | 96 | func startAnimation() { 97 | let moveAndFadeIn: (UIView) -> Void = { section in 98 | section.alpha = 1 99 | section.y -= self.sectionOffset 100 | } 101 | 102 | let moveAndFadeOut: (UIView) -> Void = { view in 103 | view.alpha = 0 104 | view.transform = CGAffineTransform(scaleX: 0.98, y: 0.98) 105 | } 106 | 107 | UIView.beginAnimationChain(0.8, options: .curveEaseInOut) { 108 | self.phone.y = 170 109 | }.thenAfterStart(0.1) { 110 | self.subheader.y = 105 111 | moveAndFadeIn(self.firstSection) 112 | }.thenAfterStart(0.15) { 113 | moveAndFadeIn(self.secondSection) 114 | self.subheader.alpha = 1 115 | self.headline.y = 45 116 | }.thenAfterStart(0.1) { 117 | moveAndFadeIn(self.thirdSection) 118 | self.headline.alpha = 1 119 | }.thenAfterStart(0.1) { 120 | moveAndFadeIn(self.fourthSection) 121 | }.completion { _ in 122 | print("First completion") 123 | }.chainAfterCompletion(1.2, delay: 0.5, options: .curveEaseIn) { 124 | moveAndFadeOut(self.displayImageView) 125 | moveAndFadeOut(self.phone) 126 | }.thenAfterStart(0.2) { 127 | moveAndFadeOut(self.headline) 128 | }.thenAfterStart(0.1) { 129 | moveAndFadeOut(self.subheader) 130 | }.completion { _ in 131 | print("Second completion") 132 | }.animate() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /ChainedAnimation/AnimationChain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChainedAnimation.swift 3 | // ChainedAnimation 4 | // 5 | // Created by Silvan Dähn on 10/09/2016. 6 | // Copyright © 2016 Silvan Dähn. All rights reserved. 7 | // 8 | 9 | public struct AnimationChain { 10 | 11 | let options: UIView.AnimationOptions 12 | var animations: [[AnimationConfiguration]] 13 | var currentOffset: TimeInterval 14 | 15 | init(options: UIView.AnimationOptions, animations: [[AnimationConfiguration]], currentOffset : TimeInterval = 0) { 16 | self.options = options 17 | self.animations = animations 18 | self.currentOffset = currentOffset 19 | } 20 | 21 | /** 22 | Appends a new animation closure to the current chain with the given offset and 23 | returns a new `AnimationChain` constiting of all `BoxedAnimations`. 24 | 25 | - parameter offset The offset of execution in seconds after the previous animation. 26 | - parameter animation The animation that should be added with the given ofset. 27 | 28 | - returns A new `AnimationChain` including the added animation. 29 | */ 30 | public func thenAfterStart(_ offset: TimeInterval, animation: @escaping Animation) -> AnimationChain { 31 | 32 | var previousAnimations = animations 33 | let previousChain = previousAnimations.removeLast() 34 | let previousDuration = previousChain.last?.duration ?? 0 35 | let previousDelay = previousChain.last?.delay ?? 0 36 | let boxedFunction = AnimationConfiguration( 37 | animation, 38 | duration: previousDuration, 39 | delay: self.currentOffset + offset + previousDelay, 40 | options: options 41 | ) 42 | previousAnimations.append(previousChain + [boxedFunction]) 43 | return AnimationChain(options: options, animations: previousAnimations, currentOffset: currentOffset + offset) 44 | } 45 | 46 | /** 47 | Appends a completion closure to the current chain and returns the 48 | new chain. The completion closure will be executed after the animation in the 49 | closure before was executed. 50 | 51 | - parameter completion The closure that will be executed after the animation before completed. 52 | 53 | - return A new `AnimationChain` with the added completion closure. 54 | **/ 55 | public func completion(_ completion: @escaping Completion) -> AnimationChain { 56 | 57 | var previousAnimations = animations 58 | var lastChain = previousAnimations.removeLast() 59 | let lastAnimation = lastChain.removeLast() 60 | let boxedFunction = AnimationConfiguration( 61 | lastAnimation.animation, 62 | duration: lastAnimation.duration, 63 | delay: lastAnimation.delay, 64 | completion: completion 65 | ) 66 | let newChain = lastChain + [boxedFunction] 67 | previousAnimations.append(newChain) 68 | return AnimationChain(options: options, animations: previousAnimations) 69 | } 70 | 71 | /** 72 | Starts a new animation chain after the previous one completed. 73 | This is useful if you want another set of sequential animations to start after a previous one. 74 | Each chain can have an own completion closure. 75 | The appended chain will start executing together with the completion closure of the previous one. 76 | 77 | - parameter duration The duration that will be used for all animations added to the `AnimationChain`. 78 | - parameter delay The delay before the execution of the first animation in the chain, default to 0. 79 | - parameter options The `UIViewAnimationOptions` that will be used for every animation in the chain, default is nil. 80 | - parameter animations The animations that should be executed first in the chain. 81 | 82 | - return A new `AnimationChain` with the new chain appended. 83 | **/ 84 | public func chainAfterCompletion( 85 | _ duration: TimeInterval, 86 | delay: TimeInterval = 0, 87 | options: UIView.AnimationOptions = [], 88 | animations newAnimations: @escaping Animation 89 | ) -> AnimationChain { 90 | 91 | let boxedFunction = AnimationConfiguration(newAnimations, duration: duration, delay: delay, options: options) 92 | let newChain = animations + [[boxedFunction]] 93 | return AnimationChain(options: options, animations: newChain) 94 | } 95 | 96 | /** 97 | Starts the animation of all animations in the current chain. 98 | Calls the completion closures added to individual chains after their completion. 99 | **/ 100 | public func animate() { 101 | 102 | var animationChains: [[AnimationConfiguration]] = [] 103 | if animations.count > 1 { 104 | stride(from: animations.count - 1, to: 0, by: -1).forEach { index in 105 | var (current, previous) = (animations[index], animations[index-1]) 106 | let lastBox = previous.removeLast() 107 | let wrappedAnimations = wrap(configurations: current, inCompletionOf: lastBox) 108 | previous.append(wrappedAnimations) 109 | animationChains.insert(previous, at: 0) 110 | } 111 | } else { 112 | animationChains = animations 113 | } 114 | 115 | for chain in animationChains { 116 | chain.forEach(Animator.perform) 117 | } 118 | } 119 | 120 | private func wrap(configurations: [AnimationConfiguration], inCompletionOf configuration: AnimationConfiguration) -> AnimationConfiguration { 121 | var completionConfiguration = configuration 122 | completionConfiguration.add { _ in 123 | configurations.forEach(Animator.perform) 124 | } 125 | 126 | return completionConfiguration 127 | } 128 | } 129 | 130 | extension UIView { 131 | 132 | /** 133 | Used to start a new `AnimationChain`. 134 | More animations can be added by calling `-thenAfter` on the return value. 135 | A completion closure can be added by calling `-completion` on the return value. 136 | All animations will be executed after calling `-animate`. 137 | 138 | - parameter duration The duration that will be used for all animations added to the `AnimationChain`. 139 | - parameter delay The delay before the execution of the first animation in the chain, default to 0. 140 | - parameter options The `UIViewAnimationOptions` that will be used for every animation in the chain, default is nil. 141 | - parameter animations The animations that should be executed first in the chain. 142 | 143 | - return A new `AnimationChain` with the added animation. 144 | **/ 145 | public class func beginAnimationChain( 146 | _ duration: TimeInterval, 147 | delay: TimeInterval = 0, 148 | options: UIView.AnimationOptions = [], 149 | animations: @escaping Animation 150 | ) -> AnimationChain { 151 | 152 | let boxedFunction = AnimationConfiguration(animations, duration: duration, delay: delay, options: options) 153 | return AnimationChain(options: options, animations: [[boxedFunction]]) 154 | } 155 | } 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /ChainedAnimation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AF6A02501D84204900F89BD9 /* ChainedAnimation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF6A02461D84204900F89BD9 /* ChainedAnimation.framework */; }; 11 | AF6A02551D84204900F89BD9 /* ChainedAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF6A02541D84204900F89BD9 /* ChainedAnimationTests.swift */; }; 12 | AF6A02571D84204900F89BD9 /* ChainedAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = AF6A02491D84204900F89BD9 /* ChainedAnimation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | AF6A02611D84205400F89BD9 /* AnimationChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF6A02601D84205400F89BD9 /* AnimationChain.swift */; }; 14 | AF6A02631D84265700F89BD9 /* MockAnimationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF6A02621D84265700F89BD9 /* MockAnimationProvider.swift */; }; 15 | AF79A3C11D8430C50060857F /* AnimationConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3C01D8430C50060857F /* AnimationConfigurationTests.swift */; }; 16 | AF79A3C31D8431100060857F /* ChainedAnimationTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3C21D8431100060857F /* ChainedAnimationTestCase.swift */; }; 17 | AF79A3C51D8438090060857F /* AnimationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3C41D8438090060857F /* AnimationProvider.swift */; }; 18 | AF79A3C71D8438410060857F /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3C61D8438410060857F /* Animator.swift */; }; 19 | AF79A3C91D8438770060857F /* AnimationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3C81D8438770060857F /* AnimationConfiguration.swift */; }; 20 | AF79A3CB1D8438F60060857F /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3CA1D8438F60060857F /* Types.swift */; }; 21 | AF79A3D31D843C0E0060857F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3D21D843C0E0060857F /* AppDelegate.swift */; }; 22 | AF79A3D51D843C0E0060857F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3D41D843C0E0060857F /* ViewController.swift */; }; 23 | AF79A3D81D843C0E0060857F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AF79A3D61D843C0E0060857F /* Main.storyboard */; }; 24 | AF79A3DA1D843C0E0060857F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF79A3D91D843C0E0060857F /* Assets.xcassets */; }; 25 | AF79A3DD1D843C0E0060857F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AF79A3DB1D843C0E0060857F /* LaunchScreen.storyboard */; }; 26 | AF79A3E31D843C450060857F /* UIView+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF79A3E21D843C450060857F /* UIView+Helper.swift */; }; 27 | AF79A3E41D843D2B0060857F /* ChainedAnimation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF6A02461D84204900F89BD9 /* ChainedAnimation.framework */; }; 28 | AF79A3E51D843D2B0060857F /* ChainedAnimation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AF6A02461D84204900F89BD9 /* ChainedAnimation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | AF6A02511D84204900F89BD9 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = AF6A023D1D84204900F89BD9 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = AF6A02451D84204900F89BD9; 37 | remoteInfo = ChainedAnimation; 38 | }; 39 | AF79A3E61D843D2B0060857F /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = AF6A023D1D84204900F89BD9 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = AF6A02451D84204900F89BD9; 44 | remoteInfo = ChainedAnimation; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXCopyFilesBuildPhase section */ 49 | AF79A3E81D843D2B0060857F /* Embed Frameworks */ = { 50 | isa = PBXCopyFilesBuildPhase; 51 | buildActionMask = 2147483647; 52 | dstPath = ""; 53 | dstSubfolderSpec = 10; 54 | files = ( 55 | AF79A3E51D843D2B0060857F /* ChainedAnimation.framework in Embed Frameworks */, 56 | ); 57 | name = "Embed Frameworks"; 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXCopyFilesBuildPhase section */ 61 | 62 | /* Begin PBXFileReference section */ 63 | AF6A02461D84204900F89BD9 /* ChainedAnimation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ChainedAnimation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | AF6A02491D84204900F89BD9 /* ChainedAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChainedAnimation.h; sourceTree = ""; }; 65 | AF6A024A1D84204900F89BD9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | AF6A024F1D84204900F89BD9 /* ChainedAnimationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChainedAnimationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | AF6A02541D84204900F89BD9 /* ChainedAnimationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedAnimationTests.swift; sourceTree = ""; }; 68 | AF6A02561D84204900F89BD9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | AF6A02601D84205400F89BD9 /* AnimationChain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationChain.swift; sourceTree = ""; }; 70 | AF6A02621D84265700F89BD9 /* MockAnimationProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockAnimationProvider.swift; sourceTree = ""; }; 71 | AF79A3C01D8430C50060857F /* AnimationConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationConfigurationTests.swift; sourceTree = ""; }; 72 | AF79A3C21D8431100060857F /* ChainedAnimationTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChainedAnimationTestCase.swift; sourceTree = ""; }; 73 | AF79A3C41D8438090060857F /* AnimationProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationProvider.swift; sourceTree = ""; }; 74 | AF79A3C61D8438410060857F /* Animator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animator.swift; sourceTree = ""; }; 75 | AF79A3C81D8438770060857F /* AnimationConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationConfiguration.swift; sourceTree = ""; }; 76 | AF79A3CA1D8438F60060857F /* Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; 77 | AF79A3D01D843C0E0060857F /* ChainedAnimationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChainedAnimationExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | AF79A3D21D843C0E0060857F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 79 | AF79A3D41D843C0E0060857F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 80 | AF79A3D71D843C0E0060857F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 81 | AF79A3D91D843C0E0060857F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 82 | AF79A3DC1D843C0E0060857F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 83 | AF79A3DE1D843C0E0060857F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84 | AF79A3E21D843C450060857F /* UIView+Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Helper.swift"; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | AF6A02421D84204900F89BD9 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | AF6A024C1D84204900F89BD9 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | AF6A02501D84204900F89BD9 /* ChainedAnimation.framework in Frameworks */, 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | AF79A3CD1D843C0E0060857F /* Frameworks */ = { 104 | isa = PBXFrameworksBuildPhase; 105 | buildActionMask = 2147483647; 106 | files = ( 107 | AF79A3E41D843D2B0060857F /* ChainedAnimation.framework in Frameworks */, 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | /* End PBXFrameworksBuildPhase section */ 112 | 113 | /* Begin PBXGroup section */ 114 | AF6A023C1D84204900F89BD9 = { 115 | isa = PBXGroup; 116 | children = ( 117 | AF6A02481D84204900F89BD9 /* ChainedAnimation */, 118 | AF6A02531D84204900F89BD9 /* ChainedAnimationTests */, 119 | AF79A3D11D843C0E0060857F /* ChainedAnimationExample */, 120 | AF6A02471D84204900F89BD9 /* Products */, 121 | ); 122 | sourceTree = ""; 123 | }; 124 | AF6A02471D84204900F89BD9 /* Products */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | AF6A02461D84204900F89BD9 /* ChainedAnimation.framework */, 128 | AF6A024F1D84204900F89BD9 /* ChainedAnimationTests.xctest */, 129 | AF79A3D01D843C0E0060857F /* ChainedAnimationExample.app */, 130 | ); 131 | name = Products; 132 | sourceTree = ""; 133 | }; 134 | AF6A02481D84204900F89BD9 /* ChainedAnimation */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | AF6A02491D84204900F89BD9 /* ChainedAnimation.h */, 138 | AF6A02601D84205400F89BD9 /* AnimationChain.swift */, 139 | AF79A3CA1D8438F60060857F /* Types.swift */, 140 | AF79A3C81D8438770060857F /* AnimationConfiguration.swift */, 141 | AF79A3C61D8438410060857F /* Animator.swift */, 142 | AF79A3C41D8438090060857F /* AnimationProvider.swift */, 143 | AF6A024A1D84204900F89BD9 /* Info.plist */, 144 | ); 145 | path = ChainedAnimation; 146 | sourceTree = ""; 147 | }; 148 | AF6A02531D84204900F89BD9 /* ChainedAnimationTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | AF6A02541D84204900F89BD9 /* ChainedAnimationTests.swift */, 152 | AF79A3C21D8431100060857F /* ChainedAnimationTestCase.swift */, 153 | AF79A3C01D8430C50060857F /* AnimationConfigurationTests.swift */, 154 | AF6A02621D84265700F89BD9 /* MockAnimationProvider.swift */, 155 | AF6A02561D84204900F89BD9 /* Info.plist */, 156 | ); 157 | path = ChainedAnimationTests; 158 | sourceTree = ""; 159 | }; 160 | AF79A3D11D843C0E0060857F /* ChainedAnimationExample */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | AF79A3D21D843C0E0060857F /* AppDelegate.swift */, 164 | AF79A3E21D843C450060857F /* UIView+Helper.swift */, 165 | AF79A3D41D843C0E0060857F /* ViewController.swift */, 166 | AF79A3D61D843C0E0060857F /* Main.storyboard */, 167 | AF79A3D91D843C0E0060857F /* Assets.xcassets */, 168 | AF79A3DB1D843C0E0060857F /* LaunchScreen.storyboard */, 169 | AF79A3DE1D843C0E0060857F /* Info.plist */, 170 | ); 171 | path = ChainedAnimationExample; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXHeadersBuildPhase section */ 177 | AF6A02431D84204900F89BD9 /* Headers */ = { 178 | isa = PBXHeadersBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | AF6A02571D84204900F89BD9 /* ChainedAnimation.h in Headers */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | /* End PBXHeadersBuildPhase section */ 186 | 187 | /* Begin PBXNativeTarget section */ 188 | AF6A02451D84204900F89BD9 /* ChainedAnimation */ = { 189 | isa = PBXNativeTarget; 190 | buildConfigurationList = AF6A025A1D84204900F89BD9 /* Build configuration list for PBXNativeTarget "ChainedAnimation" */; 191 | buildPhases = ( 192 | AF6A02411D84204900F89BD9 /* Sources */, 193 | AF6A02421D84204900F89BD9 /* Frameworks */, 194 | AF6A02431D84204900F89BD9 /* Headers */, 195 | AF6A02441D84204900F89BD9 /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | ); 201 | name = ChainedAnimation; 202 | productName = ChainedAnimation; 203 | productReference = AF6A02461D84204900F89BD9 /* ChainedAnimation.framework */; 204 | productType = "com.apple.product-type.framework"; 205 | }; 206 | AF6A024E1D84204900F89BD9 /* ChainedAnimationTests */ = { 207 | isa = PBXNativeTarget; 208 | buildConfigurationList = AF6A025D1D84204900F89BD9 /* Build configuration list for PBXNativeTarget "ChainedAnimationTests" */; 209 | buildPhases = ( 210 | AF6A024B1D84204900F89BD9 /* Sources */, 211 | AF6A024C1D84204900F89BD9 /* Frameworks */, 212 | AF6A024D1D84204900F89BD9 /* Resources */, 213 | ); 214 | buildRules = ( 215 | ); 216 | dependencies = ( 217 | AF6A02521D84204900F89BD9 /* PBXTargetDependency */, 218 | ); 219 | name = ChainedAnimationTests; 220 | productName = ChainedAnimationTests; 221 | productReference = AF6A024F1D84204900F89BD9 /* ChainedAnimationTests.xctest */; 222 | productType = "com.apple.product-type.bundle.unit-test"; 223 | }; 224 | AF79A3CF1D843C0E0060857F /* ChainedAnimationExample */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = AF79A3E11D843C0E0060857F /* Build configuration list for PBXNativeTarget "ChainedAnimationExample" */; 227 | buildPhases = ( 228 | AF79A3CC1D843C0E0060857F /* Sources */, 229 | AF79A3CD1D843C0E0060857F /* Frameworks */, 230 | AF79A3CE1D843C0E0060857F /* Resources */, 231 | AF79A3E81D843D2B0060857F /* Embed Frameworks */, 232 | ); 233 | buildRules = ( 234 | ); 235 | dependencies = ( 236 | AF79A3E71D843D2B0060857F /* PBXTargetDependency */, 237 | ); 238 | name = ChainedAnimationExample; 239 | productName = ChainedAnimationExample; 240 | productReference = AF79A3D01D843C0E0060857F /* ChainedAnimationExample.app */; 241 | productType = "com.apple.product-type.application"; 242 | }; 243 | /* End PBXNativeTarget section */ 244 | 245 | /* Begin PBXProject section */ 246 | AF6A023D1D84204900F89BD9 /* Project object */ = { 247 | isa = PBXProject; 248 | attributes = { 249 | LastSwiftUpdateCheck = 0800; 250 | LastUpgradeCheck = 1000; 251 | ORGANIZATIONNAME = "Silvan Dähn"; 252 | TargetAttributes = { 253 | AF6A02451D84204900F89BD9 = { 254 | CreatedOnToolsVersion = 8.0; 255 | LastSwiftMigration = 1000; 256 | ProvisioningStyle = Automatic; 257 | }; 258 | AF6A024E1D84204900F89BD9 = { 259 | CreatedOnToolsVersion = 8.0; 260 | LastSwiftMigration = 1000; 261 | ProvisioningStyle = Automatic; 262 | }; 263 | AF79A3CF1D843C0E0060857F = { 264 | CreatedOnToolsVersion = 8.0; 265 | LastSwiftMigration = 0900; 266 | ProvisioningStyle = Automatic; 267 | }; 268 | }; 269 | }; 270 | buildConfigurationList = AF6A02401D84204900F89BD9 /* Build configuration list for PBXProject "ChainedAnimation" */; 271 | compatibilityVersion = "Xcode 3.2"; 272 | developmentRegion = English; 273 | hasScannedForEncodings = 0; 274 | knownRegions = ( 275 | en, 276 | Base, 277 | ); 278 | mainGroup = AF6A023C1D84204900F89BD9; 279 | productRefGroup = AF6A02471D84204900F89BD9 /* Products */; 280 | projectDirPath = ""; 281 | projectRoot = ""; 282 | targets = ( 283 | AF6A02451D84204900F89BD9 /* ChainedAnimation */, 284 | AF6A024E1D84204900F89BD9 /* ChainedAnimationTests */, 285 | AF79A3CF1D843C0E0060857F /* ChainedAnimationExample */, 286 | ); 287 | }; 288 | /* End PBXProject section */ 289 | 290 | /* Begin PBXResourcesBuildPhase section */ 291 | AF6A02441D84204900F89BD9 /* Resources */ = { 292 | isa = PBXResourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | AF6A024D1D84204900F89BD9 /* Resources */ = { 299 | isa = PBXResourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | AF79A3CE1D843C0E0060857F /* Resources */ = { 306 | isa = PBXResourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | AF79A3DD1D843C0E0060857F /* LaunchScreen.storyboard in Resources */, 310 | AF79A3DA1D843C0E0060857F /* Assets.xcassets in Resources */, 311 | AF79A3D81D843C0E0060857F /* Main.storyboard in Resources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | /* End PBXResourcesBuildPhase section */ 316 | 317 | /* Begin PBXSourcesBuildPhase section */ 318 | AF6A02411D84204900F89BD9 /* Sources */ = { 319 | isa = PBXSourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | AF79A3C71D8438410060857F /* Animator.swift in Sources */, 323 | AF6A02611D84205400F89BD9 /* AnimationChain.swift in Sources */, 324 | AF79A3C51D8438090060857F /* AnimationProvider.swift in Sources */, 325 | AF79A3CB1D8438F60060857F /* Types.swift in Sources */, 326 | AF79A3C91D8438770060857F /* AnimationConfiguration.swift in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | AF6A024B1D84204900F89BD9 /* Sources */ = { 331 | isa = PBXSourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | AF79A3C31D8431100060857F /* ChainedAnimationTestCase.swift in Sources */, 335 | AF79A3C11D8430C50060857F /* AnimationConfigurationTests.swift in Sources */, 336 | AF6A02551D84204900F89BD9 /* ChainedAnimationTests.swift in Sources */, 337 | AF6A02631D84265700F89BD9 /* MockAnimationProvider.swift in Sources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | AF79A3CC1D843C0E0060857F /* Sources */ = { 342 | isa = PBXSourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | AF79A3D51D843C0E0060857F /* ViewController.swift in Sources */, 346 | AF79A3E31D843C450060857F /* UIView+Helper.swift in Sources */, 347 | AF79A3D31D843C0E0060857F /* AppDelegate.swift in Sources */, 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | /* End PBXSourcesBuildPhase section */ 352 | 353 | /* Begin PBXTargetDependency section */ 354 | AF6A02521D84204900F89BD9 /* PBXTargetDependency */ = { 355 | isa = PBXTargetDependency; 356 | target = AF6A02451D84204900F89BD9 /* ChainedAnimation */; 357 | targetProxy = AF6A02511D84204900F89BD9 /* PBXContainerItemProxy */; 358 | }; 359 | AF79A3E71D843D2B0060857F /* PBXTargetDependency */ = { 360 | isa = PBXTargetDependency; 361 | target = AF6A02451D84204900F89BD9 /* ChainedAnimation */; 362 | targetProxy = AF79A3E61D843D2B0060857F /* PBXContainerItemProxy */; 363 | }; 364 | /* End PBXTargetDependency section */ 365 | 366 | /* Begin PBXVariantGroup section */ 367 | AF79A3D61D843C0E0060857F /* Main.storyboard */ = { 368 | isa = PBXVariantGroup; 369 | children = ( 370 | AF79A3D71D843C0E0060857F /* Base */, 371 | ); 372 | name = Main.storyboard; 373 | sourceTree = ""; 374 | }; 375 | AF79A3DB1D843C0E0060857F /* LaunchScreen.storyboard */ = { 376 | isa = PBXVariantGroup; 377 | children = ( 378 | AF79A3DC1D843C0E0060857F /* Base */, 379 | ); 380 | name = LaunchScreen.storyboard; 381 | sourceTree = ""; 382 | }; 383 | /* End PBXVariantGroup section */ 384 | 385 | /* Begin XCBuildConfiguration section */ 386 | AF6A02581D84204900F89BD9 /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | ALWAYS_SEARCH_USER_PATHS = NO; 390 | CLANG_ANALYZER_NONNULL = YES; 391 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 392 | CLANG_CXX_LIBRARY = "libc++"; 393 | CLANG_ENABLE_MODULES = YES; 394 | CLANG_ENABLE_OBJC_ARC = YES; 395 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 396 | CLANG_WARN_BOOL_CONVERSION = YES; 397 | CLANG_WARN_COMMA = YES; 398 | CLANG_WARN_CONSTANT_CONVERSION = YES; 399 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 400 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 401 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 414 | CLANG_WARN_UNREACHABLE_CODE = YES; 415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 416 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 417 | COPY_PHASE_STRIP = NO; 418 | CURRENT_PROJECT_VERSION = 1; 419 | DEBUG_INFORMATION_FORMAT = dwarf; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | ENABLE_TESTABILITY = YES; 422 | GCC_C_LANGUAGE_STANDARD = gnu99; 423 | GCC_DYNAMIC_NO_PIC = NO; 424 | GCC_NO_COMMON_BLOCKS = YES; 425 | GCC_OPTIMIZATION_LEVEL = 0; 426 | GCC_PREPROCESSOR_DEFINITIONS = ( 427 | "DEBUG=1", 428 | "$(inherited)", 429 | ); 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 437 | MTL_ENABLE_DEBUG_INFO = YES; 438 | ONLY_ACTIVE_ARCH = YES; 439 | SDKROOT = iphoneos; 440 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 441 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 442 | SWIFT_VERSION = 4.0; 443 | TARGETED_DEVICE_FAMILY = "1,2"; 444 | VERSIONING_SYSTEM = "apple-generic"; 445 | VERSION_INFO_PREFIX = ""; 446 | }; 447 | name = Debug; 448 | }; 449 | AF6A02591D84204900F89BD9 /* Release */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | ALWAYS_SEARCH_USER_PATHS = NO; 453 | CLANG_ANALYZER_NONNULL = YES; 454 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 455 | CLANG_CXX_LIBRARY = "libc++"; 456 | CLANG_ENABLE_MODULES = YES; 457 | CLANG_ENABLE_OBJC_ARC = YES; 458 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 459 | CLANG_WARN_BOOL_CONVERSION = YES; 460 | CLANG_WARN_COMMA = YES; 461 | CLANG_WARN_CONSTANT_CONVERSION = YES; 462 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 463 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 464 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INFINITE_RECURSION = YES; 468 | CLANG_WARN_INT_CONVERSION = YES; 469 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 470 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 471 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 472 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 473 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 474 | CLANG_WARN_STRICT_PROTOTYPES = YES; 475 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 476 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 477 | CLANG_WARN_UNREACHABLE_CODE = YES; 478 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 479 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 480 | COPY_PHASE_STRIP = NO; 481 | CURRENT_PROJECT_VERSION = 1; 482 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 483 | ENABLE_NS_ASSERTIONS = NO; 484 | ENABLE_STRICT_OBJC_MSGSEND = YES; 485 | GCC_C_LANGUAGE_STANDARD = gnu99; 486 | GCC_NO_COMMON_BLOCKS = YES; 487 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 488 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 489 | GCC_WARN_UNDECLARED_SELECTOR = YES; 490 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 491 | GCC_WARN_UNUSED_FUNCTION = YES; 492 | GCC_WARN_UNUSED_VARIABLE = YES; 493 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 494 | MTL_ENABLE_DEBUG_INFO = NO; 495 | SDKROOT = iphoneos; 496 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 497 | SWIFT_VERSION = 4.0; 498 | TARGETED_DEVICE_FAMILY = "1,2"; 499 | VALIDATE_PRODUCT = YES; 500 | VERSIONING_SYSTEM = "apple-generic"; 501 | VERSION_INFO_PREFIX = ""; 502 | }; 503 | name = Release; 504 | }; 505 | AF6A025B1D84204900F89BD9 /* Debug */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | CLANG_ENABLE_MODULES = YES; 509 | CODE_SIGN_IDENTITY = ""; 510 | DEFINES_MODULE = YES; 511 | DYLIB_COMPATIBILITY_VERSION = 1; 512 | DYLIB_CURRENT_VERSION = 1; 513 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 514 | INFOPLIST_FILE = ChainedAnimation/Info.plist; 515 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 517 | PRODUCT_BUNDLE_IDENTIFIER = com.silvandaehn.ChainedAnimation; 518 | PRODUCT_NAME = "$(TARGET_NAME)"; 519 | SKIP_INSTALL = YES; 520 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 521 | SWIFT_VERSION = 4.2; 522 | }; 523 | name = Debug; 524 | }; 525 | AF6A025C1D84204900F89BD9 /* Release */ = { 526 | isa = XCBuildConfiguration; 527 | buildSettings = { 528 | CLANG_ENABLE_MODULES = YES; 529 | CODE_SIGN_IDENTITY = ""; 530 | DEFINES_MODULE = YES; 531 | DYLIB_COMPATIBILITY_VERSION = 1; 532 | DYLIB_CURRENT_VERSION = 1; 533 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 534 | INFOPLIST_FILE = ChainedAnimation/Info.plist; 535 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 536 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 537 | PRODUCT_BUNDLE_IDENTIFIER = com.silvandaehn.ChainedAnimation; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | SKIP_INSTALL = YES; 540 | SWIFT_VERSION = 4.2; 541 | }; 542 | name = Release; 543 | }; 544 | AF6A025E1D84204900F89BD9 /* Debug */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 548 | INFOPLIST_FILE = ChainedAnimationTests/Info.plist; 549 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 550 | PRODUCT_BUNDLE_IDENTIFIER = com.silvandaehn.ChainedAnimationTests; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | SWIFT_VERSION = 4.2; 553 | }; 554 | name = Debug; 555 | }; 556 | AF6A025F1D84204900F89BD9 /* Release */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 560 | INFOPLIST_FILE = ChainedAnimationTests/Info.plist; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 562 | PRODUCT_BUNDLE_IDENTIFIER = com.silvandaehn.ChainedAnimationTests; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | SWIFT_VERSION = 4.2; 565 | }; 566 | name = Release; 567 | }; 568 | AF79A3DF1D843C0E0060857F /* Debug */ = { 569 | isa = XCBuildConfiguration; 570 | buildSettings = { 571 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 572 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 573 | INFOPLIST_FILE = ChainedAnimationExample/Info.plist; 574 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 575 | PRODUCT_BUNDLE_IDENTIFIER = com.silvandaehn.ChainedAnimationExample; 576 | PRODUCT_NAME = "$(TARGET_NAME)"; 577 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 578 | }; 579 | name = Debug; 580 | }; 581 | AF79A3E01D843C0E0060857F /* Release */ = { 582 | isa = XCBuildConfiguration; 583 | buildSettings = { 584 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 585 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 586 | INFOPLIST_FILE = ChainedAnimationExample/Info.plist; 587 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 588 | PRODUCT_BUNDLE_IDENTIFIER = com.silvandaehn.ChainedAnimationExample; 589 | PRODUCT_NAME = "$(TARGET_NAME)"; 590 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 591 | }; 592 | name = Release; 593 | }; 594 | /* End XCBuildConfiguration section */ 595 | 596 | /* Begin XCConfigurationList section */ 597 | AF6A02401D84204900F89BD9 /* Build configuration list for PBXProject "ChainedAnimation" */ = { 598 | isa = XCConfigurationList; 599 | buildConfigurations = ( 600 | AF6A02581D84204900F89BD9 /* Debug */, 601 | AF6A02591D84204900F89BD9 /* Release */, 602 | ); 603 | defaultConfigurationIsVisible = 0; 604 | defaultConfigurationName = Release; 605 | }; 606 | AF6A025A1D84204900F89BD9 /* Build configuration list for PBXNativeTarget "ChainedAnimation" */ = { 607 | isa = XCConfigurationList; 608 | buildConfigurations = ( 609 | AF6A025B1D84204900F89BD9 /* Debug */, 610 | AF6A025C1D84204900F89BD9 /* Release */, 611 | ); 612 | defaultConfigurationIsVisible = 0; 613 | defaultConfigurationName = Release; 614 | }; 615 | AF6A025D1D84204900F89BD9 /* Build configuration list for PBXNativeTarget "ChainedAnimationTests" */ = { 616 | isa = XCConfigurationList; 617 | buildConfigurations = ( 618 | AF6A025E1D84204900F89BD9 /* Debug */, 619 | AF6A025F1D84204900F89BD9 /* Release */, 620 | ); 621 | defaultConfigurationIsVisible = 0; 622 | defaultConfigurationName = Release; 623 | }; 624 | AF79A3E11D843C0E0060857F /* Build configuration list for PBXNativeTarget "ChainedAnimationExample" */ = { 625 | isa = XCConfigurationList; 626 | buildConfigurations = ( 627 | AF79A3DF1D843C0E0060857F /* Debug */, 628 | AF79A3E01D843C0E0060857F /* Release */, 629 | ); 630 | defaultConfigurationIsVisible = 0; 631 | defaultConfigurationName = Release; 632 | }; 633 | /* End XCConfigurationList section */ 634 | }; 635 | rootObject = AF6A023D1D84204900F89BD9 /* Project object */; 636 | } 637 | --------------------------------------------------------------------------------