├── LICENSE ├── README.md ├── Recorder.podspec └── Recorder.swift /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andy Drizen 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Record and export a view hierarchy to PNG or JPEG frames for flip book style animations (as used in WatchKit). 2 | 3 | # Installation 4 | 5 | Simply add this class into your project. 6 | 7 | # Usage 8 | 9 | ## In your Swift code 10 | 11 | Create an instance of Recorder and set the view that you wish to record: 12 | 13 | ``` 14 | let recorder = Recorder() 15 | recorder.view = activityIndicator 16 | ``` 17 | 18 | When you are ready to start recording, simply: 19 | 20 | ``` 21 | recorder.start() 22 | ``` 23 | 24 | and when you are finished: 25 | 26 | ``` 27 | recorder.stop() 28 | ``` 29 | 30 | ## In your Objective-C code 31 | 32 | Before you start, make sure your `UIViewRecorder` code has been properly integrated in your Objective-C project. Refer to official Apple guide [Swift and Objective-C in the Same Project](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_75) section *Importing Swift into Objective-C*. 33 | 34 | Create an instance of Recorder and set the view that you wish to record: 35 | 36 | ``` 37 | Recorder *myRecorder = [[Recorder alloc] init]; 38 | myRecorder.view = activityIndicator; 39 | ``` 40 | 41 | When you are ready to start recording, simply: 42 | 43 | ``` 44 | [myRecorder start]; 45 | ``` 46 | 47 | and when you are finished: 48 | 49 | ``` 50 | [myRecorder stop]; 51 | ``` 52 | 53 | # Output 54 | 55 | The console will output some useful information such as: 56 | 57 | * Location of the files 58 | * Duration of the recording 59 | * Number of frames 60 | 61 | You can specify the output path by setting the `path` property of the recorder. Also, if you would rather have JPGs, set the `outputJPG` property of the recorder to `true`. 62 | -------------------------------------------------------------------------------- /Recorder.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Recorder" 3 | s.version = "0.0.3" 4 | s.summary = "Record and export a view hierarchy to PNG or JPEG frames for flip book style animations (as used in WatchKit)." 5 | 6 | s.description = <<-DESC 7 | Record and export a view hierarchy to PNG or JPEG frames for flip book style animations (as used in WatchKit). 8 | Example usage: output a UIView animation to PNG or JPG and then play it on the Apple Watch. 9 | DESC 10 | 11 | s.homepage = "https://github.com/andydrizen/UIViewRecorder" 12 | s.license = "MIT" 13 | s.author = { "Andy Drizen" => "andydrizen@gmail.com" } 14 | s.social_media_url = "http://twitter.com/andydrizen" 15 | s.platform = :ios, '7.0' 16 | s.source = { :git => "https://github.com/andydrizen/UIViewRecorder.git", :tag => "0.0.3" } 17 | s.source_files = "Recorder.swift" 18 | end 19 | -------------------------------------------------------------------------------- /Recorder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Recorder.swift 3 | // aninative 4 | // 5 | // Created by Andy Drizen on 03/01/2015. 6 | // Copyright (c) 2015 Andy Drizen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc public class Recorder: NSObject { 12 | var displayLink : CADisplayLink? 13 | 14 | var imageCounter = 0 15 | public var view : UIView? 16 | var outputPath : NSString? 17 | var referenceDate : NSDate? 18 | public var name = "image" 19 | public var outputJPG = false 20 | 21 | public func start() { 22 | 23 | if (view == nil) { 24 | NSException(name: "No view set", reason: "You must set a view before calling start.", userInfo: nil).raise() 25 | } 26 | else { 27 | displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:") 28 | displayLink!.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) 29 | 30 | referenceDate = NSDate() 31 | } 32 | } 33 | 34 | public func stop() { 35 | displayLink?.invalidate() 36 | 37 | let seconds = referenceDate?.timeIntervalSinceNow 38 | if (seconds != nil) { 39 | println("Recorded: \(imageCounter) frames\nDuration: \(-1 * seconds!) seconds\nStored in: \(outputPathString())") 40 | } 41 | } 42 | 43 | lazy var applicationDocumentsDirectory: NSURL = { 44 | // The directory the application uses to store the Core Data store file. This code uses a directory named "uk.co.andydrizen.test" in the application's documents Application Support directory. 45 | let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 46 | return urls[urls.count - 1] as NSURL 47 | }() 48 | 49 | func handleDisplayLink(displayLink : CADisplayLink) { 50 | if (view != nil) { 51 | createImageFromView(view!) 52 | } 53 | } 54 | 55 | func outputPathString() -> String { 56 | if (outputPath != nil) { 57 | return outputPath! 58 | } 59 | else { 60 | return applicationDocumentsDirectory.absoluteString! 61 | } 62 | } 63 | 64 | func createImageFromView(captureView : UIView) { 65 | UIGraphicsBeginImageContextWithOptions(captureView.bounds.size, false, 0) 66 | captureView.drawViewHierarchyInRect(captureView.bounds, afterScreenUpdates: false) 67 | 68 | let image = UIGraphicsGetImageFromCurrentImageContext(); 69 | 70 | var fileExtension = "png" 71 | var data : NSData? 72 | if (outputJPG) { 73 | data = UIImageJPEGRepresentation(image, 1) 74 | fileExtension = "jpg" 75 | } 76 | else { 77 | data = UIImagePNGRepresentation(image) 78 | } 79 | 80 | var path = outputPathString() 81 | path = path.stringByAppendingPathComponent("/\(name)-\(imageCounter).\(fileExtension)") 82 | 83 | imageCounter = imageCounter + 1 84 | 85 | if let imageRaw = data { 86 | imageRaw.writeToURL(NSURL(string: path)!, atomically: false) 87 | } 88 | 89 | UIGraphicsEndImageContext(); 90 | } 91 | } 92 | --------------------------------------------------------------------------------