我用“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
本身。
如何解决出口这些问题?
作为一般规则(至少根据我的经验),将大型表格视图渲染为
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
):