MacOS SwiftUI中创建NSImage并显示的两类技术问题求助
问题描述
环境信息
- Xcode 15.2
- Mac mini M1 64位
- macOS Sonoma 14.6.1
此前已实现通过C++视频回调函数向macOS的SwiftUI视图传递帧计数并显示,现尝试传递RGB原始数据及数据大小以显示图像,但遇到以下问题:
相关代码
1. MyCLibrary.h
typedef void callback_t1(uint8_t* rgbBufferPtr, uint32_t rgbBufferBytes); callback_t1* callback1 = NULL;
2. CWrapperBridging.h
void setCallBack1(callback_t1 _callback1);
3. CWrapper.cpp
std::vector<uint8_t> vctFrameData; extern "C" void setCallBack1(callback_t1 _callback1) { callback1 = _callback1; } extern "C" void videoCallbackFunction(void* RGBbufferPtr, uint32_t RGBbufferBytes) { int sz = (int)vctFrameData.size(); if(sz != (int)RGBbufferBytes) { vctFrameData.resize(RGBbufferBytes); std::cout << "*** vctFrameData is resized @ videoCallbackFunction()" << std::endl; } std::memcpy(&vctFrameData[0], RGBbufferPtr, RGBbufferBytes); uint8_t* buffer_ptr = &vctFrameData[0]; if(callback1) callback1(buffer_ptr, RGBbufferBytes); }
4. MyCLibraryInterface.swift
import Foundation import AppKit class MyCLibraryInterface: ObservableObject { @Published var nsimage: NSImage static let shared = MyCLibraryInterface() init() { setCallBack1 { let newNsImage = ??? // 不知道怎么创建NSImage DispatchQueue.main.async { MyCLibraryInterface.shared.nsimage = newNsImage as NSImage } } } }
5. ContentView.swift
import SwiftUI struct ContentView: View { @ObservedObject var myCLibInterface: MyCLibraryInterface = MyCLibraryInterface.shared var body: some View { VStack { Image(myCLibInterface.nsimage) // 报错:<Missing argument label 'nsImage:' in call>,怎么解决? .frame(width: 640.0, height: 480.0) } .padding() } } #Preview { ContentView() }
具体问题
- 在
MyCLibraryInterface类的init方法中调用setCallBack1时,如何创建NSImage? - 使用
Image(myCLibInterface.nsimage)时出现<Missing argument label 'nsImage:' in call>错误,该如何解决?
解决方案
问题1:从RGB原始数据创建NSImage
要从RGB缓冲区生成NSImage,需要先将原始数据封装为CGImage,再转换为NSImage。注意必须明确图像的宽、高、像素格式(比如是24位RGB还是32位RGBA),以下以常见的640×480分辨率、24位RGB格式为例(可根据实际视频参数调整):
修改MyCLibraryInterface的回调代码:
init() { // 初始化默认占位图,避免nil报错 self.nsimage = NSImage(size: CGSize(width: 640, height: 480)) setCallBack1 { rgbBufferPtr, rgbBufferBytes in // 替换为实际视频帧的宽高和像素参数 let width = 640 let height = 480 let bytesPerPixel = 3 // 24位RGB每个像素占3字节 let bytesPerRow = width * bytesPerPixel // 校验缓冲区大小是否匹配 guard rgbBufferBytes == width * height * bytesPerPixel else { print("RGB缓冲区大小与图像参数不匹配") return } // 将C指针数据转为Swift Data let data = Data(bytes: rgbBufferPtr, count: Int(rgbBufferBytes)) guard let dataProvider = CGDataProvider(data: data as CFData) else { print("创建CGDataProvider失败") return } // 创建CGImage guard let cgImage = CGImage( width: width, height: height, bitsPerComponent: 8, // 每个颜色分量8位 bitsPerPixel: bytesPerPixel * 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), // 无alpha通道 provider: dataProvider, decode: nil, shouldInterpolate: true, intent: .defaultIntent ) else { print("创建CGImage失败") return } // 转换为NSImage let newNsImage = NSImage(cgImage: cgImage, size: CGSize(width: width, height: height)) // 切换到主线程更新UI DispatchQueue.main.async { MyCLibraryInterface.shared.nsimage = newNsImage } } }
补充:如果是32位RGBA格式,需将
bytesPerPixel改为4,bitsPerPixel改为32,bitmapInfo改为CGImageAlphaInfo.premultipliedLast.rawValue。建议从C++端同时传递宽高参数,避免硬编码。
问题2:修复Image初始化的参数标签错误
SwiftUI的Image初始化器接收NSImage时,必须显式指定参数标签nsImage:,修改代码如下:
Image(nsImage: myCLibInterface.nsimage) .frame(width: 640.0, height: 480.0)
内容的提问来源于stack exchange,提问作者Wunian
相关产品推荐
相关产品推荐

