UITableView 添加 WebView 和 ImageView 单元格

Posted

技术标签:

【中文标题】UITableView 添加 WebView 和 ImageView 单元格【英文标题】:UITableView add WebView and ImageView Cells 【发布时间】:2017-09-06 23:32:05 【问题描述】:

我需要以编程方式将两种不同类型的单元格添加到我的 UITableView。这些单元格的内容直到运行时才知道。第一种是 html 格式的字符串,第二种是图像。这些单元格可以任意组合,并且可以以任意顺序出现。

在 IB 中,我设置了两个原型单元,一个 id 为“htmlCell”,另一个为“imageCell”。以下是相关代码:

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        var cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "htmlCell")
        let thisSection = self.sections[indexPath.row]

        if thisSection.type! == "html" 


            let htmlString = thisSection.text
            let htmlHeight = contentHeights[indexPath.row]
            let webView:UIWebView = UIWebView(frame: CGRect(x:0, y:0, width:cell.frame.size.width, height:htmlHeight))
            cell.addSubview(webView)
            webView.tag = indexPath.row
            webView.scrollView.isScrollEnabled = false
            webView.isUserInteractionEnabled = false
            webView.delegate = self
            webView.loadHTMLString(htmlString!, baseURL: nil)

         else if thisSection.type! == "image" 
            cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "imageCell")
            let imageName = "logo"
            let image = UIImage(named: imageName)
            let imageView = UIImageView(image: image)

            imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width-20, height: 200)
            cell.addSubview(imageView)
            return cell

        

        return cell
    

    func webViewDidFinishLoad(_ webView: UIWebView) 
        if (contentHeights[webView.tag] != 0.0) 
            // we already know height, no need to reload cell
            return
        

        let strHeight = webView.stringByEvaluatingjavascript(from: "document.body.scrollHeight")
        contentHeights[webView.tag] = CGFloat(Float(strHeight!)!)
        tableView.reloadRows(at: [NSIndexPath(item: webView.tag, section: 0) as IndexPath], with: UITableViewRowAnimation.automatic)
    

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return self.contentHeights[indexPath.row]
    

内容已加载,但有点混乱。这是提供示例的屏幕截图。在这种情况下,图像应该出现在两个 webview 之间。但正如您所见,图像直接落在第二个 webview 的顶部,两个顶部边框对齐。

此外,当我单击图像时,它会完全消失。我假设它落后于第二个 webview。只是猜测。

我正在使用的 CGRects 目前有一些任意的宽度和高度。我怀疑这就是部分问题所在。最后,无论 webview 内容的真实高度如何,webViewDidFinishLoad 总是返回 667.0 的高度。

有人知道我怎样才能让这些视图按正确的顺序出现吗?谢谢。

【问题讨论】:

【参考方案1】:

这里有很多潜在的问题,让我们解决其中的一些问题,看看能不能解决问题:)

如果您在 IB 中定义了原型单元格,并且您的 tableView 正确链接到 xib 或故事板文件,那么您应该将单元格出列而不是构造它们。

试试这个:

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

    let cell: UITableViewCell //declare but don't assign yet. Use let because after we assign once, we wont be reassigning.
    ...

    if thisSection.type! == "html" 
        cell = tableView.dequeueReusableCell(withIdentifier: "htmlCell", for: indexPath)
        ...

     else if thisSection.type! == "image" 
        cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath)
        ...

    

    return cell

注意,我们在***范围内声明并返回单元格,并且只有在我们知道单元格将是什么类型时才分配单元格。现在您正在出列,这些单元格将在适当的时候被重用,并减少用户滚动时分配的数量,使您的应用程序更流畅。

接下来,由于您要向单元格添加子视图,因此每次将单元格出列时都需要清除子视图。这需要一些额外的代码,但从长远来看会使事情变得更整洁。

在每次调用dequeueReusableCell...后添加:

cell.contentView.subviews.forEach( $0.removeFromSuperview() )

到目前为止一切顺利。您已经让单元格出列干净,但现在我们需要确保正确设置单元格。

首先,html单元格: 删除该行:

let htmlHeight = contentHeights[indexPath.row]

tableView 最终负责调整单元格的大小,内容将占据整个单元格的大小。将接下来的 2 行替换为:

let webView = UIWebView(frame: cell.contentView.bounds)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
cell.contentView.addSubview(webView)

自动调整大小的掩码确保如果单元格的内容视图大小发生变化,webview 也将调整大小。由于内容视图由 ios 管理,webview 将始终是 tableview 的宽度,高度将是您在委托方法 heightForRow 中提供的任何内容。另请注意,我们从不将子视图直接添加到单元格上,始终添加到它的 contentView 上。

接下来,我们将对 imageCell 执行相同的操作:

let image = UIImage(named: "logo")
let imageView = UIImageView(frame: cell.contentView.bounds)
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.contentMode = .aspectFit //this makes sure the image take ups the full size of the image view, but no more. and this keeps the image's aspect ratio. Adjust this to other content modes if necessary.
imageView.image = image
cell.contentView.addSubview(imageView)

好的,现在我们有正在设置的单元格,并且不考虑大小。因此,让我们在正确的位置调整大小:

在构建它时您将面临的最大挑战是正确设置单元格高度。由于图像在您的捆绑包中,您将在实例化 UIImage 后立即知道它们的大小,但是我们不想在绝对必要的情况下执行此操作。稍后您可能想提出一种更高效的方法来执行此操作,但现在我们将创建一个 UIImage 并获取它的高度:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
    if thisSection.type! == "html" 
        return self.contentHeights[indexPath.row]
     else if thisSection.type! == "image" 
        let image = UIImage(named: "logo")
        let imageHeight = (image.size.height * tableView.bounds.size.width) / image.size.width // The proportional size of the image stretched to the table view's width
        return imageHeight
    

您计算网页视图高度和重新加载单元格的方法可能有效,但对用户来说可能显得笨拙。在这一点上,我的建议是为 Web 内容返回一个固定的高度。当用户点击一个 web 单元格时,您可以推送一个新的视图控制器,该控制器带有一个可以显示所有内容的 web 视图。否则,您可以尝试让所有 html 生成大小大致相同的网页内容。

好的,这很长,但希望能让您有一个良好的开端 :) 如果您需要更多说明,我很乐意跟进。

【讨论】:

谢谢一百万。这真的很有帮助。我的第一个 Swift 项目,进展有点慢。无法告诉你我对此有多感激。

以上是关于UITableView 添加 WebView 和 ImageView 单元格的主要内容,如果未能解决你的问题,请参考以下文章

如何打印具有多个数据的标题和uitableview的uiwebview

如何使用多个数据打印带有标题和uitableview的uiwebview

webdeviceagent无法获得webview内容ios

在 webview_flutter 中启用捏合和缩放,在哪里添加代码片段 [this.webView.getSettings().setBuiltInZoomControls(true);]

webView导致tableview滞后

UIWebView 在 webview 完成加载时高度不正确