Skip to content

Commit 5da7595

Browse files
committed
Integrated alphanumeric logic
1 parent b09f4fd commit 5da7595

File tree

6 files changed

+175
-43
lines changed

6 files changed

+175
-43
lines changed

TOPasscodeViewController/TOPasscodeSettingsViewController.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ NS_ASSUME_NONNULL_BEGIN
6666
/* Create a new instance with the desird light or dark style */
6767
- (instancetype)initWithStyle:(TOPasscodeSettingsViewStyle)style;
6868

69+
/* Changes the passcode type and animates if required */
70+
- (void)setPasscodeType:(TOPasscodeType)passcodeType animated:(BOOL)animated;
71+
6972
@end
7073

7174
NS_ASSUME_NONNULL_END

TOPasscodeViewController/TOPasscodeSettingsViewController.m

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ @interface TOPasscodeSettingsViewController ()
2323

2424
/* Layout Calculations */
2525
@property (nonatomic, assign) CGFloat verticalMidPoint;
26+
@property (nonatomic, assign) CGRect keyboardFrame;
27+
@property (nonatomic, readonly) CGRect contentOverlapFrame; // Either the keypad or the system keyboard
2628

2729
/* Views */
2830
@property (nonatomic, strong) UIView *containerView;
@@ -33,6 +35,10 @@ @interface TOPasscodeSettingsViewController ()
3335
@property (nonatomic, strong) TOPasscodeSettingsKeypadView *keypadView;
3436
@property (nonatomic, strong) TOPasscodeSettingsWarningLabel *warningLabel;
3537

38+
/* Bar Items */
39+
@property (nonatomic, strong) UIBarButtonItem *nextBarButtonItem;
40+
@property (nonatomic, strong) UIBarButtonItem *doneBarButtonItem;
41+
3642
@end
3743

3844
@implementation TOPasscodeSettingsViewController
@@ -61,6 +67,12 @@ - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibB
6167
- (void)setUp
6268
{
6369
_failedPasscodeAttemptCount = 0;
70+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
71+
}
72+
73+
- (void)dealloc
74+
{
75+
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
6476
}
6577

6678
#pragma mark - View Set-up -
@@ -92,7 +104,7 @@ - (void)viewDidLoad {
92104
self.inputField = [[TOPasscodeInputField alloc] init];
93105
self.inputField.tintColor = [UIColor blackColor];
94106
self.inputField.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
95-
self.inputField.passcodeCompletedHandler = ^(NSString *passcode) { [weakSelf numberViewDidEnterPasscode:passcode]; };
107+
self.inputField.passcodeCompletedHandler = ^(NSString *passcode) { [weakSelf inputViewDidCompletePasscode:passcode]; };
96108
[self.inputField sizeToFit];
97109
[self.containerView addSubview:self.inputField];
98110

@@ -146,6 +158,10 @@ - (void)viewDidLoad {
146158
+ kTOPasscodeSettingsLabelInputSpacing;
147159
self.verticalMidPoint *= 0.5f;
148160

161+
// Bar button items
162+
self.nextBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Next", @"") style:UIBarButtonItemStylePlain target:self action:@selector(nextButtonTapped:)];
163+
self.doneBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneButtonTapped:)];
164+
149165
// Apply light/dark mode
150166
[self applyThemeForStyle:self.style];
151167
}
@@ -155,21 +171,25 @@ - (void)viewWillAppear:(BOOL)animated
155171
[super viewWillAppear:animated];
156172

157173
self.state = self.requireCurrentPasscode ? TOPasscodeSettingsViewStateEnterCurrentPassword : TOPasscodeSettingsViewStateEnterNewPassword;
158-
[self updateContentForState:self.state type:self.passcodeType];
174+
[self updateContentForState:self.state type:self.passcodeType animated:NO];
159175
}
160176

161177
#pragma mark - View Update -
162178

