IQKeyboardManager是一個(gè)自動(dòng)處理鍵盤(pán)彈出、隱藏的三方庫(kù),使用非常方便,只需要將庫(kù)引入工程即可使用,不需要做任何多余的設(shè)置,我們?cè)陧?xiàng)目中也不需要再單獨(dú)去監(jiān)聽(tīng)鍵盤(pán)的willShow和willHide了。

IQKeyboardManager發(fā)展到今天已經(jīng)很成熟了,常見(jiàn)的Bug基本已經(jīng)解決了。我們今天就簡(jiǎn)單的記錄下IQKeyboardManager的使用和實(shí)現(xiàn)原理。
IQKeyboardManager屬性含義記錄
首先我們先簡(jiǎn)單記錄下常用的屬性設(shè)置含義,方便以后查詢(xún)
// 設(shè)置鍵盤(pán)自動(dòng)彈起
[self setEnable:YES];
// 設(shè)置鍵盤(pán)和輸入框之間的距離
[self setKeyboardDistanceFromTextField:10.0];
// 當(dāng)鍵盤(pán)彈起時(shí),點(diǎn)擊背景,鍵盤(pán)就會(huì)收回
[self setShouldResignOnTouchOutside:YES];
// 是否添加鍵盤(pán)上的toolBar
[self setEnableAutoToolbar:YES];
// 是否在toolBar顯示輸入框中的placeHoder
[self setShouldShowTextFieldPlaceholder:YES];
// 有多個(gè)輸入框時(shí),可以通過(guò)點(diǎn)擊Toolbar 上的“前一個(gè)”“后一個(gè)”按鈕來(lái)實(shí)現(xiàn)移動(dòng)到不同的輸入框
[self setToolbarManageBehaviour:IQAutoToolbarBySubviews];
// 設(shè)置toolBar上的顏色文字顏色是否用戶(hù)自定義
[self setShouldToolbarUsesTextFieldTintColor:NO];
如果在整個(gè)項(xiàng)目中引入了IQKeyboardManager,而某一個(gè)UIViewController內(nèi)不使用IQKeyboardManager,可以如下設(shè)置:
- (void) viewWillAppear: (BOOL)animated
{
//打開(kāi)鍵盤(pán)事件相應(yīng)
[IQKeyboardManager sharedManager].enable = NO;
}
- (void) viewWillDisappear: (BOOL)animated
{
//關(guān)閉鍵盤(pán)事件相應(yīng)
[IQKeyboardManager sharedManager].enable = YES;
}
這樣我們就可以在這個(gè)UIViewController中自己添加代碼監(jiān)聽(tīng)系統(tǒng)的鍵盤(pán)通知。
如果在某個(gè)頁(yè)面,有多個(gè)TextField,而只有某一個(gè)TextField不需要IQ鍵盤(pán)上面的toolBar時(shí),只需要設(shè)置下其inputAccessoryView
// 如果需要針對(duì)某個(gè)textField,不需要鍵盤(pán)上的toolBar 可以單獨(dú)設(shè)置
textField.inputAccessoryView = [[UIView alloc] init];
IQKeyboardManager實(shí)現(xiàn)原理
我們先觀察下IQKeyboardManager的類(lèi)文件,就會(huì)發(fā)現(xiàn)核心代碼都在IQKeyboardManager.m這個(gè)類(lèi)中,其他的類(lèi)文件,基本是輔助類(lèi)。
在IQKeyboardManager.m的init方法中,我們看到它添加了鍵盤(pán)的彈出和消失通知,并且注冊(cè)了textField和textView的開(kāi)始編輯和結(jié)束編輯的通知
// 鍵盤(pán)將要彈出的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
// 鍵盤(pán)將要隱藏的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
// UITextField開(kāi)始編輯和結(jié)束編輯的通知
[self registerTextFieldViewClass:[UITextField class]
didBeginEditingNotificationName:UITextFieldTextDidBeginEditingNotification didEndEditingNotificationName:UITextFieldTextDidEndEditingNotification];
// UITextView開(kāi)始編輯和結(jié)束編輯的通知
[self registerTextFieldViewClass:[UITextView class] didBeginEditingNotificationName:UITextViewTextDidBeginEditingNotification didEndEditingNotificationName:UITextViewTextDidEndEditingNotification];
正是添加了這4個(gè)方法,IQKeyboardManager監(jiān)聽(tīng)到了一系列通知,并能在-(void)textFieldViewDidBeginEditing:(NSNotification*)notification方法中記錄下是哪個(gè)控件要開(kāi)始編輯,并需要鍵盤(pán)彈起
通過(guò)記錄下這個(gè)_textFieldView,IQKeyboardManager知道了輸入源原來(lái)的坐標(biāo)值,PlaceHolder等值并一一記錄下來(lái),方便在鍵盤(pán)發(fā)出willShow通知時(shí)去調(diào)整控件的坐標(biāo)。
也正是由于知道了這個(gè)輸入源_textFieldView,IQKeyboardManager才能在-(void)keyboardWillShow:(NSNotification*)aNotification方法中,
- _textFieldView 直接被添加到UIViewController中
借用響應(yīng)鏈原理的具體應(yīng)用[_textFieldView viewController]方法找到_textFieldView被添加進(jìn)了哪個(gè)UIViewController中
// 確定_textFieldView的下一個(gè)UIResponder是否為UIViewController,如果是UIViewController類(lèi),則返回
-(UIViewController*)viewController
{
UIResponder *nextResponder = self;
do
{
nextResponder = [nextResponder nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
return (UIViewController*)nextResponder;
} while (nextResponder != nil);
return nil;
}
并通過(guò)topMostController確定該UIViewController是否是真的為_(kāi)textFieldView的所在的VC 。
-(UIViewController *)topMostController
{
NSMutableArray *controllersHierarchy = [[NSMutableArray alloc] init];
UIViewController *topController = self.window.rootViewController;
if (topController)
{
[controllersHierarchy addObject:topController];
}
while ([topController presentedViewController])
{
topController = [topController presentedViewController];
[controllersHierarchy addObject:topController];
}
UIResponder *matchController = [self viewController];
while (matchController != nil && [controllersHierarchy containsObject:matchController] == NO)
{
do
{
matchController = [matchController nextResponder];
} while (matchController != nil && [matchController isKindOfClass:[UIViewController class]] == NO);
}
return (UIViewController*)matchController;
}
如果確定了_textFieldView 只是直接被添加到UIViewController上,則rootViewRect記錄下該UIViewController原始坐標(biāo),如果有需要?jiǎng)t改變r(jià)ootViewRect的坐標(biāo)值,并在鍵盤(pán)消失時(shí)恢復(fù)該UIViewController到原始坐標(biāo)。
{
// +Positive or zero.
if (move>=0)
{
rootViewRect.origin.y -= move;
// From now prevent keyboard manager to slide up the rootView to more than keyboard height. (Bug ID: #93)
if (_preventShowingBottomBlankSpace == YES)
{
rootViewRect.origin.y = MAX(rootViewRect.origin.y, MIN(0, -kbSize.height+keyboardDistanceFromTextField));
}
[self showLog:@"Moving Upward"];
// Setting adjusted rootViewRect
[self setRootViewFrame:rootViewRect];
_movedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y);
}
// -Negative
else
{
CGFloat disturbDistance = CGRectGetMinY(rootViewRect)-CGRectGetMinY(_topViewBeginRect);
// disturbDistance Negative = frame disturbed. Pull Request #3
// disturbDistance positive = frame not disturbed.
if(disturbDistance<0)
{
rootViewRect.origin.y -= MAX(move, disturbDistance);
[self showLog:@"Moving Downward"];
// Setting adjusted rootViewRect
[self setRootViewFrame:rootViewRect];
_movedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y);
}
}
}
如果確定了_textFieldView 只是直接被添加到UIScrollView上,則
UIScrollView superView = (UIScrollView)[_textFieldView superviewOfClassType:[UIScrollView class]];記錄該UIScrollView為_(kāi)lastScrollView
else if(superScrollView)
{
_lastScrollView = superScrollView;
_startingContentInsets = superScrollView.contentInset;
_startingContentOffset = superScrollView.contentOffset;
_startingScrollIndicatorInsets = superScrollView.scrollIndicatorInsets;
}
利用UIScrollView的偏移量去主動(dòng)滑動(dòng)
superScrollView.contentOffset = CGPointMake(superScrollView.contentOffset.x, shouldOffsetY);
在鍵盤(pán)將要消失時(shí),由于我們已經(jīng)存儲(chǔ)的_lastScrollView就是添加_textFieldView的那個(gè)UIScrollView,所以就能恢復(fù)原始位置了,這樣就實(shí)現(xiàn)了_textFieldView隨鍵盤(pán)的彈出和消失而自動(dòng)調(diào)整了。
這里只是簡(jiǎn)單的分析了下輸入控件是UITextField,其被添加到UIScrollView 和 UIViewController中的場(chǎng)景,其他場(chǎng)景實(shí)現(xiàn)原理基本類(lèi)似,就不在寫(xiě)了。
如果你喜歡我的創(chuàng)作,請(qǐng)點(diǎn)贊! 你的贊是我繼續(xù)創(chuàng)作的動(dòng)力,謝謝!如果不喜歡,請(qǐng)留言,謝謝!