请稍侯

iOS问题汇总之iOS应用 - Web Hybrid混合页面开发关键点

08 September 2020

iOS问题汇总之iOS应用 - Web Hybrid混合页面开发关键点

Web Hybrid关键点

HybridWeb混合页面

视图结构

safeAreaView(UIView)
    |——searchBar(UIView)
    |——scrollView(UIScrollView)
        |——contentView(UIView)
            |——bannerView(UIView)
            |——mainWebView(WKWebView)

需要根据document.body.offsetHeight动态更新WebView的高度,以保证只有最外层的Scroll有效

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"contentSize"] && self.canKVOSetScrollViewHeight) {
        [self evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable scrollHeight, NSError *_Nullable error) {
            CGFloat height = [scrollHeight floatValue];

            if (self.webContentHeight != height && height > 1) {
                self.webContentHeight = height;
                [self.mainWebView mas_updateConstraints:^(MASConstraintMaker *make) {
                    make.height.mas_greaterThanOrEqualTo(self.webContentHeight);
                }];
                [self.safeAreaView layoutIfNeeded];
            }
        }];
    }
}

VirtualView虚拟视图+JSCore关键点

VirtualView

  • CALayer的属性包装成虚拟视图结点;图层渲染前创建的过程,就是虚拟Node结点合并的过程;
  • KVO实现对Layout、颜色等属性变化的更新;
  • 核心类VVViewContainer继承自UIView,所有视图组合的图层的承载容器;
  • 自定属性结点可组合自定义UIView;

VirtualViewDelegate

- (void)virtualViewClickedWithAction:(NSString *)action andValue:(NSString *)value;

把一个类做成可字典方式取值与设值,实现两个方法即可:

- (JSValue *)objectForKeyedSubscript:(id)key ;
- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key ;

JSCore与JSContext

从UIWebView获得JSContext实例

self.webViewJSContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//注册native方法
self.webViewJSContext[@"pnu_log"] = ^(NSString *text) {
    PPDNativeLog(@"[PPDNative] pnu_log:%@", text);
    //        [[[UIAlertView alloc] initWithTitle:nil message:text delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
};

直接生成JSContext实例

JSContext *jsContext = [[JSContext alloc] init];
[jsContext evaluateScript:jsText withSourceURL:[NSURL fileURLWithPath:jsPath]];

采用JSContext实现JS调用原生,使用案例

注册native方法

self.webViewJSContext[@"native_request"] = ^(id value,JSValue *callback) {
    strongify(self)
    //把value构造成jsReuest
    JSRequest *jsRequest = [self jsRequestWithValue:value]; 
    if(!jsRequest){
        return;
    }
    jsRequest.successCallback = callback;
    jsRequest.errorCallback = callback;
    //拆解jsRequest,构造原生的网络请求
    [self requestWithJSRequest:jsRequest];
};

JS端调用

function nativeRequest(request, responseCallback) {
    var request = {
	    "url": "http://baidu.com/xxxx",
	    "method": "POST",
	    "params": {"name":"David"}
    }
    native_request(JSON.stringify(request),
        function (responseString) {
            responseCallback(JSON.parse(responseString))
        }
    )
}

其他

同步方法获得iOS设备权限

请求相机权限

//请求相机权限
+ (BOOL)checkCameraAccess {
    BOOL isPhotoSupport = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
    if(!isPhotoSupport){
        [LWBaseViewController showHUDWithMessage:NSLocalizedString(@"Not Surpport Camera", nil)];
        return NO;
    }

    __block BOOL isSuccess = NO;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            //非主线程
            if (granted) {
                isSuccess = YES;
                dispatch_semaphore_signal(semaphore);
            } else {
                dispatch_semaphore_signal(semaphore);
            }
        }];
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return isSuccess;
}