iOS_鍵盤彈出視圖上移效果

先上效果圖

鍵盤.gif

消息中心(NSNotificationCenter)

實(shí)現(xiàn)視圖對鍵盤彈出的響應(yīng)主要利用的是通知機(jī)制。這里我們來了解下通知機(jī)制。

不同于代理用于一對一的傳遞消息,通知機(jī)制用于多對多的傳遞消息。這個(gè)信息的傳遞是通過消息中心(NSNotificationCenter),每一個(gè)應(yīng)用程序都有一個(gè)通知中心(NSNotificationCenter)實(shí)例,專門負(fù)責(zé)協(xié)助不同對象之間的消息通信。任何一個(gè)對象都可以向通知中心發(fā)布通知(NSNotification),描述自己在做什么。其他感興趣的對象(Observer)可以申請?jiān)谀硞€(gè)特定通知發(fā)布時(shí)(或在某個(gè)特定的對象發(fā)布通知時(shí))收到這個(gè)通知。這樣便能實(shí)現(xiàn)消息的傳遞。

消息中心(NSNotificationCenter)是唯一的(即單例),在創(chuàng)建時(shí)使用類方法 [NSNotificationCenter defaultCenter] 即可。
通知(NSNotification)
而發(fā)送的消息叫做通知(NSNotification),一個(gè)完整的通知一般包含3個(gè)屬性:
(NSString *)name; // 通知的名稱
(id)object; // 通知發(fā)布者(是誰要發(fā)布通知)
(NSDictionary *)userInfo; // 一些額外的信息(通知發(fā)布者傳遞給通知接收者的信息內(nèi)容)

初始化一個(gè)通知(NSNotification)對象有這些方法

+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;

+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)UserInfo;

- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;

發(fā)布通知

通知中心提供了相應(yīng)的方法來幫助發(fā)布通知

- (void)postNotification:(NSNotification *)notification;

//發(fā)布一個(gè)notification通知,可在notification對象中設(shè)置通知的名稱、通知發(fā)布者、額外信息等

- (void)postNotificationName:(NSString *)aName object:(id)anObject;
//發(fā)布一個(gè)名稱為aName的通知,anObject為這個(gè)通知的發(fā)布者

- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
//發(fā)布一個(gè)名稱為aName的通知,anObject為這個(gè)通知的發(fā)布者,aUserInfo為額外信息

監(jiān)聽通知

通知中心(NSNotificationCenter)提供了方法來注冊一個(gè)監(jiān)聽通知的監(jiān)聽器(Observer)

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;

其中:

observer:監(jiān)聽器,即誰要接收這個(gè)通知
aSelector:收到通知后,回調(diào)監(jiān)聽器的這個(gè)方法,并且把通知對象當(dāng)做參數(shù)傳入
aName:通知的名稱。如果為nil,那么無論通知的名稱是什么,監(jiān)聽器都能收到這個(gè)通知
anObject:通知發(fā)布者。如果為anObject和aName都為nil,監(jiān)聽器都收到所有的通知
值得注意的是,aSelector 指示的方法傳入的參數(shù)即是通知對象本身,我們可以通過通知對象的 userInfo; // 一些額外的信息(通知發(fā)布者傳遞給通知接收者的信息內(nèi)容)

初始化一個(gè)通知(NSNotification)對象有這些方法

+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;

+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)UserInfo;

- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;

取消注冊通知監(jiān)聽器

由于通知中心不會保留(retain)監(jiān)聽器對象,在通知中心注冊過的對象,必須在該對象釋放前取消注冊。否則,當(dāng)相應(yīng)的通知再次出現(xiàn)時(shí),通知中心仍然會向該監(jiān)聽器發(fā)送消息。因?yàn)橄鄳?yīng)的監(jiān)聽器對象已經(jīng)被釋放了,所以可能會導(dǎo)致應(yīng)用崩潰!!

通知中心提供了相應(yīng)的方法來取消注冊監(jiān)聽器
- (void)removeObserver:(id)observer;

- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

一般我們用第一種方法就可以了,可以(也需要)移除所有監(jiān)聽器對象注冊的監(jiān)聽。

一般在監(jiān)聽器銷毀之前取消注冊(如在監(jiān)聽器中加入下列代碼):

