支付宝小程序iOS端input输入框键盘光标错位原理猜测与验证和实现(既:如何使用原生的输入框替代h5的输入框达到光标、清空按钮、字体、键盘等h5无法实现的样式定制)

运行官网下载的demo

查看页面结构可以看到就是一个单纯的webview:

然后点击”最大长度“这个输入框

发现光标错位

看一下页面结构

发现多了一个原生的输入框

<H5KeyboardField: 0x7fbe57985a00; baseClass = UITextField; frame = (131 138.5; 160 25); text = ''; opaque = NO; tag = 2; gestureRecognizers = <NSArray: 0x6000012c2b50>; layer = <CALayer: 0x600001a76fc0>>

而且还加载到了WKScrollView上面

看来蚂蚁的同学为了实现webview输入框调用原生键盘,原生光标,也是花了不少心思。

原理猜测:将原生输入框盖住h5输入框,并设置第一响应,输入的内容在传给h5,升降键盘再做高度的适配。

验证1: 先看看H5KeyboardField的坐标设置

#import "H5KeyboardField+YYY.h"
#import <objc/runtime.h>
@implementation H5KeyboardField (YYY)
+(void)load{
    {
        Method originalMethod = class_getInstanceMethod([self class], @selector(setFrame:));
        Method swizzledMethod = class_getInstanceMethod([self class], @selector(setFrameS:));
           method_exchangeImplementations(originalMethod, swizzledMethod);
        
    }
}
- (void)setFrameS:(CGRect)frame{

    [self setFrameS:frame];
}
@end

可以看到设置坐标的调用堆栈

看看这个方法-[H5Keyboard createNativeInputWithParam:idx:isFromReset:]

void * -[H5Keyboard createNativeInputWithParam:idx:isFromReset:](void * self, void * _cmd, void * arg2, unsigned long long arg3, bool arg4) {
    。。。
}

方法太长了,先结合之前的打印日志看一下

可以看到x,y,yt,w,h,这些坐标都是h5传过来的。

上面的方法只是取值展示。

看来问题原因是h5把坐标算错了。

解决办法:可以我们自己把正确的坐标传过来。

验证2: H5KeyboardField怎么加载到webview上的

可以看到

可以看到和上面的一样

这样的输入框在webview上下滑动的时候,是会跟着一起动的,达到了以假乱真的效果。

验证3: hopper一下H5KeyboardField看看

.h没啥

#import "UITextField.h"

@interface H5KeyboardField : UITextField
{
    _Bool _controlled;
    _Bool _canPaste;
    _Bool _textFieldShouldReturn;
    long long _cursorPosition;
    long long _selectionStart;
    long long _selectionEnd;
}

@property(nonatomic) _Bool canPaste; // @synthesize canPaste=_canPaste;
- (_Bool)canPerformAction:(SEL)arg1 withSender:(id)arg2;
@property(nonatomic) _Bool controlled; // @synthesize controlled=_controlled;
@property(nonatomic) long long cursorPosition; // @synthesize cursorPosition=_cursorPosition;
- (struct _NSRange)markedTextRangeInField;
@property(nonatomic) struct _NSRange selectedRange;
@property(nonatomic) long long selectionEnd; // @synthesize selectionEnd=_selectionEnd;
@property(nonatomic) long long selectionStart; // @synthesize selectionStart=_selectionStart;
- (void)setSelectedRangeWithPostion;
@property(nonatomic) _Bool textFieldShouldReturn; // @synthesize textFieldShouldReturn=_textFieldShouldReturn;

@end

.m看几个重要的方法

H5Keyboard重要的方法,打上断点确实走的这里

结束编辑:

void -[H5Keyboard textFieldDidEndEditing:](void * self, void * _cmd, void * arg2) {
    r14 = self;
    var_-104 = [arg2 retain];
    rax = [r14 activeTextField];
    rax = [rax retain];
    rcx = [rax tag];
    var_-112 = [[NSString stringWithFormat:@"window.AlipayH5Keyboad && AlipayH5Keyboad.restoreInputElement(%ld);", rcx] retain];
    [rax release];
    rax = [r14 activeTextField];
    rax = [rax retain];
    [rax setHidden:0x1];
    [rax release];
    rax = [r14 currentActiveInputData];
    rax = [rax retain];
    r15 = [[rax objectForKey:@"position"] retain];
    [rax release];
    var_-120 = r15;
    if ((r15 == 0x0) || ([r15 isEqualToString:@"fixed"] == 0x0)) goto loc_100c4404c;

loc_100c43f94:
    rax = [r14 curWebview];
    rax = [rax retain];
    r15 = rax;
    rax = [rax scrollView];
    rax = [rax retain];
    [rax contentOffset];
    var_-48 = intrinsic_movsd(var_-48, xmm1);
    [rax release];
    [r15 release];
    rax = [r14 curWebview];
    rax = [rax retain];
    rbx = rax;
    rax = [rax scrollView];
    rax = [rax retain];
    r15 = rax;
    var_-64 = rbx;
    if (rax != 0x0) {
            rdx = @selector(contentInset);
            r13 = rdx;
            objc_msgSend_stret(&var_-320, r15, rdx);
            xmm0 = intrinsic_movsd(xmm0, *(&var_-320 + 0x10));
    }
    else {
            rdx = @selector(contentInset);
            r13 = rdx;
            intrinsic_movaps(var_-304, 0x0);
            intrinsic_movaps(var_-320, 0x0);
            xmm0 = 0x0;
    }
    var_-56 = intrinsic_movsd(var_-56, xmm0);
    rax = [r14 curWebview];
    rax = [rax retain];
    r12 = rax;
    rax = [rax scrollView];
    rax = [rax retain];
    rbx = rax;
    if (rax != 0x0) {
            rdx = r13;
            objc_msgSend_stret(&var_-224, rbx, rdx);
            xmm1 = intrinsic_movsd(xmm1, var_-224);
    }
    else {
            xmm0 = 0x0;
            intrinsic_movaps(var_-208, xmm0);
            intrinsic_movaps(var_-224, xmm0);
            xmm1 = 0x0;
    }
    xmm0 = intrinsic_movsd(xmm0, var_-56);
    NSLog(@"curWebview.scrollView.contentInset:::%f %f", rsi, rdx);
    [rbx release];
    [r12 release];
    [r15 release];
    [var_-64 release];
    if ([r14 isModifyContentInset] != 0x0) {
            rax = [r14 curWebview];
            rax = [rax retain];
            rbx = [[rax scrollView] retain];
            xmm0 = intrinsic_xorpd(xmm0, xmm0);
            intrinsic_movapd(var_-176, xmm0);
            stack[2048] = intrinsic_movapd(var_-192, xmm0);
            [rbx setContentInset:rdx, rcx];
            [rbx release];
            [rax release];
    }
    if ([r14 isResizeWebViewFrameHeight] != 0x0) {
            rsi = @selector(curWebview);
            rax = _objc_msgSend(r14, rsi);
            rax = [rax retain];
            rbx = rax;
            if (rax != 0x0) {
                    rdx = @selector(frame);
                    rsi = rbx;
                    objc_msgSend_stret(&var_-96, rsi, rdx);
            }
            else {
                    xmm0 = intrinsic_xorpd(xmm0, xmm0);
                    var_-80 = intrinsic_movapd(var_-80, xmm0);
                    var_-96 = intrinsic_movapd(var_-96, xmm0);
            }
            [rbx release];
            [r14 currentWebViewSourceHeight];
            intrinsic_movsd(var_-72, xmm0);
            rbx = [[r14 curWebview] retain];
            stack[2048] = var_-96;
            [rbx setFrame:rdx, rcx];
            [rbx release];
    }
    [r14 mMoveDistance];
    xmm1 = intrinsic_movsd(xmm1, var_-48);
    xmm1 = intrinsic_subsd(xmm1, xmm0);
    xmm0 = intrinsic_xorpd(xmm0, xmm0);
    var_-48 = intrinsic_movsd(var_-48, intrinsic_maxsd(xmm1, xmm0));
    rax = [r14 curWebview];
    rax = [rax retain];
    r15 = rax;
    rbx = [[rax scrollView] retain];
    rsi = @selector(setContentOffset:);
    intrinsic_xorpd(xmm0, xmm0);
    intrinsic_movsd(xmm1, var_-48);
    _objc_msgSend(rax, rsi);
    goto loc_100c444c6;

loc_100c444c6:
    [rbx release];
    [r15 release];
    goto loc_100c444d9;

loc_100c444d9:
    var_-48 = @selector(curWebview);
    rbx = [[r14 curWebview] retain];
    r13 = _objc_msgSend;
    r15 = [rbx isKindOfClass:[WKWebView class]];
    [rbx release];
    rbx = var_-104;
    if (r15 != 0x0) {
            rax = (r13)(@class(NSString), @selector(stringWithFormat:), @"%@%@", @"_keyboardWi", @"llHide:");
            rax = [rax retain];
            r15 = rax;
            r12 = NSSelectorFromString(rax);
            rax = (r13)(r14, var_-48, @"%@%@");
            rax = [rax retain];
            r13 = (r13)(rax, @selector(respondsToSelector:), r12);
            [rax release];
            if (r13 != 0x0) {
                    r13 = [_objc_msgSend(r14, var_-48) retain];
                    rax = [r14 lastNotification];
                    rax = [rax retain];
                    [r13 performSelector:r12 withObject:rax];
                    [rax release];
                    [r13 release];
            }
            [r15 release];
            rbx = var_-104;
            r13 = _objc_msgSend;
    }
    (r13)(r14, @selector(setCurrentActiveInputData:), 0x0);
    (r13)(r14, @selector(setShouldIgnorKeyBoardNotify:), 0x0);
    objc_initWeak(&var_-96, r14);
    (r13)(r14, @selector(_updateValueByText:), rbx);
    r13 = rbx;
    rbx = [_objc_msgSend(r14, var_-48) retain];
    *(&var_-328 + 0xffffffffffffffe0) = __NSConcreteStackBlock;
    *(&var_-328 + 0xffffffffffffffe8) = 0xc2000000;
    *(&var_-328 + 0xfffffffffffffff0) = ___37-[H5Keyboard textFieldDidEndEditing:]_block_invoke;
    *(&var_-328 + 0xfffffffffffffff8) = ___block_descriptor_40_e8_32w_e20_v24?08"NSError"16l;
    objc_copyWeak(&var_-328, &var_-96);
    [rbx evaluateJavaScript:var_-112 completionHandler:&var_-360];
    [rbx release];
    [r14 setIsModifyContentInset:0x0];
    [r14 setIsResizeWebViewFrameHeight:0x0];
    [r14 setLastNotification:rdx];
    objc_destroyWeak(&var_-328);
    objc_destroyWeak(&var_-96);
    [var_-120 release];
    [var_-112 release];
    [r13 release];
    return;

loc_100c4404c:
    rax = [r14 curWebview];
    rax = [rax retain];
    var_-64 = rax;
    rax = [rax scrollView];
    rax = [rax retain];
    r12 = rax;
    rdx = @selector(contentInset);
    if (rax != 0x0) {
            rdx = @selector(contentInset);
            objc_msgSend_stret(&var_-288, r12, rdx);
            xmm0 = intrinsic_movsd(xmm0, *(&var_-288 + 0x10));
    }
    else {
            intrinsic_movaps(var_-272, 0x0);
            intrinsic_movaps(var_-288, 0x0);
            xmm0 = 0x0;
    }
    var_-56 = intrinsic_movsd(var_-56, xmm0);
    var_-48 = r14;
    rax = [r14 curWebview];
    rax = [rax retain];
    r14 = rax;
    rax = [rax scrollView];
    rax = [rax retain];
    r13 = rax;
    if (rax != 0x0) {
            [&var_-256 contentInset];
            intrinsic_movsd(xmm1, var_-256);
    }
    else {
            xmm0 = 0x0;
            intrinsic_movaps(var_-240, xmm0);
            intrinsic_movaps(var_-256, xmm0);
    }
    xmm0 = intrinsic_movsd(xmm0, var_-56);
    NSLog(@"curWebview.scrollView.contentInset:::%f %f", rsi, rdx);
    [r13 release];
    [r14 release];
    [r12 release];
    [var_-64 release];
    r14 = var_-48;
    if ([r14 isModifyContentInset] == 0x0) goto loc_100c444d9;

loc_100c4418e:
    rax = [r14 curWebview];
    rax = [rax retain];
    r15 = rax;
    rbx = [[rax scrollView] retain];
    xmm0 = intrinsic_xorpd(xmm0, xmm0);
    intrinsic_movapd(var_-144, xmm0);
    var_-160 = intrinsic_movapd(var_-160, xmm0);
    rsi = @selector(setContentInset:);
    stack[2048] = var_-160;
    _objc_msgSend(rbx, rsi, rdx, rcx);
    goto loc_100c444c6;
}

