Skip to content

Instantly share code, notes, and snippets.

@matt-curtis
Last active December 31, 2020 14:29
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save matt-curtis/28461ec559d495101be8 to your computer and use it in GitHub Desktop.
Save matt-curtis/28461ec559d495101be8 to your computer and use it in GitHub Desktop.
Done Button for UIKeyboard NumberPad (must be set as inputAccessoryView)
#import <UIKit/UIKit.h>
@interface NumberPadDoneBtn : UIView
@end
@interface NumberPadButton : UIButton
@end
#import "NumberPadDoneBtn.h"
@implementation NumberPadDoneBtn {
NumberPadButton *doneBtn;
}
#pragma mark -
#pragma mark Initialization/Deallocation
- (id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:CGRectMake(0, 0, 1, 1)];
if(!self) return self;
self.clipsToBounds = false;
// Create UIButton
doneBtn = [NumberPadButton new];
doneBtn.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:16.0f];
[doneBtn setTitle:@"DONE" forState:UIControlStateNormal];
[doneBtn addTarget:self action:@selector(stopEditing) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:doneBtn];
// Register for UIKeyboard frame changes...
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChanged:) name:UIKeyboardWillChangeFrameNotification object:nil];
return self;
}
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark -
#pragma mark View Layouting
- (void) keyboardFrameChanged:(NSNotification*)notification {
// Calc View Height
float height; float width;
if(UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)){
width = CGRectGetWidth([UIScreen mainScreen].bounds);
height = CGRectGetHeight([notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]);
} else {
width = CGRectGetHeight([UIScreen mainScreen].bounds);
height = CGRectGetWidth([notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]);
}
// Calc Button Frame
// @Byte - ಠ_ಠ
float btnHeight = height/4; // Four Rows
float btnWidth = (width/3)-2; // Three Columns - 2px to account for lines
doneBtn.frame = CGRectMake(0, (height-btnHeight)+1, btnWidth, btnHeight);
// Set Button Text Color to adapt to keyboard type..
if([self findFirstResponderIn:nil].keyboardAppearance == UIKeyboardAppearanceDark){
doneBtn.tintColor = [UIColor colorWithRed:97/255.0f green:97/255.0f blue:97/255.0f alpha:1.0f];
[doneBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
} else {
doneBtn.tintColor = [UIColor whiteColor];
[doneBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
}
- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(CGRectContainsPoint(doneBtn.frame, point)){
return true;
// Normally a touch wouldn't register on the UIButton, as it's not in frame/bounds. This fixes that.
}
return [super pointInside:point withEvent:event];
}
#pragma mark -
#pragma mark Responder Management/Retreival
- (UIView<UITextInputTraits>*) findFirstResponderIn:(UIView*)view {
if(view == nil) view = [self currentWindow];
UIView<UITextInputTraits> *firstResponder;
for (UIView *iView in view.subviews){
if([iView isFirstResponder]){
return firstResponder = (UIView<UITextInputTraits>*)iView;
} else if(!firstResponder && iView.subviews.count > 0){
firstResponder = [self findFirstResponderIn:iView];
}
}
return firstResponder;
}
- (UIWindow*) currentWindow {
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *window in windows){
if(!window.hidden && window.isKeyWindow) return window;
}
return nil;
}
- (void) stopEditing {
[[self findFirstResponderIn:nil] resignFirstResponder];
}
@end
@implementation NumberPadButton
- (void) setHighlighted:(BOOL)highlighted {
if (highlighted){
self.backgroundColor = self.tintColor;
} else {
self.backgroundColor = [UIColor clearColor];
}
}
@end
@matt-curtis
Copy link
Author

Very simple UIView subclass that adds a Done button for the NumberPad. Relies on NSNotificationCenter (and iOS continuing to give inputAccessoryView's a higher place in the view hierarchy than the UIKeyboard). Your UITextField or UITextView's inputAccessoryView must be set to an instance of it.

Next step is figure out how to make frame rotations nicer. Currently it stutters tad bit as UIKeyboardWillChangeFrameNotification is sent after rotation completes. UIKeyboard changes the width of the inputAccessoryView during rotation, so overriding setFrame: setting alpha to 0 and fading it back in UIKeyboardWillChangeFrameNotification is one approach.

Feel free to extend! This doesn't adapt to situations where you need your inputAccessoryView set to something else, however it is simple enough to convert it into an encapsulation view with proper adjustments for resulting frame changes.

@TheFinestArtist
Copy link

Awesome!! Thank You :)

@elbuild
Copy link

elbuild commented Oct 8, 2014

Well done

@chipp
Copy link

chipp commented Apr 15, 2015

Be careful: this solution isn't working on iOS8.

@ajay2c9
Copy link

ajay2c9 commented Apr 16, 2015

Hai This button is displayed button action is not working from iOS8.3

@pedroroliveira
Copy link

Not working on iOS 8.3

@ashispoddar
Copy link

Yes. Looks like a problem . Any fixes ?

@xcodebuild
Copy link

Won't work when use third-party input method

@xcodebuild
Copy link

On iOS 8.3,it displays great but can't touch.

@matt-curtis
Copy link
Author

Hello everyone - I wouldn't really recommend using this solution because it's unstable, but I'll look into getting this working. The best alternative is likely using a UIWindow, or manipulating pointInside:/hitTest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment