123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- //Copyright (c) 2012, Denis Hennessy (Peer Assembly - http://peerassembly.com)
- //All rights reserved.
- //
- //Redistribution and use in source and binary forms, with or without
- //modification, are permitted provided that the following conditions are met:
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above copyright
- // notice, this list of conditions and the following disclaimer in the
- // documentation and/or other materials provided with the distribution.
- // * Neither the name of Peer Assembly, Denis Hennessy nor the
- // names of its contributors may be used to endorse or promote products
- // derived from this software without specific prior written permission.
- //
- //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- //DISCLAIMED. IN NO EVENT SHALL PEER ASSEMBLY OR DENIS HENNESSY BE LIABLE FOR ANY
- //DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #import <QuartzCore/QuartzCore.h>
- #import "PAPasscodeViewController.h"
- #define NAVBAR_HEIGHT 66
- #define PROMPT_HEIGHT 90
- #define DIGIT_SPACING 25
- #define DIGIT_WIDTH 19
- #define DIGIT_HEIGHT 1
- #define MARKER_WIDTH 16
- #define MARKER_HEIGHT 1
- #define MARKER_X 1.5
- #define MARKER_Y -18
- #define MESSAGE_HEIGHT 90
- #define FAILED_LCAP 19
- #define FAILED_RCAP 19
- #define FAILED_HEIGHT 26
- #define FAILED_MARGIN 10
- #define TEXTFIELD_MARGIN 8
- #define SLIDE_DURATION 0.3
- @interface PAPasscodeViewController ()
- - (void)cancel:(id)sender;
- - (void)handleFailedAttempt;
- - (void)handleCompleteField;
- - (void)passcodeChanged:(id)sender;
- - (void)resetFailedAttempts;
- - (void)showFailedAttempts;
- - (void)showScreenForPhase:(NSInteger)phase animated:(BOOL)animated;
- @end
- @implementation PAPasscodeViewController
- - (id)initForAction:(PasscodeAction)action {
- self = [super init];
- if (self) {
- _action = action;
- switch (action) {
- case PasscodeActionSet:
- self.title = NSLocalizedString(@"PASSCODE_SET", nil);
- _enterPrompt = NSLocalizedString(@"ENTER_PASSCODE", nil);
- _confirmPrompt = NSLocalizedString(@"REENTER_PASSCODE", nil);
- break;
-
- case PasscodeActionEnter:
- self.title = NSLocalizedString(@"ENTER_PASSCODE", nil);
- _enterPrompt = NSLocalizedString(@"ENTER_PASSCODE", nil);
- break;
-
- case PasscodeActionChange:
- self.title = NSLocalizedString(@"PASSCODE_CHANGE", nil);
- _changePrompt = NSLocalizedString(@"PASSCODE_ENTER_OLD", nil);
- _enterPrompt = NSLocalizedString(@"PASSCODE_ENTER_NEW", nil);
- _confirmPrompt = NSLocalizedString(@"PASSCODE_REENTER_NEW", nil);
- break;
- }
- self.modalPresentationStyle = UIModalPresentationFormSheet;
- _simple = YES;
- }
- return self;
- }
- - (void)loadView {
- UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
- view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
- self.navigationController.navigationBarHidden = YES;
- UINavigationBar *navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, view.bounds.size.width, NAVBAR_HEIGHT)];
- navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- navigationBar.items = @[self.navigationItem];
- navigationBar.barTintColor = [UIColor VLCOrangeTintColor];
- navigationBar.tintColor = [UIColor whiteColor];
- navigationBar.titleTextAttributes = @{ NSForegroundColorAttributeName : [UIColor whiteColor] };
- [view addSubview:navigationBar];
-
- contentView = [[UIView alloc] initWithFrame:CGRectMake(0, NAVBAR_HEIGHT, view.bounds.size.width, view.bounds.size.height-NAVBAR_HEIGHT)];
- contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
- if (_backgroundView) {
- [contentView addSubview:_backgroundView];
- }
- contentView.backgroundColor = [UIColor VLCDarkBackgroundColor];
- [view addSubview:contentView];
- CGFloat panelWidth = DIGIT_WIDTH*4+DIGIT_SPACING*3;
- if (_simple) {
- UIView *digitPanel = [[UIView alloc] initWithFrame:CGRectMake(0, 0, panelWidth, DIGIT_HEIGHT)];
- digitPanel.frame = CGRectOffset(digitPanel.frame, (contentView.bounds.size.width - panelWidth) / 2., PROMPT_HEIGHT);
- digitPanel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
- [contentView addSubview:digitPanel];
-
- UIImage *backgroundImage = [UIImage imageNamed:@"papasscode_background"];
- UIImage *markerImage = [UIImage imageNamed:@"papasscode_marker"];
- CGFloat xLeft = 0;
- for (int i=0;i<4;i++) {
- UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
- backgroundImageView.frame = CGRectOffset(backgroundImageView.frame, xLeft, 0);
- [digitPanel addSubview:backgroundImageView];
- digitImageViews[i] = [[UIImageView alloc] initWithImage:markerImage];
- digitImageViews[i].autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
- digitImageViews[i].frame = CGRectOffset(digitImageViews[i].frame, backgroundImageView.frame.origin.x+MARKER_X, MARKER_Y);
- [digitPanel addSubview:digitImageViews[i]];
- xLeft += DIGIT_SPACING + backgroundImage.size.width;
- }
- passcodeTextField = [[UITextField alloc] initWithFrame:digitPanel.frame];
- passcodeTextField.hidden = YES;
- } else {
- UIView *passcodePanel = [[UIView alloc] initWithFrame:CGRectMake(0, 0, panelWidth, DIGIT_HEIGHT)];
- passcodePanel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
- passcodePanel.frame = CGRectOffset(passcodePanel.frame, (contentView.bounds.size.width-passcodePanel.bounds.size.width)/2, PROMPT_HEIGHT);
- passcodePanel.frame = CGRectInset(passcodePanel.frame, TEXTFIELD_MARGIN, TEXTFIELD_MARGIN);
- passcodePanel.layer.borderColor = [UIColor colorWithRed:0.65 green:0.67 blue:0.70 alpha:1.0].CGColor;
- passcodePanel.layer.borderWidth = 1.0;
- passcodePanel.layer.cornerRadius = 5.0;
- passcodePanel.layer.shadowColor = [UIColor whiteColor].CGColor;
- passcodePanel.layer.shadowOffset = CGSizeMake(0, 1);
- passcodePanel.layer.shadowOpacity = 1.0;
- passcodePanel.layer.shadowRadius = 1.0;
- passcodePanel.backgroundColor = [UIColor whiteColor];
- [contentView addSubview:passcodePanel];
- passcodeTextField = [[UITextField alloc] initWithFrame:CGRectInset(passcodePanel.frame, 6, 6)];
- }
- passcodeTextField.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
- passcodeTextField.borderStyle = UITextBorderStyleNone;
- passcodeTextField.secureTextEntry = YES;
- passcodeTextField.textColor = [UIColor colorWithRed:0.23 green:0.33 blue:0.52 alpha:1.0];
- passcodeTextField.keyboardType = UIKeyboardTypeNumberPad;
- [passcodeTextField addTarget:self action:@selector(passcodeChanged:) forControlEvents:UIControlEventEditingChanged];
- [contentView addSubview:passcodeTextField];
- promptLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, contentView.bounds.size.width, PROMPT_HEIGHT)];
- promptLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- promptLabel.backgroundColor = [UIColor clearColor];
- promptLabel.textColor = [UIColor VLCLightTextColor];
- promptLabel.font = [UIFont boldSystemFontOfSize:17];
- promptLabel.shadowColor = [UIColor VLCDarkTextShadowColor];
- promptLabel.shadowOffset = CGSizeMake(0, 1);
- promptLabel.textAlignment = NSTextAlignmentCenter;
- promptLabel.numberOfLines = 0;
- [contentView addSubview:promptLabel];
-
- messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, PROMPT_HEIGHT+DIGIT_HEIGHT, contentView.bounds.size.width, MESSAGE_HEIGHT)];
- messageLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- messageLabel.backgroundColor = [UIColor clearColor];
- messageLabel.textColor = [UIColor VLCLightTextColor];
- messageLabel.font = [UIFont boldSystemFontOfSize:14];
- messageLabel.shadowColor = [UIColor VLCDarkTextShadowColor];
- messageLabel.shadowOffset = CGSizeMake(0, 1);
- messageLabel.textAlignment = NSTextAlignmentCenter;
- messageLabel.numberOfLines = 0;
- messageLabel.text = _message;
- [contentView addSubview:messageLabel];
-
- UIImage *failedBg = [[UIImage imageNamed:@"papasscode_failed_bg"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, FAILED_LCAP, 0, FAILED_RCAP)];
- failedImageView = [[UIImageView alloc] initWithImage:failedBg];
- failedImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
- failedImageView.hidden = YES;
- [contentView addSubview:failedImageView];
-
- failedAttemptsLabel = [[UILabel alloc] initWithFrame:CGRectZero];
- failedAttemptsLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
- failedAttemptsLabel.backgroundColor = [UIColor clearColor];
- failedAttemptsLabel.textColor = [UIColor whiteColor];
- failedAttemptsLabel.font = [UIFont boldSystemFontOfSize:15];
- failedAttemptsLabel.shadowColor = [UIColor blackColor];
- failedAttemptsLabel.shadowOffset = CGSizeMake(0, -1);
- failedAttemptsLabel.textAlignment = NSTextAlignmentCenter;
- failedAttemptsLabel.hidden = YES;
- [contentView addSubview:failedAttemptsLabel];
-
- self.view = view;
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
-
- if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidCancel:)]) {
- if (_simple) {
- self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)];
- } else {
- self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)];
- }
- }
-
- if (_failedAttempts > 0) {
- [self showFailedAttempts];
- }
- }
- - (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
- [self showScreenForPhase:0 animated:NO];
- [passcodeTextField becomeFirstResponder];
- }
- - (void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
- [passcodeTextField resignFirstResponder];
- }
- - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
- return UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown;
- }
- - (void)cancel:(id)sender {
- [_delegate PAPasscodeViewControllerDidCancel:self];
- }
- #pragma mark - implementation helpers
- - (void)handleCompleteField {
- NSString *text = passcodeTextField.text;
- switch (_action) {
- case PasscodeActionSet:
- if (phase == 0) {
- _passcode = text;
- messageLabel.text = @"";
- [self showScreenForPhase:1 animated:YES];
- } else {
- if ([text isEqualToString:_passcode]) {
- if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidSetPasscode:)]) {
- [_delegate PAPasscodeViewControllerDidSetPasscode:self];
- }
- } else {
- [self showScreenForPhase:0 animated:YES];
- messageLabel.text = NSLocalizedString(@"PASSCODES_DID_NOT_MATCH", nil);
- }
- }
- break;
-
- case PasscodeActionEnter:
- if ([text isEqualToString:_passcode]) {
- [self resetFailedAttempts];
- if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidEnterPasscode:)]) {
- [_delegate PAPasscodeViewControllerDidEnterPasscode:self];
- }
- } else {
- if (_alternativePasscode && [text isEqualToString:_alternativePasscode]) {
- [self resetFailedAttempts];
- if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidEnterAlternativePasscode:)]) {
- [_delegate PAPasscodeViewControllerDidEnterAlternativePasscode:self];
- }
- } else {
- [self handleFailedAttempt];
- [self showScreenForPhase:0 animated:NO];
- }
- }
- break;
-
- case PasscodeActionChange:
- if (phase == 0) {
- if ([text isEqualToString:_passcode]) {
- [self resetFailedAttempts];
- [self showScreenForPhase:1 animated:YES];
- } else {
- [self handleFailedAttempt];
- [self showScreenForPhase:0 animated:NO];
- }
- } else if (phase == 1) {
- _passcode = text;
- messageLabel.text = @"";
- [self showScreenForPhase:2 animated:YES];
- } else {
- if ([text isEqualToString:_passcode]) {
- if ([_delegate respondsToSelector:@selector(PAPasscodeViewControllerDidChangePasscode:)]) {
- [_delegate PAPasscodeViewControllerDidChangePasscode:self];
- }
- } else {
- [self showScreenForPhase:1 animated:YES];
- messageLabel.text = NSLocalizedString(@"PASSCODES_DID_NOT_MATCH", nil);
- }
- }
- break;
- }
- }
- - (void)handleFailedAttempt {
- _failedAttempts++;
- [self showFailedAttempts];
- if ([_delegate respondsToSelector:@selector(PAPasscodeViewController:didFailToEnterPasscode:)]) {
- [_delegate PAPasscodeViewController:self didFailToEnterPasscode:_failedAttempts];
- }
- }
- - (void)resetFailedAttempts {
- messageLabel.hidden = NO;
- failedImageView.hidden = YES;
- failedAttemptsLabel.hidden = YES;
- _failedAttempts = 0;
- }
- - (void)showFailedAttempts {
- messageLabel.hidden = YES;
- failedImageView.hidden = NO;
- failedAttemptsLabel.hidden = NO;
- if (_failedAttempts == 1) {
- failedAttemptsLabel.text = NSLocalizedString(@"PASSCODE_FAILED_1", nil);
- } else {
- failedAttemptsLabel.text = [NSString stringWithFormat:NSLocalizedString(@"PASSCODE_FAILED_FORMAT", nil), _failedAttempts];
- }
- [failedAttemptsLabel sizeToFit];
- CGFloat bgWidth = failedAttemptsLabel.bounds.size.width + FAILED_MARGIN*2;
- CGFloat x = floor((contentView.bounds.size.width-bgWidth)/2);
- CGFloat y = PROMPT_HEIGHT+DIGIT_HEIGHT+floor((MESSAGE_HEIGHT-FAILED_HEIGHT)/2);
- failedImageView.frame = CGRectMake(x, y, bgWidth, FAILED_HEIGHT);
- x = failedImageView.frame.origin.x+FAILED_MARGIN;
- y = failedImageView.frame.origin.y+floor((failedImageView.bounds.size.height-failedAttemptsLabel.frame.size.height)/2);
- failedAttemptsLabel.frame = CGRectMake(x, y, failedAttemptsLabel.bounds.size.width, failedAttemptsLabel.bounds.size.height);
- }
- - (void)passcodeChanged:(id)sender {
- NSString *text = passcodeTextField.text;
- if (_simple) {
- if ([text length] > 4) {
- text = [text substringToIndex:4];
- }
- for (int i=0;i<4;i++) {
- digitImageViews[i].hidden = i >= [text length];
- }
- if ([text length] == 4) {
- [self handleCompleteField];
- }
- } else {
- self.navigationItem.rightBarButtonItem.enabled = [text length] > 0;
- }
- }
- - (void)showScreenForPhase:(NSInteger)newPhase animated:(BOOL)animated {
- CGFloat dir = (newPhase > phase) ? 1 : -1;
- if (animated) {
- UIGraphicsBeginImageContext(self.view.bounds.size);
- [contentView.layer renderInContext:UIGraphicsGetCurrentContext()];
- UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- snapshotImageView = [[UIImageView alloc] initWithImage:snapshot];
- snapshotImageView.frame = CGRectOffset(snapshotImageView.frame, -contentView.frame.size.width*dir, 0);
- [contentView addSubview:snapshotImageView];
- }
- phase = newPhase;
- passcodeTextField.text = @"";
- if (!_simple) {
- BOOL finalScreen = _action == PasscodeActionSet && phase == 1;
- finalScreen |= _action == PasscodeActionEnter && phase == 0;
- finalScreen |= _action == PasscodeActionChange && phase == 2;
- if (finalScreen) {
- self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(handleCompleteField)];
- } else {
- self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"BUTTON_NEXT", nil) style:UIBarButtonItemStylePlain target:self action:@selector(handleCompleteField)];
- }
- self.navigationItem.rightBarButtonItem.enabled = NO;
- }
-
- switch (_action) {
- case PasscodeActionSet:
- if (phase == 0) {
- promptLabel.text = _enterPrompt;
- } else {
- promptLabel.text = _confirmPrompt;
- }
- break;
-
- case PasscodeActionEnter:
- promptLabel.text = _enterPrompt;
- break;
-
- case PasscodeActionChange:
- if (phase == 0) {
- promptLabel.text = _changePrompt;
- } else if (phase == 1) {
- promptLabel.text = _enterPrompt;
- } else {
- promptLabel.text = _confirmPrompt;
- }
- break;
- }
- for (int i=0;i<4;i++) {
- digitImageViews[i].hidden = YES;
- }
- if (animated) {
- contentView.frame = CGRectOffset(contentView.frame, contentView.frame.size.width*dir, 0);
- [UIView animateWithDuration:SLIDE_DURATION animations:^() {
- contentView.frame = CGRectOffset(contentView.frame, -contentView.frame.size.width*dir, 0);
- } completion:^(BOOL finished) {
- [snapshotImageView removeFromSuperview];
- snapshotImageView = nil;
- }];
- }
- }
- @end
|