function ___37-[H5Keyboard textFieldDidEndEditing:]_block_invoke {
    rbx = rdi;
    rax = [rdx retain];
    r12 = rax;
    if (rax != 0x0) {
            NSLog(@"[H5Keyboard Error] %@", r12);
    }
    rbx = rbx + 0x20;
    rax = objc_loadWeakRetained(rbx);
    r15 = rax;
    if ([rax hasClickRetun] != 0x0) {
            rax = objc_loadWeakRetained(rbx);
            var_-48 = r12;
            r12 = rax;
            rax = [rax activeTextField];
            rax = [rax retain];
            r14 = [rax returnKeyType] != 0x0 ? 0x1 : 0x0;
            [rax release];
            rdi = r12;
            r12 = var_-48;
            [rdi release];
    }
    else {
            r14 = 0x0;
    }
    [r15 release];
    r15 = objc_loadWeakRetained(rbx);
    rbx = [@(0x0) retain];
    if (r14 != 0x0) {
            stack[2048] = 0x0;
            rdx = @"blur";
            r8 = @"r";
    }
    else {
            stack[2048] = 0x0;
            rdx = @"blur";
            r8 = @"";
    }
    [r15 _dispatchEventWithElement:rdx keyCode:rbx strKey:r8 marked:0x0 range:stack[2048]];
    [rbx release];
    [r15 release];
    rax = [r12 release];
    return rax;
}

