【问题标题】:UITableViewCell With UIWebView Dynamic HeightUITableViewCell 与 UIWebView 动态高度
【发布时间】:2015-12-09 12:12:20
【问题描述】:

我有一个表格视图,其中包含有 webView 的单元格,我希望单元格的高度与 webView 的高度相匹配。

这是我使用的代码:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("newsCell") as! NewsTableViewCell

    cell.webView.loadHTMLString("test<br>test<br>test<br>test<br>test<br>test", baseURL: nil)
    cell.webView.delegate = self

    var webFrame = cell.webView.frame
    var cellFrame = cell.frame
    cell.frame = CGRectMake(cellFrame.origin.x, cellFrame.origin.y, cellFrame.width, webFrame.height + 20)
    cell.backgroundColor = UIColor.redColor()

    return cell
}

func webViewDidFinishLoad(webView: UIWebView) {
    println("finished loading")
    var frame = webView.frame
    frame.size.height = 1
    webView.frame = frame

    var fittingSize = webView.sizeThatFits(CGSizeZero)
    frame.size = fittingSize
    webView.frame = frame

    var height: CGFloat = frame.height
    println(height)

    webView.frame = CGRectMake(frame.origin.x, frame.origin.x, frame.size.width, frame.size.height)

    newsTable.beginUpdates()
    newsTable.endUpdates()
}    

结果如下:https://postimg.cc/image/8qew1lqjj/

webView 是正确的高度,但单元格不是,我该如何解决这个问题?

【问题讨论】:

  • 好问题!谢谢!

标签: ios xcode swift uitableview uiwebview


【解决方案1】:

TableView 会自行调整单元格大小,您只需要实现tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -&gt; CGFloat 委托方法即可。

是的,你最初并不知道WebView的高度,但是你可以计算出来然后让TableView重新加载cell。像这样的:

class TableViewController: UITableViewController, UIWebViewDelegate
{
    var content : [String] = ["test1<br>test1<br>test1<br>test1<br>test1<br>test1", "test22<br>test22<br>test22<br>test22<br>test22<br>test22"]
    var contentHeights : [CGFloat] = [0.0, 0.0]

    // ...

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCellWithIdentifier("newsCell", forIndexPath: indexPath) as! NewsTableViewCell
        let htmlString = content[indexPath.row]
        let htmlHeight = contentHeights[indexPath.row]

        cell.webView.tag = indexPath.row
        cell.webView.delegate = self
        cell.webView.loadHTMLString(htmlString, baseURL: nil)
        cell.webView.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)

        return cell
    }

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        return contentHeights[indexPath.row]
    }

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

        contentHeights[webView.tag] = webView.scrollView.contentSize.height
        tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: webView.tag, inSection: 0)], withRowAnimation: .Automatic)
    }

    // ...
}

【讨论】:

  • 由于某种原因,表格显示如下:postimg.org/image/wp43uf291,你知道为什么吗?
  • @OmerN,你是说黑线吗?我认为这与this question中的问题相同。
  • @6245Htarwara 你找到解决方案了吗?
  • 从服务器获取数据后,我正在重新加载表。所以 contentHeights 列出了一些如何包含相同的值而不是 0。所以每个单元格高度固定到第一个单元格高度。所以我在 webViewDidFinishLoad 方法中改变了条件 - --- if (contentHeights[webView.tag] == webView.scrollView.contentSize.height) { // 我们已经知道高度,不需要重新加载单元格 return }
  • 当我尝试这种方法时,由于某种原因,我的 web view frame 永远不会超过 20px。我查看了视图调试器,结果发现 Web 视图的滚动视图(一个 UIWebBrowserView,一个内部 Apple 类)的子视图之一是正确的大小,但 Web 视图正在剪裁它。我将 contentHeight 设置为 webView.scrollView.subviews.first?.frame.height ,瞧!它就像一个魅力。 (警告:UIWebBrowserView 是一个未记录的 API,但由于我们没有直接引用它,所以应该没问题。)
【解决方案2】:

斯威夫特 4

ifau 的答案针对 Swift 4 进行了修改,使用 UITableView 而不是 UITableViewController

在情节提要中,输入UITableView。向其中添加 1 个原型单元。将单元格的重用标识符保持为“cell”。将webView 拖入单元格并将前导、尾随、顶部和底部约束设置为0。将单元格类设置为TableViewCell

TableViewCell.swift

将 webView 出口连接到情节提要:

import UIKit

class TableViewCell: UITableViewCell {

    @IBOutlet weak var webView: UIWebView!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

ViewController.swift

将 tableView 插座连接到情节提要:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIWebViewDelegate {

    @IBOutlet weak var tableView: UITableView!

    var content : [String] = ["test1<br>test1<br>test1", "test22<br>test22<br>test22<br>test22<br>test22<br>test22"]

    var contentHeights : [CGFloat] = [0.0, 0.0]

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        tableView.dataSource = self
        tableView.delegate = self
    }

    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return content.count
    }

    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
        let cell : TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell

        let htmlString = content[indexPath.row]
        let htmlHeight = contentHeights[indexPath.row]

        cell.webView.tag = indexPath.row
        cell.webView.delegate = self
        cell.webView.loadHTMLString(htmlString, baseURL: nil)
        cell.webView.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: htmlHeight)

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if contentHeights[indexPath.row] != 0 {
            return contentHeights[indexPath.row]
        }
    }

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

        contentHeights[webView.tag] = webView.scrollView.contentSize.height
        tableView.reloadRows(at: [IndexPath(row: webView.tag, section: 0)], with: .automatic)
    }
}

