WARNING: Calling -[UIPrinterPickerController presentAnimated:completionHandler:] on iPad - swift4

I am using UIPrinterPickerController for selecting any one printer in printer list but UIPickerController doesn't open when I am running code to IPAD simulator but this same code I will run to IPHONE simulator is correct and show my all simulator printer and I can select any printer and get printer name as well as there url.
let pickerController = UIPrinterPickerController(initiallySelectedPrinter: nil)
if UIDevice.current.userInterfaceIdiom == .pad {
pickerController.present(animated: true) {(controller,completed,error) in
if completed == true {
let ipadprintername = controller.selectedPrinter!.displayName
}
}
}else{
pickerController.present(animated: true){(controller, completed,error)in
if completed == true {
let iphoneprintername = controller.selectedPrinter!.displayName
}
}
}

if UIDevice.current.userInterfaceIdiom == .pad {
let pickerController = UIPrinterPickerController(initiallySelectedPrinter: nil)
pickerController.present(from: CGRect(x: 400, y: 200, width: 0, height: 0), in: view, animated: true){( controller,completed,error) in
if completed == true {
let printername = controller.selectedPrinter!.displayName
}
}
}else{
let pickerController = UIPrinterPickerController(initiallySelectedPrinter: nil)
pickerController.present(animated: true) { (controller, completed, error) in
if completed == true {
let printername = controller.selectedPrinter!.displayName
}
}
}

Related

Dismiss alert view controller in a popover page