是否应该结束编辑:

bool -[H5Keyboard textFieldShouldEndEditing:](void * self, void * _cmd, void * arg2) {
    rax = [arg2 retain];
    r14 = rax;
    [rax resignFirstResponder];
    objc_initWeak(&var_-48, self);
    rax = [r14 text];
    rax = [rax retain];
    r15 = [[rax stringByReplacingOccurrencesOfString:@"x'" withString:@"x"] retain];
    [rax release];
    rax = [self currentActiveInputData];
    rax = [rax retain];
    r12 = [[rax objectForKey:@"value"] retain];
    [rax release];
    if ([r12 isEqualToString:r15] == 0x0) {
            rax = objc_loadWeakRetained(&var_-48);
            var_-64 = rax;
            r13 = [[rax curWebview] retain];
            rax = [NSString stringWithFormat:@"window.AlipayH5Keyboad && AlipayH5Keyboad.dispatchEventWithElement(window._currentInput,'%@',0)", @"change"];
            rax = [rax retain];
            [r13 evaluateJavaScript:rax completionHandler:^ {/* block implemented at ___40-[H5Keyboard textFieldShouldEndEditing:]_block_invoke */ } }];
            [rax release];
            [r13 release];
            [var_-64 release];
            r14 = r14;
            r15 = r15;
            r12 = r12;
    }
    [r12 release];
    [r15 release];
    objc_destroyWeak(&var_-48);
    [r14 release];
    return 0x1;
}

void ___40-[H5Keyboard textFieldShouldEndEditing:]_block_invoke(void * _block, void * arg1, struct NSError * arg2) {
    return;
}

文字变化

