平台简介
新手指南
API协议
API文档
单点登录集成
小程序插件
常见问题
开放平台API接口调用常见报错以及解决方法汇总
帮助中心 / 开放平台帮助文档 / APP内嵌问题
APP内嵌问题
第三方APP内嵌契约锁H5页面的常见问题及解决方法

1. WebView内嵌的H5认证页面不支持实时动态检测,或实时动态检测时唤起相机黑屏、无法预览拍摄画面

  • iOS

    WKWebView 从 iOS14.3 开始支持 getUserMedia() 的调用,但是 WKWebView.configuration 的属性 allowsInlineMediaPlayback 默认值是 NO,该属性决定了H5是否可以播放内联HTML5视频。需要将 allowsInlineMediaPlayback 属性的值置为 YES ,即可正常展示活体检测预览。注:如果使用SFSafariViewController加载H5页面,则不存在该问题。

      // 需要先初始化WKWebViewConfiguration并修改好参数,再初始化WKWebView并传入configuration,否则配置可能会不生效!
      WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
      config.allowsInlineMediaPlayback = YES; // 默认是NO,这个值决定了用内嵌HTML5播放视频还是用本地的全屏控制
      if (@available(iOS 10.0, *)) {
          config.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; // 音视频的播放不需要用户手势触发,即为自动播放
      } else {
          config.requiresUserActionForMediaPlayback = NO;
      }
    
      // 初始化WKWebViewConfiguration后,再初始化WKWebView并传入config
      WKWebView *webview = [[WKWebView alloc] initWithFrame:frame configuration:config];
    
  • Android

    为了使腾讯云/旷世的实时动态检测能够正常生效,同时适配可能出现的实时动态检测降级到录制视频的场景,请参考如下代码进行兼容性配置,从而确保能够正常唤起相机。

    进行实时动态检测时,如遇到相机加载异常,请确保应用已经获取相机权限,或者通过监听 onPermissionRequest() 动态申请权限,该方法会在即将进行实时动态检测之前触发。

      // 监听WebChromeClient中onPermissionRequest(),判断当前是否授权相机权限
      @Override
      public void onPermissionRequest(PermissionRequest request) {
          if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
              if (request != null && request.getOrigin() != null && QYSH5FaceVerifySDK.getInstance().isQiyuesuoFaceVerify(request.getOrigin().toString())) {
                  this.request = request;
                  ActivityCompat.requestPermissions(mActivity, new String[]{
                                  Manifest.permission.CAMERA,},
                          REQUEST_CODE_CAMERA_WEBRTC);
              }
          }
      }
    
      // 以下为部分参考代码,完整文件见下方附件
      // 设置 setWebChromeClient
      qysWebChromeClient = new QYSWebChromeClient(this.mWebview);
      mWebview.setWebChromeClient(qysWebChromeClient);
      mWebview.setWebViewClient(new WebViewClient());
    
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
          super.onActivityResult(requestCode, resultCode, data);
          qysWebChromeClient.onActivityResult(requestCode,resultCode,data);
      }
    
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          qysWebChromeClient.onRequestPermissionsResult(requestCode,permissions,grantResults);
      }
    
      // 详细示例代码下载地址:https://dl.qiyuesuo.com/public/app/doc/webRTC-demo.zip

