我正在尝试制作一个可以在 pdf 上绘图的应用程序。要求图形形状绑定到页面上的特定位置。
为了解决这个问题,我使用PDFView和CALayer,监听内部UIScrollView的scrollViewDidScroll和scrollViewDidZoom的事件来重绘CALayer
我找到了两种绘画方法:
我注意到在第二种情况下,当滚动或缩放时,图形明显落后于 pdf 文档。
问题是,“path”方法到底有什么“魔力”,可以实现即时重画?是否可以修改“draw”方法,使其与“path”同步工作?那么“路径”方法未来的稳定性如何?
查看要编译和运行的项目:https://github.com/alzhuravlev/DrawTest.git
示例代码:
class AnnotationsLayer: CAShapeLayer {
...
override func draw(in ctx: CGContext) {
guard let backgroundView else { return }
ctx.setFillColor(UIColor.red.withAlphaComponent(0.4).cgColor)
ctx.setStrokeColor(UIColor.green.withAlphaComponent(1.0).cgColor)
ctx.setLineWidth(10)
backgroundView.pages.forEach { page in
let p1 = backgroundView.convert(point: CGPoint(x: page.size.width/3, y: page.size.height/3), from: page.index)
let p2 = backgroundView.convert(point: CGPoint(x: page.size.width/3*2, y: page.size.height/3*2), from: page.index)
ctx.move(to: CGPoint(x: p1.x, y: p1.y))
ctx.addLine(to: CGPoint(x: p2.x, y: p1.y))
ctx.addLine(to: CGPoint(x: p2.x, y: p2.y))
ctx.addLine(to: CGPoint(x: p1.x, y: p2.y))
ctx.closePath()
}
ctx.drawPath(using: CGPathDrawingMode.fillStroke)
}
func rebuildShapes() {
// false - example 1
// true - example 2
if (false) {
setNeedsDisplay()
} else {
guard let backgroundView else { return }
let p = UIBezierPath()
fillColor = UIColor.red.withAlphaComponent(0.4).cgColor
strokeColor = UIColor.green.withAlphaComponent(1.0).cgColor
lineWidth = 10
backgroundView.pages.forEach { page in
let p1 = backgroundView.convert(point: CGPoint(x: page.size.width/3, y: page.size.height/3), from: page.index)
let p2 = backgroundView.convert(point: CGPoint(x: page.size.width/3*2, y: page.size.height/3*2), from: page.index)
p.move(to: CGPoint(x: p1.x, y: p1.y))
p.addLine(to: CGPoint(x: p2.x, y: p1.y))
p.addLine(to: CGPoint(x: p2.x, y: p2.y))
p.addLine(to: CGPoint(x: p1.x, y: p2.y))
p.close()
}
path = p.cgPath
}
}
...
根据OP的评论,这是我将使用的内容,而不是在
draw(in ctx: CGContext)
中覆盖CAShapeLayer
:
class AnnotationsView: UIView {
var backgroundView: BackgroundView? {
didSet {
print("did set background view")
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
backgroundColor = .clear
}
func rebuildShapes() {
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let backgroundView else { return }
let p = UIBezierPath()
let fillColor = UIColor.red.withAlphaComponent(0.4)
let strokeColor = UIColor.green.withAlphaComponent(1.0)
let lineWidth = 10
backgroundView.pages.forEach { page in
let p1 = backgroundView.convert(point: CGPoint(x: page.size.width/3, y: page.size.height/3), from: page.index)
let p2 = backgroundView.convert(point: CGPoint(x: page.size.width/3*2, y: page.size.height/3*2), from: page.index)
let r: CGRect = .init(x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y)
if r.intersects(rect) {
p.append(.init(rect: r))
}
}
fillColor.setFill()
p.fill()
strokeColor.setStroke()
p.lineWidth = CGFloat(lineWidth)
p.stroke()
}
}
无需添加任何层。