Multiple Native Platform Views not working

22 views Asked by At

I have three uiViewController with seperate nativePlatformViewFactor for each. But only one is being presented on the window view hierarchy even though I have properly initialised each one of them due to this I have to resort using single uiViewController for these different features based on flags.

AppDelegate.swift

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
      // This is required to make any communication available in the action isolate.
        FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
          GeneratedPluginRegistrant.register(with: registry)
        }
    GeneratedPluginRegistrant.register(with: self)
  
    //runner or flutter
    if #available(iOS 14.0, *) {
      let plugin : FlutterPluginRegistrar = registrar(forPlugin: "flutter")!
      let nativeMlView = FLNativeViewFactory(messenger: plugin.messenger())
      let reportViewFactory = ReportNativeViewFactory(messenger: plugin.messenger())
      let inhalerDetectionViewFactory = InhalerDetectionViewFactory(messenger: plugin.messenger())
      plugin.register(nativeMlView, withId: "native_ml_view")
      plugin.register(reportViewFactory, withId: "edit_report_native_ml_view")
      plugin.register(inhalerDetectionViewFactory, withId: "verify_inhaler_view")
    } else {
        // Fallback on earlier versions
    }
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

In the below viewFactory I am compelled to use MainViewController inspite of OcrViewController because it won't attach its view to superview.

InhalerDetectionViewFactory.swift

@available(iOS 14.0, *)
class InhalerDetectionViewFactory: NSObject, FlutterPlatformViewFactory {
  
  private var messenger: FlutterBinaryMessenger
  
  init(messenger: FlutterBinaryMessenger) {
    self.messenger = messenger
    super.init()
  }
  
  func create(
    withFrame frame: CGRect,
    viewIdentifier viewId: Int64,
    arguments args: Any?
  ) -> FlutterPlatformView {
    return InhalerDetectionNativeView(
      frame: frame,
      viewIdentifier: viewId,
      arguments: args
    )
  }
  
  public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
    return FlutterStandardMessageCodec.sharedInstance()
  }
}

class InhalerDetectionNativeView: NSObject, FlutterPlatformView {
  
  let width: String
  let height: String
  private var _view: UIView
  let medicineList : [String]
  
  init(
    frame: CGRect,
    viewIdentifier viewId: Int64,
    arguments args: Any?
  ) {
    _view = UIView()
    _view.backgroundColor = UIColor.black
    
    
    if let arguments = args as? [String: Any],
       let wdth = arguments["width"] as? String,
       let ht = arguments["height"] as? String,
       let meds = arguments["medicineList"] as? [String] {
      width = wdth;
      height = ht;
      medicineList = meds;
    } else {
      width = "0"
      height = "0"
      medicineList = ["test1", "test2", "test3", "test4", "test5","test6","test7","test8"]
    }
    
    let controller : FlutterViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.keyWindow?.rootViewController as! FlutterViewController
    
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
  
//    let vc = mainStoryboard.instantiateViewController(identifier: "OcrVC")
//    if let ocrVC : OcrViewController = vc as? OcrViewController {
//      ocrVC.channel = FlutterMethodChannel(name: "inhaler_verification", binaryMessenger: controller.binaryMessenger)
//        
//        print("OcrViewController initialised")
//        print(String.init(describing: "Vc hash: \(vc.hash), isLoaded: \(vc.isViewLoaded)"))
//    }
    
    let vc = mainStoryboard.instantiateViewController(identifier: "MainVC")
      if let mainVC : MainViewController = vc as? MainViewController {
          mainVC.channel = FlutterMethodChannel(name: "inhaler_verification", binaryMessenger: controller.binaryMessenger)
            mainVC.stepId = ""
            mainVC.withSpacer = false
            mainVC.isEditReport = false
            mainVC.medicineList = medicineList
          
          print("MainViewController initialised")
          print(String.init(describing: "Vc hash: \(vc.hash), isLoaded: \(vc.isViewLoaded)"))
      }
    
    _view.frame = CGRect(x: 0, y: 0, width: Int(width) ?? 0, height: Int(height) ?? 0)
    _view.addSubview(vc.view)
    super.init()
  }
  
  func view() -> UIView {
    return _view
  }
  
}

Same for the rest of platformViewFactories I am compelled to use MainViewController as the rendering UiView.

MainViewController.swift

import UIKit
import Vision
import Flutter
import AVFoundation
import VisionKit

@available(iOS 14.0, *)
class MainViewController: UIViewController {
    
    var previewLayer: AVCaptureVideoPreviewLayer?
    
    @IBOutlet var imageView: UIImageView!
    
    @IBOutlet weak var labelStack: UIStackView!
    
    @IBOutlet weak var actionLabel: UILabel!
    
    @IBOutlet weak var cameraButton: UIButton!
    
    var stepNumber:Int = 1
    
    var videoCapture: VideoCapture!

    var videoProcessingChain: VideoProcessingChain!
    
    var actionFrameCounts = [String: Int]()
    
    
    @IBOutlet weak var shakeStack: UIStackView!
    
    
    @IBOutlet weak var shakeCountLabel: UILabel!
    
