I'm trying to upgrade mi app to swift 4, but the barcode reader is not working.
I have isolated the barcode reader code, and still not working. The camera works but it does not detect the barcode.
The code worked just fine on swift 3 iOS 10.
This is the complete code
import AVFoundation
import UIKit
class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
captureSession = AVCaptureSession()
let videoCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video)
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice!)
} catch {
return
}
if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
failed();
return;
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.ean8, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.pdf417]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
previewLayer.frame = view.layer.bounds;
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill;
view.layer.addSublayer(previewLayer);
captureSession.startRunning();
}
func failed() {
let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
captureSession = nil
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if (captureSession?.isRunning == false) {
captureSession.startRunning();
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning();
}
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: readableObject.stringValue!);
}
dismiss(animated: true)
}
func found(code: String) {
print(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
I am using iOS 11 on my iPhone, upgraded to beta 9.
Any idea? Thank you.
I figured it out but Apple didn't make it so obvious. The callback function from the delegate AVCaptureMetadataOutputObjectsDelegate has been renamed and the parameter names are different!
So, replace
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
to
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
My view controller is now scanning QR Codes as before after this. It has the same parameters but the first parameter name is different. Change the function and parameter names and build/run.
After changing the delegate call back :
From
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
To
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
I need to set all available types for metadataObjectTypes too as below-
output.metadataObjectTypes=output.availableMetadataObjectTypes
After changing your code from:
func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
to:
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
everything works again.
You can use QRCodeScanner83 to scan barcodes:
import QRCodeScanner83
import AVFoundation
...
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "CodeScannerViewController") as? CodeScannerViewController else {
return
}
vc.callbackCodeScanned = { code in
print("SCANNED CODE: \(code)")
vc.dismiss(animated: true, completion: nil)
}
self.present(vc, animated: true, completion: nil)
If you need custom UI, then you can nest from CodeScannerViewController and set CodeScannerViewController.delegate to receive updates of the scanner state.
Related
I have a question about how one can connect a Search Bar with MapKit, so that it is able to search for places/ locations (not using StoryBoard). I have already written the code for the Search Bar and for the MapView in separate files, but even after trying literally every code and tutorial on the internet, I couldn't find a way to connect the Search Bar to search for locations. Below one can see respectively the used SearchBar.swift file, the MapViewController.swift and a snippet of the ContentView.swift.
SearchBar.swift
import UIKit
import Foundation
import SwiftUI
import MapKit
struct SearchBar: UIViewRepresentable {
// Binding: A property wrapper type that can read and write a value owned by a source of truth.
#Binding var text: String
// NSObject: The root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.
// UISearchBarDelegate: A collection of optional methods that you implement to make a search bar control functional.
class Coordinator: NSObject, UISearchBarDelegate {
#Binding var text: String
let Map = MapViewController()
init(text: Binding<String>) {
_text = text
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
text = searchText
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
text = ""
searchBar.showsCancelButton = true
searchBar.endEditing(true)
searchBar.resignFirstResponder()
}
}
func makeCoordinator() -> SearchBar.Coordinator {
return Coordinator(text: $text)
}
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
searchBar.showsCancelButton = true
searchBar.searchBarStyle = .minimal
//searchBar.backgroundColor = .opaqueSeparator
searchBar.showsCancelButton = true
return searchBar
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.text = text
}
}
MapViewController.swift
class MapViewController: UIViewController, CLLocationManagerDelegate {
let mapView = MKMapView()
let locationManager = CLLocationManager()
#Published var permissionDenied = false
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
checkLocationServices()
}
func setupMapView() {
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)
guard let location = locations.last else { return }
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.setRegion(region, animated: true)
let categories:[MKPointOfInterestCategory] = [.cafe, .restaurant]
let filters = MKPointOfInterestFilter(including: categories)
mapView.pointOfInterestFilter = .some(filters)
// Enables the scrolling around the user location without hopping back
locationManager.stopUpdatingLocation()
}
func checkLocalAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
followUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
permissionDenied.toggle()
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// Show alert
break
case .authorizedAlways:
break
#unknown default:
fatalError()
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocalAuthorization()
} else {
// user did not turn it on
}
}
func followUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocalAuthorization()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
The methods are then called in the ContentView.swift, using these methods:
struct MapViewRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
struct ContentView: View {
#State private var searchText : String = ""
var body: some View {
ZStack(alignment: .top) {
MapViewRepresentable()
.edgesIgnoringSafeArea(.all)
.onTapGesture {
self.endTextEditing()
}
SearchBar(text: $searchText)
}
}
}
Is it possible to connect both like I explained, or is there another method you advice? I really hope you guys can help me! Thanks in advance :)
I am trying to capture my system's screen and process the data. But I get nil value for CMSampleBufferGetDataBuffer for the sample buffer I get in captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) delegate method. Any idea? below is my code:
import Cocoa
import AVFoundation
class ViewController: NSViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
private lazy var sampleBufferDelegateQueue = DispatchQueue(label: "VideoCapture")
private lazy var captureSession: AVCaptureSession = {
let session = AVCaptureSession()
session.sessionPreset = .hd1280x720
if let input = AVCaptureScreenInput.init(displayID: CGMainDisplayID()) {
session.addInput(input)
}
let output = AVCaptureVideoDataOutput()
output.videoSettings = [
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
kCVPixelBufferMetalCompatibilityKey as String: true
]
output.setSampleBufferDelegate(self, queue: self.sampleBufferDelegateQueue)
session.addOutput(output)
return session
}()
#IBAction func startAction(_ sender: Any) {
self.start()
}
override func viewDidLoad() {
super.viewDidLoad()
}
func start() {
guard !self.captureSession.isRunning else {
return
}
self.captureSession.startRunning()
}
func stop() {
guard self.captureSession.isRunning else {
return
}
self.captureSession.stopRunning()
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
print(blockBuffer ?? "nil")
}
}
I’m new to RxSwift and I’m looking for a basic example of how to print in console live data streaming from an backend. I have a backend which is streaming some dummy data every 1 second, I can see it with a curl request and I want to be able to observe and subscribe and print it out in console automatically, please help!
I assume you are opening a socket connection to your server in order to receive live data. You can use RxWebSocket to handle this data flow. There are examples in the documentation/readme for the API.
https://github.com/daltoniam/Starscream/tree/master/examples/SimpleTest/SimpleTest
import UIKit
import Starscream
class ViewController: UIViewController, WebSocketDelegate {
var socket: WebSocket!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
var request = URLRequest(url: URL(string: "wss://echo.websocket.org")!)
request.timeoutInterval = 5
socket = WebSocket(request: request)
socket.delegate = self
socket.connect()
}
// MARK: Websocket Delegate Methods.
func websocketDidConnect(socket: WebSocketClient) {
print("websocket is connected")
}
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
if let e = error as? WSError {
print("websocket is disconnected: \(e.message)")
} else if let e = error {
print("websocket is disconnected: \(e.localizedDescription)")
} else {
print("websocket disconnected")
}
}
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
self.label.text = text
print("Received text: \(text)")
}
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
print("Received data: \(data.count)")
}
// MARK: Write Text Action
#IBAction func send(_ sender: UIButton) {
socket.write(string: "Hello \(textField.text!)")
}
// MARK: Disconnect Action
#IBAction func disconnect(_ sender: UIBarButtonItem) {
if socket.isConnected {
sender.title = "Connect"
socket.disconnect()
} else {
sender.title = "Disconnect"
socket.connect()
}
}
}
extension URL {
init(staticString string: StaticString) {
guard let url = URL(string: "\(string)") else {
preconditionFailure("Invalid static URL string: \(string)")
}
self = url
}
}
I'm trying to write a simple command line app that can display some info on a notification. But, the Delegate is not being called, and neither is the Notification and I'm not sure what's missing here.
Judging from my output, I think the whole problem stems from the AppDelegate not being instantiated. But I am creating one just before I show call showNotification.
What am I missing here?
src/main.swift
import Foundation
import AppKit
var sema = DispatchSemaphore( value: 0 )
let server: String = "http://jsonip.com"
let port: String = "80"
let path: String = "/"
let todoEndpoint: String = server + ":" + port + path
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: todoEndpoint)!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
{
print(json)
let ad = AppDelegate()
ad.showNotification(title: "Title", subtitle: "SubTitle", informativeText: String(describing: json))
sema.signal()
}
} catch {
print("error in JSONSerialization")
}
}
})
print("Resume Task")
task.resume()
print("Wait for Semaphore")
sema.wait()
src/AppDelegate.swift
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
func applicationDidFinishLaunching(aNotification: Notification) {
NSUserNotificationCenter.default.delegate = self
print("Delegate Self")
}
// NSUserNotificationCenterDelegate implementation
private func userNotificationCenter(center: NSUserNotificationCenter!, didDeliverNotification notification: NSUserNotification!) {
//implementation
}
private func userNotificationCenter(center: NSUserNotificationCenter!, didActivateNotification notification: NSUserNotification!) {
//implementation
}
private func userNotificationCenter(center: NSUserNotificationCenter!, shouldPresentNotification notification: NSUserNotification!) -> Bool {
//implementation
return true
}
func showNotification(title: String, subtitle: String, informativeText: String) -> Void {
let notification: NSUserNotification = NSUserNotification()
print("Show Notification")
notification.title = title
notification.subtitle = subtitle
notification.informativeText = informativeText
//notification.contentImage = contentImage
notification.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.default.deliver(notification)
print(notification.isPresented)
}
}
Output
Resume Task
Wait for Semaphore
["about": /about, "reject-fascism":
Impeach Trump!, "ip": 110.50.73.141, "Pro!": http://getjsonip.com]
Show Notification
false
Program ended with exit code: 0
I would like to open the camera and library in landscape mode, but I not be able to do it.
Please someone could explain and give me an example.
I have used the below code but seems some code is missing (for instance: import library), I will need a complete example, how I could implement, functions to open library and the camera and save in the camera roll.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
if let connection = self.previewLayer?.connection {
var currentDevice: UIDevice = UIDevice.currentDevice()
var orientation: UIDeviceOrientation = currentDevice.orientation
var previewLayerConnection : AVCaptureConnection = connection
if (previewLayerConnection.supportsVideoOrientation)
{
switch (orientation)
{
case .Portrait:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
break
case .LandscapeRight:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft
break
case .LandscapeLeft:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
break
case .PortraitUpsideDown:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.PortraitUpsideDown
break
default:
previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
break
}
}
}
}
Also indicate that I already have some code implemented then
I find it easier to implement without the use of additional libraries like AVFoundation.
I am currently using this code in order to open library and camera.
#IBAction func openLibrary(sender: AnyObject) { ///accion del boton Library
imagePicker.allowsEditing = false
imagePicker.sourceType = .PhotoLibrary
presentViewController(imagePicker, animated:true, completion: nil)
}
#IBAction func openCamera(sender: AnyObject) { ///accion del boton Camara
imagePicker.allowsEditing = false
imagePicker.sourceType = .Camera
presentViewController(imagePicker, animated:true, completion: nil)
}
in ur app delegate use this function:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
if let presentedView = self.window?.rootViewController?.presentedViewController as? CameraViewController {
//cameraView
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
} else {
//other view
}
}
Swift 2
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if let presentedView = self.window?.rootViewController?.presentedViewController as? YourCameraViewController {
//cameraView
return UIInterfaceOrientationMask.Landscape
} else {
//other view
// return here ur orientation for other view
}
}