I want to show a MJPEG stream in a UIImageView.
I have implemented the stream loading and send the images to a CameraViewModel via a delegate.
final class CameraViewModel: ObservableObject {
lazy var cameraService = CameraService(delegate: self)
@Published var cameraImage = UIImage(systemName: "cloud.heavyrain.fill")
func play() {
cameraService.play()
}
func stop() {
cameraService.stop()
}
}
extension CameraViewModel: CameraServiceDelegateProtocol {
func frame(image: UIImage) {
cameraImage = image
print("updated image \(self.cameraImage!)")
}
}
The delegate function gets called with every new image as expected and I'm assigning it to @Published var cameraImage.
On the View side the @Published property is used to populate a UIImageView, but the image is not updating.
struct CameraView: View {
@Binding var showCamera: Bool
@ObservedObject var cameraViewModel: CameraViewModel = CameraViewModel()
var body: some View {
VStack {
Image(uiImage: cameraViewModel.cameraImage!).aspectRatio(contentMode: .fit).frame(height: 200)
Button("Close") {
cameraViewModel.stop()
showCamera.toggle()
}.onAppear() {
cameraViewModel.play()
}
}
}
}
The Image(....) in the VStack is not updating and I can't seem to figure out whats wrong.
For reference, here is the CameraService:
protocol CameraServiceDelegateProtocol {
func frame(image: UIImage) -> Void
}
protocol CameraServiceProtocol {
var rosServiceDelegate: CameraServiceDelegateProtocol { get set }
}
class CameraService: NSObject, ObservableObject {
var cameraServiceDelegate: CameraServiceDelegateProtocol
let realUrl = URL(string: "http://192.168.45.100:8080/stream?topic=/image_raw")
var dataTask: URLSessionDataTask?
var receivedData: NSMutableData = NSMutableData()
var session: URLSession?
init(delegate: CameraServiceDelegateProtocol) {
cameraServiceDelegate = delegate
}
func play() {
session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
dataTask = session?.dataTask(with: realUrl!)
dataTask?.resume()
}
func stop() {
dataTask?.cancel()
}
}
extension CameraService: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
if self.receivedData.length > 0,
let receivedImage = UIImage(data: self.receivedData as Data) {
DispatchQueue.main.async {
self.cameraServiceDelegate.frame(image: receivedImage)
}
self.receivedData = NSMutableData()
}
completionHandler(URLSession.ResponseDisposition.allow) //.Cancel,If you want to stop the download
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.receivedData.append(data)
}
}
In CameraView
replace
@ObservedObject var cameraViewModel
with
@StateObject var cameraViewModel