IOS開發中的鍵盤遮擋處理

1、鍵盤遮擋的場景分類ios

1. 開始頁面錄入。輸入控件在屏幕的下部,鍵盤出現後遮擋輸入控件
2. 切換焦點。新輸入框被當前鍵盤部分遮擋,可點擊
3. 切換輸入法。
4. 屏幕旋轉。屏幕高度發生變化,原未被遮擋輸入框旋轉後被遮擋
 
 
2、UI需上移的距離計算
計算控件底部與鍵盤終點頂部的距離,調整閥值自定。一般選擇輸入控件最近的UIViewController->view做爲同一參照

            NSDictionary *userInfo = [notification userInfo];app

            NSValue* aValue        = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];iphone

            CGRect keyboardRect = [aValue CGRectValue];async

            keyboardRect        = [self.view convertRect:keyboardRect fromView:nil];ide

            CGFloat keyboardTop = keyboardRect.origin.y;函數

 

            CGFloat margin = 20;//文本框距鍵盤頂邊最小距離測試

            

            CGRect textFieldFrame = [self.viewconvertRect:_textFieldCall4Adjust.framefromView: _textFieldCall4Adjust.superview];動畫

            CGFloat textFieldBottom = textFieldFrame.origin.y + textFieldFrame.size.height;ui

 

            CGFloat delta = textFieldBottom  + margin - keyboardTop;atom

 

3、獲取鍵盤的動畫時間和時間函數
UIKeyboardAnimationCurveUserInfoKey
UIKeyboardAnimationDurationUserInfoKey
 