- (void)dealloc {
    //[super dealloc];  非ARC中需要調(diào)用此句
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

注意我們現(xiàn)在一般都是使用的 ARC 模式,所以不需要調(diào)用 [super dealloc] 以及 release 了。 delloc 方法在 ARC 依然保留的原因就是為了方便我們做取消監(jiān)聽注冊等情況。

常見的通知名稱

UIDevice 通知

UIDevice類提供了一個(gè)單例對象,它代表著設(shè)備,通過它可以獲得一些設(shè)備相關(guān)的信息,比如電池電量值(batteryLevel)、電池狀態(tài)(batteryState)、設(shè)備的類型(model,比如iPod、iPhone等)、設(shè)備的系統(tǒng)(systemVersion)
,通過[UIDevice currentDevice]可以獲取這個(gè)單例對象。

UIDevice對象會不間斷地發(fā)布一些通知,下列是UIDevice對象所發(fā)布通知的名稱常量:

UIDeviceOrientationDidChangeNotification // 設(shè)備旋轉(zhuǎn)
UIDeviceBatteryStateDidChangeNotification // 電池狀態(tài)改變
UIDeviceBatteryLevelDidChangeNotification // 電池電量改變
UIDeviceProximityStateDidChangeNotification // 近距離傳感器(比如設(shè)備貼近了使用者的臉部)

鍵盤通知

鍵盤狀態(tài)改變的時(shí)候,系統(tǒng)會發(fā)出一些特定的通知

UIKeyboardWillShowNotification // 鍵盤即將顯示
UIKeyboardDidShowNotification // 鍵盤顯示完畢
UIKeyboardWillHideNotification // 鍵盤即將隱藏
UIKeyboardDidHideNotification // 鍵盤隱藏完畢
UIKeyboardWillChangeFrameNotification // 鍵盤的位置尺寸即將發(fā)生改變
UIKeyboardDidChangeFrameNotification // 鍵盤的位置尺寸改變完畢

實(shí)現(xiàn)鍵盤彈出界面上移

首先需要注冊鍵盤監(jiān)聽的通知,在 viewDidLoad 方法中加入:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];

這樣表示任何對象發(fā)布的名為 UIKeyboardWillChangeFrameNotification 的通知都會被監(jiān)聽并且觸發(fā) self(即 ViewController)執(zhí)行 keyboardWillChangeFrame: 方法,這名字是自己定義的。

keyboardWillChangeFrame: 方法實(shí)現(xiàn)
下面來自定義

- (void)keyboardWillChangeFrame:(NSNotification *)notification;

的實(shí)現(xiàn)。注意發(fā)布通知會把通知對象本身當(dāng)作參數(shù)傳入,因此可以獲取 notification 的內(nèi)容。在方法中加入

NSLog(@"%@",notification);

運(yùn)行輸出 notification 的內(nèi)容。我這里用的是 iphone 8,iOS 11.1。
輸出內(nèi)容如下:

{name = UIKeyboardWillChangeFrameNotification; 
 userInfo = {
    UIKeyboardAnimationCurveUserInfoKey = 7;//鍵盤彈出的節(jié)奏、
    UIKeyboardAnimationDurationUserInfoKey = "0.25";//鍵盤彈出動畫執(zhí)行的時(shí)間
    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {375, 258}}";
    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {187.5, 796}";
    UIKeyboardCenterEndUserInfoKey = "NSPoint: {187.5, 538}";

//彈出鍵盤:
    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 667}, {375, 258}}";//鍵盤彈出前的frame
    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 409}, {375, 258}}";//鍵盤彈出后的frame,說明鍵盤的上移后y為409,高度為258.
    UIKeyboardIsLocalUserInfoKey = 1;
}}

這其中就以字典的形式存放了 鍵盤彈出的節(jié)奏、時(shí)間、鍵盤彈出前后的 frame 等等內(nèi)容。以此我們可以通過 notification 獲取鍵盤的 frame 的 y 值已確定視圖需要上移的高度。前面給出的 UIKeyboard…Key 就是字典需要的關(guān)鍵字了。

//1. 獲取鍵盤的 Y 值
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
//或者  keyboardFrame = [[notification.userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];

//注意從字典取出來的是對象,而 CGRect CGFloat 都是基本數(shù)據(jù)類型,一次需要轉(zhuǎn)換

CGFloat keyboardY = keyboardFrame.origin.y;

移動主視圖可以通過修改 self.view 的 frame、center、或者是 transform 屬性來進(jìn)行變形。這里以 transform 為例。主視圖上移或者下移時(shí)移動到的位置應(yīng)該是 keyboardY – self.view.frame.size.height ,即 主視圖原位置為 0 ,彈出時(shí)上移移動到 “-鍵盤的高度”。隱藏時(shí)下移移動到 0。代碼為:

self.view.transform = CGAffineTransformMakeTranslation(0, keyboardY - self.view.frame.size.height);

實(shí)現(xiàn)鍵盤隱藏界面下移
首先需要給 tableView 添加代理,設(shè)置代理對象為 ViewController,然后自定義方法:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    [self.view endEditing:YES];//結(jié)束編輯狀態(tài),即可以關(guān)閉鍵盤
}

界面上移的動畫效果
可以由此前的 notification 的 userInfo 內(nèi)容可以知道,鍵盤上移的動畫時(shí)間是 0.25s 。而 self.view 移動理論上是瞬時(shí)的(在我的 iOS 11.1 的動畫是和鍵盤同步的,但是在之前看的教程中 iOS 8 里系統(tǒng)中是瞬時(shí)的,我上網(wǎng)查了也沒找到原因,肯能是 apple 對此作了優(yōu)化,但是為了保險(xiǎn)起見還是手動設(shè)置界面上移的動畫效果),另外動畫移動的節(jié)奏也要與鍵盤一致。將代碼修改為:

double timeInterval = [[notification.userInfo objectForKey:@"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];

NSInteger animationOption = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];

[UIView animateWithDuration:timeInterval delay:0.0 options:animationOption << 16  animations:^{
    self.view.transform = CGAffineTransformMakeTranslation(0, keyboardY - self.view.frame.size.height);
} completion:nil];

至此鍵盤處理完成~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,750評論 1 375
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容