In a popover page, I'm trying to store some data in CoreData, and I need to show a loading view until the process is done.
I found a good and easy way, using a alert controller to show a loading label. In the function, I added a shouldPresent Boolean to make it false when the coreData process is done and hide the alert.
private func presentLoadingView(shouldPresent: Bool) {
let alert = UIAlertController(title: nil, message: "Retrieving Owner Data", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 3, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.medium
loadingIndicator.startAnimating()
alert.view.addSubview(loadingIndicator)
presentAnimated(alert)
if !shouldPresent {
alert.dismiss(animated: true)
}
}
The problem is, when I use
dismiss(animated: true)
the entire popover will be dismissed and when I use
alert.dismiss(animated: true)
Nothing happen, could anyone help me on this. Thanks in advance.
Try replaceing
presentAnimated(alert)
if !shouldPresent {
alert.dismiss(animated: true)
}
with
if shouldPresent {
presentAnimated(alert)
}
It seems it will do the work , accroding to your example code.
I think using alert view for loading is not good enough for your needs.
You can use this extension easily for showing a loading view.
extension UIViewController{
func showActivityIndicator(){
DispatchQueue.main.async {[weak self] in
guard let self = self else {return}
let indicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
indicator.backgroundColor = UIColor.init(hexString: "#010101").withAlphaComponent(0.6)
indicator.layer.cornerRadius = 6
indicator.center = self.view.center
indicator.hidesWhenStopped = true
indicator.color = UIColor(hexString: "#FFEE00")
indicator.style = UIActivityIndicatorView.Style.large
indicator.startAnimating()
self.view.isUserInteractionEnabled = false
indicator.tag = 1000000
for subView in self.view.subviews{
if subView.tag == 1000000{
print("Already Added")
return
}
}
self.view.addSubview(indicator)
}
}
func hideActivityIndicator(){
DispatchQueue.main.async { [weak self] in
guard let self = self else {return}
let indicator = self.view.viewWithTag(1000000) as? UIActivityIndicatorView
indicator?.stopAnimating()
indicator?.removeFromSuperview()
self.view.isUserInteractionEnabled = true
indicator?.isHidden = true
}
}
}
In your view controller just call self.showActivityIndicator() when you want to show loader
and call self.hideActivityIndicator() for dismiss the loader.
You can use this technique
var alert:UIAlertController? = UIAlertController() //set variable outside in your popover
private func presentLoadingView(shouldPresent: Bool) {
if shouldPresent {
if self.alert == nil{
self.alert = UIAlertController(title: nil, message: "Retrieving Owner Data", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 3, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.medium
loadingIndicator.startAnimating()
alert.view.addSubview(loadingIndicator)
}
if !self.alert.isBeingPresented {
self.present(self.alert, animated: true, completion: nil)
}
}else{
self.alert.dismiss(animated: false, completion: nil)
}
}

AdMob Native ad loading but not showing

I'm using Google AdMob to display native ads on my app. I made everything as in the Google Documentation. After testing, I figured out that test ad values are loading successfully since they aren't nil and when I print them, I can see them as they should display. However data is not displaying on my GADNativeAdView. Here is some of my code:
var nativeAdView: GADNativeAdView! {
didSet {
nativeAdView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(nativeAdView)
nativeAdView.leftAnchor.constraint(equalTo: collectionView4.leftAnchor).isActive = true
nativeAdView.rightAnchor.constraint(equalTo: collectionView4.rightAnchor).isActive = true
nativeAdView.topAnchor.constraint(equalTo: collectionView4.bottomAnchor, constant: 10).isActive = true
nativeAdView.heightAnchor.constraint(equalToConstant: 310).isActive = true
}
}
var adLoader: GADAdLoader!
// MARK: - Google Mobile Ads
extension HomeController: GADNativeAdLoaderDelegate, GADNativeAdDelegate {
private func setupAd() {
let multipleAdsOptions = GADMultipleAdsAdLoaderOptions()
multipleAdsOptions.numberOfAds = 1
adLoader = GADAdLoader(adUnitID: testNativeId, rootViewController: self,
adTypes: [.native],
options: [multipleAdsOptions])
adLoader.delegate = self
adLoader.load(GADRequest())
}
func imageOfStars(from starRating: NSDecimalNumber?) -> UIImage? {
guard let rating = starRating?.doubleValue else {
return nil
}
if rating >= 5 {
return UIImage(named: "stars_5")
} else if rating >= 4.5 {
return UIImage(named: "stars_4_5")
} else if rating >= 4 {
return UIImage(named: "stars_4")
} else if rating >= 3.5 {
return UIImage(named: "stars_3_5")
} else {
return nil
}
}
func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADNativeAd) {
// Create and place ad in view hierarchy.
let nibView = Bundle.main.loadNibNamed("NativeAdView", owner: nil, options: nil)?.first
guard let nativeAdView = nibView as? GADNativeAdView else {
return
}
self.nativeAdView = nativeAdView
nativeAd.delegate = self
(nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline
nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent
(nativeAdView.bodyView as? UILabel)?.text = nativeAd.body
nativeAdView.bodyView?.isHidden = nativeAd.body == nil
(nativeAdView.callToActionView as? UIButton)?.setTitle(nativeAd.callToAction, for: .normal)
nativeAdView.callToActionView?.isHidden = nativeAd.callToAction == nil
(nativeAdView.iconView as? UIImageView)?.image = nativeAd.icon?.image
nativeAdView.iconView?.isHidden = nativeAd.icon == nil
(nativeAdView.starRatingView as? UIImageView)?.image = imageOfStars(from: nativeAd.starRating)
nativeAdView.starRatingView?.isHidden = nativeAd.starRating == nil
(nativeAdView.storeView as? UILabel)?.text = nativeAd.store
nativeAdView.storeView?.isHidden = nativeAd.store == nil
(nativeAdView.priceView as? UILabel)?.text = nativeAd.price
nativeAdView.priceView?.isHidden = nativeAd.price == nil
(nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser
nativeAdView.advertiserView?.isHidden = nativeAd.advertiser == nil
nativeAdView.callToActionView?.isUserInteractionEnabled = false
nativeAdView.nativeAd = nativeAd
}
func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: Error) {
print("ERROR: \(error.localizedDescription)")
}
}
Here is my .xib file:
The views custom class on the .xib file are set.
After trial and error I figured out that using #IBOutlet for views in the .xib file and calling them on the adLoader(_:) worked.

after enabled touch id successfully how to login with touch id while open app next time

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
for family in UIFont.familyNames {
print("\(family)")
for name in UIFont.fontNames(forFamilyName: family) {
print(" \(name)")
}
}
if UserDefaults.standard.value(forKey: "userEmail") != nil {
let RegisterUser = UserDefaults.standard.value(forKey: "RegisterUser") as? NSDictionary ?? [:]
if RegisterUser.object(forKey: "AddAddress") as? String ?? "" == "0" {
initialViewController = Registration_iPohoneVC(nibName:"Registration_iPohoneVC",bundle:nil)
}else if RegisterUser.object(forKey: "JobInfo") as? String ?? "" == "0" {
initialViewController = JobInfo_iPohoneVC(nibName:"JobInfo_iPohoneVC",bundle:nil)
}else if RegisterUser.object(forKey: "UploadResume") as? String ?? "" == "0" {
initialViewController = Uploadresume_iPhoneVC1(nibName:"Uploadresume_iPhoneVC1",bundle:nil)
}else if RegisterUser.object(forKey: "UploadVideo") as? String ?? "" == "0" {
initialViewController = Createvideo_iPhoneVC(nibName:"Createvideo_iPhoneVC",bundle:nil)
}else {
if UserDefaults.standard.value(forKey: "Touchid") as? Bool ?? false == true {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Touch the home button with your finger to LogIn."
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason, reply:
{(succes, error) in
DispatchQueue.main.async {
if succes {
if UserDefaults.standard.value(forKey: "Touchid") as? Bool ?? false == true {
if UserDefaults.standard.value(forKey: "PlanSelected") as? Int ?? 0 == 90 {
self.initialViewController = Homescreen_iPhoneVC(nibName:"Homescreen_iPhoneVC",bundle:nil)
}else {
self.initialViewController = Homescreen6_iPhoneVC(nibName:"Homescreen6_iPhoneVC",bundle:nil)
}
}
}else {
let alertController = UIAlertController(title: "Touch ID Authentication Failed", message: "", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) {
UIAlertAction in
UserDefaults.standard.removeObject(forKey: "userEmail")
UserDefaults.standard.removeObject(forKey: "userPassword")
UserDefaults.standard.synchronize()
self.initialViewController = Login_iPhoneVC(nibName:"Login_iPhoneVC",bundle:nil)
let frame = UIScreen.main.bounds
self.window = UIWindow(frame: frame)
self.window!.rootViewController = appdelegate!.initialViewController!
self.window!.makeKeyAndVisible()
}
alertController.addAction(okAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
}
})
}else {
print("touch id not available...")
}
}else {
if UserDefaults.standard.value(forKey: "PlanSelected") as? Int ?? 0 == 90 {
self.initialViewController = Homescreen_iPhoneVC(nibName:"Homescreen_iPhoneVC",bundle:nil)
}else {
self.initialViewController = Homescreen6_iPhoneVC(nibName:"Homescreen6_iPhoneVC",bundle:nil)
}
}
}
let navigationcontroller = UINavigationController()
navigationcontroller.viewControllers = [appdelegate!.initialViewController!]
navigationcontroller.navigationBar.barTintColor = AppBG1Color
navigationcontroller.interactivePopGestureRecognizer?.delegate = self as UIGestureRecognizerDelegate
navigationcontroller.interactivePopGestureRecognizer?.isEnabled = true
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
window!.rootViewController = navigationcontroller
window!.makeKeyAndVisible()
}else {
initialViewController = Login_iPhoneVC(nibName: "Login_iPhoneVC", bundle: nil)
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
window!.rootViewController = initialViewController
window!.makeKeyAndVisible()
}
UINavigationBar.appearance().tintColor = .white
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard(_:)))
tapGesture.cancelsTouchesInView = false
window?.rootViewController?.view.addGestureRecognizer(tapGesture)
return true
}
here is my Appdelegate code, in my else condition i want first touch id popup for user to Ask touch the home button to login while user has Already enabled touch id but it will crash directly, i want while user has Already enabled touch id then he can Access App with touch every time he open App Again. if Anyone can help it would be greatful Thankyou.
If he enabled touch id set bollean flag as UserDefaults, and next time he runs the app read it, if its true then let him use touch id, if not - need to ask him.
P.S.You should probably move all this logic to SceneDelegate :) AppDelegate isn't place for this things anymore