2. WebView内嵌的H5页面无法正常的调用相机、相册

  • iOS

    根据苹果商店的审核条款,需要在 info.plist 中添加请求相关权限时的提示文案,否则可能会出现闪退的情况。目前契约锁的H5中可能使用到的权限包括「相机」「相册」「麦克风」。配置项如下(已配置项无需再次配置):

    -w585

  • Android

    H5页面在Android原生WebView中唤起系统相机或相册,在部分机型上可能会出现响应错误或者不响应的问题,可以通过重写 WebChromeClient->onShowFileChooser() ,根据 fileChooserParams.getAcceptTypes() 中的操作类型打开Android原生相机,相册。Android 6.0以上打开相机、相册前需要动态申请文件读取权限。

      // step1 Android 7.0以上文件获取Uri 配置fileprovider
      QYSFileProviderUtils.initAuthorityId(BuildConfig.APPLICATION_ID + ".qysdemo.fileprovider");
    
      // step2 可以参考Demo中QYSWebChromeClient中实现
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      @Override
      public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
          // 根据fileChooserParams.getAcceptTypes() 判断是需要唤起 相机/相机/文件/录制视频
          // 根据对应需要唤起的操作判断是否需要动态申请权限,如相机拍照后保存路径设置外部路径,需要读写权限
          // 有权限之后App通过Intent唤起本地相机/相机/文件/录制视频
          // 例如拍照完成后,获取到文件的uri,通过filePathCallback回传,filePathCallback.onReceiveValue(new Uri[]{uri});
          boolean hasPermission = PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
          if (hasPermission){
              mUploadHandler = new QYSUploadHandler(mActivity,filePathCallback,fileChooserParams);
          }else {
              QYSWebChromeClient.this.filePathCallback = filePathCallback;
              QYSWebChromeClient.this.fileChooserParams = fileChooserParams;
              ActivityCompat.requestPermissions(mActivity, new String[]{
                      Manifest.permission.WRITE_EXTERNAL_STORAGE},
                      REQUEST_CODE_FILE_CHOOSER);
          }
          return true;
      }
    
      // step3 动态申请权限操作
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          qysWebChromeClient.onRequestPermissionsResult(requestCode,permissions,grantResults);
      }
    
      // step4 执行选择文件/拍照后上传操作
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
          super.onActivityResult(requestCode, resultCode, data);
          qysWebChromeClient.onActivityResult(requestCode,resultCode,data);
      }
    
      // 备注:以上代码仅供参考,请根据自己的业务场景进行具体实现,详细示例代码下载地址:https://dl.qiyuesuo.com/public/app/doc/filecamera-demo.zip

3. WebView内嵌的H5页面无法跳转到微信、支付宝等第三方APP

在没有对WebView做任何处理的情况下,无法主动唤起微信、支付宝等第三方APP。在WebView中,可以通过拦截支付宝的认证链接,以打开URL Scheme的方式唤醒第三方APP。

  • iOS

    在info.plist,把需要跳转的URL Scheme添加到白名单中;

    -w585

    实现WKWebView中的代理方法

      #pragma mark - <WKNavigationDelegate>
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
          NSURL *url = navigationAction.request.URL;
          NSString *urlString = url.absoluteString;
          if (urlString && urlString.length) {
              if (([urlString hasPrefix:@"weixin://"] || [urlString hasPrefix:@"alipays://"]) && [[UIApplication sharedApplication] canOpenURL:url]) { // 第三方APP的URL Scheme
                  if (@available(iOS 10.0, *)) {
                      [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
                  } else {
                      [[UIApplication sharedApplication] openURL:url];
                  }
                  decisionHandler(WKNavigationActionPolicyCancel);
                  return;
              }
          }
          decisionHandler(WKNavigationActionPolicyAllow);
      }
  • Android

    在没有对WebView做任何处理的情况下,无法主动唤起微信、支付宝等第三方APP。在WebView中,可以重写 WebViewClient 的 shouldOverrideUrlLoading 方法,以打开URL Scheme的方式唤醒第三方APP。

      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
          if (url.startsWith("alipays://") || url.startsWith("weixin://")) {
              try {
                  final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                  startActivity(intent);
              } catch (Exception e) {
                  // 处理没有安装支付宝/微信的情况
                  e.printStackTrace();
              }
              return true;
          }
          return super.shouldOverrideUrlLoading(view, url);
      }

4. WebView内嵌的H5认证页面跳转到支付宝认证完成后,无法自动跳回APP

第三方APP在直接或间接加载契约锁认证链接,并点击跳转至支付宝APP进行认证,完成后无法自动跳回第三方APP,若需要认证后自动跳回第三方APP,需要对认证链接按照【<auth_url>&appScheme=demo://】格式进行拼接appScheme参数(其中,"auth_url"为原始契约锁认证链接,"appScheme"为要拼接参数名,"demo://"为需跳回App的URL Scheme):

  • iOS

    WKWebView直接加载契约锁认证链接,直接将 "&appScheme=demo://" 拼接在原始认证链接之后:

      [wkWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://auth.qiyuesuo.com?<originalQuery>&appScheme=demo://"]]];

    WKWebView间接加载契约锁认证链接,需要实现WKWebView中的代理方法:

      #pragma mark - <WKNavigationDelegate>
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
          NSString *string = = navigationAction.request.URL.absoluteString;
          if ([string rangeOfString:@"https://auth.qiyuesuo.com"] != NSNotFound) {
              decisionHandler(WKNavigationActionPolicyCancel);
    
              // 截取认证链接后进行拼接
              NSString *appendUrl = [string stringByAppendingString:@"&appScheme=demo://"];
              [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:appendUrl]]];
              return;
          }
    
          ...
          // 其他第三方App业务逻辑代码
          ...
    
          decisionHandler(WKNavigationActionPolicyAllow);
      }
  • Android

    WebView直接加载契约锁认证链接,直接将 "&appScheme=demo://" 拼接在原始认证链接之后:

      webview.loadUrl("https://auth.qiyuesuo.com?<originalQuery>&appScheme=demo://");

    WebView间接加载契约锁认证链接:

      webview.setWebViewClient(new WebViewClient(){
          @Override
          public boolean shouldOverrideUrlLoading(WebView view, String url) {
              if (url.contains("https://auth.qiyuesuo.com")){
                  // 截取认证链接后进行拼接(此处仅为参考,拼接需符合http/https请求规范)
                  String appendUrl = url + "&appScheme=demo://";
                  view.loadUrl(appendUrl);
                  return true;
              }
              return super.shouldOverrideUrlLoading(view, url);
          }
      });