【讨论】:

  • 添加contentHeights[indexPath.row] != 0 防止自动布局中断
  • 天哪,我花了150个堆栈溢出点才得到这个解决方案,但没有得到一个完美的解决方案。这让我很开心。非常感谢。
【解决方案3】:

它对我有用,在 WebView 上动态加载 Html 字符串的简单方法。

首先在 TableViewCell 中使用 textView 并禁用滚动,然后使用 WebView,使用 ContentView 在所有 4 面应用零约束。现在为 textview 和 WebView 提供相同的高度约束。现在为两个视图提供相同的文本,如下面的代码所示。

对于干净的 UI 隐藏来自 XIB 或 storyBoard 的 textView。

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return 100
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "tableviewcell") as! tableviewcell
    let theString : String = "</p><img src='https://pbs.twimg.com/profile_images/655066410087940096/QSUlrrlm.png' width=100 height=100 /><h2>Subheader</h2>"

    let theAttributedString = try! NSAttributedString(data: theString.data(using: String.Encoding.utf8, allowLossyConversion: false)!,options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],  documentAttributes: nil)

    cell.textview.attributedText =  theAttributedString

    cell.webview.loadHTMLString(theString, baseURL: nil)

    return cell
}

【讨论】:

  • 甚至需要隐藏文本视图,但这种方法也适用于我。
  • 谢谢,是的,你需要隐藏textView。我在回答时添加了这个
  • 我认为如果您的 html 包含任何 img 或样式,因为在 textview 每个标签将被视为文本,如:,所以高度也会改变。
  • @Sibasish 能否提供示例代码。所以我能理解。
  • 它在大多数情况下都有效,但偶尔我会遇到这个崩溃:WebKitLegacy 0x19590f7e0 -[_WebSafeForwarder forwardInvocation:] + 132。我不确定它是否由 NSAttributedString(data:) 引起.
【解决方案4】:

计算webview高度后使用DispatchQueue重新加载tableViewcell高度DispatchQueue.main.async.避免重新加载webview框架时出现异常空白和崩溃

func webViewDidFinishLoad(_ webView: UIWebView)
{
    var frame : CGRect = webVwModule.frame;
    frame.size.height = 1;
    self.webVwModule.frame.size = webVwModule.sizeThatFits(.zero)
    frame.size = self.webVwModule.frame.size;
    webView.frame = frame;
    webViewContentHeight = self.webVwModule.frame.size.height;
    isWebViewLoaded = true

    DispatchQueue.main.async(execute: { () -> Void in
       self.tableView.beginUpdates()
        self.tableView.endUpdates()
    })
    }

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

        return webViewContentHeight!
}