    @IBOutlet weak var timerView: UIView!
    
    @IBOutlet weak var timerLabel: UILabel!
    
    @IBOutlet weak var medVerifyErrorView: UIView!
    
    var channel: FlutterMethodChannel
    let channelName = "take_test"
    var stepId: String
    var stepIndex: Int = 0
    var withSpacer: Bool
    var isEditReport: Bool
  
    var medicineList : [String] = []
    var medicineVerified = false
    
    @IBOutlet weak var instructionLableView: UIView!
    
    private let dataScannerViewController = DataScannerViewController(recognizedDataTypes: [.text()],
                                                                      qualityLevel: .balanced,
                                                                      recognizesMultipleItems: false,
                                                                      isHighFrameRateTrackingEnabled: true,
                                                                      isPinchToZoomEnabled: true,
                                                                      isGuidanceEnabled: true,
                                                                      isHighlightingEnabled: true)
    
    private var scannerAvailable: Bool { DataScannerViewController.isSupported && DataScannerViewController.isAvailable }
    
  init(stepId: String, viewId: Int64, isEditReport: Bool) {
        let controller : FlutterViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.keyWindow?.rootViewController as! FlutterViewController
        channel = FlutterMethodChannel(name: isEditReport ? "report_ocr_scan" : "take_test", binaryMessenger: controller.binaryMessenger)
        self.stepId = stepId
        self.withSpacer = false
        self.isEditReport = isEditReport
        self.medicineList = []
        super.init(nibName: "MainViewController", bundle: Bundle.main)
    }
    
    required init?(coder: NSCoder) {
        channel = FlutterMethodChannel()
        stepId = ""
        withSpacer = false;
        isEditReport = false;
        medicineList = [];
        super.init(coder: coder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Disable the idle timer to prevent the screen from locking.
        UIApplication.shared.isIdleTimerDisabled = true
        hideInstructionView()
        medVerifyErrorView.isHidden = true
        
        if (isEditReport) {
            labelStack.isHidden = true
            shakeStack.isHidden = true
            dataScannerViewController.delegate = self
            
            if scannerAvailable {
                
                present(dataScannerViewController, animated: true)
                view.layer.addSublayer(dataScannerViewController.view.layer)
                dataScannerViewController.view.frame = view.frame
                try? dataScannerViewController.startScanning()
            }
        } else {
          if (medicineList.isEmpty) {
            setupImage()
          } else {
            dataScannerViewController.delegate = self
            
            if scannerAvailable {
              
              present(dataScannerViewController, animated: true)
              view.layer.addSublayer(dataScannerViewController.view.layer)
              dataScannerViewController.view.frame = view.frame
              try? dataScannerViewController.startScanning()
            }
          }
        }
        
        
        channel.setMethodCallHandler(handle)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // Update the device's orientation.
        //videoCapture.updateDeviceOrientation()
    }
  
  private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
      case "next_step":
        let arguments = call.arguments as! [Dictionary<String, Any?>]
        let stepId = arguments.first?["stepId"] as! String
        
        self.stepId = stepId
        result("next step initialized")
        
      case "dealloc_resources":
        print("Method call from flutter to dealloc resources")
        self.videoCapture = nil
        self.videoCapture?.delegate = nil
        self.videoProcessingChain = nil
        self.videoProcessingChain?.delegate = nil
        self.dismiss(animated: false)
        result("receiveFromFlutter success")
      default:
        result(FlutterMethodNotImplemented)
    }
  }
  
  override func viewWillDisappear(_ animated: Bool) {
    print("View will disappear")
    if (!isEditReport) {
      videoCapture = nil
      videoCapture?.delegate = nil
      videoProcessingChain = nil
      videoProcessingChain?.delegate = nil
    }
    super.viewWillDisappear(animated)
  }

      func setupImage() {
        
        let captureSession = AVCaptureSession()
        
      guard let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else{ return }
        
        guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
        
        captureSession.addInput(input)
      
        let dataOutput = AVCaptureVideoDataOutput()
        dataOutput.setSampleBufferDelegate(self, queue:DispatchQueue(label: "videoQueue"))
        captureSession.addOutput(dataOutput)
        
        DispatchQueue.global(qos: .background).async {
          captureSession.startRunning()
        }
        
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.frame
        self.imageView.layer.addSublayer(previewLayer)

        previewLayer.videoGravity = .resizeAspectFill
        self.previewLayer = previewLayer
      self.shakeStack.isHidden = true  
    }
    
    func stopCaptureSession() {
        guard let captureSession = previewLayer?.session else { return }
      
      DispatchQueue.main.async {
        self.previewLayer?.removeFromSuperlayer()
        self.previewLayer = nil
      }
      
        DispatchQueue.global(qos: .background).async {
          if captureSession.isRunning {
            captureSession.stopRunning()
          }
        }
    }
}

I tried various solutions for multiple platformViews but not able to figure out so I am currently using single UIViewController for all the platformViewFactories.

0

There are 0 answers