加在keywindow的視圖IQKeyboardManager無效問題

問題

近日在寫代碼時,有時候會寫全局的彈出框,我一般是直接把view貼在keywindow上,我發現在keywindow上的視圖中有輸入框的時候,IQKeyboardManager無法自動將這個輸入框彈起。

排查過程

要找這個問題還得看IQKeyboardManager源碼,我發現IQKeyboardManager是根據相應者鏈拿到所屬UIViewController,然后再做之后的操作,如果沒找到,之后的操作都白搭,因為我的view是直接創建好加在了keywindow上,沒有歸于某個UIViewController,所以就會出現無效的問題。

查找解決辦法

于是呼我去百度了一番,沒找到解決方案。
我又找到了IQKeyboardManager的github,查看issue,發現果然有人更我有同樣的困惑,并且開發者回復的是

This library don't support textFields that are directly added to a UIWindow, it must come throught UIViewController or one of it's subclass.

此庫不支持直接添加到uiwindow的文本字段,它必須通過uiviewcontroller或其子類之一。

issue截圖

WTF!開發者直接就說不適配加在keywindow上的輸入框。

解決辦法

萬萬沒想到,最終我還是解決了這個問題。
我模仿IQKeyboardManager,自己實現了一個支持keywindow的KeyboardManager,思路是這樣的:

  1. 創建一個單例。
  2. 重寫+ (void)load;方法,模仿IQKeyboardManager的拖進項目就生效功能。
  3. 監聽keyboard的彈出通知和UITextField的開始結束編輯通知。
  4. [主要操作]在開始編輯時用響應者鏈查找UIViewController如果有的話不處理,正常的還是交給IQKeyboardManager處理吧,畢竟人家是專業的。每找到一個UIView就做記錄,直到找到UIWindow,這時候我就記錄了uiview之前響應者鏈中的最后一個view,然后在鍵盤彈起時如果鍵盤高度高于textfield就將這個找到的view向上提起一部分,我用的是CGAffineTransform做的移動,因為方便恢復,恢復的話只需要把view的transform設置為CGAffineTransformIdentity就可以復原了。

上代碼

下面代碼是整個manager的.m代碼,只是實現了一個簡單的UITextfield上提功能

#import "WZZKeyboardManager.h"

WZZKeyboardManager * wzzKeyboardManager;

@interface WZZKeyboardManager ()

@property (assign, nonatomic) CGRect keyboardFrame;
@property (weak, nonatomic) UITextField * nowTF;

@end

@implementation WZZKeyboardManager

+ (void)load {
    [self shareInstance];
}

+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        wzzKeyboardManager = [[WZZKeyboardManager alloc] init];
        [wzzKeyboardManager setup];
    });
    return wzzKeyboardManager;
}

- (void)setup {
    [[NSNotificationCenter defaultCenter] addObserver:wzzKeyboardManager selector:@selector(KeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:wzzKeyboardManager selector:@selector(KeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:wzzKeyboardManager selector:@selector(TextFieldTextDidBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:wzzKeyboardManager selector:@selector(TextFieldTextDidEndEditing:) name:UITextFieldTextDidEndEditingNotification object:nil];
}

- (void)KeyboardWillShow:(NSNotification *)noti {
    NSValue * vv = noti.userInfo[UIKeyboardFrameEndUserInfoKey];
    self.keyboardFrame = [vv CGRectValue];
    UITextField * tf = self.nowTF;
    UIView * view = [self getViewOnWindow:tf];
    if (view) {
        CGRect fr = [tf.superview convertRect:tf.frame toView:[UIApplication sharedApplication].keyWindow];
        CGRect fr2 = [view.superview convertRect:view.frame toView:[UIApplication sharedApplication].keyWindow];
        CGFloat y = CGRectGetMaxY(fr);
        CGFloat y2 = self.keyboardFrame.origin.y;
        CGFloat y3 = y2-y-8;
        if (y3 < 0) {
            //tf提起
            [UIView animateWithDuration:0.25f animations:^{
                view.transform = CGAffineTransformMakeTranslation(fr2.origin.x, fr2.origin.y+y3);
            }];
        }
    }
}

- (void)KeyboardWillHide:(NSNotification *)noti {
//    NSLog(@"wzz%@", noti.userInfo);
}

- (void)TextFieldTextDidBeginEditing:(NSNotification *)noti {
    UITextField * tf = noti.object;
    self.nowTF = tf;
}

- (void)TextFieldTextDidEndEditing:(NSNotification *)noti {
    UITextField * tf = noti.object;
    UIView * view = [self getViewOnWindow:tf];
    if (view) {
        [UIView animateWithDuration:0.25f animations:^{
            view.transform = CGAffineTransformIdentity;
        }];
    }
}

- (UIView *)getViewOnWindow:(UIView *)view {
    UIResponder *nextResponder = view;
    
    while (![nextResponder isKindOfClass:[UIWindow class]]) {
nextResponder = [nextResponder nextResponder];

        if (!nextResponder) {
            return nil;
        }
        if ([nextResponder isKindOfClass:[UIViewController class]]) {
            return nil;
        }
        if (![nextResponder isKindOfClass:[UIWindow class]] && [nextResponder isKindOfClass:[UIView class]]) {
            view = (UIView *)nextResponder;
        }
    }
    
    return view;
}

@end

代碼地址

看到有些小伙伴可能看長篇大論有點煩,直接附一個代碼地址,我做了一個demo,WZZKeyboardManager文件夾直接拉到自己項目里就行
詳情請見:項目下載地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容