163-
- (void)updateContentForState:(TOPasscodeSettingsViewState)state type:(TOPasscodeType)type
179+
- (void)updateContentForState:(TOPasscodeSettingsViewState)state type:(TOPasscodeType)type animated:(BOOL)animated
164180
{
165181
BOOL confirmingPasscode = state == TOPasscodeSettingsViewStateEnterCurrentPassword;
182+
BOOL variableSizePasscode = (type >= TOPasscodeTypeCustomNumeric);
166183

167184
// Update the visibility of the options button
168185
self.optionsButton.hidden = !(state == TOPasscodeSettingsViewStateEnterNewPassword);
169186

170187
// Clear the input view
171188
self.inputField.passcode = nil;
172189

190+
// Disable the input view
191+
self.inputField.enabled = NO;
192+
173193
// Update the warning label
174194
self.warningLabel.hidden = !(confirmingPasscode && self.failedPasscodeAttemptCount > 0);
175195
self.warningLabel.numberOfWarnings = self.failedPasscodeAttemptCount;
@@ -179,7 +199,7 @@ - (void)updateContentForState:(TOPasscodeSettingsViewState)state type:(TOPasscod
179199
self.warningLabel.frame = frame;
180200

181201
// Change the input view if needed
182-
if (type < TOPasscodeTypeCustomNumeric) {
202+
if (!variableSizePasscode) {
183203
self.inputField.style = TOPasscodeInputFieldStyleFixed;
184204
self.inputField.fixedInputView.length = (self.passcodeType == TOPasscodeTypeSixDigits) ? 6 : 4;
185205
}
@@ -191,12 +211,15 @@ - (void)updateContentForState:(TOPasscodeSettingsViewState)state type:(TOPasscod
191211
switch (state) {
192212
case TOPasscodeSettingsViewStateEnterCurrentPassword:
193213
self.titleLabel.text = NSLocalizedString(@"Enter your passcode", @"");
214+
self.navigationItem.rightBarButtonItem = nil;
194215
break;
195216
case TOPasscodeSettingsViewStateEnterNewPassword:
196217
self.titleLabel.text = NSLocalizedString(@"Enter a new passcode", @"");
218+
self.navigationItem.rightBarButtonItem = variableSizePasscode ? self.nextBarButtonItem : nil;
197219
break;
198220
case TOPasscodeSettingsViewStateConfirmNewPassword:
199221
self.titleLabel.text = NSLocalizedString(@"Confirm new passcode", @"");
222+
self.navigationItem.rightBarButtonItem = variableSizePasscode ? self.doneBarButtonItem : nil;
200223
break;
201224
}
202225

@@ -211,6 +234,28 @@ - (void)updateContentForState:(TOPasscodeSettingsViewState)state type:(TOPasscod
211234
frame = self.inputField.frame;
212235
frame.origin.x = (CGRectGetWidth(self.containerView.frame) - CGRectGetWidth(frame)) * 0.5f;
213236
self.inputField.frame = CGRectIntegral(frame);
237+
238+
// If we're the alphanumeric type, present the keyboard
239+
if (type == TOPasscodeTypeCustomAlphanumeric) {
240+
self.inputField.enabled = YES;
241+
[self.inputField becomeFirstResponder];
242+
}
243+
else {
244+
if (self.inputField.isFirstResponder) {
245+
[self.inputField resignFirstResponder];
246+
}
247+
}
248+
249+
// If not animated, force a blanket re-layout
250+
if (!animated) {
251+
[self viewDidLayoutSubviews];
252+
return;
253+
}
254+
255+
// If animated, perform the animation
256+
[UIView animateWithDuration:0.3f animations:^{
257+
[self viewDidLayoutSubviews];
258+
}];
214259
}
215260

216261
- (void)transitionToState:(TOPasscodeSettingsViewState)state animated:(BOOL)animated
@@ -279,7 +324,11 @@ - (void)viewDidLayoutSubviews
279324
frame.size.height = MAX(frame.size.height, kTOPasscodeKeypadMinHeight);
280325
frame.size.height = MIN(frame.size.height, kTOPasscodeKeypadMaxHeight);
281326
frame.size.width = viewSize.width;
282-
frame.origin.y = viewSize.height - frame.size.height;
327+
frame.origin.y = viewSize.height;
328+
if (self.passcodeType != TOPasscodeTypeCustomAlphanumeric) {
329+
frame.origin.y -= frame.size.height;
330+
}
331+
283332
self.keypadView.frame = CGRectIntegral(frame);
284333

285334
BOOL horizontalLayout = frame.size.height < kTOPasscodeKeypadMinHeight + FLT_EPSILON;
@@ -290,13 +339,13 @@ - (void)viewDidLayoutSubviews
290339

291340
// Layout the container view
292341
frame = self.containerView.frame;
293-
frame.origin.y = (((viewSize.height - (topContentHeight + self.keypadView.frame.size.height))) * 0.5f) - self.verticalMidPoint;
342+
frame.origin.y = (((viewSize.height - (topContentHeight + self.contentOverlapFrame.size.height))) * 0.5f) - self.verticalMidPoint;
294343
frame.origin.y += topContentHeight;
295344
self.containerView.frame = CGRectIntegral(frame);
296345

297346
// Layout the passcode options button
298347
frame = self.optionsButton.frame;
299-
frame.origin.y = CGRectGetMinY(self.keypadView.frame) - kTOPasscodeSettingsOptionsButtonOffset - CGRectGetHeight(frame);
348+
frame.origin.y = CGRectGetMinY(self.contentOverlapFrame) - kTOPasscodeSettingsOptionsButtonOffset - CGRectGetHeight(frame);
300349
frame.origin.x = (CGRectGetWidth(self.view.frame) - CGRectGetWidth(frame)) * 0.5f;
301350
self.optionsButton.frame = frame;
302351

@@ -362,7 +411,7 @@ - (void)applyThemeForStyle:(TOPasscodeSettingsViewStyle)style
362411
}
363412

364413
#pragma mark - Data Management -
365-
- (void)numberViewDidEnterPasscode:(NSString *)passcode
414+
- (void)inputViewDidCompletePasscode:(NSString *)passcode
366415
{
367416
switch (self.state) {
368417
case TOPasscodeSettingsViewStateEnterCurrentPassword:
@@ -414,6 +463,31 @@ - (void)confirmNewPasscode:(NSString *)passcode
414463
[self.delegate passcodeSettingsViewController:self didChangeToNewPasscode:self.potentialPasscode ofType:self.passcodeType];
415464
}
416465

466+
#pragma mark - System Keyboard Handling -
467+
- (void)keyboardWillChangeFrame:(NSNotification *)notification
468+
{
469+
self.keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
470+
[self viewDidLayoutSubviews];
471+
}
472+
473+
- (CGRect)contentOverlapFrame
474+
{
475+
if (self.passcodeType < TOPasscodeTypeCustomAlphanumeric) {
476+
return self.keypadView.frame;
477+
}
478+
479+
// Work out where our view is in relation to the screen
480+
UIWindow *window = [UIApplication sharedApplication].keyWindow;
481+
CGRect viewFrame = [self.view.superview convertRect:self.view.frame toView:window];
482+
483+
CGFloat overlap = CGRectGetMaxY(viewFrame) - CGRectGetMinY(self.keyboardFrame);
484+
485+
CGRect overlapFrame = self.keyboardFrame;
486+
overlapFrame.origin.y = MIN(viewFrame.size.height - overlap, viewFrame.size.height);
487+
overlapFrame.size.height = MAX(overlap, 0.0f);
488+
return overlapFrame;
489+
}
490+
417491
#pragma mark - Button Callbacks -
418492

419493
- (void)optionsCodeButtonTapped:(id)sender
@@ -456,28 +530,43 @@ - (void)optionsCodeButtonTapped:(id)sender
456530
[self presentViewController:alertController animated:YES completion:nil];
457531
}
458532

533+
- (void)nextButtonTapped:(id)sender
534+
{
535+
[self transitionToState:TOPasscodeSettingsViewStateConfirmNewPassword animated:YES];
536+
}
537+
538+
- (void)doneButtonTapped:(id)sender
539+
{
540+
[self confirmNewPasscode:self.inputField.passcode];
541+
}
542+
459543
#pragma mark - Accessors -
460544
- (void)setPasscodeType:(TOPasscodeType)passcodeType
545+
{
546+
[self setPasscodeType:passcodeType animated:NO];
547+
}
548+
549+
- (void)setPasscodeType:(TOPasscodeType)passcodeType animated:(BOOL)animated
461550
{
462551
if (_passcodeType == passcodeType) { return; }
463552
_passcodeType = passcodeType;
464553

465-
[self updateContentForState:self.state type:_passcodeType];
554+
[self updateContentForState:self.state type:_passcodeType animated:animated];
466555
}
467556

468557
- (void)setState:(TOPasscodeSettingsViewState)state
469558
{
470559
if (_state == state) { return; }
471560
_state = state;
472561

473-
[self updateContentForState:_state type:self.passcodeType];
562+
[self updateContentForState:_state type:self.passcodeType animated:NO];
474563
}
475564

476565
- (void)setFailedPasscodeAttemptCount:(NSInteger)failedPasscodeAttemptCount
477566
{
478567
if (_failedPasscodeAttemptCount == failedPasscodeAttemptCount) { return; }
479568
_failedPasscodeAttemptCount = failedPasscodeAttemptCount;
480-
[self updateContentForState:self.state type:self.passcodeType];
569+
[self updateContentForState:self.state type:self.passcodeType animated:NO];
481570
}
482571

483572
@end

TOPasscodeViewController/Views/Shared/TOPasscodeFixedInputView.m

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,23 +84,27 @@ - (void)setCircleViewsForLength:(NSInteger)length
8484
[circleViews addObjectsFromArray:self.circleViews];
8585
}
8686

87-
while (circleViews.count != length) {
88-
// Remove any extra circle views
89-
if (circleViews.count > length) {
90-
TOPasscodeCircleView *lastCircle = circleViews.lastObject;
91-
[lastCircle removeFromSuperview];
92-
[circleViews removeLastObject];
93-
continue;
87+
[UIView performWithoutAnimation:^{
88+
while (circleViews.count != length) {
89+
// Remove any extra circle views
90+
if (circleViews.count > length) {
91+
TOPasscodeCircleView *lastCircle = circleViews.lastObject;
92+
[lastCircle removeFromSuperview];
93+
[circleViews removeLastObject];
94+
continue;
95+
}
96+
97+
// Add any new circle views
98+
TOPasscodeCircleView *newCircleView = [[TOPasscodeCircleView alloc] init];
99+
[self setImagesOfCircleView:newCircleView];
100+
[self addSubview:newCircleView];
101+
[circleViews addObject:newCircleView];
94102
}
95103

96-
// Add any new circle views
97-
TOPasscodeCircleView *newCircleView = [[TOPasscodeCircleView alloc] init];
98-
[self setImagesOfCircleView:newCircleView];
99-
[self addSubview:newCircleView];
100-
[circleViews addObject:newCircleView];
101-
}
102-
103-
self.circleViews = [NSArray arrayWithArray:circleViews];
104+
self.circleViews = [NSArray arrayWithArray:circleViews];
105+
[self setNeedsLayout];
106+
[self layoutIfNeeded];
107+
}];
104108
}
105109

106110
- (void)setCircleImagesForDiameter:(CGFloat)diameter
@@ -138,6 +142,13 @@ - (void)setCircleDiameter:(CGFloat)circleDiameter
138142
[self sizeToFit];
139143
}
140144

145+
- (void)setLength:(NSInteger)length
146+
{
147+
if (_length == length) { return; }
148+
_length = length;
149+
[self setCircleViewsForLength:length];
150+
}
151+
141152
- (void)setHighlightedLength:(NSInteger)highlightedLength
142153
{
143154
[self setHighlightedLength:highlightedLength animated:NO];

TOPasscodeViewController/Views/Shared/TOPasscodeInputField.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ typedef NS_ENUM(NSInteger, TOPasscodeInputFieldStyle) {
3939
/* The alpha value of the views in this view (For tranclucent styling) */
4040
@property (nonatomic, assign) CGFloat contentAlpha;
4141

42+
/* Whether the view may be tapped to enable character input (Default is NO) */
43+
@property (nonatomic, assign) BOOL enabled;
44+
4245
/** Called when the number of digits has been entered, or the user tapped 'Done' on the keyboard */
4346
@property (nonatomic, copy) void (^passcodeCompletedHandler)(NSString *code);
4447

0 commit comments

Comments
 (0)