bool -[H5Keyboard textField:shouldChangeCharactersInRange:replacementString:](void * self, void * _cmd, void * arg2, struct _NSRange arg3, void * arg4) {
    memcpy(&rcx, &arg3, 0x8);
    memcpy(&r8, &arg3 + 0x8, 0x8);
    var_-48 = r8;
    var_-88 = rcx;
    r15 = self;
    var_-72 = [arg2 retain];
    r13 = [r9 retain];
    rbx = _objc_msgSend;
    r12 = [@(0x0) retain];
    if ([r13 length] == 0x0) goto loc_100c43ab2;

loc_100c43a5b:
    if ([r13 isEqualToString:@"."] == 0x0) goto loc_100c43ae9;

loc_100c43a7d:
    rbx = [@(0xbe) retain];
    [r12 release];
    r12 = rbx;
    rbx = _objc_msgSend;
    goto loc_100c43b2b;

loc_100c43b2b:
    var_-56 = r13;
    (rbx)(r15, @selector(setLastTapKeyCode:), r12);
    (rbx)(r15, @selector(setLastTapKey:), r13);
    rcx = r12;
    r8 = r13;
    (rbx)(r15, @selector(_dispatchEventWithElement:keyCode:strKey:marked:range:), @"keydown", rcx, r8, 0x0, stack[2048]);
    rsp = (rsp - 0x10) + 0x10;
    rax = (rbx)(r15, @selector(activeTextField), @"keydown");
    rax = [rax retain];
    r13 = rbx;
    r14 = (r13)(rax, @selector(controlled), @"keydown");
    [rax release];
    var_-64 = r15;
    r15 = 0x1;
    if (r14 != 0x0) {
            rax = (r13)(var_-72, @selector(text), @"keydown", rcx, r8);
            rax = [rax retain];
            r15 = rax;
            var_-80 = rax;
            rax = (r13)(rax, @selector(stringByReplacingCharactersInRange:withString:), var_-88, var_-48, var_-56);
            rax = [rax retain];
            (r13)(var_-64, @selector(_updateValue:), rax, var_-48, var_-56);
            CMP((r13)(r15, @selector(length), rdx, rcx, var_-56), (r13)(rax, @selector(length), rdx, rcx, var_-56));
            r15 = 0x0;
            (r13)(var_-64, @selector(_dispatchEventWithElement:keyCode:strKey:marked:range:cursor:), @"input", r12, var_-56, 0x0, stack[2048]);
            rsp = (rsp - 0x10) + 0x10;
            r12 = r12;
            [rax release];
            [var_-80 release];
    }
    [r12 release];
    [var_-56 release];
    [var_-72 release];
    rax = r15;
    return rax;

loc_100c43ae9:
    r14 = _objc_msgSend;
    rax = [r13 characterAtIndex:0x0];
    rax = [NSNumber numberWithUnsignedChar:rax & 0xff];
    rax = [rax retain];
    goto loc_100c43b19;

loc_100c43b19:
    [r12 release];
    r12 = rax;
    rbx = r14;
    goto loc_100c43b2b;

loc_100c43ab2:
    if ([r13 length] != 0x0) goto loc_100c43b2b;

loc_100c43ac3:
    rax = @(0x2e);
    rax = [rax retain];
    r14 = _objc_msgSend;
    goto loc_100c43b19;
}

点击return按钮

bool -[H5Keyboard textFieldShouldReturn:](void * self, void * _cmd, void * arg2) {
    r15 = self;
    var_-48 = [arg2 retain];
    rax = @(0xd);
    rax = [rax retain];
    r12 = 0x0;
    [r15 _dispatchEventWithElement:@"keydown" keyCode:rax strKey:@"" marked:0x0 range:r12];
    [rax release];
    rax = @(0xd);
    rax = [rax retain];
    stack[2048] = r12;
    rcx = rax;
    [r15 _dispatchEventWithElement:@"keyup" keyCode:rcx strKey:@"" marked:0x0 range:stack[2048]];
    rdi = rax;
    rbx = var_-48;
    [rdi release];
    if ([rbx textFieldShouldReturn] != 0x0) {
            [r15 setHasClickRetun:0x1, rcx, @""];
            [rbx resignFirstResponder];
            r12 = 0x1;
    }
    [rbx release];
    rax = r12;
    return rax;
}

----------------------------------------------分割--------------------------------------------

支付宝小程序还不能在本地加载,就用加载本地html试一下,实现一下.

1.准备:经测试纯原生的html input输入框 在页面最下端 + 纯原生的uiwebview或wkwebview 页面会自动上移,切换平滑,点击其他区域收键盘,点击键盘return无反应,点工具栏完成收回。键盘工具栏可正常去掉,不去掉则上下切换顺滑。

2.实现代码

原理:html的input点击的时候,获取坐标然后调用原生方法创建一个文本框,并设为第一响应。文本框加载到webview的subviews[0],盖住h5的输入框,并且可以在webview滑动的时候跟着移动.

H5完整代码


