提问人:Alexey Zhuravlev 提问时间:10/19/2023 最后编辑:Alexey Zhuravlev 更新时间:10/29/2023 访问量:87
将 UIView CALayer 绘制与 UIScrollView 同步
Synchronize UIView CALayer draw with UIScrollView
问:
我正在尝试制作一个将在 pdf 之上绘制的应用程序。要求将图形形状绑定到页面上的特定位置。
为了解决这个问题,我使用 PDFView 和 CALayer,监听内部 UIScrollView scrollViewDidScroll 和 scrollViewDidZoom 的事件来重绘 CALayer
我找到了两种绘画方法:
我注意到,在第二种情况下,滚动或缩放时,该图明显滞后于 pdf 文档。
问题是,在导致即时重绘的“路径”方法中做了什么“魔术”?是否可以修改“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
}
}
...
答:
0赞
DonMag
10/29/2023
#1
根据 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()
}
}
无需添加任何图层。
评论
CAShapeLayer
with path 由 Apple 工程师高度优化。如果无法运行代码,就很难知道代码在哪里减慢了速度。如果您真的想覆盖绘制,请发布一个我们可以运行的最小可重现示例。draw(in ctx: CGContext)
CAShapeLayer
.path
draw(_ rect: CGRect)
AnnotationsView