List all available audio devices

I want to list all available audio devices in swift to provide a selection for input and output. My application should listen on a audio channel and "write" to another. I do not want the system default!
let devices = AVCaptureDevice.devices(for: .audio)
print(devices.count)
for device in devices {
print(device.localizedName)
}
The Code lists 0 devices. But I expect at least the internal output.
Some links to CoreAudio, AudioToolbox and AVFoundation that explain the audio source selection would be nice.
Here's some Swift 5 code that will enumerate all the audio devices.
You can use the uid with AVAudioPlayer's currentDevice property to output to a specific device.
import Cocoa
import AVFoundation
class AudioDevice {
var audioDeviceID:AudioDeviceID
init(deviceID:AudioDeviceID) {
self.audioDeviceID = deviceID
}
var hasOutput: Bool {
get {
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyStreamConfiguration),
mScope:AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement:0)
var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size);
var result:OSStatus = AudioObjectGetPropertyDataSize(self.audioDeviceID, &address, 0, nil, &propsize);
if (result != 0) {
return false;
}
let bufferList = UnsafeMutablePointer<AudioBufferList>.allocate(capacity:Int(propsize))
result = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, bufferList);
if (result != 0) {
return false
}
let buffers = UnsafeMutableAudioBufferListPointer(bufferList)
for bufferNum in 0..<buffers.count {
if buffers[bufferNum].mNumberChannels > 0 {
return true
}
}
return false
}
}
var uid:String? {
get {
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceUID),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var name:CFString? = nil
var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size)
let result:OSStatus = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, &name)
if (result != 0) {
return nil
}
return name as String?
}
}
var name:String? {
get {
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceNameCFString),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var name:CFString? = nil
var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size)
let result:OSStatus = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, &name)
if (result != 0) {
return nil
}
return name as String?
}
}
}
class AudioDeviceFinder {
static func findDevices() {
var propsize:UInt32 = 0
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioHardwarePropertyDevices),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var result:OSStatus = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &address, UInt32(MemoryLayout<AudioObjectPropertyAddress>.size), nil, &propsize)
if (result != 0) {
print("Error \(result) from AudioObjectGetPropertyDataSize")
return
}
let numDevices = Int(propsize / UInt32(MemoryLayout<AudioDeviceID>.size))
var devids = [AudioDeviceID]()
for _ in 0..<numDevices {
devids.append(AudioDeviceID())
}
result = AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &propsize, &devids);
if (result != 0) {
print("Error \(result) from AudioObjectGetPropertyData")
return
}
for i in 0..<numDevices {
let audioDevice = AudioDevice(deviceID:devids[i])
if (audioDevice.hasOutput) {
if let name = audioDevice.name,
let uid = audioDevice.uid {
print("Found device \"\(name)\", uid=\(uid)")
}
}
}
}
}
The code you posted works perfectly fine for audio input devices when I paste it into an Xcode Playground.
Note, however, that AVCaptureDevice API does not list audio output devices as they are no capture devices but playback devices. If a device supports both, input and output, you can still use the device's uniqueID in an output context, for example with AVPlayer's audioOutputDeviceUniqueID.
(Also note, that if you want your code to work on iOS as well, devices(for:) is marked as deprecated since iOS 11 and you should move to AVCaptureDevice.DiscoverySession instead.)
Regarding your request for additional info on Core Audio and AudioToolbox, this SO question has some pretty comprehensive answers on the matter. The question asks for input devices but the answers provide enough context to let you understand handling of the output side as well. There's even an answer with some (dated) Swift code. On a personal note I have to say calling Core Audio API from Swift is oftentimes more pain than gain. Because of that it might be faster, although a bit unsafer, wrapping those portions of code into Objective-C or plain C and exposing them via the Swift bridging header, if your project allows it.
If you want something like a actionSheet and need to switch between audio devices seamlessly. Use this code.
Code
import Foundation
import AVFoundation
import UIKit
#objc class AudioDeviceHandler: NSObject {
#objc static let shared = AudioDeviceHandler()
/// Present audio device selection alert
/// - Parameters:
/// - presenterViewController: viewController where the alert need to present
/// - sourceView: alertController source view in case of iPad
#objc func presentAudioOutput(_ presenterViewController : UIViewController, _ sourceView: UIView) {
let speakerTitle = "Speaker"
let headphoneTitle = "Headphones"
let deviceTitle = (UIDevice.current.userInterfaceIdiom == .pad) ? "iPad" : "iPhone"
let cancelTitle = "Cancel"
var deviceAction = UIAlertAction()
var headphonesExist = false
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
guard let availableInputs = AVAudioSession.sharedInstance().availableInputs else {
print("No inputs available ")
return
}
for audioPort in availableInputs {
switch audioPort.portType {
case .bluetoothA2DP, .bluetoothHFP, .bluetoothLE :
let bluetoothAction = UIAlertAction(title: audioPort.portName, style: .default) { _ in
self.setPreferredInput(port: audioPort)
}
if isCurrentOutput(portType: audioPort.portType) {
bluetoothAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(bluetoothAction)
case .builtInMic, .builtInReceiver:
deviceAction = UIAlertAction(title: deviceTitle, style: .default, handler: { _ in
self.setToDevice(port: audioPort)
})
case .headphones, .headsetMic:
headphonesExist = true
let headphoneAction = UIAlertAction(title: headphoneTitle, style: .default) { _ in
self.setPreferredInput(port: audioPort)
}
if isCurrentOutput(portType: .headphones) || isCurrentOutput(portType: .headsetMic) {
headphoneAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(headphoneAction)
case .carAudio:
let carAction = UIAlertAction(title: audioPort.portName, style: .default) { _ in
self.setPreferredInput(port: audioPort)
}
if isCurrentOutput(portType: audioPort.portType) {
carAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(carAction)
default:
break
}
}
// device actions only required if no headphone available
if !headphonesExist {
if (isCurrentOutput(portType: .builtInReceiver) ||
isCurrentOutput(portType: .builtInMic)) {
deviceAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(deviceAction)
}
// configure speaker action
let speakerAction = UIAlertAction(title: speakerTitle, style: .default) { _ in
self.setOutputToSpeaker()
}
if isCurrentOutput(portType: .builtInSpeaker) {
speakerAction.setValue(true, forKey: "checked")
}
optionMenu.addAction(speakerAction)
// configure cancel action
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel)
optionMenu.addAction(cancelAction)
optionMenu.modalPresentationStyle = .popover
if let presenter = optionMenu.popoverPresentationController {
presenter.sourceView = sourceView
presenter.sourceRect = sourceView.bounds
}
presenterViewController.present(optionMenu, animated: true, completion: nil)
// auto dismiss after 5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
optionMenu.dismiss(animated: true, completion: nil)
}
}
#objc func setOutputToSpeaker() {
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} catch let error as NSError {
print("audioSession error turning on speaker: \(error.localizedDescription)")
}
}
fileprivate func setPreferredInput(port: AVAudioSessionPortDescription) {
do {
try AVAudioSession.sharedInstance().setPreferredInput(port)
} catch let error as NSError {
print("audioSession error change to input: \(port.portName) with error: \(error.localizedDescription)")
}
}
fileprivate func setToDevice(port: AVAudioSessionPortDescription) {
do {
// remove speaker if needed
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.none)
// set new input
try AVAudioSession.sharedInstance().setPreferredInput(port)
} catch let error as NSError {
print("audioSession error change to input: \(AVAudioSession.PortOverride.none.rawValue) with error: \(error.localizedDescription)")
}
}
#objc func isCurrentOutput(portType: AVAudioSession.Port) -> Bool {
AVAudioSession.sharedInstance().currentRoute.outputs.contains(where: { $0.portType == portType })
}
}
How to use
class ViewController: UIViewController {
#IBOutlet weak var audioButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func selectAudio(_ sender: Any) {
// present audio device selection action sheet
AudioDeviceHandler.shared.presentAudioOutput(self, audioButton)
}
}
Result
It is possible to list input and output devices. This is a simplification of stevex's answer.
For output devices:
if (audioDevice.hasOutput) {
if let name = audioDevice.name,
let uid = audioDevice.uid {
print("Found device \"\(name)\", uid=\(uid)")
}
}
For input devices:
if (!audioDevice.hasOutput) {
if let name = audioDevice.name,
let uid = audioDevice.uid {
print("Found device \"\(name)\", uid=\(uid)")
}
}
(Notice the ! before audioDevice.hasOutput.)

