10.28
实际使用中出现了很多意外的情况,并不像自己想像的那么简单,已经放弃这种方法的使用。建议使用:TPKeyboardAvoiding
一、历程
键盘遮挡这个问题在每个应用中几乎都会遇到。在解决这个问题的时候,大致经过了两个阶段:
<1> 人工配置,如果测试中发现被遮挡,就通过notification的方法把对视图做一下调整。 这个方法很低效,效果也一般。
<2> 我想到去找一下其他人的解决方案,发现基本有下面种方案:
(1) 和第一种一样,人式配置,然后学到的一点改进是可以不必添加UIKeyboardWillShowNotification
、UIKeyboardWillHideNotification
两个通知,而只要添加UIKeyboardWillChangeFrame
这个通知来调整视图。
(2) 通过自定义视图的方法来避免这种情况,即是这个git上项目的方法(star数挺高的,应该比较靠谱):TPKeyboardAvoiding,它给出的方案中给出了四个自定义的view:TPKeyboardAvoidingScrollView
,TPKeyboardAvoidingTableView
,TPKeyboardAvoidingTextField
,TPKeyboardAvoidingTextView
,只要继承自他的view,这个视图就会自动去避免遮挡视图。
这个方法我没有进行仔细的研究,因为我看了他的demo,感觉效果并不是很好,主要是因为自己一直有个想法,想用自己的方法来解决这个问题。所以对他的方法没有仔细研究,下面说说自己的想法和实现。
二、自己的实现
- 思路
我的思路其实想起来还是比较简单的.首先,现在大部分人开发的时候都会有一个
BaseViewController
来对一些常用的属性进行设置,所以,我就想通过判断firstResponder,即输入时的UItextField 或者 UITextView 会不会被键盘遮挡来调整viewcontroller的view的位置,如果被遮挡,就让其向上偏移,再加上其他一些细节的调整,应该就可以实现传说中的零代码实现避免键盘遮挡(呵呵).
- 实现
<1>首先中找到当前的firstResponder ,我是通知递归的方式来找到在当前viewcontroller的view上的第一响应者。另外有种比较简单的方式:
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
但是这个方法是私有方法,审核是通不过的。下面是递归的方法:
- (UIView *)findFirstResponderForView:(UIView *)view
{
if (view.isFirstResponder) {
return view;
}
if (view.subviews.count > 0) {
for (UIView *obj in view.subviews) {
UIView *firstResponder = [self findFirstResponderForView:obj];
if (firstResponder != nil) {
return firstResponder;
}
}
}
return nil;
}
<2> 然后是willShow的方法
- (void)keyboardWillShowHandle:(NSNotification *)notify
{
CGFloat kbHeight = [[notify.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;//获取键盘高度
double duration = [[notify.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];//键盘动画时长
UIView *firstResponder = [self findFirstResponderForView:self.view];//第一响应者
UIView *superView = firstResponder.superview;
CGPoint fixedOrigin = [superView convertPoint:firstResponder.frame.origin toView:self.view];
float viewBottom = fixedOrigin.y + firstResponder.height;
//若键盘没有遮挡住视图则不进行整个视图上移, 但是如果已经有偏移就把它移下来
// duration = duration * _delta/kbHeight;
[UIView animateWithDuration:duration animations:^{
self.view.top = 0.0; //self.view.top 是UIVIew 的扩展,即self.view.frame.origin.x
}];
if (viewBottom + kbHeight < self.view.bottom) return;
float offset = viewBottom + kbHeight - self.view.bottom + 30;
/*这里将self.view向上偏移用了三种方法
<1> 通过transform ,但是不行,整个view都不见了,偏移量也检查过是100多,不知道为什么.
<2> 通过self.view.alignmentRectInsets,还是不行,这个属性是只读的
<3> 通过frame来设置,目前来看没有什么问题
*/
//当直接切换(键盘没有落下)第二个响应者,刚偏移的量会叠加
if (self.view.top == 0) {
_delta = offset;
}else{
_delta = _delta - offset;
}
// duration = duration * _delta/kbHeight;
[UIView animateWithDuration:duration animations:^void(void){
self.view.top -= _delta;
}];
}
<3> willHide 的方法
- (void)keyboardWillHideHandle:(NSNotification *)notify
{
double duration = [[notify.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration animations:^void(void){
self.view.top += _delta;//_delta 是全局变量,方便回到原来位置,这里直接把self.view.top 设置为0也可以。
}];
_delta = 0.0;
}
<4> 上面是主要的代码:下面谈谈其他细节。
(1) 什么时候添加通知,什么时候移除通知
`通知会占用系统资源,所以为每个viewcontroller都添加这样的通知没有必要,所以我给BaseViewController添加了两个方法,一个添加通知,一个移除通知的方法。在某个viewcontroller需要避免遮挡的时候就给其添加通知,然后在-(void)dealloc方法中移除通知,其他情况对其没有影响。
三、总结
我觉得第二种方案也是一种比较好的方法,毕竟有那么多人用,问题可能都解决的差不多了。我这种方法我目前没发现什么问题,而且也比较方便。如果有什么问题,欢迎大家指正!
网友评论