先看效果圖
問(wèn)題描述
在開發(fā)中經(jīng)常會(huì)遇到點(diǎn)擊輸入框激活鍵盤的時(shí)候口予,彈出的鍵盤導(dǎo)致輸入框被遮擋的現(xiàn)象兽泣。常用的解決方案有兩種:
1.鍵盤彈出的時(shí)候奶镶,將整個(gè)視圖上移褐耳。
2.鍵盤彈出的時(shí)候诈闺,懸浮輸入框到適當(dāng)位置。
第一種方案比較簡(jiǎn)單铃芦,常用的實(shí)現(xiàn)方式有三種:
- 給UITextField注冊(cè)監(jiān)聽事件:
UIControlEventEditingDidBegin
和UIControlEventEditingDidEnd
- 協(xié)議+代理:通過(guò)UITextField的代理方法來(lái)實(shí)現(xiàn)
- 用通知來(lái)實(shí)現(xiàn)
幾種方式分析
- 方法1:如果給
UITextField
注冊(cè)監(jiān)聽事件雅镊,監(jiān)聽正在編輯和編輯完畢,需要自己寫一個(gè)正在編輯和編輯完畢時(shí)候視圖位置改變的方法刃滓,如果有多個(gè)UITextField
仁烹,那么每個(gè)都要添加監(jiān)聽事件,很麻煩咧虎。
UITextField * usernameTextField = [[UITextField alloc]initWithFrame:CGRectMake(30, 30, 100, 30)];
// UITextField * passwordTextField = [[UITextField alloc]initWithFrame:CGRectMake(30, 160, 100, 30)];
// 添加監(jiān)聽事件
// 開始輸入
[usernameTextField addTarget:self action:@selector(textFieldEditBegin) forControlEvents:UIControlEventEditingDidBegin];
// 輸入結(jié)束
[usernameTextField addTarget:self action:@selector(textFieldEditEnd) forControlEvents:UIControlEventEditingDidEnd];
[self.view addSubview:usernameTextField];
- 方法二:通過(guò)
UITextField
的代理方法進(jìn)行設(shè)置卓缰,代理方法有:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField; // 文本框是否可以編輯
- (void)textFieldDidBeginEditing:(UITextField *)textField; // 開始編輯時(shí)調(diào)用
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField; // 文本是否結(jié)束編輯
- (void)textFieldDidEndEditing:(UITextField *)textField; // 結(jié)束編輯時(shí)調(diào)用
- (BOOL)textFieldShouldClear:(UITextField *)textField; // 是否可以清楚文本框的內(nèi)容
- (BOOL)textFieldShouldReturn:(UITextField *)textField; // 是否可以return
在代理方法
textFieldDidBeginEditing
和textFieldDidEndEditing
里面實(shí)現(xiàn)視圖的偏移方法3:用
UITextField
的通知UIKeyboardWillChangeFrameNotification
來(lái)實(shí)現(xiàn),只需要在監(jiān)聽通知砰诵,設(shè)置視圖偏移的動(dòng)畫
所以采用通知的形式來(lái)實(shí)現(xiàn)
完整代碼(重要)
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 監(jiān)聽通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
#pragma mark 鍵盤處理
- (void)keyboardWillChangeFrame:(NSNotification *)note{
// 取出鍵盤最終的frame
CGRect rect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// 取出鍵盤彈出需要花費(fèi)的時(shí)間
double duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改transform
[UIView animateWithDuration:duration animations:^{
CGFloat ty = [UIScreen mainScreen].bounds.size.height - rect.origin.y;
self.view.transform = CGAffineTransformMakeTranslation(0, - ty);
}];
}
/**
* 點(diǎn)擊屏幕的時(shí)候
*/
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.view endEditing:YES];
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
補(bǔ)充 - 關(guān)于通知
通知中心(NSNotificationCenter
)
- 每一個(gè)應(yīng)用程序都有一個(gè)通知中心(
NSNotificationCenter
)實(shí)例征唬,專門負(fù)責(zé)協(xié)助不同對(duì)象之間的消息通信。 - 任何一個(gè)對(duì)象都可以向通知中心發(fā)布通知(
NSNotification
),描述自己在在做什么胧砰。 -
其他感興趣的對(duì)象(
Observer
)可以申請(qǐng)?jiān)谀硞€(gè)特定通知發(fā)布時(shí)(或在某個(gè)特定的對(duì)象發(fā)布通知的時(shí)候)收到這個(gè)通知鳍鸵。
通知(NSNotificationCenter
)
- 通知包含的屬性:
- 通知名稱;
- 通知發(fā)布者;
- /額外信息(通知發(fā)布者傳遞給通知接收著的信息內(nèi)容).
- (NSString *)name; // 通知名稱
- (id)object; // 通知發(fā)布者
- (NSDictionary *)userInfo; //額外信息(通知發(fā)布者傳遞給通知接收著的信息內(nèi)容)
- 初始化通知對(duì)象的方法:
+ (instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject;
+ (instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
- (instancetype)init
發(fā)布通知
- (void)postNotification:(NSNotification *)notification;
發(fā)布一個(gè)notification
通知苇瓣,可在notification
對(duì)象中設(shè)置通知的名稱尉间、通知發(fā)布者、額外信息等
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
發(fā)布一個(gè)名稱為aName
的通知击罪,anObject
為這個(gè)通知的發(fā)布者
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
發(fā)布一個(gè)名稱為aName
的通知哲嘲,anObject
為這個(gè)通知的發(fā)布者,aUserInfo
為額外信息
監(jiān)聽通知
- 通知中心提供了方法來(lái)注冊(cè)一個(gè)監(jiān)聽通知的監(jiān)聽器(Observer)
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
observer
:監(jiān)聽器
aSelector
:接收通知后媳禁,回調(diào)監(jiān)聽器的這個(gè)放啊眠副,并且把通知對(duì)象當(dāng)參數(shù)傳入
aName
:通知名稱。如果為nil
竣稽,那么無(wú)論通知名稱是什么囱怕,監(jiān)聽器都能收到這個(gè)通知
anObject
:通知發(fā)布者。如果anObject
和aName
都為nil
毫别,監(jiān)聽器都能收到所有通知
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block ;
name
:通知名稱
obj
:通知發(fā)布者
block
:收到對(duì)應(yīng)的通知時(shí)娃弓,會(huì)回調(diào)這個(gè)block
queue
:決定了block
在哪個(gè)操作隊(duì)列中執(zhí)行,如果為nil
岛宦,默認(rèn)在當(dāng)前操作隊(duì)列中同步執(zhí)行台丛。
取消注冊(cè)通知監(jiān)聽器
通知中心不會(huì)保留(
retain
)監(jiān)聽器對(duì)象,在通知中心注冊(cè)過(guò)的對(duì)象砾肺,必須在該對(duì)象釋放前取消注冊(cè)挽霉。否則防嗡,當(dāng)相應(yīng)的通知再次出現(xiàn)時(shí),通知中心仍然會(huì)想該監(jiān)聽器發(fā)送消息侠坎。因?yàn)橄鄳?yīng)的監(jiān)聽器對(duì)象已經(jīng)釋放了蚁趁,所以可能會(huì)導(dǎo)致應(yīng)用崩潰。
- 通知中心提供了相應(yīng)的方法來(lái)取消注冊(cè)注冊(cè)監(jiān)聽器
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
鍵盤通知
-
UIKeyboardWillShowNotification
:鍵盤即將顯示 -
UIKeyboardDidShowNotification
:鍵盤已經(jīng)顯示完畢 -
UIKeyboardWillHideNotification
:鍵盤即將隱藏 -
UIKeyboardDidHideNotification
:鍵盤已經(jīng)隱藏完畢 -
UIKeyboardWillChangeFrameNotification
:鍵盤的位置尺寸即將發(fā)生改變 -
UIKeyboardDidChangeFrameNotification
:鍵盤的位置尺寸已經(jīng)發(fā)生改變