5. WebView内嵌的H5认证页面认证完成后无法自动返回,或点击退出认证按钮无反应

认证的H5在认证完成后,会调用JS方法 returnApp,APP内的WebView需要监听该JS方法的调用,以实现退出后的逻辑处理。

示例代码如下:

  • iOS

      // 为JS方法「returnApp」添加响应对象「jsHandler」,「jsHandler」可以是实现 <WKScriptMessageHandler> 协议的任意对象
      [webView.configuration.userContentController addScriptMessageHandler:jsHandler name:@"returnApp"];
    
      //「jsHandler」实现代理方法
      - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
          if ([message.name isEqualToString:@"returnApp"]) {
              // 在此处编写退出页面或其他退出逻辑的代码
          }
      }
  • Android

      protected void addJavascriptInterface(WebView mWebView) {
          mWebView.addJavascriptInterface(new JsToH5Api(), "android");
      }
    
      class JsToH5Api{
          @JavascriptInterface
          public void returnApp() {
              // 调用finish方法退出界面 或 处理自己的退出逻辑
              finish();
          }
      }

6. WebView内嵌的H5页面无法下载文件或下载文件乱码

契约锁前端H5页面中的文件下载,并不是通过跳转资源文件目录触发下载的形式实现的,而是通过下载接口(需要登录态)使用blob触发的下载事件。所以第三方APP内嵌契约锁H5时,原生想要截获文件下载事件就必须通过监听blob,从而触发APP原生相关的下载操作。

  • iOS

    WKWebView从iOS14.5开始新增了 WKNavigationActionPolicyDownload 的导航操作策略,可通过 WKNavigationDelegate 代理方法截获blob从而触发下载事件。

      #pragma mark - <WKNavigationDelegate>
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
          NSURL *url = navigationAction.request.URL;
          if ([url.absoluteString hasPrefix:@"blob"]) { // 截获blob下载事件
              if (@available(iOS 14.5, *)) {
                  decisionHandler(WKNavigationActionPolicyDownload); // 仅在iOS14.5及更新版本支持WKNavigationActionPolicyDownload
              } else {
                  decisionHandler(WKNavigationActionPolicyCancel); // iOS14.5以下版本默认不支持
              }
              return;
          }
    
          ...
          // 其他第三方App业务逻辑代码
          ...
    
          decisionHandler(WKNavigationActionPolicyAllow);
      }
    
      // ↓↓↓以下代理方法均只适用于iOS14.5及更高版本↓↓↓
      #pragma mark - <WKNavigationDelegate>
      - (void)webView:(WKWebView *)webView navigationAction:(WKNavigationAction *)navigationAction didBecomeDownload:(WKDownload *)download API_AVAILABLE(ios(14.5)) {
          download.delegate = self;// 设置WKDownloadDelegate
      }
    
      #pragma mark - <WKDownloadDelegate>
      - (void)download:(WKDownload *)download decideDestinationUsingResponse:(NSURLResponse *)response suggestedFilename:(NSString *)suggestedFilename completionHandler:(void (^)(NSURL * _Nullable))completionHandler API_AVAILABLE(ios(14.5)) {
          NSURL *tmpDir = [[NSFileManager defaultManager] temporaryDirectory];    // 根据具体业务场景决定下载路径
          NSURL *url = [tmpDir URLByAppendingPathComponent:suggestedFilename];    // 此处仅为示例,实际场景中建议文件名去重,同样路径同样文件名会导致下载失败
          completionHandler(url);                                                 // 返回目标url
      }
    
      - (void)downloadDidFinish:(WKDownload *)download API_AVAILABLE(ios(14.5)) {    
          // 下载成功后的代理回调方法
      }
    
      - (void)download:(WKDownload *)download didFailWithError:(NSError *)error resumeData:(nullable NSData *)resumeData API_AVAILABLE(ios(14.5)) {
          // 下载失败后的代理回调方法
      }
  • Android

    Android 原生不支持blob协议,但是我们可以监听前端blob下载,自己实现blob转成Base64从而进行文件下载。

      // step 1 需要支持javaScript
      mWebview.getSettings().setJavaScriptEnabled(true);
      mWebview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
    
      // step 2 注册 javaScript监听
      DownloadBlobFileJSInterface mDownloadBlobFileJSInterface = new DownloadBlobFileJSInterface();
      mWebview.addJavascriptInterface(mDownloadBlobFileJSInterface, "Android");
    
      // step 3 监听 DownloadListener,这里根据业务逻辑动态申请文件权限
      mWebview.setDownloadListener(new DownloadListener() {
          @Override
          public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
              if (url.startsWith("blob")) {
                  // 动态申请文件权限
                  mWebview.loadUrl(DownloadBlobFileJSInterface.getBase64StringFromBlobUrl(url));
              }
          }
      });
    
      // step 4 支持监听文件下载状态
      mDownloadBlobFileJSInterface.setDownloadListener(new DownloadBlobFileJSInterface.BlobDownloadListener() {
          @Override
          public void onSuccess(String absolutePath) {
              Log.d(getClass().getSimpleName(),String.format("文件下载成功,路径%s",absolutePath));
          }
    
          @Override
          public void onFaild(String message) {
              Log.d(getClass().getSimpleName(),String.format("文件下载失败,原因%s",message));
          }
      });
    
      // step 5 DownloadBlobFileJSInterface 实现,仅做参考
      public class DownloadBlobFileJSInterface {
          private BlobDownloadListener blobDownloadListener;
    
          @JavascriptInterface
          public void receiveBlobData(String base64Data,String fileName) {
              // 此处为保存文件路径,可根据实际场景自行调整
              File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + File.separator + fileName);
              try {
                  base64Data = base64Data.contains(",") ? base64Data.split(",")[1] : base64Data;
                  byte[] fileBytes = Base64.decode(base64Data, 0);
                  FileOutputStream os = new FileOutputStream(file, false);
                  os.write(fileBytes);
                  os.flush();
                  os.close();
                  if (blobDownloadListener != null) blobDownloadListener.onSuccess(file.getAbsolutePath());
              } catch (Exception e) {
                  e.printStackTrace();
                  if (blobDownloadListener != null) blobDownloadListener.onFaild(e.getMessage());
              }
          }
    
          public static String getBase64StringFromBlobUrl(String blobUrl) {
              return "javascript: " +
                      "var xhr = new XMLHttpRequest();" +
                      "xhr.open('GET', '" + blobUrl + "', true);" +
                      "xhr.responseType = 'blob';" +
                      "xhr.onload = function() {" +
                      "    var blob = xhr.response;" +
                      "    var reader = new FileReader();" +
                      "    reader.onloadend = function() {" +
                      "        var base64data = reader.result.split(',')[1];" +
                      "        var contentType = blob.type;" +
                      "        var contentDisposition = xhr.getResponseHeader('Content-Disposition');" +
                      "        var fileName = 'downloaded_file';" +
                      "        if (contentDisposition) {" +
                      "            var matches = /filename=\"([^\"]+)\"/.exec(contentDisposition);" +
                      "            if (matches != null && matches[1]) { fileName = matches[1]; }" +
                      "        }else{" +
                      "           var urlParts = xhr.responseURL.split('/');" +
                      "           var possibleFileName = urlParts[urlParts.length - 1];" +
                      "           if (possibleFileName) { fileName = possibleFileName; }" +
                      "        }" +
                      "        Android.receiveBlobData(base64data, fileName);" +
                      "    };" +
                      "    reader.readAsDataURL(blob);" +
                      "};" +
                      "xhr.send();";
          }
    
          public void setDownloadListener(BlobDownloadListener listener) {
              blobDownloadListener = listener;
          }
    
          public interface BlobDownloadListener {
              void onSuccess(String absolutePath);
    
              void onFaild(String message);
          }
      }

