最近做项目时做了一个发送短信的界面,有点类似QQ聊天。在输入文本时textField(或者UITextView,这里以UITextField为例)会随着键盘的弹起而改变位置,避免输入时textField被键盘挡住。我的做法是直接根据keyboard的固定高度来改变textField所在的view的frame,把view上移。在模拟器上经过各种测试发现没什么问题,但是用真机测试时发现当改变输入法时,keyboard的高度也会跟着改变,这个时候textField所在的view有可能依然会被遮住。尤其是iOS7的真机上,因为引入了中文九宫格输入法,导致keyboard的frame有好几种:
// 英文键盘高度 216.0f
// 中文键盘高度 252.0f
// 中文九宫格键盘未输入高度 184.f
// 中文九宫格输入键盘高度 251.5f
那么怎样根据键盘高度的变化来改变textField的位置以达到textField不被遮住的目的呢?
网上搜索之后好像没找到类似的方法,于是自己动手解决,经过试验大体总结出两种方法,都是用notification实现的,但是在实现方式上有些不同。
第一种方法:使用UIKeyboardDidChangeFrameNotification
#import "TestViewController.h"
#define IOS_VERSION ([[UIDevice currentDevice] systemVersion].floatValue)
@interface TestViewController ()<UITextFieldDelegate>{
CGFloat deltaY;
float duration; // 动画持续时间
CGFloat originalY; // TextField原来的纵坐标
}
@property (retain, nonatomic) UITextField *testField;
@property (assign, nonatomic) NSInteger keyboardPresentFlag; // 键盘弹起的标志,弹出=1,收起=0
@end
@implementation TestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_testField = [[UITextField alloc] initWithFrame:CGRectMake(60, 300, 200, 30)];
_testField.delegate = self;
_testField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:_testField];
// 获取textField初始位置的Y轴坐标,这里就是30
originalY = self.testField.frame.origin.y;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameDidChange:) name:UIKeyboardDidChangeFrameNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
CGSize keyboardSize = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// 获得弹出keyboard的动画时间,也可以手动赋值,如0.25f
duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
// 得到keyboard在当前controller的view中的Y轴坐标
CGFloat keyboardOriginY = self.view.frame.size.height - keyboardSize.height;
// textField下边到view顶点的距离减去keyboard的Y轴坐标就是textField要移动的距离,这里是刚好让textField完全显示出来,也可以再在deltaY的基础上再加上一定距离,如20f、30f等
deltaY = self.testField.frame.origin.y + self.testField.frame.size.height - keyboardOriginY;
// 当deltaY大于0时说明textField会被键盘遮住,需要上移
if (deltaY > 0) {
// 以动画的方式改变textField的frame
[UIView animateWithDuration:duration delay:0.f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.testField.frame = CGRectOffset(self.testField.frame, 0, -deltaY);
} completion:nil];
}
}
- (void)keyboardWillHide:(NSNotification *)notification {
// 键盘收起时将textField的位置还原
[UIView animateWithDuration:duration delay:0.f options:UIViewAnimationOptionCurveEaseIn animations:^{
CGRect frame = self.testField.frame;
frame.origin.y = originalY;
self.testField.frame = frame;
} completion:nil];
}
/**
* 键盘frame变化时执行的通知方法
* @note 键盘弹出,收起,改变输入法时这个方法都会执行
*/
- (void)keyboardFrameDidChange:(NSNotification *)notification {
CGSize keyboardSize = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
CGFloat keyboardOriginY = self.view.frame.size.height - keyboardSize.height;
deltaY = self.testField.frame.origin.y + self.testField.frame.size.height - keyboardOriginY;
// 键盘在弹出的情况下如果frame有变化就改变textField的位置
if (self.keyboardPresentFlag == 1) {
[UIView animateWithDuration:duration delay:0.f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.testField.frame = CGRectOffset(self.testField.frame, 0, -deltaY);
} completion:nil];
}
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.keyboardPresentFlag ++;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
self.keyboardPresentFlag --;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
@end
第二种方法,根据UITextInputMode来实现,改变输入法时键盘高度会发生变化,根据这个固定高度来实现,这种方法实际上有一定缺陷,如果是iOS8里第三方输入法,键盘高度有可能可以手动调整,这样就会出问题。我只用了iOS6的iPod touch 4测试了一下,以上面说到过的英文键盘高度和中文键盘高度为例。
#import "TestViewController.h"
#define IOS7 ([[UIDevice currentDevice] systemVersion].floatValue >= 7.0)
#define KEYBOARD_HEIGHT_EN 216
#define KEYBOARD_HEIGHT_ZH 252
@interface TestViewController ()<UITextFieldDelegate>{
CGFloat deltaY;
CGFloat originalY; // TextField原来的纵坐标
}
@property (retain, nonatomic) UITextField *testField;
@property (assign, nonatomic) NSInteger keyboardPresentFlag; // 键盘弹起的标志,弹出=1,收起=0
@property (copy, nonatomic) NSString *currentInputMode; // 当前输入法
@end
@implementation TestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_testField = [[UITextField alloc] initWithFrame:CGRectMake(60, 400, 200, 30)];
_testField.delegate = self;
_testField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:_testField];
// 获取textField初始位置的Y轴坐标,这里就是30
originalY = self.testField.frame.origin.y;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputModeDidChange:) name:UITextInputCurrentInputModeDidChangeNotification object:nil];
}
- (void)inputModeDidChange:(NSNotification *)notification {
if (IOS7) {
self.currentInputMode = self.testField.textInputMode.primaryLanguage;
} else {
self.currentInputMode = [[UITextInputMode currentInputMode] primaryLanguage];
}
NSLog(@"input mode: %@", self.currentInputMode);
[self getDeltaYByInputMode:self.currentInputMode];
[UIView animateWithDuration:0.25f animations:^{
self.testField.frame = CGRectOffset(self.testField.frame, 0, -deltaY);
}];
if (self.keyboardPresentFlag == 0) {
self.keyboardPresentFlag ++;
}
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[self getDeltaYByInputMode:self.currentInputMode];
if (deltaY > 0 && self.keyboardPresentFlag == 0) {
[UIView animateWithDuration:0.25f animations:^{
self.testField.frame = CGRectOffset(self.testField.frame, 0, -deltaY);
}];
self.keyboardPresentFlag ++;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
// 结束输入时将textField移回原处
if (self.keyboardPresentFlag == 1) {
[UIView animateWithDuration:0.25f animations:^{
CGRect frame = self.testField.frame;
frame.origin.y = originalY;
self.testField.frame = frame;
}];
self.keyboardPresentFlag --;
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
/**
* 根据当前输入法改变textField的位移
*/
- (void)getDeltaYByInputMode:(NSString *)inputMode {
if ([inputMode isEqualToString:@"zh-Hans"]) {
deltaY = self.testField.frame.origin.y + self.testField.frame.size.height - (self.view.frame.size.height - KEYBOARD_HEIGHT_ZH);
} else if ([inputMode isEqualToString:@"en-US"]) {
deltaY = self.testField.frame.origin.y + self.testField.frame.size.height - (self.view.frame.size.height - KEYBOARD_HEIGHT_EN);
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
@end
个人推荐第一种方法,这样不管键盘的frame怎样变化总能够把textField调整到合适的位置,第二种取固定值的方式虽然也能达到效果但是不太稳妥,如果使用第三方输入法或者系统键盘固定高度改变,都会出现问题。