在 swift 中从分组表创建图像或 pdf 时遇到问题?

问题描述 投票:0回答:1

我用“Inset Grouped”风格创建了

UITableView
。我还对
tableView.layer
应用了阴影(细胞部分有阴影)。

例如我使用这里的代码: 如何在 iOS 中将整个 UITableView 渲染为 UIImage?

func printScreen(){
    UIGraphicsBeginImageContextWithOptions(Table_LigaParcial.contentSize, false, UIScreen.mainScreen().scale)
    Table_LigaParcial.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
    Table_LigaParcial.layer.renderInContext(UIGraphicsGetCurrentContext()!)
    let row = Table_LigaParcial.numberOfRowsInSection(0)
    let numberofRowthatShowinscreen = 4
    var scrollCount = row / numberofRowthatShowinscreen

    for var i=0;i < scrollCount ; i+=1 {
        Table_LigaParcial.scrollToRowAtIndexPath(NSIndexPath(forRow: (i)*numberofRowthatShowinscreen, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
        Table_LigaParcial.layer.renderInContext(UIGraphicsGetCurrentContext()!)
    }

    let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext();
}

根据新的 swift 版本稍微修复了它并使用自定义尺寸,因为

tableView.contentSize
返回错误的尺寸:

    static func printScreen(tableView: UITableView){
        UIGraphicsBeginImageContextWithOptions(CGSize(width: 375, height: 6075 + 34), false, UIScreen.main.scale)
        tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
        tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
        let row = tableView.numberOfRows(inSection: 0)
        let numberofRowthatShowinscreen = 4
        var scrollCount = row / numberofRowthatShowinscreen

        for i in 0..<scrollCount {
            tableView.scrollToRow(at: IndexPath(row: (i)*numberofRowthatShowinscreen, section: 0), at: .top, animated: false)
            tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
        }

        let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext();
        if let data = image.pngData() {
            let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
            print(filename)
            try? data.write(to: filename)
        }
    }

但是仍然存在两个问题:

  • 第一个和最后一个单元格变成圆角矩形(顶部和底部角都是圆角的)
  • 阴影看起来像是由多个相互重叠的阴影组成的

注意:无论您使用哪个单元格 - 即使使用默认值

UITableViewCell
也会发生这种情况。所以问题应该出在 pdf/image 导出或
UITableView
本身。

如何解决出口这些问题?

ios swift uitableview pdf export
1个回答
0
投票

作为一般规则(至少根据我的经验),将大型表格视图渲染为

UIImage
或 PDF 文件是不常见的。更常见的是格式化数据,以便在“页面”格式上看起来更好。

但是,假设您有充分的理由这样做......

A

UITableView
在“幕后”做了很多工作,因为它管理布局、内存使用、选择状态等。

假设我们有 100 个可变高度行,屏幕上只能容纳 6 或 7 行。当视图第一次出现时,表视图只会呈现它需要显示的行。到那时,它只能在最后的 .contentSize.height

猜测
——它无法知道其他 93 个单元格需要多高。

如果单元格高度相同,并且我们用例如

tableView.rowHeight = 120.0
通知表格视图,我们 do 会得到有效的
.contentSize
。否则,对于动态、可变高度的单元格,我们不会获得有效的
.contentSize
,直到我们一直滚动到底部 - 并且单元格已渲染

所以...您可以采取的一种方法是创建两个表视图。一种是可见的,用户可以与之交互,另一种是隐藏的,但设置具有相同的属性、相同的细胞类别、相同的数据等。

然后我们告诉隐藏的表格视图滚动到底部,强制它更新其布局,然后更改其框架高度以匹配其valid

.contentSize.height

这是一些示例代码...

我正在使用

.insetGrouped
,5 个部分,每个部分有 7 行,每第三行将有多行文本(以确认我们正在使用自动调整大小的单元格)。我还添加了一个
UIImageView
来保存
UIImage
捕获的表格视图。

选择任何行都会将整个表格(隐藏的表格)捕获为

UIImage
并显示图像视图。点击图像视图将再次隐藏它:

class RenderTableVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var tableView: UITableView!
    let capturedImageView = UIImageView()
    var outputTableView: UITableView!
    
    let nSections: Int = 5
    let nRows: Int = 7
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView = UITableView(frame: .zero, style: .insetGrouped)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        capturedImageView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(capturedImageView)
        
        // we need to add the "output table view" to a container view
        //  and then hide the container view
        let containerView = UIView()
        outputTableView = UITableView(frame: .zero, style: .insetGrouped)
        containerView.addSubview(outputTableView)
        view.addSubview(containerView)
        containerView.isHidden = true

        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            
            // inset the image view 40-points on all 4 sides
            capturedImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            capturedImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            capturedImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
            capturedImageView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
            
        ])
        
        tableView.register(RenderTableCell.self, forCellReuseIdentifier: RenderTableCell.identifier)
        tableView.dataSource = self
        tableView.delegate = self
        
        outputTableView.register(RenderTableCell.self, forCellReuseIdentifier: RenderTableCell.identifier)
        outputTableView.dataSource = self
        outputTableView.delegate = self
        
        capturedImageView.contentMode = .scaleAspectFit
        capturedImageView.backgroundColor = .systemRed
        
        // start with capturedImageView hidden
        capturedImageView.isHidden = true
        
        // add a tap gesture to the image view so we can hide it on tap
        let t = UITapGestureRecognizer(target: self, action: #selector(hideImageView(_:)))
        capturedImageView.addGestureRecognizer(t)
        capturedImageView.isUserInteractionEnabled = true
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        outputTableView.frame = tableView.frame
        outputTableView.scrollToRow(at: IndexPath(row: nRows - 1, section: nSections - 1), at: .top, animated: false)
        DispatchQueue.main.async {
            self.outputTableView.frame.size.height = self.outputTableView.contentSize.height
        }
    }
    
    @objc func hideImageView(_ sender: UITapGestureRecognizer) {
        capturedImageView.isHidden = true
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return nSections
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return nRows
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: RenderTableCell.identifier, for: indexPath) as! RenderTableCell
        // give every 3rd row a couple lines of text to confirm sizing works
        if indexPath.row % 3 == 0 {
            c.label.text = "\(indexPath)\nLine 2\nLine 3"
        } else {
            c.label.text = "\(indexPath)"
        }
        c.selectionStyle = .none
        return c
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        if !capturedImageView.isHidden {
            capturedImageView.isHidden = true
            return()
        }

        // make sure the hidden table view layout is updated
        outputTableView.setNeedsLayout()
        outputTableView.layoutIfNeeded()

        // set its frame height to match its contentSize.height
        outputTableView.frame.size.height = outputTableView.contentSize.height
        
        // render the table view as a UIImage
        let renderer = UIGraphicsImageRenderer(size: outputTableView.frame.size)
        let imgCapture = renderer.image { ctx in
            self.outputTableView.drawHierarchy(in: .init(origin: .zero, size: self.outputTableView.frame.size), afterScreenUpdates: true)
        }
        capturedImageView.image = imgCapture
        capturedImageView.isHidden = false

    }
}

因此,发布时看起来像这样:

点击一行后就像这样(图像视图设置为带有蓝色背景的

.aspectFit
):

© www.soinside.com 2019 - 2024. All rights reserved.