UITextView
不支持任何类型的表格,但我正在尝试创建一个文本视图实现,它将能够在普通文本旁边显示表格。
基本上:在创建 PDF 时,我的 macOS 端口使用
NSTextTable
渲染一些彼此相邻的段落。我正在尝试想出一种方法在 iOS 上实现这一目标。
TextKit 2 的文档和实际实现仍然有点模糊,但我很感兴趣是否可以使用
NSTextElement
和 NSTextElementProvider
创建我自己的内容类型。如果我没读错的话,我会定义一个由我自己的实现/布局控制的范围?
由于文档非常稀疏,我非常不确定如何实际实现
NSTextElement
,它在保存为 PDF 时也会呈现为文本 - 或者这是否可能。
任何帮助或提示,我们将不胜感激。
没有真正的方法可以解决实际问题,但您可以创建一个类似于表格的视图提供程序。
这个例子只支持一行,所以它基本上只是一个列视图,但你应该能够很容易地扩展它。如果您想要一个二维表格,调整单元格的大小将是最棘手的部分。
实际的文本附件,它注册一个视图提供程序来显示假表:
@objc public class TableAttachment:NSTextAttachment {
// We need to forward these values to the provider when needed
var cells:[TextTableCell]?
var spacing = 0.0
var margin = 0.0
@objc public init(cells:[TextTableCell], spacing:CGFloat = 0.0, margin:CGFloat = 0.0) {
NSTextAttachment.registerViewProviderClass(TextTableProvider.self, forFileType: "public.data")
self.cells = cells
self.spacing = spacing
self.margin = margin
super.init(data: nil, ofType: "public.data")
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
充当表格单元格的标签:
@objc public class TextTableCell:UILabel {
var width = 0.0
@objc public init(content:NSAttributedString, width: Double, height:Double = 0.0) {
self.width = width
let rect = CGRect(x: 0.0, y: 0.0, width: width, height: height)
super.init(frame: rect)
self.attributedText = content
self.lineBreakMode = .byWordWrapping
self.numberOfLines = 50 // Arbitrary value
// Make the text field behave correctly in a PDF
self.layer.shouldRasterize = false
}
var height:CGFloat {
guard var attributedText = self.attributedText else { return 0.0 }
// Calculate frame size
let rect = attributedText.boundingRect(with: CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
self.frame.size.height = rect.size.height
self.frame.origin.y = 0
return self.frame.height
}
}
最后,文本附件提供程序,它接收一堆表格单元格并显示它们。
@objc public class TextTableProvider : NSTextAttachmentViewProvider {
var cells:[TextTableCell] = []
var spacing = 0.0
var margin = 0.0
override init(textAttachment: NSTextAttachment, parentView: UIView?, textLayoutManager: NSTextLayoutManager?, location: NSTextLocation) {
if let attachment = textAttachment as? TableAttachment {
self.cells = attachment.cells ?? []
self.spacing = attachment.spacing
self.margin = attachment.margin
}
super.init(textAttachment: textAttachment, parentView: parentView, textLayoutManager: textLayoutManager, location: location)
tracksTextAttachmentViewBounds = true
}
@objc public override func loadView() {
super.loadView()
view = UIView()
// Add subviews
var x = self.margin
for column in self.cells {
column.frame.origin.x = x
view?.addSubview(column)
x = spacing + CGRectGetMaxX(column.frame)
}
}
/// Returns height of the tallest column in this view
@objc public override func attachmentBounds(
for attributes: [NSAttributedString.Key : Any],
location: NSTextLocation,
textContainer: NSTextContainer?,
proposedLineFragment: CGRect,
position: CGPoint
) -> CGRect {
var height = 0.0
for column in self.cells {
let colHeight = column.height
if colHeight > height { height = colHeight }
}
// I have no idea what this - 15.0 is, something for my own purposes, probably
return CGRect(x: 0, y: 0, width: proposedLineFragment.width - 15.0, height: height)
}
}