7. WebView的UserAgent相关问题

H5页面在某些场景下,需要判断当前是否处于APP的WebView内嵌环境下,此时需要第三方APP在WebView的UserAgent中,增加契约锁指定的UserAgent标识:QYSCustomerApp/private

  • iOS

    获取WKWebView的完整UserAgent需要执行 navigator.userAgent,建议最好在原始的完整UserAgent末尾拼接契约锁指定标识。

      [self.webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
          // 获取原始UserAgent
          NSMutableString *userAgent = [result mutableCopy];
    
          // 判断标识是否已存在,若不存在则拼接契约锁指定的UA标识
          if (![userAgent containsString:@"QYSCustomerApp/private"]) { 
              [userAgent appendString:@" QYSCustomerApp/private"];
          }
    
          // 配置全局UserAgent
          [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent":[userAgent copy]}];
          [[NSUserDefaults standardUserDefaults] synchronize];
    
          // 配置当前WKWebView的UserAgent并且立即生效
          self.webView.customUserAgent = [userAgent copy];
      }];
  • Android

    获取WebView的完整UserAgent需要执行 webView.getSettings().getUserAgentString(),建议最好在原始的完整UserAgent末尾拼接契约锁指定标识,此设置放在webview.loadUrl()之前。

      String appendUa = " QYSCustomerApp/private";
      WebSettings settings = webView.getSettings();
      settings.setUserAgentString(settings.getUserAgentString() + appendUa);

