我在 NSView 中有一個CATiledLayer ,它是NSScrollView的documentView屬性。Storyboard 設定非常簡單:將NSScrollView添加到默認視圖控制器,并將View類分配給剪切視圖的NSView。
以下代碼繪制了許多隨機顏色的正方形。滾動的作業方式與CATiledLayer中的作業方式完全相同,但縮放效果不佳:

發現了大量的CATiledLayer問題,并且所有建議的解決方案都對我不起作用(例如使用 0 fadeDuration進行子類化或禁用CATransaction操作)。我猜這setNeedsDisplay() 搞砸了,但無法找出正確的方法來做到這一點。如果我使用CALayer,那么我看不到閃爍的問題,但我無法處理里面有數千個盒子的大層。
視圖類源:
import Cocoa
import CoreGraphics
import Combine
let rows = 1000
let columns = 1000
let width = 50.0
let height = 50.0
class View: NSView {
typealias Coordinate = (x: Int, y: Int)
private let colors: [[CGColor]]
private let rect = CGRect(origin: .zero, size: CGSize(width: width, height: height))
private var store = Set<AnyCancellable>()
private var scale: CGFloat {
guard let scrollView = self.superview?.superview as? NSScrollView else { fatalError() }
return NSScreen.main!.backingScaleFactor * scrollView.magnification
}
required init?(coder: NSCoder) {
colors = (0..<rows).map { _ in (0..<columns).map { _ in .random } }
super.init(coder: coder)
setFrameSize(NSSize(width: width * CGFloat(columns), height: height * CGFloat(rows)))
wantsLayer = true
NotificationCenter.default.publisher(for: NSScrollView.didEndLiveMagnifyNotification).sink { [unowned self] _ in
self.layer?.contentsScale = scale
self.layer?.setNeedsDisplay()
}.store(in: &store)
}
override func makeBackingLayer() -> CALayer {
let layer = CATiledLayer()
layer.tileSize = CGSize(width: 1000, height: 1000)
return layer
}
override func draw(_ dirtyRect: NSRect) {
guard let context = NSGraphicsContext.current?.cgContext else { return }
let (min, max) = coordinates(in: dirtyRect)
context.translateBy(x: CGFloat(min.x) * width, y: CGFloat(min.y) * height)
(min.y...max.y).forEach { row in
context.saveGState()
(min.x...max.x).forEach { column in
context.setFillColor(colors[row][column])
context.addRect(rect)
context.drawPath(using: .fillStroke)
context.translateBy(x: width, y: 0)
}
context.restoreGState()
context.translateBy(x: 0, y: height)
}
}
private func coordinates(in rect: NSRect) -> (Coordinate, Coordinate) {
var minX = Int(rect.minX / width)
var minY = Int(rect.minY / height)
var maxX = Int(rect.maxX / width)
var maxY = Int(rect.maxY / height)
if minX >= columns {
minX = columns - 1
}
if maxX >= columns {
maxX = columns - 1
}
if minY >= rows {
minY = rows - 1
}
if maxY >= rows {
maxY = rows - 1
}
return ((minX, minY), (maxX, maxY))
}
}
extension CGColor {
class var random: CGColor {
let random = { CGFloat(arc4random_uniform(255)) / 255.0 }
return CGColor(red: random(), green: random(), blue: random(), alpha: random())
}
}
uj5u.com熱心網友回復:
為了能夠支持放大到CATiledLayer,您設定圖層的levelOfDetailBias. 您無需觀察滾動視圖的放大通知、更改圖層 contentScale 或觸發手動重繪。
這是一個快速實作,它顯示了您在不同縮放級別獲得了哪些型別的dirtyRects:
class View: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
wantsLayer = true
}
required init?(coder: NSCoder) {
super.init(coder: coder)
wantsLayer = true
}
override func makeBackingLayer() -> CALayer {
let layer = CATiledLayer()
layer.tileSize = CGSize(width: 400, height: 400)
layer.levelsOfDetailBias = 3
return layer
}
override func draw(_ dirtyRect: NSRect) {
let context = NSGraphicsContext.current!
let scale = context.cgContext.ctm.a
NSColor.red.setFill()
dirtyRect.frame(withWidth: 10 / scale, using: .overlay)
NSColor.black.setFill()
let string: NSString = "Scale: \(scale)" as NSString
let attributes = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: 40 / scale)]
let size = string.size(withAttributes: attributes)
string.draw(at: CGPoint(x: dirtyRect.midX - size.width / 2, y: dirtyRect.midY - size.height / 2),
withAttributes: attributes)
}
}
當前的繪圖背景關系已經被縮放以匹配當前的縮放級別(并且對于每個細節級別,dirtyRect 變得越來越小)。如果需要,您可以從 CGContext 的變換矩陣中提取當前比例,如上所示。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/449904.html
標籤:迅速 苹果系统 核心动画 catiledlayer
下一篇:如何修復“zsh:找不到命令:python”錯誤?(macOSMonterey12.3、python3.10、AtomIDE、atom-python-run0.9.7)
