├── Assets ├── digitCell.psd └── failedBg.psd ├── Screenshots ├── screen1.png ├── screen2.png └── screen3.png ├── .gitignore ├── PAPasscode Example ├── PAPasscode Example │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ ├── ViewController_iPad.xib │ │ └── ViewController_iPhone.xib │ ├── Default.png │ ├── Default@2x.png │ ├── Default-568h@2x.png │ ├── PAPasscode Example-Prefix.pch │ ├── main.m │ ├── AppDelegate.h │ ├── ViewController.h │ ├── PAPasscode Example-Info.plist │ ├── AppDelegate.m │ └── ViewController.m └── PAPasscode Example.xcodeproj │ └── project.pbxproj ├── PAPasscode.podspec ├── LICENSE ├── PAPasscode ├── PAPasscodeViewController.h └── PAPasscodeViewController.m └── README.md /Assets/digitCell.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/Assets/digitCell.psd -------------------------------------------------------------------------------- /Assets/failedBg.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/Assets/failedBg.psd -------------------------------------------------------------------------------- /Screenshots/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/Screenshots/screen1.png -------------------------------------------------------------------------------- /Screenshots/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/Screenshots/screen2.png -------------------------------------------------------------------------------- /Screenshots/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/Screenshots/screen3.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # MAC NOISE =========== 2 | 3 | .DS_Store 4 | Releases/ 5 | project.xcworkspace 6 | xcuserdata 7 | build -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/PAPasscode Example/PAPasscode Example/Default.png -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/PAPasscode Example/PAPasscode Example/Default@2x.png -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhennessy/PAPasscode/HEAD/PAPasscode Example/PAPasscode Example/Default-568h@2x.png -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/PAPasscode Example-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'PAPasscode Example' target in the 'PAPasscode Example' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PAPasscode Example 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PAPasscode Example 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ViewController; 12 | 13 | @interface AppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) ViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PAPasscode.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'PAPasscode' 3 | s.version = '1.0' 4 | s.license = 'BSD' 5 | s.summary = 'A Passcode View Controller for iOS similar to the one in Settings.app' 6 | s.homepage = 'https://github.com/dhennessy/PAPasscode' 7 | s.authors = { 'Denis Hennessy' => 'denis@hennessynet.com' } 8 | s.source = { :git => 'https://github.com/dhennessy/PAPasscode.git', :tag => s.version.to_s } 9 | s.platform = :ios, '7.0' 10 | s.requires_arc = true 11 | 12 | s.source_files = 'PAPasscode' 13 | s.framework = 'QuartzCore' 14 | end 15 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // PAPasscode Example 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PAPasscodeViewController.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @property (weak, nonatomic) IBOutlet UILabel *passcodeLabel; 15 | @property (weak, nonatomic) IBOutlet UISwitch *simpleSwitch; 16 | 17 | - (IBAction)setPasscode:(id)sender; 18 | - (IBAction)enterPasscode:(id)sender; 19 | - (IBAction)changePasscode:(id)sender; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Denis Hennessy (Peer Assembly - http://peerassembly.com) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of Peer Assembly, Denis Hennessy nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL PEER ASSEMBLY OR DENIS HENNESSY BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/PAPasscode Example-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.peerassembly.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /PAPasscode/PAPasscodeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PAPasscodeViewController.h 3 | // PAPasscode 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | PasscodeActionSet, 13 | PasscodeActionEnter, 14 | PasscodeActionChange 15 | } PasscodeAction; 16 | 17 | @class PAPasscodeViewController; 18 | 19 | @protocol PAPasscodeViewControllerDelegate 20 | 21 | @optional 22 | 23 | - (void)PAPasscodeViewControllerDidCancel:(PAPasscodeViewController *)controller; 24 | - (void)PAPasscodeViewControllerDidChangePasscode:(PAPasscodeViewController *)controller; 25 | - (void)PAPasscodeViewControllerDidEnterAlternativePasscode:(PAPasscodeViewController *)controller; 26 | - (void)PAPasscodeViewControllerDidEnterPasscode:(PAPasscodeViewController *)controller; 27 | - (void)PAPasscodeViewControllerDidSetPasscode:(PAPasscodeViewController *)controller; 28 | - (void)PAPasscodeViewController:(PAPasscodeViewController *)controller didFailToEnterPasscode:(NSInteger)attempts; 29 | 30 | @end 31 | 32 | @interface PAPasscodeViewController : UIViewController { 33 | NSArray *_installedConstraints; 34 | UIView *_inputPanel; 35 | NSLayoutConstraint *_keyboardHeightConstraint; 36 | UIView *contentView; 37 | NSInteger phase; 38 | UILabel *promptLabel; 39 | UILabel *messageLabel; 40 | UIView *_failedAttemptsView; 41 | UILabel *failedAttemptsLabel; 42 | UITextField *passcodeTextField; 43 | UILabel *_digitLabels[4]; 44 | UIImageView *snapshotImageView; 45 | } 46 | 47 | @property (strong) UIView *backgroundView; 48 | @property (readonly) PasscodeAction action; 49 | @property (weak) id delegate; 50 | @property (strong) NSString *alternativePasscode; 51 | @property (strong) NSString *passcode; 52 | @property (assign) BOOL simple; 53 | @property (assign) NSInteger failedAttempts; 54 | @property (strong) NSString *enterPrompt; 55 | @property (strong) NSString *confirmPrompt; 56 | @property (strong) NSString *changePrompt; 57 | @property (strong) NSString *message; 58 | 59 | - (id)initForAction:(PasscodeAction)action; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PAPasscode Example 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | #import "ViewController.h" 12 | 13 | @implementation AppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 18 | // Override point for customization after application launch. 19 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 20 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil]; 21 | } else { 22 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil]; 23 | } 24 | self.window.rootViewController = self.viewController; 25 | [self.window makeKeyAndVisible]; 26 | return YES; 27 | } 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application 30 | { 31 | // 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. 32 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 33 | } 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application 36 | { 37 | // 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. 38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 39 | } 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application 42 | { 43 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 44 | } 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application 47 | { 48 | // 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. 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application 52 | { 53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // PAPasscode Example 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (IBAction)setPasscode:(id)sender { 18 | PAPasscodeViewController *passcodeViewController = [[PAPasscodeViewController alloc] initForAction:PasscodeActionSet]; 19 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { 20 | passcodeViewController.backgroundView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped]; 21 | } 22 | passcodeViewController.delegate = self; 23 | passcodeViewController.simple = _simpleSwitch.on; 24 | [self presentViewController:[[UINavigationController alloc] initWithRootViewController:passcodeViewController] animated:YES completion:nil]; 25 | } 26 | 27 | - (IBAction)enterPasscode:(id)sender { 28 | PAPasscodeViewController *passcodeViewController = [[PAPasscodeViewController alloc] initForAction:PasscodeActionEnter]; 29 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { 30 | passcodeViewController.backgroundView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped]; 31 | } 32 | passcodeViewController.delegate = self; 33 | passcodeViewController.passcode = _passcodeLabel.text; 34 | passcodeViewController.alternativePasscode = @"9999"; 35 | passcodeViewController.simple = _simpleSwitch.on; 36 | [self presentViewController:[[UINavigationController alloc] initWithRootViewController:passcodeViewController] animated:YES completion:nil]; 37 | } 38 | 39 | - (IBAction)changePasscode:(id)sender { 40 | PAPasscodeViewController *passcodeViewController = [[PAPasscodeViewController alloc] initForAction:PasscodeActionChange]; 41 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { 42 | passcodeViewController.backgroundView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped]; 43 | } 44 | passcodeViewController.delegate = self; 45 | passcodeViewController.passcode = _passcodeLabel.text; 46 | passcodeViewController.simple = _simpleSwitch.on; 47 | [self presentViewController:[[UINavigationController alloc] initWithRootViewController:passcodeViewController] animated:YES completion:nil]; 48 | } 49 | 50 | #pragma mark - PAPasscodeViewControllerDelegate 51 | 52 | - (void)PAPasscodeViewControllerDidCancel:(PAPasscodeViewController *)controller { 53 | [self dismissViewControllerAnimated:YES completion:nil]; 54 | } 55 | 56 | - (void)PAPasscodeViewControllerDidEnterAlternativePasscode:(PAPasscodeViewController *)controller { 57 | [self dismissViewControllerAnimated:YES completion:^() { 58 | [[[UIAlertView alloc] initWithTitle:nil message:@"Alternative Passcode entered correctly" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; 59 | }]; 60 | } 61 | 62 | - (void)PAPasscodeViewControllerDidEnterPasscode:(PAPasscodeViewController *)controller { 63 | [self dismissViewControllerAnimated:YES completion:^() { 64 | [[[UIAlertView alloc] initWithTitle:nil message:@"Passcode entered correctly" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; 65 | }]; 66 | } 67 | 68 | - (void)PAPasscodeViewControllerDidSetPasscode:(PAPasscodeViewController *)controller { 69 | [self dismissViewControllerAnimated:YES completion:^() { 70 | _passcodeLabel.text = controller.passcode; 71 | }]; 72 | } 73 | 74 | - (void)PAPasscodeViewControllerDidChangePasscode:(PAPasscodeViewController *)controller { 75 | [self dismissViewControllerAnimated:YES completion:^() { 76 | _passcodeLabel.text = controller.passcode; 77 | }]; 78 | } 79 | 80 | - (void)viewDidUnload { 81 | [self setSimpleSwitch:nil]; 82 | [super viewDidUnload]; 83 | } 84 | @end 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PAPasscode 2 | 3 | PAPasscode is a standalone view controller for iOS to allow a user to set, 4 | enter or change a passcode. It's designed to mimic the behaviour in Settings.app 5 | while still allowing some customization. It includes a sample project which 6 | shows how it appears on iPhone and iPad devices. 7 | 8 | ![screen1](https://raw.github.com/dhennessy/PAPasscode/master/Screenshots/screen1.png) 9 | ![screen2](https://raw.github.com/dhennessy/PAPasscode/master/Screenshots/screen2.png) 10 | 11 | Other features: 12 | * Supports both simple (PIN-style) and regular passcodes 13 | * Allows customization of title and prompts 14 | * Animates screens left and right 15 | * Requires ARC 16 | 17 | ## Adding PAPasscode to your project 18 | 19 | The simplest way to add PAPasscode to your project is to use [CocoaPods](http://cocoapods.org). 20 | Simply add the following line to your Podfile: 21 | 22 | ``` 23 | pod 'PAPasscode' 24 | ``` 25 | 26 | If you'd prefer to manually integrate it, simply copy `PAPasscode/*.{m,h}` and `Assets/*.png` 27 | into your project. Make sure you link with the QuartzCore framework. 28 | 29 | ## Asking the user to set a passcode 30 | 31 | First, implement the following delegate methods on your view controller: 32 | 33 | ```objective-c 34 | - (void)PAPasscodeViewControllerDidCancel:(PAPasscodeViewController *)controller { 35 | [self dismissViewControllerAnimated:YES completion:nil]; 36 | } 37 | 38 | - (void)PAPasscodeViewControllerDidSetPasscode:(PAPasscodeViewController *)controller { 39 | // Do stuff with controller.passcode... 40 | [self dismissViewControllerAnimated:YES completion:nil]; 41 | } 42 | ``` 43 | 44 | Then invoke the view controller as follows: 45 | 46 | ```objective-c 47 | PAPasscodeViewController *passcodeViewController = [[PAPasscodeViewController alloc] initForAction:PasscodeActionSet]; 48 | passcodeViewController.delegate = self; 49 | [self presentViewController:[[UINavigationController alloc] initWithRootViewController:passcodeViewController] animated:YES completion:nil]; 50 | ``` 51 | 52 | The `initForAction:` allows you to specify the flow you'd like. Possible actions are: 53 | * `PasscodeActionSet` - set a new passcode 54 | * `PasscodeActionEnter` - enter an existing passcode 55 | * `PasscodeActionChange` - change an existing passcode 56 | 57 | The included example project demonstrates the each of the flows. 58 | 59 | ## Changelog 60 | 61 | ### 1.0 62 | * Update to support auto-layout and change UI to match iOS 7 styling. Note that if you require support for iOS 6, you should stick with version 0.3. 63 | 64 | ### 0.3 65 | * The `PAPasscodeViewControllerDidCancel:` delegate method is now optional. If missing, then the Cancel button will be removed. 66 | 67 | ### 0.2 68 | * Add property to specify custom background view 69 | 70 | ### 0.1 71 | * Initial release 72 | 73 | ## Contact 74 | 75 | To hear about updates to this and other libraries follow me on Twitter ([@denishennessy](http://twitter.com/denishennessy)) or App.net ([@denishennessy](http://alpha.app.net/denishennessy)). 76 | 77 | If you encounter a bug or just thought of a terrific new feature, then opening a github issue is probably the best 78 | way to share it. Actually, the best way is to send me a pull request... 79 | 80 | For anything else, email always works: [denis@peerassembly.com](mailto:denis@peerassembly.com) 81 | 82 | ## License 83 | 84 | ``` 85 | Copyright (c) 2012-2015, Denis Hennessy (Peer Assembly - http://peerassembly.com) 86 | All rights reserved. 87 | 88 | Redistribution and use in source and binary forms, with or without 89 | modification, are permitted provided that the following conditions are met: 90 | * Redistributions of source code must retain the above copyright 91 | notice, this list of conditions and the following disclaimer. 92 | * Redistributions in binary form must reproduce the above copyright 93 | notice, this list of conditions and the following disclaimer in the 94 | documentation and/or other materials provided with the distribution. 95 | * Neither the name of Peer Assembly, Denis Hennessy nor the 96 | names of its contributors may be used to endorse or promote products 97 | derived from this software without specific prior written permission. 98 | 99 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 100 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 101 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 102 | DISCLAIMED. IN NO EVENT SHALL PEER ASSEMBLY OR DENIS HENNESSY BE LIABLE FOR ANY 103 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 104 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 105 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 106 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 107 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 108 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 109 | ``` 110 | 111 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9208C459162C547900D7B1F7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9208C458162C547900D7B1F7 /* UIKit.framework */; }; 11 | 9208C45B162C547900D7B1F7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9208C45A162C547900D7B1F7 /* Foundation.framework */; }; 12 | 9208C45D162C547900D7B1F7 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9208C45C162C547900D7B1F7 /* CoreGraphics.framework */; }; 13 | 9208C463162C547900D7B1F7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9208C461162C547900D7B1F7 /* InfoPlist.strings */; }; 14 | 9208C465162C547900D7B1F7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9208C464162C547900D7B1F7 /* main.m */; }; 15 | 9208C469162C547900D7B1F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9208C468162C547900D7B1F7 /* AppDelegate.m */; }; 16 | 9208C46B162C547900D7B1F7 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 9208C46A162C547900D7B1F7 /* Default.png */; }; 17 | 9208C46D162C547900D7B1F7 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9208C46C162C547900D7B1F7 /* Default@2x.png */; }; 18 | 9208C46F162C547900D7B1F7 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9208C46E162C547900D7B1F7 /* Default-568h@2x.png */; }; 19 | 9208C472162C547900D7B1F7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9208C471162C547900D7B1F7 /* ViewController.m */; }; 20 | 9208C475162C547900D7B1F7 /* ViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9208C473162C547900D7B1F7 /* ViewController_iPhone.xib */; }; 21 | 9208C478162C547900D7B1F7 /* ViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9208C476162C547900D7B1F7 /* ViewController_iPad.xib */; }; 22 | 92471367162CB60D0085CDA3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92471366162CB60D0085CDA3 /* QuartzCore.framework */; }; 23 | 92471370162CCA890085CDA3 /* PAPasscodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9247136F162CCA890085CDA3 /* PAPasscodeViewController.m */; }; 24 | 927AF85D1B0CE32E009CE2B1 /* PAPlaceholderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 927AF85C1B0CE32E009CE2B1 /* PAPlaceholderView.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 9208C454162C547900D7B1F7 /* PAPasscode Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PAPasscode Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 9208C458162C547900D7B1F7 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 30 | 9208C45A162C547900D7B1F7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | 9208C45C162C547900D7B1F7 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 32 | 9208C460162C547900D7B1F7 /* PAPasscode Example-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PAPasscode Example-Info.plist"; sourceTree = ""; }; 33 | 9208C462162C547900D7B1F7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34 | 9208C464162C547900D7B1F7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 9208C466162C547900D7B1F7 /* PAPasscode Example-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PAPasscode Example-Prefix.pch"; sourceTree = ""; }; 36 | 9208C467162C547900D7B1F7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | 9208C468162C547900D7B1F7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 38 | 9208C46A162C547900D7B1F7 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 39 | 9208C46C162C547900D7B1F7 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 40 | 9208C46E162C547900D7B1F7 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 41 | 9208C470162C547900D7B1F7 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 42 | 9208C471162C547900D7B1F7 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 43 | 9208C474162C547900D7B1F7 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPhone.xib; sourceTree = ""; }; 44 | 9208C477162C547900D7B1F7 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPad.xib; sourceTree = ""; }; 45 | 9208C47F162C547900D7B1F7 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 46 | 92471366162CB60D0085CDA3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 47 | 9247136E162CCA890085CDA3 /* PAPasscodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PAPasscodeViewController.h; path = ../../PAPasscode/PAPasscodeViewController.h; sourceTree = ""; }; 48 | 9247136F162CCA890085CDA3 /* PAPasscodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPasscodeViewController.m; path = ../../PAPasscode/PAPasscodeViewController.m; sourceTree = ""; }; 49 | 927AF85B1B0CE32E009CE2B1 /* PAPlaceholderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PAPlaceholderView.h; path = ../../../PAPlaceholder/PAPlaceholder/PAPlaceholderView.h; sourceTree = ""; }; 50 | 927AF85C1B0CE32E009CE2B1 /* PAPlaceholderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPlaceholderView.m; path = ../../../PAPlaceholder/PAPlaceholder/PAPlaceholderView.m; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 9208C451162C547900D7B1F7 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | 92471367162CB60D0085CDA3 /* QuartzCore.framework in Frameworks */, 59 | 9208C459162C547900D7B1F7 /* UIKit.framework in Frameworks */, 60 | 9208C45B162C547900D7B1F7 /* Foundation.framework in Frameworks */, 61 | 9208C45D162C547900D7B1F7 /* CoreGraphics.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 9208C449162C547900D7B1F7 = { 69 | isa = PBXGroup; 70 | children = ( 71 | 9208C45E162C547900D7B1F7 /* PAPasscode Example */, 72 | 9208C457162C547900D7B1F7 /* Frameworks */, 73 | 9208C455162C547900D7B1F7 /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | 9208C455162C547900D7B1F7 /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 9208C454162C547900D7B1F7 /* PAPasscode Example.app */, 81 | ); 82 | name = Products; 83 | sourceTree = ""; 84 | }; 85 | 9208C457162C547900D7B1F7 /* Frameworks */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 92471366162CB60D0085CDA3 /* QuartzCore.framework */, 89 | 9208C458162C547900D7B1F7 /* UIKit.framework */, 90 | 9208C45A162C547900D7B1F7 /* Foundation.framework */, 91 | 9208C45C162C547900D7B1F7 /* CoreGraphics.framework */, 92 | 9208C47F162C547900D7B1F7 /* SenTestingKit.framework */, 93 | ); 94 | name = Frameworks; 95 | sourceTree = ""; 96 | }; 97 | 9208C45E162C547900D7B1F7 /* PAPasscode Example */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 927AF85B1B0CE32E009CE2B1 /* PAPlaceholderView.h */, 101 | 927AF85C1B0CE32E009CE2B1 /* PAPlaceholderView.m */, 102 | 9247136D162CCA6F0085CDA3 /* PAPasscode */, 103 | 9208C467162C547900D7B1F7 /* AppDelegate.h */, 104 | 9208C468162C547900D7B1F7 /* AppDelegate.m */, 105 | 9208C470162C547900D7B1F7 /* ViewController.h */, 106 | 9208C471162C547900D7B1F7 /* ViewController.m */, 107 | 9208C473162C547900D7B1F7 /* ViewController_iPhone.xib */, 108 | 9208C476162C547900D7B1F7 /* ViewController_iPad.xib */, 109 | 9208C45F162C547900D7B1F7 /* Supporting Files */, 110 | ); 111 | path = "PAPasscode Example"; 112 | sourceTree = ""; 113 | }; 114 | 9208C45F162C547900D7B1F7 /* Supporting Files */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 9208C460162C547900D7B1F7 /* PAPasscode Example-Info.plist */, 118 | 9208C461162C547900D7B1F7 /* InfoPlist.strings */, 119 | 9208C464162C547900D7B1F7 /* main.m */, 120 | 9208C466162C547900D7B1F7 /* PAPasscode Example-Prefix.pch */, 121 | 9208C46A162C547900D7B1F7 /* Default.png */, 122 | 9208C46C162C547900D7B1F7 /* Default@2x.png */, 123 | 9208C46E162C547900D7B1F7 /* Default-568h@2x.png */, 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | 9247136D162CCA6F0085CDA3 /* PAPasscode */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 9247136E162CCA890085CDA3 /* PAPasscodeViewController.h */, 132 | 9247136F162CCA890085CDA3 /* PAPasscodeViewController.m */, 133 | ); 134 | name = PAPasscode; 135 | sourceTree = ""; 136 | }; 137 | /* End PBXGroup section */ 138 | 139 | /* Begin PBXNativeTarget section */ 140 | 9208C453162C547900D7B1F7 /* PAPasscode Example */ = { 141 | isa = PBXNativeTarget; 142 | buildConfigurationList = 9208C490162C547900D7B1F7 /* Build configuration list for PBXNativeTarget "PAPasscode Example" */; 143 | buildPhases = ( 144 | 9208C450162C547900D7B1F7 /* Sources */, 145 | 9208C451162C547900D7B1F7 /* Frameworks */, 146 | 9208C452162C547900D7B1F7 /* Resources */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | ); 152 | name = "PAPasscode Example"; 153 | productName = "PAPasscode Example"; 154 | productReference = 9208C454162C547900D7B1F7 /* PAPasscode Example.app */; 155 | productType = "com.apple.product-type.application"; 156 | }; 157 | /* End PBXNativeTarget section */ 158 | 159 | /* Begin PBXProject section */ 160 | 9208C44B162C547900D7B1F7 /* Project object */ = { 161 | isa = PBXProject; 162 | attributes = { 163 | LastUpgradeCheck = 0450; 164 | ORGANIZATIONNAME = "Peer Assembly"; 165 | }; 166 | buildConfigurationList = 9208C44E162C547900D7B1F7 /* Build configuration list for PBXProject "PAPasscode Example" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | ); 173 | mainGroup = 9208C449162C547900D7B1F7; 174 | productRefGroup = 9208C455162C547900D7B1F7 /* Products */; 175 | projectDirPath = ""; 176 | projectRoot = ""; 177 | targets = ( 178 | 9208C453162C547900D7B1F7 /* PAPasscode Example */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXResourcesBuildPhase section */ 184 | 9208C452162C547900D7B1F7 /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | 9208C463162C547900D7B1F7 /* InfoPlist.strings in Resources */, 189 | 9208C46B162C547900D7B1F7 /* Default.png in Resources */, 190 | 9208C46D162C547900D7B1F7 /* Default@2x.png in Resources */, 191 | 9208C46F162C547900D7B1F7 /* Default-568h@2x.png in Resources */, 192 | 9208C475162C547900D7B1F7 /* ViewController_iPhone.xib in Resources */, 193 | 9208C478162C547900D7B1F7 /* ViewController_iPad.xib in Resources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXResourcesBuildPhase section */ 198 | 199 | /* Begin PBXSourcesBuildPhase section */ 200 | 9208C450162C547900D7B1F7 /* Sources */ = { 201 | isa = PBXSourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 9208C465162C547900D7B1F7 /* main.m in Sources */, 205 | 9208C469162C547900D7B1F7 /* AppDelegate.m in Sources */, 206 | 927AF85D1B0CE32E009CE2B1 /* PAPlaceholderView.m in Sources */, 207 | 9208C472162C547900D7B1F7 /* ViewController.m in Sources */, 208 | 92471370162CCA890085CDA3 /* PAPasscodeViewController.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 9208C461162C547900D7B1F7 /* InfoPlist.strings */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 9208C462162C547900D7B1F7 /* en */, 219 | ); 220 | name = InfoPlist.strings; 221 | sourceTree = ""; 222 | }; 223 | 9208C473162C547900D7B1F7 /* ViewController_iPhone.xib */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 9208C474162C547900D7B1F7 /* en */, 227 | ); 228 | name = ViewController_iPhone.xib; 229 | sourceTree = ""; 230 | }; 231 | 9208C476162C547900D7B1F7 /* ViewController_iPad.xib */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 9208C477162C547900D7B1F7 /* en */, 235 | ); 236 | name = ViewController_iPad.xib; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | 9208C48E162C547900D7B1F7 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 247 | CLANG_CXX_LIBRARY = "libc++"; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 251 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 252 | COPY_PHASE_STRIP = NO; 253 | GCC_C_LANGUAGE_STANDARD = gnu99; 254 | GCC_DYNAMIC_NO_PIC = NO; 255 | GCC_OPTIMIZATION_LEVEL = 0; 256 | GCC_PREPROCESSOR_DEFINITIONS = ( 257 | "DEBUG=1", 258 | "$(inherited)", 259 | ); 260 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 262 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 263 | GCC_WARN_UNUSED_VARIABLE = YES; 264 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 265 | ONLY_ACTIVE_ARCH = YES; 266 | SDKROOT = iphoneos; 267 | TARGETED_DEVICE_FAMILY = "1,2"; 268 | }; 269 | name = Debug; 270 | }; 271 | 9208C48F162C547900D7B1F7 /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 276 | CLANG_CXX_LIBRARY = "libc++"; 277 | CLANG_ENABLE_OBJC_ARC = YES; 278 | CLANG_WARN_EMPTY_BODY = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 281 | COPY_PHASE_STRIP = YES; 282 | GCC_C_LANGUAGE_STANDARD = gnu99; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 284 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 285 | GCC_WARN_UNUSED_VARIABLE = YES; 286 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 287 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 288 | SDKROOT = iphoneos; 289 | TARGETED_DEVICE_FAMILY = "1,2"; 290 | VALIDATE_PRODUCT = YES; 291 | }; 292 | name = Release; 293 | }; 294 | 9208C491162C547900D7B1F7 /* Debug */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 298 | GCC_PREFIX_HEADER = "PAPasscode Example/PAPasscode Example-Prefix.pch"; 299 | INFOPLIST_FILE = "PAPasscode Example/PAPasscode Example-Info.plist"; 300 | PRODUCT_NAME = "$(TARGET_NAME)"; 301 | WRAPPER_EXTENSION = app; 302 | }; 303 | name = Debug; 304 | }; 305 | 9208C492162C547900D7B1F7 /* Release */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 309 | GCC_PREFIX_HEADER = "PAPasscode Example/PAPasscode Example-Prefix.pch"; 310 | INFOPLIST_FILE = "PAPasscode Example/PAPasscode Example-Info.plist"; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | WRAPPER_EXTENSION = app; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | 9208C44E162C547900D7B1F7 /* Build configuration list for PBXProject "PAPasscode Example" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | 9208C48E162C547900D7B1F7 /* Debug */, 323 | 9208C48F162C547900D7B1F7 /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | 9208C490162C547900D7B1F7 /* Build configuration list for PBXNativeTarget "PAPasscode Example" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | 9208C491162C547900D7B1F7 /* Debug */, 332 | 9208C492162C547900D7B1F7 /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = 9208C44B162C547900D7B1F7 /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/en.lproj/ViewController_iPad.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C60 6 | 2843 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1929 12 | 13 | 14 | IBProxyObject 15 | IBUIButton 16 | IBUILabel 17 | IBUISwitch 18 | IBUIView 19 | 20 | 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | PluginDependencyRecalculationVersion 25 | 26 | 27 | 28 | 29 | IBFilesOwner 30 | IBIPadFramework 31 | 32 | 33 | IBFirstResponder 34 | IBIPadFramework 35 | 36 | 37 | 38 | 274 39 | 40 | 41 | 42 | 292 43 | {{264, 169}, {87, 21}} 44 | 45 | 46 | _NS:9 47 | NO 48 | YES 49 | 7 50 | NO 51 | IBIPadFramework 52 | Simple: 53 | 54 | 1 55 | MCAwIDAAA 56 | darkTextColor 57 | 58 | 59 | 0 60 | 2 61 | 62 | 1 63 | 17 64 | 65 | 66 | Helvetica 67 | 17 68 | 16 69 | 70 | NO 71 | 72 | 73 | 74 | 292 75 | {{344, 169}, {94, 27}} 76 | 77 | 78 | _NS:9 79 | NO 80 | IBIPadFramework 81 | 0 82 | 0 83 | YES 84 | 85 | 86 | 87 | 292 88 | {{244, 227}, {280, 44}} 89 | 90 | 91 | _NS:9 92 | NO 93 | IBIPadFramework 94 | 0 95 | 0 96 | 1 97 | Set Passcode 98 | 99 | 3 100 | MQA 101 | 102 | 103 | 1 104 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 105 | 106 | 107 | 3 108 | MC41AA 109 | 110 | 111 | 2 112 | 15 113 | 114 | 115 | Helvetica-Bold 116 | 15 117 | 16 118 | 119 | 120 | 121 | 122 | 292 123 | {{244, 278}, {280, 44}} 124 | 125 | 126 | _NS:9 127 | NO 128 | IBIPadFramework 129 | 0 130 | 0 131 | 1 132 | Enter Passcode 133 | 134 | 135 | 1 136 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 292 145 | {{244, 329}, {280, 44}} 146 | 147 | _NS:9 148 | NO 149 | IBIPadFramework 150 | 0 151 | 0 152 | 1 153 | Change Passcode 154 | 155 | 156 | 1 157 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 292 166 | {{264, 131}, {87, 21}} 167 | 168 | 169 | _NS:9 170 | NO 171 | YES 172 | 7 173 | NO 174 | IBIPadFramework 175 | Passcode: 176 | 177 | 178 | 0 179 | 2 180 | 181 | 182 | NO 183 | 184 | 185 | 186 | 292 187 | {{359, 131}, {185, 21}} 188 | 189 | 190 | _NS:9 191 | NO 192 | YES 193 | 7 194 | NO 195 | IBIPadFramework 196 | 0000 197 | 198 | 199 | 0 200 | 201 | 202 | NO 203 | 204 | 205 | {{0, 20}, {768, 1004}} 206 | 207 | 208 | 209 | 3 210 | MQA 211 | 212 | 2 213 | 214 | 215 | 216 | 2 217 | 218 | IBIPadFramework 219 | 220 | 221 | 222 | 223 | 224 | 225 | view 226 | 227 | 228 | 229 | 3 230 | 231 | 232 | 233 | passcodeLabel 234 | 235 | 236 | 237 | 17 238 | 239 | 240 | 241 | simpleSwitch 242 | 243 | 244 | 245 | 20 246 | 247 | 248 | 249 | setPasscode: 250 | 251 | 252 | 7 253 | 254 | 14 255 | 256 | 257 | 258 | enterPasscode: 259 | 260 | 261 | 7 262 | 263 | 15 264 | 265 | 266 | 267 | changePasscode: 268 | 269 | 270 | 7 271 | 272 | 16 273 | 274 | 275 | 276 | 277 | 278 | 0 279 | 280 | 281 | 282 | 283 | 284 | -1 285 | 286 | 287 | File's Owner 288 | 289 | 290 | -2 291 | 292 | 293 | 294 | 295 | 2 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 9 310 | 311 | 312 | 313 | 314 | 10 315 | 316 | 317 | 318 | 319 | 11 320 | 321 | 322 | 323 | 324 | 12 325 | 326 | 327 | 328 | 329 | 13 330 | 331 | 332 | 333 | 334 | 18 335 | 336 | 337 | 338 | 339 | 19 340 | 341 | 342 | 343 | 344 | 345 | 346 | ViewController 347 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 348 | UIResponder 349 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 350 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 351 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 352 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 353 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 354 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 355 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 356 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 357 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 358 | 359 | 360 | 361 | 362 | 363 | 20 364 | 365 | 366 | 0 367 | IBIPadFramework 368 | YES 369 | 3 370 | 1929 371 | 372 | 373 | -------------------------------------------------------------------------------- /PAPasscode Example/PAPasscode Example/en.lproj/ViewController_iPhone.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C60 6 | 2843 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1929 12 | 13 | 14 | IBProxyObject 15 | IBUIButton 16 | IBUILabel 17 | IBUISwitch 18 | IBUIView 19 | 20 | 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | PluginDependencyRecalculationVersion 25 | 26 | 27 | 28 | 29 | IBFilesOwner 30 | IBCocoaTouchFramework 31 | 32 | 33 | IBFirstResponder 34 | IBCocoaTouchFramework 35 | 36 | 37 | 38 | 274 39 | 40 | 41 | 42 | 292 43 | {{20, 161}, {280, 44}} 44 | 45 | 46 | _NS:9 47 | NO 48 | IBCocoaTouchFramework 49 | 0 50 | 0 51 | 1 52 | Set Passcode 53 | 54 | 3 55 | MQA 56 | 57 | 58 | 1 59 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 60 | 61 | 62 | 3 63 | MC41AA 64 | 65 | 66 | 2 67 | 15 68 | 69 | 70 | Helvetica-Bold 71 | 15 72 | 16 73 | 74 | 75 | 76 | 77 | 292 78 | {{20, 212}, {280, 44}} 79 | 80 | 81 | _NS:9 82 | NO 83 | IBCocoaTouchFramework 84 | 0 85 | 0 86 | 1 87 | Enter Passcode 88 | 89 | 90 | 1 91 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 292 100 | {{20, 263}, {280, 44}} 101 | 102 | 103 | _NS:9 104 | NO 105 | IBCocoaTouchFramework 106 | 0 107 | 0 108 | 1 109 | Change Passcode 110 | 111 | 112 | 1 113 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 292 122 | {{20, 20}, {87, 21}} 123 | 124 | 125 | _NS:9 126 | NO 127 | YES 128 | 7 129 | NO 130 | IBCocoaTouchFramework 131 | Passcode: 132 | 133 | 1 134 | MCAwIDAAA 135 | darkTextColor 136 | 137 | 138 | 0 139 | 2 140 | 141 | 1 142 | 17 143 | 144 | 145 | Helvetica 146 | 17 147 | 16 148 | 149 | NO 150 | 151 | 152 | 153 | 292 154 | {{20, 71}, {87, 21}} 155 | 156 | 157 | _NS:9 158 | NO 159 | YES 160 | 7 161 | NO 162 | IBCocoaTouchFramework 163 | Simple: 164 | 165 | 166 | 0 167 | 2 168 | 169 | 170 | NO 171 | 172 | 173 | 174 | 292 175 | {{115, 20}, {185, 21}} 176 | 177 | 178 | _NS:9 179 | NO 180 | YES 181 | 7 182 | NO 183 | IBCocoaTouchFramework 184 | 0000 185 | 186 | 187 | 0 188 | 189 | 190 | NO 191 | 192 | 193 | 194 | 292 195 | {{100, 71}, {94, 27}} 196 | 197 | 198 | _NS:9 199 | NO 200 | IBCocoaTouchFramework 201 | 0 202 | 0 203 | YES 204 | 205 | 206 | {{0, 20}, {320, 548}} 207 | 208 | 209 | 210 | NO 211 | 212 | 213 | IBUIScreenMetrics 214 | 215 | YES 216 | 217 | 218 | 219 | 220 | 221 | {320, 568} 222 | {568, 320} 223 | 224 | 225 | IBCocoaTouchFramework 226 | Retina 4 Full Screen 227 | 2 228 | 229 | IBCocoaTouchFramework 230 | 231 | 232 | 233 | 234 | 235 | 236 | view 237 | 238 | 239 | 240 | 7 241 | 242 | 243 | 244 | passcodeLabel 245 | 246 | 247 | 248 | 19 249 | 250 | 251 | 252 | simpleSwitch 253 | 254 | 255 | 256 | 22 257 | 258 | 259 | 260 | setPasscode: 261 | 262 | 263 | 7 264 | 265 | 13 266 | 267 | 268 | 269 | enterPasscode: 270 | 271 | 272 | 7 273 | 274 | 14 275 | 276 | 277 | 278 | changePasscode: 279 | 280 | 281 | 7 282 | 283 | 16 284 | 285 | 286 | 287 | 288 | 289 | 0 290 | 291 | 292 | 293 | 294 | 295 | -1 296 | 297 | 298 | File's Owner 299 | 300 | 301 | -2 302 | 303 | 304 | 305 | 306 | 6 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 8 321 | 322 | 323 | 324 | 325 | 9 326 | 327 | 328 | 329 | 330 | 10 331 | 332 | 333 | 334 | 335 | 11 336 | 337 | 338 | 339 | 340 | 12 341 | 342 | 343 | 344 | 345 | 20 346 | 347 | 348 | 349 | 350 | 21 351 | 352 | 353 | 354 | 355 | 356 | 357 | ViewController 358 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 359 | UIResponder 360 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 361 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 362 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 363 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 364 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 365 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 366 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 367 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 368 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 369 | 370 | 371 | 372 | 373 | 374 | 22 375 | 376 | 377 | 0 378 | IBCocoaTouchFramework 379 | YES 380 | 3 381 | 1929 382 | 383 | 384 | -------------------------------------------------------------------------------- /PAPasscode/PAPasscodeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PAPasscodeViewController.m 3 | // PAPasscode 4 | // 5 | // Created by Denis Hennessy on 15/10/2012. 6 | // Copyright (c) 2012 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PAPasscodeViewController.h" 11 | 12 | static NSString *BulletCharacter = @"\u25CF"; 13 | static NSString *DashCharacter = @"\u2010"; 14 | static NSInteger FailedBackgroundHeight = 24; 15 | static NSTimeInterval AnimationDuration = 0.3; 16 | 17 | @interface PAPasscodeViewController () 18 | - (void)cancel:(id)sender; 19 | - (void)handleFailedAttempt; 20 | - (void)handleCompleteField; 21 | - (void)passcodeChanged:(id)sender; 22 | - (void)resetFailedAttempts; 23 | - (void)showFailedAttempts; 24 | - (void)showScreenForPhase:(NSInteger)phase animated:(BOOL)animated; 25 | @end 26 | 27 | @implementation PAPasscodeViewController 28 | 29 | - (id)initForAction:(PasscodeAction)action { 30 | self = [super init]; 31 | if (self) { 32 | _action = action; 33 | switch (action) { 34 | case PasscodeActionSet: 35 | self.title = NSLocalizedString(@"Set Passcode", nil); 36 | _enterPrompt = NSLocalizedString(@"Enter a passcode", nil); 37 | _confirmPrompt = NSLocalizedString(@"Re-enter your passcode", nil); 38 | break; 39 | 40 | case PasscodeActionEnter: 41 | self.title = NSLocalizedString(@"Enter Passcode", nil); 42 | _enterPrompt = NSLocalizedString(@"Enter your passcode", nil); 43 | break; 44 | 45 | case PasscodeActionChange: 46 | self.title = NSLocalizedString(@"Change Passcode", nil); 47 | _changePrompt = NSLocalizedString(@"Enter your old passcode", nil); 48 | _enterPrompt = NSLocalizedString(@"Enter your new passcode", nil); 49 | _confirmPrompt = NSLocalizedString(@"Re-enter your new passcode", nil); 50 | break; 51 | } 52 | self.modalPresentationStyle = UIModalPresentationFormSheet; 53 | _simple = YES; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)dealloc { 59 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 60 | } 61 | 62 | - (void)loadView { 63 | UIView *view = [[UIView alloc] init]; 64 | view.backgroundColor = [UIColor systemBackgroundColor]; 65 | 66 | // contentView is set to the visible area (below nav bar, above keyboard) 67 | contentView = [[UIView alloc] init]; 68 | contentView.translatesAutoresizingMaskIntoConstraints = NO; 69 | contentView.backgroundColor = [UIColor clearColor]; 70 | [view addSubview:contentView]; 71 | 72 | _inputPanel = [[UIView alloc] init]; 73 | _inputPanel.translatesAutoresizingMaskIntoConstraints = NO; 74 | [contentView addSubview:_inputPanel]; 75 | 76 | passcodeTextField = [[UITextField alloc] init]; 77 | passcodeTextField.translatesAutoresizingMaskIntoConstraints = NO; 78 | passcodeTextField.secureTextEntry = YES; 79 | [passcodeTextField addTarget:self action:@selector(passcodeChanged:) forControlEvents:UIControlEventEditingChanged]; 80 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showKeyboard:) 81 | name:UIKeyboardDidHideNotification object:nil]; 82 | 83 | [_inputPanel addSubview:passcodeTextField]; 84 | if (_simple) { 85 | UIFont *font = [UIFont fontWithName:@"Courier" size:32]; 86 | for (int i=0;i<4;i++) { 87 | _digitLabels[i] = [[UILabel alloc] init]; 88 | _digitLabels[i].translatesAutoresizingMaskIntoConstraints = NO; 89 | _digitLabels[i].font = font; 90 | [_inputPanel addSubview:_digitLabels[i]]; 91 | _digitLabels[i].text = DashCharacter; 92 | } 93 | passcodeTextField.hidden = YES; 94 | passcodeTextField.keyboardType = UIKeyboardTypeNumberPad; 95 | } else { 96 | _inputPanel.backgroundColor = [UIColor whiteColor]; 97 | } 98 | 99 | promptLabel = [[UILabel alloc] init]; 100 | promptLabel.translatesAutoresizingMaskIntoConstraints = NO; 101 | promptLabel.font = [UIFont systemFontOfSize:15]; 102 | promptLabel.textAlignment = NSTextAlignmentCenter; 103 | promptLabel.numberOfLines = 0; 104 | [contentView addSubview:promptLabel]; 105 | 106 | messageLabel = [[UILabel alloc] init]; 107 | messageLabel.translatesAutoresizingMaskIntoConstraints = NO; 108 | messageLabel.font = [UIFont systemFontOfSize:14]; 109 | messageLabel.textAlignment = NSTextAlignmentCenter; 110 | messageLabel.numberOfLines = 0; 111 | messageLabel.text = _message; 112 | [contentView addSubview:messageLabel]; 113 | 114 | _failedAttemptsView = [[UIView alloc] init]; 115 | _failedAttemptsView.translatesAutoresizingMaskIntoConstraints = NO; 116 | _failedAttemptsView.backgroundColor = [UIColor colorWithRed:0.75 green:0.16 blue:0.16 alpha:1]; 117 | _failedAttemptsView.layer.cornerRadius = FailedBackgroundHeight/2; 118 | _failedAttemptsView.layer.borderColor = [UIColor colorWithRed:0.55 green:0 blue:0 alpha:1].CGColor; 119 | _failedAttemptsView.layer.borderWidth = 0.5; 120 | _failedAttemptsView.hidden = YES; 121 | [contentView addSubview:_failedAttemptsView]; 122 | 123 | failedAttemptsLabel = [[UILabel alloc] init]; 124 | failedAttemptsLabel.translatesAutoresizingMaskIntoConstraints = NO; 125 | failedAttemptsLabel.textColor = [UIColor whiteColor]; 126 | failedAttemptsLabel.font = [UIFont systemFontOfSize:14]; 127 | failedAttemptsLabel.textAlignment = NSTextAlignmentCenter; 128 | [_failedAttemptsView addSubview:failedAttemptsLabel]; 129 | 130 | self.view = view; 131 | [self.view setNeedsUpdateConstraints]; 132 | } 133 | 134 | - (void)updateViewConstraints { 135 | [super updateViewConstraints]; 136 | 137 | if (_installedConstraints) { 138 | [self.view removeConstraints:_installedConstraints]; 139 | } 140 | NSMutableArray *constraints = [[NSMutableArray alloc] init]; 141 | NSDictionary *views = @{ 142 | @"contentView": contentView, 143 | @"inputPanel": _inputPanel, 144 | @"failedAttemptsLabel": failedAttemptsLabel, 145 | @"failedAttemptsView": _failedAttemptsView, 146 | @"messageLabel": messageLabel, 147 | @"passcodeTextField": passcodeTextField, 148 | @"promptLabel": promptLabel, 149 | }; 150 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:nil views:views]]; 151 | [constraints addObject:[NSLayoutConstraint constraintWithItem:contentView 152 | attribute:NSLayoutAttributeTop 153 | relatedBy:NSLayoutRelationEqual 154 | toItem:self.topLayoutGuide 155 | attribute:NSLayoutAttributeBottom 156 | multiplier:1 constant:0]]; 157 | _keyboardHeightConstraint = [NSLayoutConstraint constraintWithItem:contentView 158 | attribute:NSLayoutAttributeBottom 159 | relatedBy:NSLayoutRelationEqual 160 | toItem:self.bottomLayoutGuide 161 | attribute:NSLayoutAttributeTop 162 | multiplier:1 constant:0]; 163 | [constraints addObject:_keyboardHeightConstraint]; 164 | [constraints addObject:[NSLayoutConstraint constraintWithItem:_inputPanel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]]; 165 | 166 | if (_simple) { 167 | [constraints addObject:[NSLayoutConstraint constraintWithItem:_inputPanel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 168 | NSDictionary *digits = @{@"d0": _digitLabels[0], @"d1": _digitLabels[1], @"d2": _digitLabels[2], @"d3": _digitLabels[3]}; 169 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[d0]-(w)-[d1]-(w)-[d2]-(w)-[d3]|" options:0 metrics:@{@"w": @(16)} views:digits]]; 170 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[d0]|" options:0 metrics:nil views:digits]]; 171 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[d1]|" options:0 metrics:nil views:digits]]; 172 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[d2]|" options:0 metrics:nil views:digits]]; 173 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[d3]|" options:0 metrics:nil views:digits]]; 174 | } else { 175 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[inputPanel]|" options:0 metrics:nil views:views]]; 176 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[passcodeTextField]-|" options:0 metrics:nil views:views]]; 177 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[passcodeTextField]-|" options:0 metrics:nil views:views]]; 178 | } 179 | 180 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[failedAttemptsLabel]-|" options:0 metrics:nil views:views]]; 181 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[failedAttemptsLabel]|" options:0 metrics:nil views:views]]; 182 | 183 | [constraints addObject:[NSLayoutConstraint constraintWithItem:_failedAttemptsView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 184 | [constraints addObject:[NSLayoutConstraint constraintWithItem:messageLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 185 | [constraints addObject:[NSLayoutConstraint constraintWithItem:promptLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 186 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[promptLabel]-[inputPanel]-[messageLabel]-[failedAttemptsView(h)]" options:0 metrics:@{@"h": @(FailedBackgroundHeight)} views:views]]; 187 | 188 | _installedConstraints = constraints; 189 | [self.view addConstraints:_installedConstraints]; 190 | } 191 | 192 | - (void)showKeyboard:(id)sender { 193 | [passcodeTextField becomeFirstResponder]; 194 | } 195 | 196 | - (void)viewDidLoad { 197 | [super viewDidLoad]; 198 | 199 | if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidCancel:)]) { 200 | if (_simple) { 201 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)]; 202 | } else { 203 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)]; 204 | } 205 | } 206 | 207 | if (_failedAttempts > 0) { 208 | [self showFailedAttempts]; 209 | } 210 | 211 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil]; 212 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 213 | 214 | } 215 | 216 | - (void)viewWillAppear:(BOOL)animated { 217 | [super viewWillAppear:animated]; 218 | [self showScreenForPhase:0 animated:NO]; 219 | [passcodeTextField becomeFirstResponder]; 220 | [self.view layoutIfNeeded]; 221 | } 222 | 223 | - (void)viewWillDisappear:(BOOL)animated { 224 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 225 | [passcodeTextField resignFirstResponder]; 226 | } 227 | 228 | - (NSUInteger)supportedInterfaceOrientations { 229 | return UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown; 230 | } 231 | 232 | - (void)cancel:(id)sender { 233 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 234 | [_delegate PAPasscodeViewControllerDidCancel:self]; 235 | } 236 | 237 | #pragma mark - implementation helpers 238 | 239 | - (void)keyboardWillShow:(NSNotification *)notification { 240 | NSDictionary *info = [notification userInfo]; 241 | NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey]; 242 | NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 243 | CGRect keyboardFrame = [kbFrame CGRectValue]; 244 | 245 | _keyboardHeightConstraint.constant = -keyboardFrame.size.height; 246 | [UIView animateWithDuration:animationDuration animations:^{ 247 | [self.view layoutIfNeeded]; 248 | }]; 249 | } 250 | 251 | - (void)keyboardWillHide:(NSNotification *)notification { 252 | NSDictionary *info = [notification userInfo]; 253 | NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 254 | 255 | _keyboardHeightConstraint.constant = 0; 256 | [UIView animateWithDuration:animationDuration animations:^{ 257 | [self.view layoutIfNeeded]; 258 | }]; 259 | } 260 | 261 | - (void)handleCompleteField { 262 | NSString *text = passcodeTextField.text; 263 | switch (_action) { 264 | case PasscodeActionSet: 265 | if (phase == 0) { 266 | _passcode = text; 267 | messageLabel.text = @""; 268 | [self showScreenForPhase:1 animated:YES]; 269 | } else { 270 | if ([text isEqualToString:_passcode]) { 271 | if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidSetPasscode:)]) { 272 | [_delegate PAPasscodeViewControllerDidSetPasscode:self]; 273 | } 274 | } else { 275 | [self showScreenForPhase:0 animated:YES]; 276 | messageLabel.text = NSLocalizedString(@"Passcodes did not match. Try again.", nil); 277 | } 278 | } 279 | break; 280 | 281 | case PasscodeActionEnter: 282 | if ([text isEqualToString:_passcode]) { 283 | [self resetFailedAttempts]; 284 | if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidEnterPasscode:)]) { 285 | [_delegate PAPasscodeViewControllerDidEnterPasscode:self]; 286 | } 287 | } else { 288 | if (_alternativePasscode && [text isEqualToString:_alternativePasscode]) { 289 | [self resetFailedAttempts]; 290 | if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidEnterAlternativePasscode:)]) { 291 | [_delegate PAPasscodeViewControllerDidEnterAlternativePasscode:self]; 292 | } 293 | } else { 294 | [self handleFailedAttempt]; 295 | [self showScreenForPhase:0 animated:NO]; 296 | } 297 | } 298 | break; 299 | 300 | case PasscodeActionChange: 301 | if (phase == 0) { 302 | if ([text isEqualToString:_passcode]) { 303 | [self resetFailedAttempts]; 304 | [self showScreenForPhase:1 animated:YES]; 305 | } else { 306 | [self handleFailedAttempt]; 307 | [self showScreenForPhase:0 animated:NO]; 308 | } 309 | } else if (phase == 1) { 310 | _passcode = text; 311 | messageLabel.text = nil; 312 | [self showScreenForPhase:2 animated:YES]; 313 | } else { 314 | if ([text isEqualToString:_passcode]) { 315 | if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidChangePasscode:)]) { 316 | [_delegate PAPasscodeViewControllerDidChangePasscode:self]; 317 | } 318 | } else { 319 | [self showScreenForPhase:1 animated:YES]; 320 | messageLabel.text = NSLocalizedString(@"Passcodes did not match. Try again.", nil); 321 | } 322 | } 323 | break; 324 | } 325 | } 326 | 327 | - (void)handleFailedAttempt { 328 | _failedAttempts++; 329 | [self showFailedAttempts]; 330 | if ([_delegate respondsToSelector:@selector(PAPasscodeViewController:didFailToEnterPasscode:)]) { 331 | [_delegate PAPasscodeViewController:self didFailToEnterPasscode:_failedAttempts]; 332 | } 333 | } 334 | 335 | - (void)resetFailedAttempts { 336 | messageLabel.hidden = NO; 337 | _failedAttemptsView.hidden = YES; 338 | _failedAttempts = 0; 339 | } 340 | 341 | - (void)showFailedAttempts { 342 | messageLabel.hidden = YES; 343 | _failedAttemptsView.hidden = NO; 344 | if (_failedAttempts == 1) { 345 | failedAttemptsLabel.text = NSLocalizedString(@"1 Failed Passcode Attempt", nil); 346 | } else { 347 | failedAttemptsLabel.text = [NSString stringWithFormat:NSLocalizedString(@"%d Failed Passcode Attempts", nil), _failedAttempts]; 348 | } 349 | } 350 | 351 | - (void)passcodeChanged:(id)sender { 352 | NSString *text = passcodeTextField.text; 353 | if (_simple) { 354 | if ([text length] > 4) { 355 | text = [text substringToIndex:4]; 356 | } 357 | for (int i=0;i<4;i++) { 358 | _digitLabels[i].text = (i >= [text length]) ? DashCharacter : BulletCharacter; 359 | } 360 | if ([text length] == 4) { 361 | [self handleCompleteField]; 362 | } 363 | } else { 364 | self.navigationItem.rightBarButtonItem.enabled = [text length] > 0; 365 | } 366 | } 367 | 368 | - (void)showScreenForPhase:(NSInteger)newPhase animated:(BOOL)animated { 369 | CGFloat dir = (newPhase > phase) ? 1 : -1; 370 | if (animated) { 371 | UIGraphicsBeginImageContext(self.view.bounds.size); 372 | [contentView.layer renderInContext:UIGraphicsGetCurrentContext()]; 373 | UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); 374 | UIGraphicsEndImageContext(); 375 | snapshotImageView = [[UIImageView alloc] initWithImage:snapshot]; 376 | snapshotImageView.frame = CGRectOffset(snapshotImageView.frame, -contentView.frame.size.width*dir, 0); 377 | [contentView addSubview:snapshotImageView]; 378 | } 379 | phase = newPhase; 380 | passcodeTextField.text = @""; 381 | if (!_simple) { 382 | BOOL finalScreen = _action == PasscodeActionSet && phase == 1; 383 | finalScreen |= _action == PasscodeActionEnter && phase == 0; 384 | finalScreen |= _action == PasscodeActionChange && phase == 2; 385 | if (finalScreen) { 386 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(handleCompleteField)]; 387 | } else { 388 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Next", nil) style:UIBarButtonItemStyleBordered target:self action:@selector(handleCompleteField)]; 389 | } 390 | self.navigationItem.rightBarButtonItem.enabled = NO; 391 | } 392 | 393 | switch (_action) { 394 | case PasscodeActionSet: 395 | if (phase == 0) { 396 | promptLabel.text = _enterPrompt; 397 | } else { 398 | promptLabel.text = _confirmPrompt; 399 | } 400 | break; 401 | 402 | case PasscodeActionEnter: 403 | promptLabel.text = _enterPrompt; 404 | break; 405 | 406 | case PasscodeActionChange: 407 | if (phase == 0) { 408 | promptLabel.text = _changePrompt; 409 | } else if (phase == 1) { 410 | promptLabel.text = _enterPrompt; 411 | } else { 412 | promptLabel.text = _confirmPrompt; 413 | } 414 | break; 415 | } 416 | for (int i=0;i<4;i++) { 417 | _digitLabels[i].text = DashCharacter; 418 | } 419 | if (animated) { 420 | contentView.frame = CGRectOffset(contentView.frame, contentView.frame.size.width*dir, 0); 421 | [UIView animateWithDuration:AnimationDuration animations:^() { 422 | contentView.frame = CGRectOffset(contentView.frame, -contentView.frame.size.width*dir, 0); 423 | } completion:^(BOOL finished) { 424 | [snapshotImageView removeFromSuperview]; 425 | snapshotImageView = nil; 426 | }]; 427 | } 428 | } 429 | 430 | @end 431 | --------------------------------------------------------------------------------