<!DOCTYPE html>
<html>
    <head>
        <!--解决输入框特别小的问题-->
        <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>

        <title>H5向Native通信</title>
        
        <style type="text/css">
        /*去除input默认样式*/
        input {-webkit-appearance:none; }
        </style>
        
    </head>
    <body>
        <script>
        //获取元素的纵坐标
        function getTop(e){
            var offset=e.offsetTop;
            if(e.offsetParent!=null) offset+=getTop(e.offsetParent);
            return offset;
        }
        //获取元素的横坐标
        function getLeft(e){
            var offset=e.offsetLeft;
            if(e.offsetParent!=null) offset+=getLeft(e.offsetParent);
            return offset;
        }
        //原生调用:输入改变后改变h5文字内容
        function changeTextById(vid,text){
            var oTxt = document.getElementById(vid);
            oTxt.value = text;
        }
        //onclick事件
        function onclickActionYYY(vid){
            var oTxt = document.getElementById(vid);
            alert('测试1');
            window.webkit.messageHandlers.useNativeTextFiled.postMessage(
                {'id':vid,
                'text':oTxt.value,
                'top':getTop(oTxt),
                'left':getLeft(oTxt),
                'width':'暂时不用',
                'height':'暂时不用'
                }
                );
            alert('测试2');

        }
        //解决第一次弹出中文键盘,输入框被遮挡问题
        function layoutIntoView(){
            const input = document.getElementsByTagName('input')[0];
            setTimeout(() => {
                input.scrollIntoViewIfNeeded();
            }, 100);
        }
        //监听h5键盘的return事件
        document.onkeydown =cdk;
        function cdk(){
            if(event.keyCode ==13){
                alert('点击了return');
            }
        }
        </script>
        <div id="main" width = "100%" height = "100%">


        <input type="text" id="txt1" value="abcde" onclick="onclickActionYYY(this.id)"
            oninput="onclickActionYYY(this.id)"
            onfocus="layoutIntoView()">
            
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
            
        <input type="text" id="txt2" value="abcde" onclick="onclickActionYYY(this.id)"
            oninput="onclickActionYYY(this.id)"
            onfocus="layoutIntoView()">
            
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
        <input type="text">
            
        <input type="text" id="txt3" value="abcde" onclick="onclickActionYYY(this.id)"
            oninput="onclickActionYYY(this.id)">
            
        <input type="text" id="txt4" value="abcde" onclick="onclickActionYYY(this.id)"
            oninput="onclickActionYYY(this.id)">
            
        <input type="text" id="txt5" value="abcde" onclick="onclickActionYYY(this.id)"
            oninput="onclickActionYYY(this.id)">
            
        <input type="text" id="txt6" value="abcde" onclick="onclickActionYYY(this.id)"
            oninput="onclickActionYYY(this.id)">
            
        </div>
    </body>
</html>

iOS使用WKWebView关键代码

创建WKWebView

        //设置网页的配置文件
        WKWebViewConfiguration * Configuration = [[WKWebViewConfiguration alloc]init];
        //允许视频播放
        if (IOS9_OR_LATER) {
            Configuration.allowsAirPlayForMediaPlayback = YES;
        }
        // 允许在线播放
        Configuration.allowsInlineMediaPlayback = YES;
        // 允许可以与网页交互,选择视图
        Configuration.selectionGranularity = YES;
        // web内容处理池
        Configuration.processPool = [[WKProcessPool alloc] init];
        //自定义配置,一般用于 js调用oc方法(OC拦截URL中的数据做自定义操作)
        WKUserContentController * UserContentController = [[WKUserContentController alloc]init];
        //添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
        [UserContentController addScriptMessageHandler:self name:@"useNativeTextFiled"];
        
        // 是否支持记忆读取
        Configuration.suppressesIncrementalRendering = NO;
        // 允许用户更改网页的设置
        Configuration.userContentController = UserContentController;

        _myWKWebView = [[YYYWkWebView alloc]initWithFrame:CGRectMake(0, webViewFrameY, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height) configuration:Configuration];
        [self.view addSubview:_myWKWebView];
        
        _myWKWebView.allowsBackForwardNavigationGestures = YES;
        _myWKWebView.UIDelegate = self;
        _myWKWebView.navigationDelegate = self;
        _myWKWebView.contentMode = UIViewContentModeScaleAspectFit;
        _myWKWebView.autoresizingMask=(UIViewAutoresizingFlexibleHeight |UIViewAutoresizingFlexibleWidth);
        
        [self hideWKWebviewKeyboardShortcutBar:_myWKWebView];

WKWebView加载本地html的代码

    NSString *htmlPath = [[NSBundle mainBundle] pathForResource:html ofType:@"html"];
    [_myWKWebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:htmlPath isDirectory:NO]]];

WKWebView要实现的方法注意事项

