macos - 使用更新的数据不断重绘路径
问题描述
我正在开发一个音频可视化器 MacOS 应用程序,我想使用 Quartz/CoreGraphics 来呈现与播放音频相协调的时变频谱。我的渲染器代码是:
import Cocoa
类渲染器:NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
NSColor.white.setFill()
bounds.fill()
guard let context = NSGraphicsContext.current?.cgContext else {return}
var x : CGFloat = 0.0
var y : CGFloat = 0.0
context.beginPath()
context.move(to: CGPoint(x: x, y: y))
for bin in 0 ..< 300 {
x = CGFloat(bin)
y = CGFloat(Global.spectrum[bin])
context.addLine(to: CGPoint(x: x, y: y))
}
context.setStrokeColor(CGColor( red: 1, green: 0, blue: 0, alpha: 1))
context.setLineWidth(1.0)
context.strokePath()
self.setNeedsDisplay(dirtyRect)
}
}
这将绘制一次路径 - 使用频谱 [] 数组的初始全零值 - 然后无限期地继续绘制相同的全零线。它不会使用频谱 [] 数组中的新值进行更新。我使用 print() 语句来验证值本身是否正在更新,但绘图函数不会使用更新的频谱值重新绘制路径。我究竟做错了什么?
解决方案
以下演示展示了如何使用由单独类中的计时器创建的随机数更新 NSView 以希望模仿您的项目。它可以在 Xcode 中运行,方法是为 MacOS 设置一个 Swift 项目,将源代码复制/粘贴到一个名为“main.swift”的新文件中,并删除 Apple 提供的 AppDelegate。使用类似于您发布的绘图功能。
import Cocoa
var view : NSView!
var data = [Int]()
public extension Array where Element == Int {
static func generateRandom(size: Int) -> [Int] {
guard size > 0 else {
return [Int]()
}
return Array(0..<size).shuffled()
}
}
class DataManager: NSObject {
var timer:Timer!
@objc func fireTimer() {
data = Array.generateRandom(size:500)
view.needsDisplay = true
}
func startTimer(){
timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
}
}
let dataMgr = DataManager()
class View: NSView {
override func draw(_ rect: NSRect) {
super.draw(rect)
NSColor.white.setFill()
bounds.fill()
guard let gc = NSGraphicsContext.current?.cgContext else {return}
var xOld : CGFloat = 0.0
var yOld : CGFloat = 0.0
var xNew : CGFloat = 0.0
var yNew : CGFloat = 0.0
var counter : Int = 0
gc.beginPath()
gc.move(to: CGPoint(x: xOld, y: yOld))
for i in 0 ..< data.count {
xNew = CGFloat(counter)
yNew = CGFloat(data[i])
gc.addLine(to: CGPoint(x: xNew, y: yNew))
xOld = xNew;
yOld = yNew;
counter = counter + 1
}
gc.setStrokeColor(CGColor( red: 1, green: 0, blue: 0, alpha: 1))
gc.setLineWidth(1.0)
gc.strokePath()
}
}
class ApplicationDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
@objc func myStartAction(_ sender:AnyObject ) {
dataMgr.startTimer()
}
@objc func myStopAction(_ sender:AnyObject ) {
dataMgr.stopTimer()
}
func buildMenu() {
let mainMenu = NSMenu()
NSApp.mainMenu = mainMenu
// **** App menu **** //
let appMenuItem = NSMenuItem()
mainMenu.addItem(appMenuItem)
let appMenu = NSMenu()
appMenuItem.submenu = appMenu
appMenu.addItem(withTitle: "Quit", action:#selector(NSApplication.terminate), keyEquivalent: "q")
}
func buildWnd() {
data = Array.generateRandom(size: 500)
let _wndW : CGFloat = 800
let _wndH : CGFloat = 600
window = NSWindow(contentRect: NSMakeRect( 0, 0, _wndW, _wndH ), styleMask:[.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false)
window.center()
window.title = "Swift Test Window"
window.makeKeyAndOrderFront(window)
// **** Start Button **** //
let startBtn = NSButton (frame:NSMakeRect( 30, 20, 95, 30 ))
startBtn.bezelStyle = .rounded
startBtn.title = "Start"
startBtn.action = #selector(self.myStartAction(_:))
window.contentView!.addSubview (startBtn)
// **** Stop Button **** //
let stopBtn = NSButton (frame:NSMakeRect( 230, 20, 95, 30 ))
stopBtn.bezelStyle = .rounded
stopBtn.title = "Stop"
stopBtn.action = #selector(self.myStopAction(_:))
window.contentView!.addSubview (stopBtn)
// **** Custom view **** //
view = View( frame:NSMakeRect(20, 60, _wndW - 40, _wndH - 80))
view.autoresizingMask = [.width, .height]
window.contentView!.addSubview (view)
// **** Quit btn **** //
let quitBtn = NSButton (frame:NSMakeRect( _wndW - 50, 10, 40, 40 ))
quitBtn.bezelStyle = .circular
quitBtn.autoresizingMask = [.minXMargin,.maxYMargin]
quitBtn.title = "Q"
quitBtn.action = #selector(NSApplication.terminate)
window.contentView!.addSubview(quitBtn)
}
func applicationDidFinishLaunching(_ notification: Notification) {
buildMenu()
buildWnd()
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
let applicationDelegate = ApplicationDelegate()
// **** main.swift **** //
let application = NSApplication.shared
application.setActivationPolicy(NSApplication.ActivationPolicy.regular)
application.delegate = applicationDelegate
application.activate(ignoringOtherApps:true)
application.run()
推荐阅读
- c++ - 这个模板类定义了行为吗?
- android - Google Play 商店中的“已被您删除”应用状态是什么意思
- javascript - 创建一个遍历字符串块并将每一行分解为子组件的函数
- reactjs - 由于 React 挂钩中的依赖数组内的函数,无限重新渲染
- mysql - 如何在 Sequelize 中处理具有不同编码的列?
- javascript - 如何在 JavaScript 中一个接一个地播放音频
- c++ - 我的代码有什么问题?它显示了不需要的结果
- javascript - 在列表中呈现之前运行一些 javascript 代码
- rsync - Rsync 1 大文件不更新远程文件
- c# - IntelliSense 完成初始化,但尝试加载文档时出错