最近做項目時做了一個發送短信的界面,有點類似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調整到合适的位置,第二種取固定值的方式雖然也能達到效果但是不太穩妥,如果使用第三方輸入法或者系統鍵盤固定高度改變,都會出現問題。