[UIView setAnimationCurve:[[[notif userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

        [UIView animateWithDuration:[[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^(){

 

4、UI上移調整方式
1.調整最上層UIViewController->view的frame. 最上層VC定義與轉屏控制中的最上層VC相同,如UITableViewController,UINavigationControlller
 
2. 輸入控件在UIScrollView或其子類,調整contentSizeH + contentOffsetY
3. 輸入控件在UIScrollView或其子類,調整contentInsetB + contentOffsetY
 
注意:1.  單獨調整contetntOffset以偏移視圖的狀態是不可靠的,頁面手動滑動會彈回contentSize內。
         2. 有時單獨調整contentInset可以觸發contentOffset的變化,如更改contentInsetT時視圖上移, offset增長。
               有文章說,單獨設置contentInsetB會使contentOffset自動增長,然而Demo測試中未呈現該結果。
          3.  鍵盤隱藏下移時,單獨設置contentSize或contentInset, 恢復原contentSize或置contentInsetB爲0便可
 
5、相關係統通知的觸發
監聽通知:

UIKeyboardWillShowNotification, UIKeyboardDidShowNotification, UIKeyboardWillHideNotification, UIKeyboardDidHideNotification, 

UIKeyboardWillChangeFrameNotification, UIKeyboardDidChangeFrameNotification, UIApplicationWillChangeStatusBarOrientationNotification, UIApplicationDidChangeStatusBarOrientationNotification

鍵盤使用系統默認全鍵盤之中文,英文,數字加符號
 
  iOS7 iOS8 iOS9
1. 新得到焦點

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 
2.失去焦點

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

3.切換焦點 無(新舊鍵盤相同或等高) 無(新舊鍵盤相同或等高) 無(新舊鍵盤相同或等高)
4.更換英文至中文輸入法,並輸入中文字符使得鍵盤增高

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

(開啓輸入預測增長一個循環)

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

 

(切換時無,即時開閉輸入預測代入一次循環)

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

5.更換中文(有多一行)至英文

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

(開啓輸入預測增長一個循環)

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

 

(切換時無,即時開閉輸入預測代入一次循環)

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

6.轉屏

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIApplicationWillChangeStatusBarOrientationNotification

UIApplicationDidChangeStatusBarOrientationNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

 

(willShow的時候鍵盤爲舊轉向frame)

  didShow時鍵盤爲新轉向frame

UIApplicationWillChangeStatusBarOrientationNotification

UIApplicationDidChangeStatusBarOrientationNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

(輸入預測開啓無關)

UIApplicationWillChangeStatusBarOrientationNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIApplicationDidChangeStatusBarOrientationNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillShowNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidShowNotification

7.滑動隱藏鍵盤

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

 

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

UIKeyboardWillChangeFrameNotification

UIKeyboardWillHideNotification

 

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

UIKeyboardDidChangeFrameNotification

UIKeyboardDidHideNotification

結論:1. willShow, didShow, willHide,didHide覆蓋了全部通知存在Case

           2. willShow,willHide爲最佳同步鍵盤動畫時機。但爲適配iOS67屏. 選擇willShow, didShow, willHide三個鍵盤通知做鍵盤適應事件處理入口
           3. 切換焦點需另做處理獲取事件處理時機
           4. 轉屏時,willShow, didShow, willHide事件,如將動畫合併提交,可能會不符合預想

           5. 其它:人爲觸發endEditing 和 keyboardDismissMode設爲UIScrollViewKeyboardDismissModeOnDrag的差別,人爲觸發時能夠將retain的輸入控件釋放, UIScrollViewKeyboardDismissModeOnDrag時,不便於獲取置nil時機。
             iOS8,9 UIScrollViewKeyboardDismissModeOnDrag時,會觸發兩次keyboardXXXHide通知    
 
6、處理方案
 
1. 獲取切換焦點時機
a. VC保持每次新獲取焦點的輸入控件。輸入控件將要開始編輯時,根據新輸入控件和舊有實例判斷是否需調整鍵盤。
 
SuperClass:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {

    // 在子類中應調用該方法

    if (_textFieldFocusing && _textFieldFocusing.isFirstResponder && _textFieldFocusing != textField ) {

        // 處理切換輸入焦點不會發出鍵盤SHOW通知的問題

        _textFieldFocusing = textField;

        [self p_handleKeyboardShow:nil];

    } else {

        _textFieldFocusing = textField;

    }

    return YES;

}

SubClass:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{

    if (textField.tag == TagCouldNotEdit) {

        return NO;

    }

 

    return [super textFieldShouldBeginEditing:textField];;

 

}

 

   b.切換焦點時,沒法獲取鍵盤frame,所以需保存最近一次鍵盤通知

 

    notification = notification?:oldKeyBoardNoti;

    if (!notification) {

        return;

    }

 

    oldKeyBoardNoti = notification;

 

2. 鍵盤處理事件串行化處理

   每次動畫提交同步至上次動畫結束。

 

    dispatch_semaphore_t semaphore;

 

    dispatch_queue_t queue;

。。。。

    semaphore = dispatch_semaphore_create(1);

 

    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 

- (void) keyboardDidShow:(NSNotification *) notif {

    if (!self.textFieldFocusing || ![self.textFieldFocusingisFirstResponder]) {

        return;

    }

    // 轉屏支持

    // iphone7, ios4轉屏時只走一遍hide->show,並且willShow時鍵盤尺寸異常

    [self p_handleKeyboardShow:notif];

}

 

-(void)keyboardWillShow:(NSNotification *)notification {

    if (!self.textFieldFocusing || ![self.textFieldFocusingisFirstResponder]) {

        return;

    }

    [self p_handleKeyboardShow:notification];

}

- (void) p_handleKeyboardShow:(NSNotification *)notification {

    

    notification = notification?:oldKeyBoardNoti;

    if (!notification) {

        return;

    }

    oldKeyBoardNoti = notification;

    

    // 第一次鍵盤出現,且未獲取tableView的contentSize時,獲取tableview的size

    // 最多隻執行一次

    if (_idealTableViewSizeHeight == 0) {

        _idealTableViewSizeHeight = self.tableView.contentSize.height;

    }

    

    // 動畫塊

    void (^changeContentOffset)() = ^(){

        [UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

        [UIView animateWithDuration:([[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]?:0.2) animations:^(){

            // @IMPORTANT 每次block動畫任務被執行時,從新計算移動距離

            // 需操做的環境參數

            NSDictionary *userInfo = [notification userInfo];

            NSValue* aValue        = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

            CGRect keyboardRect = [aValue CGRectValue];

            keyboardRect        = [self.view convertRect:keyboardRect fromView:nil];

            CGFloat keyboardTop = keyboardRect.origin.y;

            CGFloat margin = 20;//文本框距鍵盤頂邊最小距離

            

            CGRect textFieldFrame = [self.viewconvertRect:_textFieldFocusing.framefromView:_textFieldFocusing.superview];

            CGFloat textFieldBottom = textFieldFrame.origin.y + textFieldFrame.size.height;

            CGFloat delta = textFieldBottom  + margin - keyboardTop;

            

            CGFloat contentSizeHeight = MAX( self.tableView.frame.size.height, self.tableView.contentSize.height);

            [self.tableViewsetContentSize:CGSizeMake(self.view.frame.size.width, contentSizeHeight + delta)];

            [self.tableView setContentOffset: CGPointMake(0, delta + oldTableViewOffset.y) animated:NO];

        } completion:^(BOOL finished) {

            dispatch_async( queue, ^{

                dispatch_semaphore_signal(semaphore);

            });

        }];

    };

    

    // 串行動畫控制

    dispatch_async(queue, ^{

        

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        

        // 需操做的環境參數

        NSDictionary *userInfo = [notification userInfo];

        NSValue* aValue        = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

        CGRect keyboardRect = [aValue CGRectValue];

        keyboardRect        = [self.view convertRect:keyboardRect fromView:nil];

        CGFloat keyboardTop = keyboardRect.origin.y;

        CGFloat margin = 20;//文本框距鍵盤頂邊最小距離

        

        CGRect textFieldFrame = [self.viewconvertRect:_textFieldFocusing.framefromView:_textFieldFocusing.superview];

        CGFloat textFieldBottom = textFieldFrame.origin.y + textFieldFrame.size.height;

        

        if (   !self.textFieldFocusing

            || ![self.textFieldFocusing isFirstResponder]

            || textFieldBottom  + margin  <= keyboardTop

            || fabs(textFieldBottom  + margin - keyboardTop) < 0.1) {

            // 不須要執行動畫

            dispatch_semaphore_signal(semaphore);

        } else {

            oldTableViewOffset = self.tableView.contentOffset;

            dispatch_async(dispatch_get_main_queue(), ^{

                changeContentOffset();

            });

        }

    });

}

 

- (void) keyboardWillHide:(NSNotification *) notif {

    // 經過drag隱藏時,會兩次發出鍵盤隱藏通知,但對UI目前沒有影響,不作緣由查找

    // 動畫塊

    void (^changeContentOffset)() = ^(){

        self.tableView.bounces = NO;

        [UIView setAnimationCurve:[[[notif userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

        [UIView animateWithDuration:[[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^(){

            [self.tableViewsetContentSize:CGSizeMake(self.view.frame.size.width,_idealTableViewSizeHeight)];

        } completion:^(BOOL finished) {

            self.tableView.bounces = YES;

            dispatch_async(queue, ^{

                dispatch_semaphore_signal(semaphore);

            });

        }] ;

    };

    // 串行動畫控制

    dispatch_async( queue, ^{

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        if (self.tableView.contentSize.height <_idealTableViewSizeHeight

            || fabs(self.tableView.contentSize.height - _idealTableViewSizeHeight)<0.1) {

            // 不須要執行動畫

            dispatch_semaphore_signal(semaphore);

        } else {

            dispatch_async(dispatch_get_main_queue(), ^{

                changeContentOffset();

            });

        }

    });  

 

}

 

7、自定義鍵盤

1.iputView

注意,當inputView的ViewController自定義,inputView的相關響應方法寫在ViewController時,使用三方鍵盤可能發生異常

禁用三方鍵盤方法

- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(NSString *)extensionPointIdentifier

{

    if ([extensionPointIdentifier isEqualToString: UIApplicationKeyboardExtensionPointIdentifier])

    {

        return NO;

    }

    return YES;

 

}

 

2.UIInputViewController

UIResponder的inputViewController爲readOnly,須要強制改成readWrite

@interface TextField:UITextField

 

@property (nonatomic, readwrite, strong) UIInputViewController*inputViewController;

 

@end

 

@implementation TextField

 

 

@end

 

3.Application Extension

先新建Application,而後新建customed Keyboard Target, XCode將生成基礎代碼。開發時不能用Interface Builder, 必須所有代碼手寫

相關文章
相關標籤/搜索