【讨论】:

    【解决方案5】:
    var heightOfWebview=0    
    
    func webViewDidFinishLoad(_ aWebView: UIWebView)
    {
        var frame: CGRect = aWebView.frame
        frame.size.height = 1
        aWebView.frame = frame
        let fittingSize = aWebView.sizeThatFits(CGSize.zero)
        frame.size = fittingSize
        aWebView.frame = frame
        heightOfWebview = Int(fittingSize.height)
        tableView.beginUpdates()
        tableView.endUpdates()
        print("Calling webViewDidFinishLoad. Cell size value: \(heightOfWebview)")
    
    }
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return CGFloat(220+heightOfWebview) /// 220 is my static value
    }
    

    【讨论】:

    • 它不会根据 webView 的内容调整大小。
    【解决方案6】:

    对我来说最好的方法。

    var heightOfWebview: CGFloat = 0    
    
    func webViewDidFinishLoad(_ aWebView: UIWebView)
    {
        var frame: CGRect = aWebView.frame
        frame.size.height = 1
        aWebView.frame = frame
        let fittingSize = aWebView.sizeThatFits(CGSize.zero)
        frame.size = fittingSize
        aWebView.frame = frame
        heightOfWebview = Int(fittingSize.height)
        tableView.beginUpdates()
        tableView.endUpdates()
        print("Calling webViewDidFinishLoad. Cell size value: \(heightOfWebview)")
    
    }
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return heightOfWebview
    }
    

    【讨论】:

      【解决方案7】:

      最简单优雅的方式是

      1. 为 webview 添加高度布局约束
      2. 添加 webview 委托完成加载,然后将 contentSize 高度设置为高度布局约束的常量。

      3. 保留 tableview 的实例或将委托 - 块 - 关闭到主 tableview。

      4. 调用 tableview beginupdate endupdate 让 tableview 知道他们应该验证布局。

      class WebViewTableViewCell: UITableViewCell {
          weak var viewController: ViewController? = nil
          @IBOutlet weak var webView: UIWebView!
          @IBOutlet weak var heightLayoutConstraint: NSLayoutConstraint!
          
          override func awakeFromNib() {
              super.awakeFromNib()
              webView.loadRequest(URLRequest(url: URL(string: "https://tinhte.vn")!))
              webView.delegate = self
              webView.scrollView.isScrollEnabled = false
              // Initialization code
          }
      
          override func setSelected(_ selected: Bool, animated: Bool) {
              super.setSelected(selected, animated: animated)
      
              // Configure the view for the selected state
          }
      
      }
      extension WebViewTableViewCell: UIWebViewDelegate {
          func webViewDidFinishLoad(_ webView: UIWebView) {
              heightLayoutConstraint.constant = webView.scrollView.contentSize.height
              viewController?.tableView.beginUpdates()
              viewController?.tableView.endUpdates()
          }
      }

      【讨论】:

      • 这不适用于动态请求。在这里,您必须在awakeFromNib 中静态设置请求。如果您稍后设置请求,例如在tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell 中,tableView.beginUpdates() and .., 行将创建循环
      【解决方案8】:

      我还有一个您应该考虑的解决方案。进行高度限制并将其链接到您的 UITableViewCell 类。然后实现委托方法func webViewDidFinishLoad(_ webView: UIWebView),调用时设置高度约束等于webView.scrollView.contentSize.height

      【讨论】:

        【解决方案9】:

        class WebViewTableViewCell: UITableViewCell {
            weak var viewController: ViewController? = nil
            @IBOutlet weak var webView: UIWebView!
            @IBOutlet weak var heightLayoutConstraint: NSLayoutConstraint!
            
            override func awakeFromNib() {
                super.awakeFromNib()
                webView.loadRequest(URLRequest(url: URL(string: "https://tinhte.vn")!))
                webView.delegate = self
                webView.scrollView.isScrollEnabled = false
                // Initialization code
            }
        
            override func setSelected(_ selected: Bool, animated: Bool) {
                super.setSelected(selected, animated: animated)
        
                // Configure the view for the selected state
            }
        
        }
        extension WebViewTableViewCell: UIWebViewDelegate {
            func webViewDidFinishLoad(_ webView: UIWebView) {
                heightLayoutConstraint.constant = webView.scrollView.contentSize.height
                viewController?.tableView.beginUpdates()
                viewController?.tableView.endUpdates()
            }
        }

        【讨论】:

          【解决方案10】:

          实现 UITableview 委托 heightforrowatindexpath 返回 webview 框架高度。

          【讨论】:

          • 但是我只在 webViewDidFinishLoad 函数之后得到了 webView 的高度
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-27
          • 1970-01-01
          • 2018-04-09
          • 2019-10-12
          相关资源
          最近更新 更多