Cocoa 借鉴可滚动视图的困境
Cocoa drawing on scrollable view dilema
我在 Swift 开发我的第一个 OSX 应用程序,但我迷失在一些 Cocoa 概念中。
我在 NSScrollView 中放置了一个自定义 NSView。我在覆盖的 drawRect 函数中绘制了复杂的图表。图表相当大,但 NSScrollView 会滚动整个视图。
我不满意的是,每次调用 drawRect 时我都会重新绘制整个图表,并且在滚动、调整大小时等时调用它的频率非常高
画图表需要大量的计算,需要画很多形状。每次调用 drawRect 时都这样做似乎是错误的。我知道我可以将绘图限制在可见/脏矩形,但它会更复杂并且需要更多计算。
有没有更好的方法绘制这样的滚动图表?整个图表一次绘制就完美了,以后不用担心重新绘制。
我想在将来为我的图表添加一些交互(鼠标点击)。这可能是重要的信息。
谢谢
如果您的自定义视图在绘图期间不需要 "live",这意味着滚动不会更改视图的内容,那么您可以通过多种方式执行此操作。
我通过了解视图何时滚动来解决此类问题。您可能想要查看的一件事是为来自滚动视图的通知注册您的自定义视图。具体来说 NSScrollViewWillStartLiveScrollNotification
。当视图滚动时,您可以将自定义视图缓存到 NSBitmapImageRep
中并改为绘制它。我以前用一个非常复杂的视图做过这个并且它工作得很好。
另外一个可能更简单的东西是 NSScrollView 的 scrollsDynamically
属性。
在Swift2中,可以使用下面的帮助
在你的awakeFromNib
NSNotificationCenter.defaultCenter().addObserver(self, selector: "startScrolling", name: NSScrollViewWillStartLiveScrollNotification, object: self.scrollView)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "endScrolling", name: NSScrollViewDidEndLiveScrollNotification, object: self.scrollView)
然后
func startScrolling() {
//viewRep is an NSBitmapImageRep variable in your class
//create a bitmap representation of your view
self.lockFocus()
self.viewRep = NSBitmapImageRep(focusedViewRect: self.bounds)
self.unlockFocus()
//set a flag which draw rect uses to know to draw the bitmaprep
//viewRep is a Bool variable in your class
self.scrollingFlag = true
self.needsDisplay = true
}
func endScrolling() {
self.scrollingFlag = false
self.needsDisplay = true
}
你的画法
override func drawRect(dirtyRect: NSRect) {
if self.scrolling {
self.viewRep.drawInRect(....) // you can draw the viewRep here
}
else {
//your normal drawing code here
}
}
我在 Swift 开发我的第一个 OSX 应用程序,但我迷失在一些 Cocoa 概念中。
我在 NSScrollView 中放置了一个自定义 NSView。我在覆盖的 drawRect 函数中绘制了复杂的图表。图表相当大,但 NSScrollView 会滚动整个视图。
我不满意的是,每次调用 drawRect 时我都会重新绘制整个图表,并且在滚动、调整大小时等时调用它的频率非常高
画图表需要大量的计算,需要画很多形状。每次调用 drawRect 时都这样做似乎是错误的。我知道我可以将绘图限制在可见/脏矩形,但它会更复杂并且需要更多计算。
有没有更好的方法绘制这样的滚动图表?整个图表一次绘制就完美了,以后不用担心重新绘制。
我想在将来为我的图表添加一些交互(鼠标点击)。这可能是重要的信息。
谢谢
如果您的自定义视图在绘图期间不需要 "live",这意味着滚动不会更改视图的内容,那么您可以通过多种方式执行此操作。
我通过了解视图何时滚动来解决此类问题。您可能想要查看的一件事是为来自滚动视图的通知注册您的自定义视图。具体来说 NSScrollViewWillStartLiveScrollNotification
。当视图滚动时,您可以将自定义视图缓存到 NSBitmapImageRep
中并改为绘制它。我以前用一个非常复杂的视图做过这个并且它工作得很好。
另外一个可能更简单的东西是 NSScrollView 的 scrollsDynamically
属性。
在Swift2中,可以使用下面的帮助
在你的awakeFromNib
NSNotificationCenter.defaultCenter().addObserver(self, selector: "startScrolling", name: NSScrollViewWillStartLiveScrollNotification, object: self.scrollView)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "endScrolling", name: NSScrollViewDidEndLiveScrollNotification, object: self.scrollView)
然后
func startScrolling() {
//viewRep is an NSBitmapImageRep variable in your class
//create a bitmap representation of your view
self.lockFocus()
self.viewRep = NSBitmapImageRep(focusedViewRect: self.bounds)
self.unlockFocus()
//set a flag which draw rect uses to know to draw the bitmaprep
//viewRep is a Bool variable in your class
self.scrollingFlag = true
self.needsDisplay = true
}
func endScrolling() {
self.scrollingFlag = false
self.needsDisplay = true
}
你的画法
override func drawRect(dirtyRect: NSRect) {
if self.scrolling {
self.viewRep.drawInRect(....) // you can draw the viewRep here
}
else {
//your normal drawing code here
}
}