Swift 3: How do I enable flash on custom AVFoundation camera?

I have a very basic AVFoundation Camera that has a captureButton that will take a photo and send that photo to the secondCameraController for it to be displayed. My problem is that there is a lot of iOS 10 deprecation and I'm not sure how I add in a flash when I press the captureButton. Any help will be highly appreciated. My code is below. Thank you guys.
class CameraController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
let captureSession = AVCaptureSession()
var previewLayer: CALayer!
var captureDevice: AVCaptureDevice!
var takePhoto: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareCamera()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: true)
}
let cameraView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
func prepareCamera() {
captureSession.sessionPreset = AVCaptureSessionPresetPhoto
if let availableDevices = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .back).devices {
captureDevice = availableDevices.first
beginSession()
}
}
func beginSession() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput)
} catch {
print(error.localizedDescription)
}
if let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) {
self.previewLayer = previewLayer
self.view.layer.addSublayer(self.previewLayer)
self.previewLayer.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.addSubview(captureButton)
let width: CGFloat = 85
captureButton.frame = CGRect(x: (previewLayer.frame.width / 2) - width / 2, y: (previewLayer.frame.height) - width - 25, width: width, height: 85)
captureSession.startRunning()
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString): NSNumber(value: kCVPixelFormatType_32BGRA)]
dataOutput.alwaysDiscardsLateVideoFrames = true
if captureSession.canAddOutput(dataOutput) {
captureSession.addOutput(dataOutput)
}
captureSession.commitConfiguration()
let queue = DispatchQueue(label: "com.cheekylabsltd.camera")
dataOutput.setSampleBufferDelegate(self, queue: queue)
}
}
func handleCapture() {
takePhoto = true
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
if takePhoto {
takePhoto = false
if let image = self.getImageFromSampleBuffer(buffer: sampleBuffer) {
let secondController = SecondCameraController()
secondController.takenPhoto = image
DispatchQueue.main.async {
self.present(secondController, animated: true, completion: {
self.stopCaptureSession()
})
}
}
}
}
func getImageFromSampleBuffer(buffer: CMSampleBuffer) -> UIImage? {
if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) {
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let context = CIContext()
let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))
if let image = context.createCGImage(ciImage, from: imageRect) {
return UIImage(cgImage: image, scale: UIScreen.main.scale, orientation: .right)
}
}
return nil
}
func stopCaptureSession() {
self.captureSession.stopRunning()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
self.captureSession.removeInput(input)
}
}
}
lazy var captureButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .white
button.layer.cornerRadius = 42.5
button.clipsToBounds = true
button.alpha = 0.40
button.layer.borderWidth = 4
button.layer.borderColor = greenColor.cgColor
button.addTarget(self, action: #selector(handleCapture), for: .touchUpInside)
return button
}()
}
Try this code :
Swift v3.0
private func flashOn(device:AVCaptureDevice)
{
do{
if (device.hasTorch)
{
try device.lockForConfiguration()
device.torchMode = .on
device.flashMode = .on
device.unlockForConfiguration()
}
}catch{
//DISABEL FLASH BUTTON HERE IF ERROR
print("Device tourch Flash Error ");
}
}
//FOR FLASH OFF CODE
private func flashOff(device:AVCaptureDevice)
{
do{
if (device.hasTorch){
try device.lockForConfiguration()
device.torchMode = .off
device.flashMode = .off
device.unlockForConfiguration()
}
}catch{
//DISABEL FLASH BUTTON HERE IF ERROR
print("Device tourch Flash Error ");
}
}
// METHOD
//private let session = AVCaptureSession()
//MARK: FLASH UITLITY METHODS
func toggleFlash() {
var device : AVCaptureDevice!
if #available(iOS 10.0, *) {
let videoDeviceDiscoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDuoCamera], mediaType: AVMediaTypeVideo, position: .unspecified)!
let devices = videoDeviceDiscoverySession.devices!
device = devices.first!
} else {
// Fallback on earlier versions
device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
}
if ((device as AnyObject).hasMediaType(AVMediaTypeVideo))
{
if (device.hasTorch)
{
self.session.beginConfiguration()
//self.objOverlayView.disableCenterCameraBtn();
if device.isTorchActive == false {
self.flashOn(device: device)
} else {
self.flashOff(device: device);
}
//self.objOverlayView.enableCenterCameraBtn();
self.session.commitConfiguration()
}
}
}
Swift 4
So there are two different behaviors to choose from in AVFoundation. One would be a capture device torch switch. Connect the torchSwitch action to some view and be sure to change CameraManager.shared.backDevice to your instance of the front or back device that provides the current input.
#IBAction func torchSwitch(_ sender: Any) {
guard let device = CameraManager.shared.backDevice else { return }
guard device.isTorchAvailable else { return }
do {
try device.lockForConfiguration()
device.torchMode = device.torchMode ? .off : .on
if device.torchMode == .on {
try device.setTorchModeOn(level: 0.7)
}
} catch {
debugPrint(error)
}
}
AVFoundation has deprecated device.flashMode
Now to set flash, declare a variable on camera or vc. The value here will be the default.
var flash: AVCaptureFlashMode = .off
Connect this action to some view
#IBAction func torchSwitch(_ sender: Any) { flash = flash ? .off : .on }
Then when you want to capture an image, use AVCapturePhotoOutput and prepare the photo settings. stillCameraOutput is an instance of AVCapturePhotoOutput.
let settings = AVCapturePhotoSettings()
settings.flashMode = flash
stillCameraOutput.capturePhoto(with: settings, delegate: self)
Swift 4 :
Following code is working fine for me
private enum FlashPhotoMode {
case on
case off
}
#IBOutlet weak var flashPhotoModeButton: UIButton!
#IBAction func toggleFlashPhotoMode(_ flashPhotoModeButton: UIButton ) {
sessionQueue.async {
self.flashPhotoMode = (self.flashPhotoMode == .on) ? .off : .on
let flashPhotoMode = self.flashPhotoMode
DispatchQueue.main.async {
if flashPhotoMode == .on {
self.flashPhotoModeButton.setBackgroundImage(UIImage(named: "flashON"), for: .normal)
print("flashON")
} else {
self.flashPhotoModeButton.setBackgroundImage(UIImage(named: "flashOFF"), for: .normal)
print("flashOFF")
}
}
}
}
#IBAction private func capturePhoto(_ photoButton: UIButton) {
................
.......................
if self.videoDeviceInput.device.isFlashAvailable {
if self.flashPhotoMode == .on {
photoSettings.flashMode = .on
print("FLASH ON ")
} else {
print("FLASH OFF ")
photoSettings.flashMode = .off
}
}
}
Thanks!