我有一个带有两个视图的 macOS Swift 应用程序。一个是 Metal MTKView,它绘制一组动画 3D 对象,第二个是使用 BezierPath 绘制的图形。 Metal 视图控制器类称为 GameViewController,Metal 绘图是在其称为 Renderer 的委托中完成的。该图是在 GraphView 类的实例中完成的,并使用称为“graph”的插座连接到 GameViewController。这是代码的概要:
class GameViewController: NSViewController {
@IBOutlet weak var graph: GraphView!
@IBAction func h1_Slider(_ sender: NSSlider) {
h1_length = sender.floatValue
dodraw() // This is only here to make a point about - it is not used normally
}
func dodraw() {
graph.needsDisplay = true
return
}
<lots of other stuff to set up Metal drawing>
}
class Renderer: NSObject, MTKViewDelegate {
public var graphRef = GameViewController() //. Allows access to GameViewController from elsewhere
<lots of stuff to set up Metal>
func draw(in view: MTKView) { // Metal drawing loop
grafRef.dodraw() // this is supposed to cause GraphView to update its drawing
<this is where the Metal drawing is done>
}
}
class GraphView:NSView
{
override func draw(_ dirtyRect:CGRect) {
<This is where the Bezier drawing takes place in a View in the Storyboard View identified as an instance of GraphView
}
}
两个绘图组件单独工作都很好。我想连接它们。
基本上,Metal 绘图的内部循环每秒发生 60 次,通过引用 graphRef 调用 GameViewController 中的 dodraw() 方法。这应该将 GraphView 中的 needDisplay Bool 设置为 true,以便可以绘制图形。相反,我得到“致命错误:隐式展开可选值时意外发现 nil”,并且图形用红色下划线表示。我还进入 Xcode 输出窗口:“[Nib Loading] 无法将(控制器)出口从 (Vectors.GameViewController) 连接到 (MTKView):缺少设置器或实例变量”
如果我用 print("Q") 替换 dodraw() 方法中的“needsDisplay = true”并且不执行任何其他操作,则字母 Q 会在 Xcode 的输出窗口中打印多次。这表明Renderer中的graphRef链接工作正常。同样,如果我放回“graph.needsDisplay = true”并删除 Metal 绘图循环中的 dodraw() 调用,则当我在 GameViewController 类中移动滑块时会显示图形。这表明“graph.needsDisplay = true”语句似乎没有问题,并且graph.needsDisplay不为零。
我认为这是一个非常奇怪的行为:我似乎可以通过附加“图表”来调用needsDisplay = true命令。在它之前,我可以毫无困难地从 Metal 内循环引用 dodraw() 方法。唯一的麻烦是我不能同时做这些。出了什么问题?这是对 Swift 工作原理的误解吗?
public var graphRef = GameViewController()
创建一个新的 GameViewController
。要使 graphRef
成为对 Storyboard 中现有 GameViewController
的引用,请将 graphRef
参数添加到 Renderer.init
并传递视图控制器。从 Xcode macOS 游戏模板开始:
class Renderer: NSObject, MTKViewDelegate {
// other properties
public var graphRef: GameViewController //. Allows access to GameViewController from elsewhere
init?(metalKitView: MTKView, graphRef: GameViewController) {
self.graphRef = graphRef
// other init stuff
}
// other methods
}
class GameViewController: NSViewController {
// properties
override func viewDidLoad() {
// other stuff
guard let newRenderer = Renderer(metalKitView: mtkView, graphRef: self) else {
print("Renderer cannot be initialized")
return
}
// other renderer stuff
}
}