Repository: rakaramos/StarButton Branch: master Commit: 644bac546603 Files: 16 Total size: 52.3 KB Directory structure: gitextract_8i_7yswl/ ├── .gitignore ├── LICENSE ├── README.md ├── StarBounce/ │ ├── AppDelegate.swift │ ├── Base.lproj/ │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Extensions.swift │ ├── Images.xcassets/ │ │ └── AppIcon.appiconset/ │ │ └── Contents.json │ ├── Info.plist │ ├── Paths.swift │ ├── StarButton.swift │ └── ViewController.swift ├── StarBounce.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ └── contents.xcworkspacedata └── StarBounceTests/ ├── Info.plist └── StarBounceTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # # Pods/ .DS_Store ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Rafael Machado Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Star Button ## Article [Creating Custom Animated Buttons](https://swift.unicorn.tv/articles/creating-custom-animated-buttons) ## Demo Source of inspiration ![alt tag](https://d13yacurqjgara.cloudfront.net/users/100791/screenshots/1555501/driblle_star.gif) ## Code result ![alt tag](http://ramosmachado.net/wp-content/uploads/2015/02/starbounce.gif) ## A Swift bounce animation :) ![alt tag](http://ramosmachado.net/wp-content/uploads/2015/02/swift.gif) ## License Released under the MIT license. See the LICENSE file for more info. ================================================ FILE: StarBounce/AppDelegate.swift ================================================ // // AppDelegate.swift // StarBounce // // Created by Rafael Machado on 2/10/15. // Copyright (c) 2015 Rafael Machado. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { return true } } ================================================ FILE: StarBounce/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: StarBounce/Base.lproj/Main.storyboard ================================================ ================================================ FILE: StarBounce/Extensions.swift ================================================ // // Extensions.swift // FavoriteStart // // Created by Rafael Machado on 2/9/15. // Copyright (c) 2015 Rafael Machado. All rights reserved. // import UIKit extension UIColor { convenience init (hex: String) { var cString = hex.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased() if cString.hasPrefix("#") { cString = (cString as NSString).substring(from: 1) } if cString.characters.count != 6 { self.init() return } let rString = (cString as NSString).substring(to: 2) let gString = ((cString as NSString).substring(from: 2) as NSString).substring(to: 2) let bString = ((cString as NSString).substring(from: 4) as NSString).substring(to: 2) var r: CUnsignedInt = 0, g: CUnsignedInt = 0, b: CUnsignedInt = 0 Scanner(string: rString).scanHexInt32(&r) Scanner(string: gString).scanHexInt32(&g) Scanner(string: bString).scanHexInt32(&b) self.init(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(1)) } } extension CALayer { func applyAnimation(_ animation: CABasicAnimation) { guard let copy = animation.copy() as? CABasicAnimation, let presentationLayer = self.presentation(), let keyPath = copy.keyPath else { return } if copy.fromValue == nil { copy.fromValue = presentationLayer.value(forKeyPath: keyPath) } self.add(copy, forKey: copy.keyPath) CATransaction.begin() CATransaction.setDisableActions(true) self.setValue(copy.toValue, forKeyPath:keyPath) CATransaction.commit() } } extension CGPath { class func rescaleForFrame(path: CGPath, frame: CGRect) -> CGPath { let boundingBox = path.boundingBoxOfPath let boundingBoxAspectRatio = boundingBox.width/boundingBox.height let viewAspectRatio = frame.width/frame.height var scaleFactor: CGFloat = 1 if boundingBoxAspectRatio > viewAspectRatio { scaleFactor = frame.width/boundingBox.width } else { scaleFactor = frame.height/boundingBox.height } var scaleTransform = CGAffineTransform.identity scaleTransform = scaleTransform.scaledBy(x: scaleFactor, y: scaleFactor) scaleTransform = scaleTransform.translatedBy(x: -boundingBox.minX, y: -boundingBox.minY) let scaledSize = boundingBox.size.applying(CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)) let centerOffset = CGSize(width: (frame.width - scaledSize.width) / (scaleFactor * 2.0), height: (frame.height - scaledSize.height) / (scaleFactor * 2.0)) scaleTransform = scaleTransform.translatedBy(x: centerOffset.width, y: centerOffset.height) if let scaleTransform = path.copy(using: &scaleTransform) { return scaleTransform } else { return path } } } ================================================ FILE: StarBounce/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: StarBounce/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: StarBounce/Paths.swift ================================================ // // Paths.swift // FavoriteStart // // Created by Rafael Machado on 2/9/15. // Copyright (c) 2015 Rafael Machado. All rights reserved. // import UIKit struct Paths { static var star: CGPath { let star = UIBezierPath() star.move(to: CGPoint(x: 112.79, y: 119)) star.addCurve(to: CGPoint(x: 107.75, y: 122.6), controlPoint1: CGPoint(x: 113.41, y: 122.8), controlPoint2: CGPoint(x: 111.14, y: 124.42)) star.addLine(to: CGPoint(x: 96.53, y: 116.58)) star.addCurve(to: CGPoint(x: 84.14, y: 116.47), controlPoint1: CGPoint(x: 93.14, y: 114.76), controlPoint2: CGPoint(x: 87.56, y: 114.71)) star.addLine(to: CGPoint(x: 72.82, y: 122.3)) star.addCurve(to: CGPoint(x: 67.84, y: 118.62), controlPoint1: CGPoint(x: 69.4, y: 124.06), controlPoint2: CGPoint(x: 67.15, y: 122.41)) star.addLine(to: CGPoint(x: 70.1, y: 106.09)) star.addCurve(to: CGPoint(x: 66.37, y: 94.27), controlPoint1: CGPoint(x: 70.78, y: 102.3), controlPoint2: CGPoint(x: 69.1, y: 96.98)) star.addLine(to: CGPoint(x: 57.33, y: 85.31)) star.addCurve(to: CGPoint(x: 59.29, y: 79.43), controlPoint1: CGPoint(x: 54.6, y: 82.6), controlPoint2: CGPoint(x: 55.48, y: 79.95)) star.addLine(to: CGPoint(x: 71.91, y: 77.71)) star.addCurve(to: CGPoint(x: 81.99, y: 70.51), controlPoint1: CGPoint(x: 75.72, y: 77.19), controlPoint2: CGPoint(x: 80.26, y: 73.95)) star.addLine(to: CGPoint(x: 87.72, y: 59.14)) star.addCurve(to: CGPoint(x: 93.92, y: 59.2), controlPoint1: CGPoint(x: 89.46, y: 55.71), controlPoint2: CGPoint(x: 92.25, y: 55.73)) star.addLine(to: CGPoint(x: 99.46, y: 70.66)) star.addCurve(to: CGPoint(x: 109.42, y: 78.03), controlPoint1: CGPoint(x: 101.13, y: 74.13), controlPoint2: CGPoint(x: 105.62, y: 77.44)) star.addLine(to: CGPoint(x: 122, y: 79.96)) star.addCurve(to: CGPoint(x: 123.87, y: 85.87), controlPoint1: CGPoint(x: 125.81, y: 80.55), controlPoint2: CGPoint(x: 126.64, y: 83.21)) star.addLine(to: CGPoint(x: 114.67, y: 94.68)) star.addCurve(to: CGPoint(x: 110.75, y: 106.43), controlPoint1: CGPoint(x: 111.89, y: 97.34), controlPoint2: CGPoint(x: 110.13, y: 102.63)) star.addLine(to: CGPoint(x: 112.79, y: 119)) return star.cgPath } static var swift: CGPath { let swiftPath = UIBezierPath() swiftPath.move(to: CGPoint(x: 376.2, y: 283.2)) swiftPath.addCurve(to: CGPoint(x: 349.8, y: 238.4), controlPoint1: CGPoint(x: 367.4, y: 258.4), controlPoint2: CGPoint(x: 349.8, y: 238.4)) swiftPath.addCurve(to: CGPoint(x: 236.5, y: 0), controlPoint1: CGPoint(x: 349.8, y: 238.4), controlPoint2: CGPoint(x: 399.7, y: 105.6)) swiftPath.addCurve(to: CGPoint(x: 269, y: 180.8), controlPoint1: CGPoint(x: 303.7, y: 101.6), controlPoint2: CGPoint(x: 269, y: 180.8)) swiftPath.addCurve(to: CGPoint(x: 181.29, y: 117.07), controlPoint1: CGPoint(x: 269, y: 180.8), controlPoint2: CGPoint(x: 211.4, y: 140.8)) swiftPath.addCurve(to: CGPoint(x: 85, y: 33.6), controlPoint1: CGPoint(x: 151.18, y: 93.35), controlPoint2: CGPoint(x: 85, y: 33.6)) swiftPath.addCurve(to: CGPoint(x: 145, y: 117.07), controlPoint1: CGPoint(x: 85, y: 33.6), controlPoint2: CGPoint(x: 128.15, y: 96.31)) swiftPath.addCurve(to: CGPoint(x: 185.78, y: 163.66), controlPoint1: CGPoint(x: 161.85, y: 137.84), controlPoint2: CGPoint(x: 185.78, y: 163.66)) swiftPath.addCurve(to: CGPoint(x: 136.36, y: 129.42), controlPoint1: CGPoint(x: 185.78, y: 163.66), controlPoint2: CGPoint(x: 161.07, y: 147.39)) swiftPath.addCurve(to: CGPoint(x: 34.6, y: 50.4), controlPoint1: CGPoint(x: 111.65, y: 111.46), controlPoint2: CGPoint(x: 34.6, y: 50.4)) swiftPath.addCurve(to: CGPoint(x: 133.8, y: 169.2), controlPoint1: CGPoint(x: 34.6, y: 50.4), controlPoint2: CGPoint(x: 82.69, y: 119.24)) swiftPath.addCurve(to: CGPoint(x: 214.6, y: 244), controlPoint1: CGPoint(x: 184.91, y: 219.16), controlPoint2: CGPoint(x: 214.6, y: 244)) swiftPath.addCurve(to: CGPoint(x: 129.8, y: 264.8), controlPoint1: CGPoint(x: 214.6, y: 244), controlPoint2: CGPoint(x: 196.2, y: 263.2)) swiftPath.addCurve(to: CGPoint(x: 0, y: 221), controlPoint1: CGPoint(x: 63.4, y: 266.4), controlPoint2: CGPoint(x: 0, y: 221)) swiftPath.addCurve(to: CGPoint(x: 206.6, y: 339.2), controlPoint1: CGPoint(x: 0, y: 221), controlPoint2: CGPoint(x: 62.5, y: 339.2)) swiftPath.addCurve(to: CGPoint(x: 325, y: 304.8), controlPoint1: CGPoint(x: 270.6, y: 339.2), controlPoint2: CGPoint(x: 288.93, y: 304.8)) swiftPath.addCurve(to: CGPoint(x: 383.3, y: 339.2), controlPoint1: CGPoint(x: 361.07, y: 304.8), controlPoint2: CGPoint(x: 381.7, y: 340)) swiftPath.addCurve(to: CGPoint(x: 376.2, y: 283.2), controlPoint1: CGPoint(x: 384.9, y: 338.4), controlPoint2: CGPoint(x: 385, y: 308)) return swiftPath.cgPath } static func circle(_ inFrame: CGRect) -> CGPath { let circle = UIBezierPath(ovalIn: inFrame) return circle.cgPath } } ================================================ FILE: StarBounce/StarButton.swift ================================================ // // StatButton.swift // FavoriteStar // // Created by Rafael Machado on 14/11/14. // Copyright (c) 2014 Rafael Machado. All rights reserved. // import UIKit import QuartzCore @IBDesignable class StarButton: UIButton, CAAnimationDelegate { fileprivate var starShape: CAShapeLayer! fileprivate var outerRingShape: CAShapeLayer! fileprivate var fillRingShape: CAShapeLayer! fileprivate let starKey = "FAVANIMKEY" fileprivate let favoriteKey = "FAVORITE" fileprivate let notFavoriteKey = "NOTFAVORITE" @IBInspectable var lineWidth: CGFloat = 1 { didSet { updateLayerProperties() } } @IBInspectable var favoriteColor: UIColor = UIColor(hex:"eecd34") { didSet { updateLayerProperties() } } @IBInspectable var notFavoriteColor: UIColor = UIColor(hex:"9e9b9b") { didSet { updateLayerProperties() } } @IBInspectable var starFavoriteColor: UIColor = UIColor(hex:"9e9b9b") { didSet { updateLayerProperties() } } var isFavorite: Bool = false { didSet { if self.isFavorite { favorite() } else { notFavorite() } } } fileprivate func updateLayerProperties() { if fillRingShape != nil { fillRingShape.fillColor = favoriteColor.cgColor } if outerRingShape != nil { outerRingShape.lineWidth = lineWidth outerRingShape.strokeColor = notFavoriteColor.cgColor } if starShape != nil { if isFavorite { starShape.fillColor = starFavoriteColor.cgColor } else { starShape.fillColor = notFavoriteColor.cgColor } } } override func layoutSubviews() { super.layoutSubviews() createLayersIfNeeded() updateLayerProperties() } fileprivate func createLayersIfNeeded() { if fillRingShape == nil { fillRingShape = CAShapeLayer() fillRingShape.path = Paths.circle(frameWithInset()) fillRingShape.bounds = (fillRingShape.path?.boundingBox)! fillRingShape.fillColor = favoriteColor.cgColor fillRingShape.lineWidth = lineWidth fillRingShape.position = CGPoint(x: fillRingShape.bounds.width/2, y: fillRingShape.bounds.height/2) fillRingShape.transform = CATransform3DMakeScale(0.2, 0.2, 0.2) fillRingShape.opacity = 0 self.layer.addSublayer(fillRingShape) } if outerRingShape == nil { outerRingShape = CAShapeLayer() outerRingShape.path = Paths.circle(frameWithInset()) outerRingShape.bounds = frameWithInset() outerRingShape.lineWidth = lineWidth outerRingShape.strokeColor = notFavoriteColor.cgColor outerRingShape.fillColor = UIColor.clear.cgColor outerRingShape.position = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2) outerRingShape.transform = CATransform3DIdentity outerRingShape.opacity = 0.5 self.layer.addSublayer(outerRingShape) } if starShape == nil { var starFrame = self.bounds starFrame.size.width = starFrame.width/2.5 starFrame.size.height = starFrame.height/2.5 starShape = CAShapeLayer() starShape.path = CGPath.rescaleForFrame(path: Paths.star, frame: starFrame) starShape.bounds = (starShape.path?.boundingBoxOfPath)! starShape.fillColor = notFavoriteColor.cgColor starShape.position = CGPoint(x: (outerRingShape.path?.boundingBox.width)!/2, y: (outerRingShape.path?.boundingBox.height)!/2) starShape.transform = CATransform3DIdentity starShape.opacity = 0.5 self.layer.addSublayer(starShape) } } fileprivate func frameWithInset() -> CGRect { return self.bounds.insetBy(dx: lineWidth/2, dy: lineWidth/2) } fileprivate func notFavorite() { let starFillColor = CABasicAnimation(keyPath: "fillColor") starFillColor.toValue = notFavoriteColor.cgColor starFillColor.duration = 0.3 let starOpacity = CABasicAnimation(keyPath: "opacity") starOpacity.toValue = 0.5 starOpacity.duration = 0.3 let starGroup = CAAnimationGroup() starGroup.animations = [starFillColor, starOpacity] starShape.add(starGroup, forKey: nil) starShape.fillColor = notFavoriteColor.cgColor starShape.opacity = 0.5 let fillCircle = CABasicAnimation(keyPath: "opacity") fillCircle.toValue = 0 fillCircle.duration = 0.3 fillCircle.setValue(notFavoriteKey, forKey: starKey) fillCircle.delegate = self fillRingShape.add(fillCircle, forKey: nil) fillRingShape.opacity = 0 let outerCircle = CABasicAnimation(keyPath: "opacity") outerCircle.toValue = 0.5 outerCircle.duration = 0.3 outerRingShape.add(outerCircle, forKey: nil) outerRingShape.opacity = 0.5 } fileprivate func favorite() { var starGoUp = CATransform3DIdentity starGoUp = CATransform3DScale(starGoUp, 1.5, 1.5, 1.5) var starGoDown = CATransform3DIdentity starGoDown = CATransform3DScale(starGoDown, 0.01, 0.01, 0.01) let starKeyFrames = CAKeyframeAnimation(keyPath: "transform") starKeyFrames.values = [NSValue(caTransform3D:CATransform3DIdentity), NSValue(caTransform3D:starGoUp), NSValue(caTransform3D:starGoDown)] starKeyFrames.keyTimes = [0.0, 0.4, 0.6] starKeyFrames.duration = 0.4 starKeyFrames.beginTime = CACurrentMediaTime() + 0.05 starKeyFrames.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) starKeyFrames.fillMode = kCAFillModeBackwards starKeyFrames.setValue(favoriteKey, forKey: starKey) starKeyFrames.delegate = self starShape.add(starKeyFrames, forKey: favoriteKey) starShape.transform = starGoDown var grayGoUp = CATransform3DIdentity grayGoUp = CATransform3DScale(grayGoUp, 1.5, 1.5, 1.5) var grayGoDown = CATransform3DIdentity grayGoDown = CATransform3DScale(grayGoDown, 0.01, 0.01, 0.01) let outerCircleAnimation = CAKeyframeAnimation(keyPath: "transform") outerCircleAnimation.values = [NSValue(caTransform3D:CATransform3DIdentity), NSValue(caTransform3D:grayGoUp), NSValue(caTransform3D:grayGoDown)] outerCircleAnimation.keyTimes = [0.0, 0.4, 0.6] outerCircleAnimation.duration = 0.4 outerCircleAnimation.beginTime = CACurrentMediaTime() + 0.01 outerCircleAnimation.fillMode = kCAFillModeBackwards outerCircleAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) outerRingShape.add(outerCircleAnimation, forKey: "Gray circle Animation") outerRingShape.transform = grayGoDown var favoriteFillGrow = CATransform3DIdentity favoriteFillGrow = CATransform3DScale(favoriteFillGrow, 1.5, 1.5, 1.5) let fillCircleAnimation = CAKeyframeAnimation(keyPath: "transform") fillCircleAnimation.values = [NSValue(caTransform3D:fillRingShape.transform), NSValue(caTransform3D:favoriteFillGrow), NSValue(caTransform3D:CATransform3DIdentity)] fillCircleAnimation.keyTimes = [0.0, 0.4, 0.6] fillCircleAnimation.duration = 0.4 fillCircleAnimation.beginTime = CACurrentMediaTime() + 0.22 fillCircleAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) fillCircleAnimation.fillMode = kCAFillModeBackwards let favoriteFillOpacity = CABasicAnimation(keyPath: "opacity") favoriteFillOpacity.toValue = 1 favoriteFillOpacity.duration = 1 favoriteFillOpacity.beginTime = CACurrentMediaTime() favoriteFillOpacity.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) favoriteFillOpacity.fillMode = kCAFillModeBackwards fillRingShape.add(favoriteFillOpacity, forKey: "Show fill circle") fillRingShape.add(fillCircleAnimation, forKey: "fill circle Animation") fillRingShape.transform = CATransform3DIdentity } func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if let key = anim.value(forKey: starKey) as? String { switch key { case favoriteKey: endFavorite() case notFavoriteKey: prepareForFavorite() default: break } } enableTouch() } fileprivate func endFavorite() { executeWithoutActions { self.starShape.fillColor = self.starFavoriteColor.cgColor self.starShape.opacity = 1 self.fillRingShape.opacity = 1 self.outerRingShape.transform = CATransform3DIdentity self.outerRingShape.opacity = 0 } var starGoUp = CATransform3DIdentity starGoUp = CATransform3DScale(starGoUp, 2, 2, 2) let starKeyFrames = CAKeyframeAnimation(keyPath: "transform") starKeyFrames.values = [NSValue(caTransform3D: starShape.transform), NSValue(caTransform3D:starGoUp), NSValue(caTransform3D:CATransform3DIdentity)] starKeyFrames.keyTimes = [0.0, 0.4, 0.6] starKeyFrames.duration = 0.2 starKeyFrames.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) starShape.add(starKeyFrames, forKey: nil) starShape.transform = CATransform3DIdentity } fileprivate func prepareForFavorite() { executeWithoutActions { self.fillRingShape.opacity = 0 self.fillRingShape.transform = CATransform3DMakeScale(0.2, 0.2, 0.2) } } fileprivate func executeWithoutActions(_ closure: () -> Void) { CATransaction.begin() CATransaction.setDisableActions(true) closure() CATransaction.commit() } func animationDidStart(_ anim: CAAnimation) { disableTouch() } fileprivate func disableTouch() { self.isUserInteractionEnabled = false } fileprivate func enableTouch() { self.isUserInteractionEnabled = true } } ================================================ FILE: StarBounce/ViewController.swift ================================================ // // ViewController.swift // FavoriteStart // // Created by Rafael Machado on 14/11/14. // Copyright (c) 2014 Rafael Machado. All rights reserved. // import UIKit class ViewController: UIViewController { @IBAction func favorite(_ sender: StarButton!) { sender.isFavorite = !sender.isFavorite } } ================================================ FILE: StarBounce.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 01D15DC41A8A60240046F649 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D15DC31A8A60240046F649 /* AppDelegate.swift */; }; 01D15DC91A8A60240046F649 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01D15DC71A8A60240046F649 /* Main.storyboard */; }; 01D15DCB1A8A60240046F649 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01D15DCA1A8A60240046F649 /* Images.xcassets */; }; 01D15DCE1A8A60240046F649 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01D15DCC1A8A60240046F649 /* LaunchScreen.xib */; }; 01D15DDA1A8A60240046F649 /* StarBounceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D15DD91A8A60240046F649 /* StarBounceTests.swift */; }; 01D15DE71A8A60620046F649 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D15DE31A8A60620046F649 /* ViewController.swift */; }; 01D15DE81A8A60620046F649 /* StarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D15DE41A8A60620046F649 /* StarButton.swift */; }; 01D15DE91A8A60620046F649 /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D15DE51A8A60620046F649 /* Paths.swift */; }; 01D15DEA1A8A60620046F649 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D15DE61A8A60620046F649 /* Extensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 01D15DD41A8A60240046F649 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 01D15DB61A8A60240046F649 /* Project object */; proxyType = 1; remoteGlobalIDString = 01D15DBD1A8A60240046F649; remoteInfo = StarBounce; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 01D15DBE1A8A60240046F649 /* StarBounce.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StarBounce.app; sourceTree = BUILT_PRODUCTS_DIR; }; 01D15DC21A8A60240046F649 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 01D15DC31A8A60240046F649 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 01D15DC81A8A60240046F649 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 01D15DCA1A8A60240046F649 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 01D15DCD1A8A60240046F649 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 01D15DD31A8A60240046F649 /* StarBounceTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StarBounceTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 01D15DD81A8A60240046F649 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 01D15DD91A8A60240046F649 /* StarBounceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarBounceTests.swift; sourceTree = ""; }; 01D15DE31A8A60620046F649 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 01D15DE41A8A60620046F649 /* StarButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StarButton.swift; sourceTree = ""; }; 01D15DE51A8A60620046F649 /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; 01D15DE61A8A60620046F649 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 01D15DBB1A8A60240046F649 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 01D15DD01A8A60240046F649 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 01D15DB51A8A60240046F649 = { isa = PBXGroup; children = ( 01D15DC01A8A60240046F649 /* StarBounce */, 01D15DD61A8A60240046F649 /* StarBounceTests */, 01D15DBF1A8A60240046F649 /* Products */, ); sourceTree = ""; }; 01D15DBF1A8A60240046F649 /* Products */ = { isa = PBXGroup; children = ( 01D15DBE1A8A60240046F649 /* StarBounce.app */, 01D15DD31A8A60240046F649 /* StarBounceTests.xctest */, ); name = Products; sourceTree = ""; }; 01D15DC01A8A60240046F649 /* StarBounce */ = { isa = PBXGroup; children = ( 01D15DC31A8A60240046F649 /* AppDelegate.swift */, 01D15DE31A8A60620046F649 /* ViewController.swift */, 01D15DE41A8A60620046F649 /* StarButton.swift */, 01D15DE51A8A60620046F649 /* Paths.swift */, 01D15DE61A8A60620046F649 /* Extensions.swift */, 01D15DC71A8A60240046F649 /* Main.storyboard */, 01D15DCA1A8A60240046F649 /* Images.xcassets */, 01D15DCC1A8A60240046F649 /* LaunchScreen.xib */, 01D15DC11A8A60240046F649 /* Supporting Files */, ); path = StarBounce; sourceTree = ""; }; 01D15DC11A8A60240046F649 /* Supporting Files */ = { isa = PBXGroup; children = ( 01D15DC21A8A60240046F649 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 01D15DD61A8A60240046F649 /* StarBounceTests */ = { isa = PBXGroup; children = ( 01D15DD91A8A60240046F649 /* StarBounceTests.swift */, 01D15DD71A8A60240046F649 /* Supporting Files */, ); path = StarBounceTests; sourceTree = ""; }; 01D15DD71A8A60240046F649 /* Supporting Files */ = { isa = PBXGroup; children = ( 01D15DD81A8A60240046F649 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 01D15DBD1A8A60240046F649 /* StarBounce */ = { isa = PBXNativeTarget; buildConfigurationList = 01D15DDD1A8A60240046F649 /* Build configuration list for PBXNativeTarget "StarBounce" */; buildPhases = ( 01D15DBA1A8A60240046F649 /* Sources */, 01D15DBB1A8A60240046F649 /* Frameworks */, 01D15DBC1A8A60240046F649 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = StarBounce; productName = StarBounce; productReference = 01D15DBE1A8A60240046F649 /* StarBounce.app */; productType = "com.apple.product-type.application"; }; 01D15DD21A8A60240046F649 /* StarBounceTests */ = { isa = PBXNativeTarget; buildConfigurationList = 01D15DE01A8A60240046F649 /* Build configuration list for PBXNativeTarget "StarBounceTests" */; buildPhases = ( 01D15DCF1A8A60240046F649 /* Sources */, 01D15DD01A8A60240046F649 /* Frameworks */, 01D15DD11A8A60240046F649 /* Resources */, ); buildRules = ( ); dependencies = ( 01D15DD51A8A60240046F649 /* PBXTargetDependency */, ); name = StarBounceTests; productName = StarBounceTests; productReference = 01D15DD31A8A60240046F649 /* StarBounceTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 01D15DB61A8A60240046F649 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftMigration = 0710; LastSwiftUpdateCheck = 0710; LastUpgradeCheck = 0820; ORGANIZATIONNAME = "Rafael Machado"; TargetAttributes = { 01D15DBD1A8A60240046F649 = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 0820; }; 01D15DD21A8A60240046F649 = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 0820; TestTargetID = 01D15DBD1A8A60240046F649; }; }; }; buildConfigurationList = 01D15DB91A8A60240046F649 /* Build configuration list for PBXProject "StarBounce" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 01D15DB51A8A60240046F649; productRefGroup = 01D15DBF1A8A60240046F649 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 01D15DBD1A8A60240046F649 /* StarBounce */, 01D15DD21A8A60240046F649 /* StarBounceTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 01D15DBC1A8A60240046F649 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 01D15DC91A8A60240046F649 /* Main.storyboard in Resources */, 01D15DCE1A8A60240046F649 /* LaunchScreen.xib in Resources */, 01D15DCB1A8A60240046F649 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 01D15DD11A8A60240046F649 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 01D15DBA1A8A60240046F649 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 01D15DEA1A8A60620046F649 /* Extensions.swift in Sources */, 01D15DE81A8A60620046F649 /* StarButton.swift in Sources */, 01D15DE71A8A60620046F649 /* ViewController.swift in Sources */, 01D15DE91A8A60620046F649 /* Paths.swift in Sources */, 01D15DC41A8A60240046F649 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 01D15DCF1A8A60240046F649 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 01D15DDA1A8A60240046F649 /* StarBounceTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 01D15DD51A8A60240046F649 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 01D15DBD1A8A60240046F649 /* StarBounce */; targetProxy = 01D15DD41A8A60240046F649 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 01D15DC71A8A60240046F649 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 01D15DC81A8A60240046F649 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 01D15DCC1A8A60240046F649 /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( 01D15DCD1A8A60240046F649 /* Base */, ); name = LaunchScreen.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 01D15DDB1A8A60240046F649 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 01D15DDC1A8A60240046F649 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.1; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 01D15DDE1A8A60240046F649 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = StarBounce/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.machado.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; name = Debug; }; 01D15DDF1A8A60240046F649 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = StarBounce/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.machado.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; name = Release; }; 01D15DE11A8A60240046F649 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = StarBounceTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.machado.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StarBounce.app/StarBounce"; }; name = Debug; }; 01D15DE21A8A60240046F649 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); INFOPLIST_FILE = StarBounceTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.machado.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StarBounce.app/StarBounce"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 01D15DB91A8A60240046F649 /* Build configuration list for PBXProject "StarBounce" */ = { isa = XCConfigurationList; buildConfigurations = ( 01D15DDB1A8A60240046F649 /* Debug */, 01D15DDC1A8A60240046F649 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 01D15DDD1A8A60240046F649 /* Build configuration list for PBXNativeTarget "StarBounce" */ = { isa = XCConfigurationList; buildConfigurations = ( 01D15DDE1A8A60240046F649 /* Debug */, 01D15DDF1A8A60240046F649 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 01D15DE01A8A60240046F649 /* Build configuration list for PBXNativeTarget "StarBounceTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 01D15DE11A8A60240046F649 /* Debug */, 01D15DE21A8A60240046F649 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 01D15DB61A8A60240046F649 /* Project object */; } ================================================ FILE: StarBounce.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: StarBounceTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: StarBounceTests/StarBounceTests.swift ================================================ // // StarBounceTests.swift // StarBounceTests // // Created by Rafael Machado on 2/10/15. // Copyright (c) 2015 Rafael Machado. All rights reserved. // import UIKit import XCTest class StarBounceTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. XCTAssert(true, "Pass") } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } }