UI: 监听和响应键盘通知

问题:

通过text field或text view等UI组件,可以让用户通过键盘输入文本内容,不过当键盘显示在屏幕上时,会遮挡住部分UI,如何避免遮挡发生呢? 

 

1.iOS 发布了很多关于屏幕上键盘的通知。下面列出了这些通知及相关介绍: 
 
UIKeyboardWillShowNotification
  当键盘即将要显示的时候将会发出这个通知。这个通知包含了用户信息字典,里面包括了键盘的各种信息,键盘将以动画的形式显示在屏幕上。
UIKeyboardDidShowNotification
  当键盘已经显示在屏幕上时将发出这个通知。

UIKeyboardWillHideNotification

  当键盘将要从屏幕上移除时将会发出此通知。通知里包含了用户信息字典,里面包括了各种关于键盘信息的详细信息,当键盘隐藏时的动画,动画的持续时间,等等。

UIKeyboardDidHideNotification

  当键盘完全隐藏后将发出此通知。

2.用户信息字典里面包含了一些有效的键值。下面是字典中的一些 key: 

   UIKeyboardAnimationCurveUserInfoKey

这个关键字的值指明了显示和隐藏键盘使用的动画类型。这个关键字包含了一个 NSNumber 类型的值,此类型包含了一个 NSUInteger 类型无符号整数 

  UIKeyboardAnimationDurationUserInfoKey

这个键值指明了键盘显示或隐藏的动画所用的时间。这个键包含一个 NSNumber 类型的 值,此类包含一个 double 类型的双字节值。 

 

  UIKeyboardFrameBeginUserInfoKey

   这个键值指明了键盘在动画之前的 frame。假如键盘将要显示时,在显示之前将这个 frame 传递给这个动画。假如键盘已经显示了并即将要隐藏时,这个 frame 将会传递给这个 隐藏动画,在键盘消失之前。这个键包含了一个 CGRect 类型的值。 

 
  UIKeyboardFrameEndUserInfoKey

  这个键值指明了动画完成后的键盘 frame。假如键盘即将要显示时,这个 frame 将会在 键盘完全显示后传递给键盘。。假如键盘已经完全显示,而且将要隐藏时,在完全隐藏后这 个 frame 将会传递给键盘。这个键值包含了一个 CGRect 类型的值。 

3.例子:

  创建一个tableView,每个cell加上textField

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    _myTableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
    _myTableView.delegate = self;
    _myTableView.dataSource = self;
    _myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;//随着父视图的改变自动调整自己的高度和宽度
    [self.view addSubview:_myTableView];
    
}
#pragma mark - textFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
    [textField resignFirstResponder];
    return YES;
}

#pragma mark - tableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 100;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//初始化cell的一般过程
    UITableViewCell *result = nil;
    static NSString *cellIdentifier = @"cellIdentifier";
    result = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (result == nil) {
        result = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
        result.selectionStyle = UITableViewCellSelectionStyleNone;
    }
    result.textLabel.text = [NSString stringWithFormat:@"Cell %ld",(long)indexPath.row];
    CGRect accessoryRect = CGRectMake(0, 0, 150, 31);
    UITextField *accessory = [[UITextField alloc]initWithFrame:accessoryRect];
    accessory.borderStyle = UITextBorderStyleRoundedRect;
    accessory.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    accessory.placeholder = @"Enter Text";
    accessory.delegate = self;
    result.accessoryView = accessory;//@property (nonatomic, retain) UIView *accessoryView; 
    
    return result;
}

现在点击textField会出现键盘并遮挡了tableView的有效显示,看不到最后5-6行

我们现在要做的就是监听 UIKeyboardWillShowNotification 和UIKeyboardWillHideNotification 的通知,并根据情况调整 table view 的 content inset: 
#pragma mark - 创建通知中心并添加监听者,移除通知监听者
- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleKeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [center addObserver:self selector:@selector(handleKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}
既然已经开始监听键盘通知,我们就来实现一下􏰀交给 NSNotificationCenter 的观察着 方法吧。handleKeyboardWillShow:方法负责设置 table view 的 content inset: 

 

#pragma mark - 接到键盘通知后的事件响应
- (void)handleKeyboardWillShow:(NSNotification *)paramNotification{
    //1.通过 UIKeyboardWillShowNotification 通知的用户信息字典,
    //我们获取键盘的不同动画属性,包括动画时间animationDuration,动画 animationCurve 以及键盘的frame:keyboardEndRect。
    NSDictionary *userInfo = [paramNotification userInfo];
    NSNumber *animationCurveObject = [userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey];
    NSNumber *animationDurationObject = [userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey];
    NSValue *keyboardEndRectObject = [userInfo valueForKey:UIKeyboardFrameEndUserInfoKey];
    NSUInteger animationCurve = [animationCurveObject integerValue];
    double animationDuration = [animationDurationObject doubleValue];
    CGRect keyboardEndRect = [keyboardEndRectObject CGRectValue];
    //2.然后开始一个动画 block,用来修改 table view 的 content inset。在这之前,我们需要知道 键盘遮挡了多少区域。
    [UIView beginAnimations:@"changeTableViewContentInset" context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:(UIViewAnimationCurve)animationCurve];
    UIWindow *window = [[[UIApplication sharedApplication]delegate]window];
        //使用CGRectIntersection函数,获得我们的windows frame和键盘frame之间的交集。通 过这个技巧,我们可以计算出键盘动画过后将要遮挡多少区域,这样我们就可以对 table view 的 bottom content inset 进行设置了。
    CGRect intersectionOfKeyboardRectAndWindowRect = CGRectIntersection(window.frame, keyboardEndRect);//返回两个rect的交集rect
    CGFloat bottomInset = intersectionOfKeyboardRectAndWindowRect.size.height;
            //UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)距离父视图上左下右边界的距离.设置一下动画 block 的属性,包括路径、持续时间并提交动画。如前所述,这个动画 block 将修改 table view 的 content inset,以使被弹出键盘遮挡的区域也可以看到。
    _myTableView.contentInset = UIEdgeInsetsMake(0, 0, bottomInset, 0);
    NSIndexPath *indexPathOfOwnerCell = nil;
    /* Also, make sure the selected textfield is visible on the screen
     让被选择的textField在屏幕上能看到*/
    NSInteger numberOfCells = [_myTableView.dataSource tableView:_myTableView numberOfRowsInSection:0];
    /* So let's go through all the cells and find their accessory text fields.
     Once we have the reference to those text fields, we can see which one of them is the first responder (has the keyboard) and we will make a call to the table view to make sure that, after the keyboard is displayed,that specific cell is NOT obstructed by the keyboard
     */
    for (NSInteger counter = 0; counter<numberOfCells; counter++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:counter inSection:0];
        UITableViewCell *cell = [_myTableView cellForRowAtIndexPath:indexPath];
        UITextField *textField = (UITextField *)cell.accessoryView;
        if ([textField isKindOfClass:[UITextField class]]==NO) {
            continue;
        }
        if ([textField isFirstResponder]) {
            //如果textField已经被选中,将其indexPath存储
            indexPathOfOwnerCell = indexPath;
            break;
        }
    }
    [UIView commitAnimations];
    if (indexPathOfOwnerCell != nil) {
        //将tableView自动滚到指定位置
        [_myTableView scrollToRowAtIndexPath:indexPathOfOwnerCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
    }
}

- (void)handleKeyboardWillHide:(NSNotification *)paramNotification{
    //1、看看table view的content inset是否已经改变了。如果没有改变,我们不需要做任何 处理。只当做我们收到了一个错误的通知,或者是另外一个视图控制器引发的通知。
    if (UIEdgeInsetsEqualToEdgeInsets(_myTableView.contentInset, UIEdgeInsetsZero)) {
        ///* Our table view's content inset is intact(完整) so no need to reset it */
        return;
    }
    //2、然后使用 UIKeyboardWillHidNotification 通知的用户信息字典,获得键盘动画的持续 时间和路径,通过这些数据,我们创建一个动画 block。
    NSDictionary *userInfo = [paramNotification userInfo];
    NSUInteger animationCurve  = [[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey]integerValue];
    double animationDuration = [[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey]doubleValue];

    //3、我们将 table view 的 content inset 设置成 UIEdgeInsetsZero 并呈􏰀交这个动画。
    [UIView beginAnimations:@"changeTableViewContentInset" context:nil];
    [UIView setAnimationCurve:(UIViewAnimationCurve)animationCurve];
    [UIView setAnimationDuration:animationDuration];
    _myTableView.contentInset = UIEdgeInsetsZero;
    [UIView commitAnimations];
}

iOS7 8中键盘通知会自动根据设备的方向来量取键盘的高度

 

 

posted @ 2014-10-17 15:21  safiri  阅读(521)  评论(0编辑  收藏  举报