I am learning Swift - making progress. My application should scan a barcode. I am trying to show the camera in the middle of the screen between two strips of spaces above and below the UIview. There is a button at bottom strip of space to save the barcode number. My issue is, I need to show the status bar at the top of the screen with signal strength, time, battery etc., when user is scanning, but it is covered by the UI view when it open camera. I have reviewed other posts and incorporated the suggestions, but the issue persists. Any help is appreciated.
I have uploaded image showing the covered status bar.
Here is my code:
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet weak var cameraView: UIView!
#IBOutlet weak var selectScan1: UIButton!
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
// view.backgroundColor = UIColor.black
captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
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 = [.ean8, .ean13, .pdf417]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
previewLayer?.frame = self.cameraView.layer.frame
cameraView.layer.addSublayer(previewLayer!)
// **** note: select click to bounds check box in storyboard
captureSession.startRunning()
}
Related
I am new in Swift and I try to implement a camera app in landscape mode (left and right).
I tried a video tutorial, the camera app basically works but just in portrait mode (not landscape).
If I enable landscape left + right in the project (and I disenable portrait mode), the camera looks like this:
What am I doing wrong? I found some posts about the same problem, but none of them helped me to solve the problem.
Best Regards
I tried affine transformation like CGAffineTransform(rotationAngle: -90) but this also doesn't solve the problem.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var cameraView: UIView!
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 10.2, *)
{
let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
do
{
let input = try AVCaptureDeviceInput(device: captureDevice!)
captureSession = AVCaptureSession()
captureSession?.addInput(input)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.frame = view.layer.bounds
cameraView.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
}
catch
{
print("error")
}
}
}
Try this:
private var previewLayer: AVCaptureVideoPreviewLayer?
previewLayer?.connection?.videoOrientation = .landscapeLeft
previewLayer?.connection?.videoOrientation = .portrait
I have a custom view in a storyboard with a ViewController to show me the output of a AVCaptureSessionPreviewLayer (in this case, webcam footage). When using a custom view in my normal window, all is fine and I see video output.
But when I want to use a custom view in a sheet dialog, the video is not visible even though the webcam indicates that is running.
Here is the ViewController code:
import Cocoa
import AVKit
class RecordViewController: NSViewController {
let captureSession = AVCaptureSession()
var captureDevice : AVCaptureDevice?
var previewLayer : AVCaptureVideoPreviewLayer?
#IBOutlet weak var camera: NSView!
override func viewDidLoad() {
super.viewDidLoad()
camera.wantsLayer = true;
camera.layer = CALayer()
// Do any additional setup after loading the view, typically from a nib.
captureSession.sessionPreset = AVCaptureSession.Preset.medium
// Get all audio and video devices on this machine
let devices = AVCaptureDevice.devices()
// Find the FaceTime HD camera object
for device in devices {
print(device)
// Camera object found and assign it to captureDevice
if ((device as AnyObject).hasMediaType(AVMediaType.video)) {
print(device)
captureDevice = device as? AVCaptureDevice
}
}
if captureDevice != nil {
do {
try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice!))
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.frame = (self.camera.layer?.frame)!
// Add previewLayer into custom view
self.camera.layer?.addSublayer(previewLayer!)
// Start camera
captureSession.startRunning()
} catch {
print(AVCaptureSessionErrorKey.description)
}
} }
}
Change the line:
previewLayer?.frame = (self.camera.layer?.frame)!
to:
previewLayer?.frame = self.camera.bounds
And if you want to make your camera fill your customView use this line too:
previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
I'm making a musicPlayer app in Swift, which is based on Tab Bar Controller and now is creating the NowPlayingViewController which present the playing scene as below screenshot showed. This NowPlayingViewController will be opened when I click the table row from another tableViewController, and the song was played.
My question is that when I dismiss this View Controller to go back the previous view or move to other view of Tab Bar Controller, the music playing is stoped. After some googles, someone suggests to use singleton to make a shared session for music playing.
/// here is my project's storyboard
/// I change my code to add one Audio Manager.swift as the singleton class. However I'm not sure if I'm doing the right thing, need help. Also don't find method about getting playingSong variable from NowPlayingVewController...
import AVKit
class AudioManager {
// use Singleton pattern keep the music continuing when user move through the App.
static let sharedInstance = AudioManager()
var audioPlayer: AVAudioPlayer!
var playingSong: SongData?
private init() {
// config for audio background playing
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
}
func startMusic() {
do {
// how to retrieve the playingSong variable from NowPlayingViewController?
// playingSong =
try audioPlayer = AVAudioPlayer(contentsOf: playingSong!.url)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print("could not load file")
}
}
func pauseMusic() {
audioPlayer.pause()
}
}
/// NowPlayViewController.swift, in case it is not fully aligned with above picture because it is just an example image to show the playing scene.
import UIKit
import AVKit
class NowPlayingViewController: UIViewController {
var playingSong: SongData?
var audioPlayer: AVAudioPlayer!
var isPlaying = true
var timer:Timer!
#IBOutlet weak var songTitle: UILabel!
#IBOutlet weak var songAlbum: UILabel!
#IBOutlet weak var songArtwork: UIImageView!
#IBOutlet weak var playOrPauseButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
songTitle.text = playingSong?.songName
songAlbum.text = playingSong?.albumName
songArtwork.image = playingSong?.albumArtwork
// start play music in AudioManager sharedInstance
AudioManager.sharedInstance.startMusic()
}
#IBAction func NaviBack(_ sender: UIButton) {
print("press button")
// not sure about this, use present modally of segue,
// so need to use dismiss to return to the previous scene/view.
self.dismiss(animated: true, completion: nil)
}
#IBAction func PlayOrPauseMusic(_ sender: UIButton) {
if isPlaying {
print("isPlaying")
AudioManager.sharedInstance.pauseMusic()
isPlaying = false
playOrPauseButton.setTitle("Pause", for: .normal)
} else {
print("isPaused")
AudioManager.sharedInstance.startMusic()
isPlaying = true
playOrPauseButton.setTitle("Play", for: .normal)
}
}
}
/// Sending data to NowPlayingViewController from tableViewController using Segue present modally.
// define segue to the transition to NowPlaying View
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "playingSong" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as! NowPlayingViewController
if resultSearchController.isActive {
controller.playingSong = filteredTableData[indexPath.row]
} else {
controller.playingSong = tableData[indexPath.row]
}
}
}
}
Oops, I found my AudioManger is actually worked by getting the playingSong: SongData from tableViewController when user click the cell row in this view. I declare this playingSong as static var in order to access/use it from AudioManager.
Now music is playing when user navigation through the Tab Bar Controller, update my code as below. Next step could be add a button on the left/right top of navi view to re-present the playing scene:)
Err, I found that it still has issue, the play and resume for the current song is working, but if I select another song in the tableViewController, then it still plays the previous song in NowPlayingView. The reason could be the init() only once, so I need to find a way to re-assign value to sharedInstance when select another cell row.
import AVKit
class AudioManager {
// use Singleton pattern keep the music continuing when user move through the App.
static let sharedInstance = AudioManager()
var audioPlayer: AVAudioPlayer!
var playingSong: SongData?
private init() {
// config for audio background playing
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
do {
playingSong = SongsTableViewController.playingSong
try audioPlayer = AVAudioPlayer(contentsOf: playingSong!.url)
audioPlayer.prepareToPlay()
} catch {
print("could not load file")
}
}
func playMusic() {
audioPlayer.play()
}
func pauseMusic() {
audioPlayer.pause()
}
}
I try to use my camera's view (blurred) as background in my main menu.
I'm a beginner and have no idea how to do this...
please don't answer with "use ARSCNView"; I've tried this.
(best: send me a code in swift; I shouldn't be too long huh?)
After trying your code I have these errors:
my code
import UIKit
import AVFoundation
class TestingViewController: UIViewController {
let session: AVCaptureSession = AVCaptureSession()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
session.sessionPreset = AVCaptureSession.Preset.high
if let device = AVCaptureDevice.default(for: AVMediaType.video) {
do {
try session.addInput(AVCaptureDeviceInput(device: device))
} catch {
print(error.localizedDescription)
}
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
self.view.layer.addSublayer(previewLayer)
previewLayer.frame = self.view.layer.bounds
}
session.startRunning()
let blur = UIBlurEffect(style: .regular)
let blurView = UIVisualEffectView(effect: blur)
blurView.frame = self.view.bounds
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(blurView)
}
}
try this updated code and dont forget to add
"Privacy - Camera Usage Description" in your info.plist file
I have the following code (I copied whole class because I don't really get what the problem is; the function with the problem is displayImage - last 4 lines of code or so):
class ViewController: NSViewController {
#IBOutlet var previewBox: NSView!
#IBOutlet var captureBox: NSImageView!
let cameraSession = AVCaptureSession()
let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) as AVCaptureDevice
var deviceInput: AVCaptureDeviceInput?
var dataOutput: AVCaptureStillImageOutput?
override func viewDidLoad() {
super.viewDidLoad()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func scan(_ sender: NSButton) {
do {
deviceInput = try AVCaptureDeviceInput(device: device)
cameraSession.beginConfiguration()
if (cameraSession.canAddInput(deviceInput) == true) {
cameraSession.addInput(deviceInput)
}
dataOutput = AVCaptureStillImageOutput()
if (cameraSession.canAddOutput(dataOutput) == true) {
cameraSession.addOutput(dataOutput)
}
cameraSession.commitConfiguration()
let previewLayer = AVCaptureVideoPreviewLayer(session: cameraSession)
previewBox.layer?.addSublayer(previewLayer!)
previewLayer?.frame = (previewBox.layer?.frame)!
previewLayer?.connection.automaticallyAdjustsVideoMirroring = false
previewLayer?.connection.isVideoMirrored = true
cameraSession.startRunning()
}
catch let error as NSError {
NSLog("\(error), \(error.localizedDescription)")
}
}
#IBAction func capture(_ sender: NSButton) {
if let videoConnection = dataOutput?.connection(withMediaType: AVMediaTypeVideo) {
dataOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: self.displayImage)
}
}
func displayImage(buffer: CMSampleBuffer?, err: Error?) {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
cameraSession.stopRunning()
captureBox.image = NSImage(data: imageData!)
}
}
Now... everything looks ok the preview is working, the buffer seems to have something in it - although that may be corrupted also since I can't display the image even if I comment the line with the crash (the main problem I'm asking about now). But... when it get's to the stopRunning() function the app crashes (print below). Any idea what I'm doing wrong here? If I comment the stop running part the app doesn't crash but the image is not set (that's why I pasted all... I'm expecting the problem to be somewhere else in code).
IMPORTANT: This is cocoa app, not iOS (and apparently there are 999999 code examples for iOS and 0 for macOS).