// 类似 UIWebView 的 -webView: shouldStartLoadWithRequest: navigationType:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;{
     
    注意对file://不要拦截

}

H5调用原生的方法

- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
    
    NSLog(@"%@",message.body);
    NSLog(@"%@",message.frameInfo);
    NSLog(@"%@",message.name);

    if([message.name isEqualToString:@"useNativeTextFiled"]){
        
        NSDictionary *dic = message.body;
        float top = [dic[@"top"] floatValue];
        float left = [dic[@"left"] floatValue];
        float height = [dic[@"height"] floatValue];
        float width = [dic[@"width"] floatValue];
        height = 22;
        width = 135;

        textActiveId = [dic[@"id"] stringValue];

        NSString *text = [dic[@"text"] stringValue];
        UIView *wkScrollView = _myWKWebView.subviews[0];//WKScrollView
        
        if (myUITextField == nil) {
             myUITextField = [[YYYTextField alloc] init];
             [myUITextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
            [myUITextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventValueChanged];
        }
        myUITextField.frame =CGRectMake(left  , top  , width, height);
        myUITextField.backgroundColor = [UIColor redColor];
        [wkScrollView addSubview:myUITextField];
        myUITextField.hidden = NO;
        myUITextField.delegate = self;
        myUITextField.text = text;

        [myUITextField becomeFirstResponder];

        
        //解决点空白区域收键盘后,光标聚焦在h5输入框
        NSString *javaScriptStr = [NSString stringWithFormat:@"var input3 =document.getElementById('%@');alert(input3); input3.blur();",textActiveId];
        
        [_myWKWebView evaluateJavaScript:javaScriptStr completionHandler:^(NSString *str , NSError * _Nullable error) {
            NSLog(@"");
        }];
   }

文字改动的方法

- (void)textFieldDidChange:(UITextField *)textField; {
    //改变文字同步到h5输入框
    NSString *javaScriptStr = [NSString stringWithFormat:@"changeTextById('%@','%@');",textActiveId,textField.text];
     [_myWKWebView evaluateJavaScript:javaScriptStr completionHandler:^(NSString *ss , NSError * _Nullable error) {
         NSLog(@"");
     }];
}

去掉纯原始WkWebview的Done工具栏就不贴了,网上找的

点击return的方法

- (BOOL)textFieldShouldReturn:(UITextField *)textField;{
    
    //为了调用失去第一响应的方法
    [_myWKWebView.scrollView endEditing:YES];
    
    //解决键盘收回了,焦点却留在了h5输入框的问题
    NSString *javaScriptStr = [NSString stringWithFormat:@"var input3 =document.getElementById('%@');alert(input3); input3.blur();",textActiveId];
    [_myWKWebView evaluateJavaScript:javaScriptStr completionHandler:^(NSString *ss , NSError * _Nullable error) {
        NSLog(@"");
    }];
     
    return YES;
}

输入框代码:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface YYYTextField : UITextField

@end





#import "YYYTextField.h"
@implementation YYYTextField
- (UIView *)inputAccessoryView{
    //textField加到wkscrollow时,解决键盘工具栏时有时无的问题(去掉键盘的工具栏)
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 0.01)];
    view.backgroundColor = [UIColor redColor];
    return view;
}

//移除输入框(点击空白地方 或者 return)
- (BOOL)resignFirstResponder{
    //解决输入框remove掉,键盘还在的问题
    BOOL isr = [super resignFirstResponder];
    [self removeFromSuperview];
    return YES;
}

5.遇到的问题(都已解决)

(1)这个是webview自带的问题:第一点击次输入框如果是中文键盘将会被遮挡

     解决办法:

const input = document.getElementsByTagName('input')[0];
setTimeout(() => {
    input.scrollIntoViewIfNeeded();
}, 100);

(2)问题:点击webview其他空白地方收键盘没问题,点击键盘的return键盘却无法回收


(3)问题:原生键盘和输入框都展示,但第一响应是后面的h5输入框。

(4)问题:从一个输入框第一响应的时候点击另一个输入框的时候页面盖住了输入框

(5)问题:原生的textFiled也带工具栏,用隐藏键盘工具栏的交换方法有时不管用,工具栏上下可由原生输入框切换其他h5输入框

(6)优点:这个方法不需要自己计算键盘高度,滚动视图的偏移

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值