Cannot Display MTKView on a sheeted view on macOS15

I use xcode16 and swiftUI for programming on a macos15 system. There is a problem. When I render a picture through mtkview, it is normal when displayed on a regular view. However, when the view is displayed through the .sheet method, the image cannot be displayed. There is no error message from xcode.

import Foundation
import MetalKit
import SwiftUI

struct CIImageDisplayView: NSViewRepresentable {
    typealias NSViewType = MTKView
    
    var ciImage: CIImage
    init(ciImage: CIImage) {
        self.ciImage = ciImage
    }
    
    func makeNSView(context: Context) -> MTKView {
        let view = MTKView()
        view.delegate = context.coordinator
        view.preferredFramesPerSecond = 60
        view.enableSetNeedsDisplay = true
        view.isPaused = true
        view.framebufferOnly = false
        if let defaultDevice = MTLCreateSystemDefaultDevice() {
            view.device = defaultDevice
        }
        view.delegate = context.coordinator
        return view
    }
    
    func updateNSView(_ nsView: MTKView, context: Context) {
        
    }
    
    func makeCoordinator() -> RawDisplayRender {
        RawDisplayRender(ciImage: self.ciImage)
    }
    
    class RawDisplayRender: NSObject, MTKViewDelegate {
        // MARK: Metal resources

        var device: MTLDevice!
        var commandQueue: MTLCommandQueue!
        
        // MARK: Core Image resources

        var context: CIContext!
        
        var ciImage: CIImage
        
        init(ciImage: CIImage) {
            self.ciImage = ciImage
            self.device = MTLCreateSystemDefaultDevice()
            self.commandQueue = self.device.makeCommandQueue()
            self.context = CIContext(mtlDevice: self.device)
        }
        
        func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
        
        func draw(in view: MTKView) {
            guard let currentDrawable = view.currentDrawable,
                  let commandBuffer = commandQueue.makeCommandBuffer()
            else {
                return
            }
            
            let dSize = view.drawableSize
        
            let drawImage = self.ciImage
            
            let destination = CIRenderDestination(width: Int(dSize.width),
                                                  height: Int(dSize.height),
                                                  pixelFormat: view.colorPixelFormat,
                                                  commandBuffer: commandBuffer,
                                                  mtlTextureProvider: { () -> MTLTexture in
                                                      return currentDrawable.texture
                                                  })
            _ = try? self.context.startTask(toClear: destination)
            _ = try? self.context.startTask(toRender: drawImage, from: drawImage.extent,
                                            to: destination, at: CGPoint(x: (dSize.width - drawImage.extent.width) / 2, y: 0))
            commandBuffer.present(currentDrawable)
            commandBuffer.commit()
        }
    }
}

struct ShowCIImageView: View {
    
    let cii = CIImage.init(contentsOf: Bundle.main.url(forResource: "9-10", withExtension: "jpg")!)!
     
    var body: some View {
        CIImageDisplayView.init(ciImage: cii).frame(width: 500, height: 500).background(.red)
    }
}


struct ContentView: View {
    
    @State var showImage = false

    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            ShowCIImageView()
            Button {
                showImage = true
                
            } label: {
                Text("showImage")
            }

        }
        .frame(width: 800, height: 800)
        .padding()
        .sheet(isPresented: $showImage) {
            ShowCIImageView()
        }
    }
}


Answered by DTS Engineer in 807467022

As mentioned elsewhere, this appears to be a constraint on the .sheet View modifier.

In particular, .sheet is expected to work with an NSViewControllerRepresentable i.e. a wrapped NSViewController, instead of an NSViewRepresentable (wrapped NSView). See https://developer.apple.com/documentation/swiftui/nsviewcontrollerrepresentable. See https://developer.apple.com/documentation/swiftui/nsviewrepresentable.

The .sheet presents whatever content the NSViewControllerRepresentable renders.

That said, to present your image in a .sheet you will need to create an NSViewControllerRepresentable with an MTKView that renders the image.

As mentioned elsewhere, this appears to be a constraint on the .sheet View modifier.

In particular, .sheet is expected to work with an NSViewControllerRepresentable i.e. a wrapped NSViewController, instead of an NSViewRepresentable (wrapped NSView). See https://developer.apple.com/documentation/swiftui/nsviewcontrollerrepresentable. See https://developer.apple.com/documentation/swiftui/nsviewrepresentable.

The .sheet presents whatever content the NSViewControllerRepresentable renders.

That said, to present your image in a .sheet you will need to create an NSViewControllerRepresentable with an MTKView that renders the image.

Cannot Display MTKView on a sheeted view on macOS15
 
 
Q