I write some code to show an image dragged from other apps(such as web browser, photos, etc). I make a delegate to perform the drop.
If DropInfo has an image item, I will try to retrieve the data as uiimage by NSItemProvider.loadObject first. If errors occur during loading, I will ask DropInfo again whether it has a url item. If the answer is YES, I will try to retrieve URL by NSItemProvider.loadObject.
It means the second loadObject will be nested in the first one. When running the simulator, I find the second loadObject completion handler is never called which is supposed to be called when I try to retrieve URL. Do I miss anything?
import SwiftUI
import Combine
struct ContentView: View{
#State private var img: UIImage?
var body: some View{
Image(uiImage: img != nil ? img! : UIImage(systemName: "photo")!)
.resizable()
.scaledToFit()
.onDrop(of: [.image], delegate: ImageDropController(img: $img))
}
}
class ImageDropController: DropDelegate{
#Binding var img: UIImage?
init(img: Binding<UIImage?>) {
_img = img
}
func performDrop(info: DropInfo) -> Bool {
if getImgFromImage(info: info){
return true
}else{
return false
}
}
func getImgFromImage(info: DropInfo) -> Bool{
guard info.hasItemsConforming(to: [.image]) else {
return getImgFromUrl(info: info)
}
createImgFromImage(from: info.itemProviders(for: [.image]).first!,info: info)
return true
}
func createImgFromImage(from provider: NSItemProvider, info: DropInfo){
provider.loadObject(ofClass: UIImage.self) { image, error in
var unwrappedImage: UIImage?
if let error = error {
print("unwrapImage failed: ", error.localizedDescription)
_ = self.getImgFromUrl(info: info)
} else {
unwrappedImage = image as? UIImage
}
if let image = unwrappedImage{
DispatchQueue.main.async {
self.img = image
}
}
}
}
func getImgFromUrl(info: DropInfo) -> Bool{
guard info.hasItemsConforming(to: [.url]) else {
return false
}
createImgFromUrl(from: info.itemProviders(for: [.url]).first!)
return true
}
private func createImgFromUrl(from provider: NSItemProvider){
var fetchUrl: URL?
print("create from url")
_ = provider.loadObject(ofClass: URL.self) { url, error in
print("nested handler") <<------- never be called
if let error = error {
print("unwrapUrl failed: ", error.localizedDescription)
} else {
fetchUrl = url
print("url", fetchUrl?.description)
}
if let url = fetchUrl{
// Do some data fetch work using url
}
}
}
}
I modified this code for MFMailComposeViewController to work with MFMessageComposeViewController. For the most part, it works. However, I am having a problem closing the messaging interface. The problem I am having is that when I tap cancel on the messaging interface nothing happens.
import SwiftUI
import UIKit
import MessageUI
struct MessengerView: UIViewControllerRepresentable{
#Binding var isShowing: Bool
#Binding var result: Result<MessageComposeResult, Error>?
class Coordinator: NSObject, MFMessageComposeViewControllerDelegate {
#Binding var isShowing: Bool
#Binding var result: Result<MessageComposeResult, Error>?
init(isShowing: Binding<Bool>,
result: Binding<Result<MessageComposeResult, Error>?>) {
_isShowing = isShowing
_result = result
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
defer {
isShowing = false
}
self.result = .success(result)
controller.dismiss(animated: true, completion: nil)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing, result: $result)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MessengerView>) -> MFMessageComposeViewController {
let vc = MFMessageComposeViewController()
vc.recipients = ["4085551212"]
vc.body = "Hello from California!"
return vc
}
func updateUIViewController(_ uiViewController: MFMessageComposeViewController,
context: UIViewControllerRepresentableContext<MessengerView>) {
}
}
In Usage:
var body: some View {
NavigationView{
if viewModel.signedIn {
VStack {
if MFMessageComposeViewController.canSendText() {
Button("Show messenger view") {
self.isShowingMailView.toggle()
}
} else {
Text("Can't send messages from this device")
}
if result != nil {
Text("Result: \(String(describing: result))")
.lineLimit(nil)
}
}
.sheet(isPresented: $isShowingMailView) {
MessengerView(isShowing: self.$isShowingMailView, result: self.$result)
}
code I modified
Is there something wrong with my messageComposeViewController
I am trying to use swiftUI to be able to create a custom EPUB reader. Ive looked around at some but none fit my needs. I want to be able to customize it. The issue I have ran into is being able to Highlight text while reading either Orange, blue, green, etc. When highlight a text and then the menu bar pops up and I click on my custom MenuBar color the app crashes. I found this article on highlighting text but uses the UIkit and not SwiftUI. Ive been trying to "translate"(not sure what the correct term is)it to use it with SwiftUI but crashes due to unrecognized selector.Im thinking I am not setting up the things correct. Not sure if it is worth using SwiftUI anymore and just switching my app to UIKit at this point since I have not been able to find many resources using swiftUI. Here is article to highlight text : https://dailong.medium.com/highlight-text-in-wkwebview-1659a19715e6
Just started learning swiftUI so not sure if the way the WebView is setup is correct.
Here is the gitHub link to all of the code https://github.com/longvudai/demo/tree/master/highlight-webview/highlight-webview With the SwiftUI all i did was copy and paste the files. Only difference is with SwiftUI was wrapping the WebView other than that everything else is the same.
SWIFTUI
`struct WebView: UIViewRepresentable {
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var webView: CustomView?
var serializedObject: SerializedObject?
private var dataStack = Stack<Highlights>()
func webView(_ webView: WKWebView?, didFinish navigation: WKNavigation!) {
self.webView = webView as? CustomView
}
// receive message from wkwebview
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
if let markerHandler = MarkerScript.Handler(message) {
guard
let dataString = message.body as? String,
let data = dataString.data(using: .utf8)
else { return }
let decoder = JSONDecoder()
guard let serialized = try? decoder.decode(
SerializedObject.self,
from: data
) else { return }
receiveMarkerMessage(markerHandler, data: serialized)
}
}
func receiveMarkerMessage(_ handler: MarkerScript.Handler, data: SerializedObject) {
switch handler {
case .serialize:
serializedObject = data
// your callback here
let script = MarkerScript.Evaluate.clearSelection()
self.webView?.evaluateJavaScript(script)
case .erase:
serializedObject = data
let highlights = data.highlights
let listId = highlights.map { $0.id }
guard let top = dataStack.top else { return }
let newData = top.filter { listId.contains($0.id) }
if newData != top {
dataStack.push(newData)
}
}
}
func highlight(_ color: MarkerColor) {
let script =
MarkerScript.Evaluate.highlightSelectedTextWithColor(color)
webView?.evaluateJavaScript(script)
print("highlightfunction")
}
func removeAll() {
let script = MarkerScript.Evaluate.removeAllHighlights()
self.webView?.evaluateJavaScript(script)
dataStack.push([])
}
func erase() {
let script = MarkerScript.Evaluate.erase()
self.webView?.evaluateJavaScript(script)
}
#objc func highlightthiscolor() {
highlight(MarkerColor.orange)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
func makeUIView(context: Context) -> CustomView {
let coordinator = makeCoordinator()
let configuration = WKWebViewConfiguration()
let uc = configuration.userContentController
uc.addUserScript(WKUserScript.injectViewPort())
// Jquery
uc.addUserScript(JQueryScript.core())
// Rangy
uc.addUserScript(RangyScript.core())
uc.addUserScript(RangyScript.classapplier())
uc.addUserScript(RangyScript.highlighter())
uc.addUserScript(RangyScript.selectionsaverestore())
uc.addUserScript(RangyScript.textrange())
// Marker
uc.addUserScript(MarkerScript.css())
uc.addUserScript(MarkerScript.jsScript())
uc.add(coordinator, name: MarkerScript.Handler.serialize.rawValue)
uc.add(coordinator, name: MarkerScript.Handler.erase.rawValue)
let _wkwebview = CustomView(frame: .zero, configuration: configuration)
_wkwebview.navigationDelegate = coordinator
return _wkwebview
}
func updateUIView(_ webView: CustomView, context: Context) {
guard let path: String = Bundle.main.path(forResource: "sample", ofType: "html") else { return }
let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
addCustomContextMenu()
}
func addCustomContextMenu(){
//Has to be type of WKWebView
let colorOrange:UIMenuItem = UIMenuItem(title: "Orange", action: #selector(Coordinator.highlightthiscolor))
UIMenuController.shared.menuItems = [colorOrange]
}
}`
UIKit
protocol MarkerLogic {
func erase()
func highlight(_ color: MarkerColor)
func removeAll()
}
class Marker: NSObject {
weak var webView: WKWebView?
var serializedObject: SerializedObject?
private var dataStack = Stack<Highlights>()
}
extension Marker: MarkerLogic {
func highlight(_ color: MarkerColor) {
let script =
MarkerScript.Evaluate.highlightSelectedTextWithColor(color)
webView?.evaluateJavaScript(script)
}
func removeAll() {
let script = MarkerScript.Evaluate.removeAllHighlights()
webView?.evaluateJavaScript(script)
dataStack.push([])
}
func erase() {
let script = MarkerScript.Evaluate.erase()
webView?.evaluateJavaScript(script)
}
}
// MARK: - WKScriptMessageHandler
extension Marker: WKScriptMessageHandler {
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
if let markerHandler = MarkerScript.Handler(message) {
guard
let dataString = message.body as? String,
let data = dataString.data(using: .utf8)
else { return }
let decoder = JSONDecoder()
guard let serialized = try? decoder.decode(
SerializedObject.self,
from: data
) else { return }
receiveMarkerMessage(markerHandler, data: serialized)
}
}
func receiveMarkerMessage(_ handler: MarkerScript.Handler, data: SerializedObject) {
switch handler {
case .serialize:
serializedObject = data
// your callback here
let script = MarkerScript.Evaluate.clearSelection()
webView?.evaluateJavaScript(script)
case .erase:
serializedObject = data
let highlights = data.highlights
let listId = highlights.map { $0.id }
guard let top = dataStack.top else { return }
let newData = top.filter { listId.contains($0.id) }
if newData != top {
dataStack.push(newData)
}
}
}
}
--- ViewDidLoad
class ViewController: UIViewController, WKScriptMessageHandler {
let marker: Marker = Marker()
let orangeButton: UIButton = {
let v = UIButton()
v.tag = 0
v.backgroundColor = MarkerColor.orange.value
v.layer.cornerRadius = 10
v.addTarget(self, action: #selector(highlight(_:)), for: .touchUpInside)
return v
}()
let cyanButton: UIButton = {
let v = UIButton()
v.tag = 1
v.backgroundColor = MarkerColor.cyan.value
v.layer.cornerRadius = 10
v.addTarget(self, action: #selector(highlight(_:)), for: .touchUpInside)
return v
}()
let pinkButton: UIButton = {
let v = UIButton()
v.tag = 2
v.backgroundColor = MarkerColor.pink.value
v.layer.cornerRadius = 10
v.addTarget(self, action: #selector(highlight(_:)), for: .touchUpInside)
return v
}()
let eraseButton: UIButton = {
let v = UIButton()
v.setTitle("Erase", for: .normal)
v.setTitleColor(.systemBlue, for: .normal)
v.addTarget(self, action: #selector(erase), for: .touchUpInside)
return v
}()
let eraseAllButton: UIButton = {
let v = UIButton(type: .close)
v.addTarget(self, action: #selector(eraseAll), for: .touchUpInside)
return v
}()
lazy var toolBars: UIStackView = {
let v = UIStackView(arrangedSubviews: [orangeButton, cyanButton, pinkButton, eraseButton, eraseAllButton])
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 20
return v
}()
// This is to make the makeUIView
lazy var webView: WKWebView = {
let config = WKWebViewConfiguration()
let uc = config.userContentController
uc.addUserScript(WKUserScript.injectViewPort())
// Jquery
uc.addUserScript(JQueryScript.core())
// Rangy
uc.addUserScript(RangyScript.core())
uc.addUserScript(RangyScript.classapplier())
uc.addUserScript(RangyScript.highlighter())
uc.addUserScript(RangyScript.selectionsaverestore())
uc.addUserScript(RangyScript.textrange())
// Marker
uc.addUserScript(MarkerScript.css())
uc.addUserScript(MarkerScript.jsScript())
uc.add(self.marker, name: MarkerScript.Handler.serialize.rawValue)
uc.add(self.marker, name: MarkerScript.Handler.erase.rawValue)
let v = WKWebView(frame: .zero, configuration: config)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
marker.webView = webView
let path = Bundle.main.path(forResource: "sample", ofType: "html")!
let url = URL(fileURLWithPath: path)
webView.loadFileURL(url, allowingReadAccessTo: url)
let views = [webView, toolBars]
views.forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
toolBars.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
toolBars.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
toolBars.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
toolBars.heightAnchor.constraint(equalToConstant: 40)
])
}
// MARK: - Selector
#objc func highlight(_ sender: UIButton) {
switch sender.tag {
case 0:
marker.highlight(MarkerColor.orange)
case 1:
marker.highlight(MarkerColor.cyan)
case 2:
marker.highlight(MarkerColor.pink)
default:
break
}
}
#objc func erase() {
marker.erase()
}
#objc func eraseAll() {
marker.removeAll()
}
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
}
}
I was finally able to get the code working by looking at the post: Call evaluateJavascript from a SwiftUI button. The problem I was running into was the fact that I was not able to run the javascript func in order to highlight. Using Combine i was able to make a button in the View and when that button is clicked being able to run the javascript code. Will post the code below for anyone who is interested.
import WebKit
import SwiftUI
import Combine
class WebViewData: ObservableObject {
#Published var parsedText: NSAttributedString? = nil
var functionCaller = PassthroughSubject<Void,Never>()
var isInit = false
var shouldUpdateView = true
}
struct ContentView: View {
#StateObject var webViewData = WebViewData()
var body: some View {
VStack {
Button(action: {
webViewData.functionCaller.send()
}) {
Text("Orange")
}
WebView(data: webViewData)
}
}
}
struct WebView: UIViewRepresentable {
#StateObject var data: WebViewData
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
//var webView: WKWebView?
var serializedObject: SerializedObject?
private var dataStack = Stack<Highlights>()
var parent: WebView
var webView: WKWebView? = nil
private var cancellable : AnyCancellable?
init(view: WebView) {
self.parent = view
super.init()
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webView = webView
}
// receive message from wkwebview
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
if let markerHandler = MarkerScript.Handler(message) {
guard
let dataString = message.body as? String,
let data = dataString.data(using: .utf8)
else { return }
let decoder = JSONDecoder()
guard let serialized = try? decoder.decode(
SerializedObject.self,
from: data
) else { return }
receiveMarkerMessage(markerHandler, data: serialized)
}
}
func receiveMarkerMessage(_ handler: MarkerScript.Handler, data: SerializedObject) {
switch handler {
case .serialize:
serializedObject = data
// your callback here
let script = MarkerScript.Evaluate.clearSelection()
self.webView?.evaluateJavaScript(script)
case .erase:
serializedObject = data
let highlights = data.highlights
let listId = highlights.map { $0.id }
guard let top = dataStack.top else { return }
let newData = top.filter { listId.contains($0.id) }
if newData != top {
dataStack.push(newData)
}
}
}
func tieFunctionCaller(data: WebViewData) {
cancellable = data.functionCaller.sink(receiveValue: { _ in
self.webView?.evaluateJavaScript("highlightSelectedTextWithColor('orange')")
})
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(view: self)
}
func makeUIView(context: Context) -> WKWebView {
let coordinator = makeCoordinator()
let configuration = WKWebViewConfiguration()
let uc = configuration.userContentController
uc.addUserScript(WKUserScript.injectViewPort())
// Jquery
uc.addUserScript(JQueryScript.core())
// Rangy
uc.addUserScript(RangyScript.core())
uc.addUserScript(RangyScript.classapplier())
uc.addUserScript(RangyScript.highlighter())
uc.addUserScript(RangyScript.selectionsaverestore())
uc.addUserScript(RangyScript.textrange())
// Marker
uc.addUserScript(MarkerScript.css())
uc.addUserScript(MarkerScript.jsScript())
uc.add(coordinator, name: MarkerScript.Handler.serialize.rawValue)
uc.add(coordinator, name: MarkerScript.Handler.erase.rawValue)
let _wkwebview = WKWebView(frame: .zero, configuration: configuration)
_wkwebview.navigationDelegate = coordinator
return _wkwebview
}
func updateUIView(_ webView: WKWebView, context: Context) {
guard data.shouldUpdateView else {
data.shouldUpdateView = false
return
}
context.coordinator.tieFunctionCaller(data: data)
context.coordinator.webView = webView
guard let path: String = Bundle.main.path(forResource: "sample", ofType: "html") else { return }
let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
}
}
I found why the cache prompt will not be shown.
If I use the ImageView directly on the ContentView, the cache prompt will not show.
If I wrap the ImageView with a View, then use the wrapper view on the ContentView, the cache prompt will show.
Here is the working code in the ContentView.swift
struct ContentView: View {
var links =
[NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-0.jpg"),
NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-1.jpg"),
NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-2.jpg"),
NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-3.jpg"),
NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-4.jpg"),
NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-5.jpg"),
NewsItem(urlString: "https://www.jobyme88.com/wp-content/uploads/2020/11/50d0-kj-classroom-6.jpg")]
var body: some View {
List(links) { news in
// working
NewsListItemView(item: news)
// not working
//NewsImageView(urlString: news.urlString)
}
}
}
This is the NewsListItemView which is just a wrapper
struct NewsListItemView: View {
var item: NewsItem
var body: some View {
NewsImageView(urlString: item.urlString)
}
}
This is my cache prompt location.
NewsImageViewModel.swift
class NewsImageViewModel: ObservableObject {
static var placeholder = UIImage(named: "NewsIcon.png")
#Published var image: UIImage?
var urlString: String?
init(urlString: String) {
self.urlString = urlString
loadImage()
}
func loadImage() {
if loadImageFromCache() {
return
}
loadImageFromURL()
}
func loadImageFromCache() -> Bool {
guard let cacheIamge = TemporaryImageCache.getShared()[urlString!] else {
return false
}
print("load from cache")
self.image = cacheIamge
return true
}
func loadImageFromURL() {
print("load from url")
guard let urlString = urlString else {
return
}
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url, completionHandler: getResponseFromURL(data:response:error:))
task.resume()
}
func getResponseFromURL(data: Data?, response: URLResponse?, error: Error?) {
guard error == nil else {
print("Error \(error!)")
return
}
guard data != nil else {
print("No founded data")
return
}
DispatchQueue.main.async {
guard let loadedImage = UIImage(data: data!) else {
print("Not supported data ")
return
}
self.image = loadedImage
TemporaryImageCache.getShared().cache.setObject(loadedImage, forKey: self.urlString! as NSString)
}
}
}
NewsImageView.swift
import SwiftUI
struct NewsImageView: View {
#ObservedObject var model: NewsImageViewModel
init(urlString: String) {
model = NewsImageViewModel(urlString: urlString)
}
var body: some View {
Image(uiImage: model.image ?? NewsImageViewModel.placeholder!)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100, alignment: .center)
}
}
NewsListItemView.swift
struct NewsListItemView: View {
var item: NewsItem
var body: some View {
NewsImageView(urlString: item.urlString)
}
}
This is ImageCache.swift
protocol ImageCache {
subscript(_ urlString: String) -> UIImage? {get set }
}
struct TemporaryImageCache: ImageCache {
subscript(urlString: String) -> UIImage? {
get {
cache.object(forKey: urlString as NSString)
}
set {
newValue == nil ? cache.removeObject(forKey: urlString as NSString) : cache.setObject(newValue!, forKey: urlString as NSString)
}
}
var cache = NSCache<NSString, UIImage>()
}
extension TemporaryImageCache {
private static var shared = TemporaryImageCache()
static func getShared() -> TemporaryImageCache {
return shared
}
}
This is NewsItem.swift
struct NewsItem: Identifiable {
var id = UUID()
var urlString: String
}
I am new to SwiftUI, and I was trying to building a app which recognizes objects using Resnet50 model and displays its recognized name, I was able to run the avcapturesession and print the objects name in console but i cant see the TextView when app is running, it always shows the camera fullScreen
This is my Content View
import SwiftUI
import AVKit
import Vision
struct ContentView: View {
#ObservedObject var cameraInfo = CameraViewController()
var body: some View {
VStack {
CameraView()
Text(cameraInfo.objectIdentifier)
}
.edgesIgnoringSafeArea(.top)
}
}
And this is my CameraView which is UIViewControllerRepresentable
struct CameraView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> CameraViewController {
return CameraViewController()
}
func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {
print("Update Called")
}
typealias UIViewControllerType = CameraViewController
}
This is the CameraViewController
final class CameraViewController: UIViewController, ObservableObject,AVCaptureVideoDataOutputSampleBufferDelegate {
var captureSession: AVCaptureSession!
#Published var objectIdentifier: String = ""
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
guard let model = try? VNCoreMLModel(for: Resnet50().model) else { return }
let request = VNCoreMLRequest(model: model) { (finishedRequest, error) in
guard let results = finishedRequest.results as? [VNClassificationObservation] else { return }
guard let firstObservation = results.first else { return }
self.objectIdentifier = firstObservation.identifier
print("DEBUG: firstObs identifier\(firstObservation.identifier),confidence\(firstObservation.confidence)")
}
try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
}
override func viewDidLoad() {
super.viewDidLoad()
captureSession = AVCaptureSession()
captureSession.sessionPreset = .photo
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
guard let input = try? AVCaptureDeviceInput(device: videoCaptureDevice) else {return}
captureSession.addInput(input)
captureSession.startRunning()
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
view.layer.addSublayer(previewLayer)
previewLayer.frame = view.frame
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "video"))
captureSession.addOutput(dataOutput)
}
}