8. Android APP无法跳转到契约锁APP

H5中存在部分操作无法在第三方APP内完成,需跳转至契约锁APP内完成相关操作;第三方APP的Android端在WebView中需要做以下处理,方可成功跳转至契约锁APP。

  • Android

    重写 WebViewClientshouldOverrideUrlLoading 方法,通过契约锁的 URL Scheme 打开契约锁APP

      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
              Uri uri = Uri.parse(url);
              if("qys".equals(uri.getScheme())){
                  try {
                      Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                      startActivity(intent);
                  } catch (Exception e) {
                      e.printStackTrace();
                      // 未安装契约锁App,跳转应用市场下载
                      Uri downloadApkUri = Uri.parse("market://details?id=com.genyannetwork.qiyuesuo");
                      Intent intent =new Intent(Intent.ACTION_VIEW, downloadApkUri);
                      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                      startActivity(intent);
                  }
                  return true;
              }
              return super.shouldOverrideUrlLoading(view, url);
      }

9. Android WebView内嵌的H5页面中的图片无法长按保存至本地

  • Android

    长按保存图片是浏览器的功能,原生WebView默认不支持,如果需要实现浏览器相似的功能,可通过监听WebView的 setOnLongClickListener 方法实现。

      mWebView.setOnLongClickListener(v -> {
          //获取所点击的内容
          final WebView.HitTestResult htr = ((WebView) v).getHitTestResult();
          //判断被点击的类型为图片
          if (htr.getType() == WebView.HitTestResult.IMAGE_TYPE || 
              htr.getType() == WebView.HitTestResult.IMAGE_ANCHOR_TYPE || 
              htr.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
              String imagePath = htr.getExtra();
              // TODO 下载当前图片到本地
              // yourSaveImageMethod(imagePath);
          }
          return false;
      });

10. iOS WKWebView内嵌的H5认证页面,每一次使用活体都需要弹窗询问权限

  • iOS

    WKWebView内嵌认证H5使用活体检测的场景下,默认会每一次都弹出获取摄像头权限的弹窗;如果不想每次认证都弹出,可以给WKWebView设置UIDelegate,并实现如下代理方法;需要注意的是,该代理方法仅适用于iOS15及更高版本。

      - (void)webView:(WKWebView *)webView requestMediaCapturePermissionForOrigin:(WKSecurityOrigin *)origin initiatedByFrame:(WKFrameInfo *)frame type:(WKMediaCaptureType)type decisionHandler:(void (^)(WKPermissionDecision decision))decisionHandler  API_AVAILABLE(ios(15.0)) {
          decisionHandler(WKPermissionDecisionGrant); // 可设置默认放行,也可根据APP自身需要调整
      }  

11. iOS WKWebView内嵌的H5页面部分头部内容被遮挡

  • iOS

    iOS的WKWebView的内部容器是UIScrollView,可能存在ScrollView内边距自适应的情况,需要禁用相关设置。

      if (@available(iOS 11.0, *)) {
          viewController.webview.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
      } else {
          viewController.automaticallyAdjustsScrollViewInsets = NO;
      }
    
      viewController.edgesForExtendedLayout = UIRectEdgeNone; // 视图不延伸至任何边缘