iOS WKWebView嵌入UITableViewCell

需求背景

产品需要展示html格式的富文本字符串,这个html是用户可编辑的,但不能有交互行为。从设计稿上看,这段富文本字符串需要展示在一个tableViewCell里,并且得让其自适应高度。

使用UILabel去实现需求

实际上,UILabel是支持attributeString的,而attributeString又可以通过html字符串生成,如下:

 

NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc]
                                     initWithData:[htmlStr
                                                   dataUsingEncoding:NSUTF8StringEncoding]
                                     options:@{
                                               NSDocumentTypeDocumentAttribute :
                                                 NSHTMLTextDocumentType,
                                               NSCharacterEncodingDocumentAttribute :
                                                 @(NSUTF8StringEncoding)
                                               }
                                     documentAttributes:nil
                                     error:nil];

而UILabel的自适应高度相对容易实现一些(配合自动布局),而且这个需求不需要这段html跟用户有交互,所以一开始是用UILabel实现的。

但是这个方法有两个坑,第一个是不能显示表格的边框。像以下的代码是渲染不出border的:

 

<table border="1">
  <tr>
    <th>Month</th>
    <th>Savings</th>
  </tr>
  <tr>
    <td>January</td>
    <td>$100</td>
  </tr>
</table>

这个坑是iOS的系统bug,随便给富文本加一个属性,就能看到这个边框了:

 

        //先判断字符串长度,以免越界崩溃
      if(attrStr.length>0){
              [attrStr addAttribute:NSBackgroundColorAttributeName value:[UIColor clearColor] range:NSMakeRange(0, 1)];
      }

第二个坑,就是无法渲染渐变字的文本,类似于这种:

 

<span style="-webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-image: linear-gradient(#FF0000, #F7B62E, #F7B62E, #3EACFF, #A3E880 );color: transparent;font-family: Chalkduster; font-size: 30px;">
 <i>grandma calls me an artist</i>
</span>

这样的文本用NSMutableAttributedString渲染,字体就只能是黑色的了,没有把渐变色渲染出来。因此要想完美实现这个需求,还得用webview

WKWebView实现需求

使用WKWebViewweb加载htmL并不难,系统已经提供了对应的方法:

 

[self.wkWebView loadHTMLString:self.htmlString baseURL:nil];

尽管有这一方法,但是这个需求实现下来还有一系列的坑,下面一一说下

在xib文件中使用webView

xib确实有wkWebView的控件,但是是ios11才能用的,因此只能用一个UIView,在这里面封装WebView(或者也可以指定该UIView的类名为WKWebView,由于我还需自定义WKWebView的特性,因此选择用封装的方式)。此外,wkWebView的自适应高度代表UITableViewCell自适应高度,需要UITableViewCell的垂直上的约束线连在一起。因此需要给wkWebView设定高度的约束。

得到wkwebView的文本高度

网上找了一圈,发现wkwebView得到文本高度可以通过两种方法。一种是用KVO监听ScrollView的contentSize,另外就是运行获取高度(document.body.scrollHeight)的JS脚本,由于这次的需求只是简单地展示html,两种方法获取高度都没有差异的,而且都是异步的。但是这里有个坑:由于这个需求是能让用户可以html,因此webView在其生命周期内可以加载多次html。再改动html后,获得的高度不准了(得到的高度越来越大),后来发现是似乎因为wkwebView没有适配屏幕大小,于是在其初始化的时候加了一段代码:

 

WKWebViewConfiguration *config = [WKWebViewConfiguration new];
    NSString *script = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
    
    WKUserScript *wkUserScript = [[WKUserScript alloc] initWithSource:script injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    
    WKUserContentController *wkUController = [[WKUserContentController alloc] init];
    
    [wkUserController addUserScript: wkUserScript];
    config.userContentController = wkUserController;
  
    self.webView = [[WKWebView alloc]initWithFrame:self.bounds configuration:config];

这样高度就不会越来越大了,此外这段代码还能解决wkwebView里面的字体过小的问题。

但是,仅仅这样是不够的,因为这两种方式得到的高度都不小于wkWebView的高度。如果给wkWebView设置过大的文本,然后又通过编辑给其设置较小的高度。这样wkWebView的高度不会减少。我的解决方法就是在wkWebView设置文本的时候重新将其高度置为0,在得到高度后才设回去。总体代码如下:

 

- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL{
  //高度置0
  self.wkWebView.height = 0;
  [self.wkWebView loadHTMLString: string baseURL:nil];
}

 

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
  [self.wkWebView evaluateJavaScript:@"document.documentElement.scrollHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
    CGFloat height = [result doubleValue];
    self.wkWebView.height = height;
    //通知cell更改约束
  }];

}

获取高度后更新视图

获取高度是异步的,因此获取之后需要通知Cell更新约束,再用Cell通知tableView刷新,由于只是更新了约束,可以只用beginUpdate/endUpdate方法即可,这样可以避免[tableView reloadData]->[wkWebView loadHTMLString]->[self.wkWebView evaluateJavaScript]->[tableView reloadData]这样的循环调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值