I'm using an iPhone 6s plus, here is the code for the speech recognition viewcontroller:
import Speech
import UIKit
protocol SpeechRecognitionDelegate: class {
func speechRecognitionComplete(query: String?)
func speechRecognitionCancelled()
}
class SpeechRecognitionViewController: UIViewController, SFSpeechRecognizerDelegate {
var textView: UITextView!
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
private var query: String?
weak var delegate: SpeechRecognitionDelegate?
var isListening: Bool = false
init(delegate: SpeechRecognitionDelegate, frame: CGRect) {
super.init(nibName: nil, bundle: nil)
self.delegate = delegate
self.view.frame = frame
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
enum ErrorMessage: String {
case denied = "To enable Speech Recognition go to Settings -> Privacy."
case notDetermined = "Authorization not determined - please try again."
case restricted = "Speech Recognition is restricted on this device."
case noResults = "No results found - please try a different search."
}
func displayErrorAlert(message: ErrorMessage) {
let alertController = UIAlertController(title: nil,
message: message.rawValue,
preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(alertAction)
OperationQueue.main.addOperation {
self.present(alertController, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
speechRecognizer?.delegate = self
//initialize textView and add it to self.view
}
func startListening() {
guard !isListening else {return}
isListening = true
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
guard let recognitionRequest = recognitionRequest else {
print("SpeechRecognitionViewController recognitionRequest \(self.recognitionRequest)")
return
}
recognitionRequest.shouldReportPartialResults = true
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
var isFinal = false
if result != nil {
self.query = result?.bestTranscription.formattedString
self.textView.text = self.query
isFinal = (result?.isFinal)!
}
if error != nil || isFinal {
print("recognitionTask error = \(error?.localizedDescription)")
self.stopListening()
}
})
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryRecord)
try audioSession.setMode(AVAudioSessionModeMeasurement)
try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
} catch {
print("Audio session isn't configured correctly")
}
let recordingFormat = audioEngine.inputNode?.outputFormat(forBus: 0)
audioEngine.inputNode?.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, time) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare()
do {
try audioEngine.start()
textView.text = "Listening..."
} catch {
print("Audio engine failed to start")
}
}
func stopListening() {
guard isListening else {return}
audioEngine.stop()
audioEngine.inputNode?.removeTap(onBus: 0)
recognitionRequest = nil
recognitionTask = nil
isListening = false
}
// MARK: SFSpeechRecognizerDelegate
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if !available {
let alertController = UIAlertController(title: nil,
message: "Speech Recognition is currently unavailable.",
preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default) { (alertAction) in
.self.stopListening()
}
alertController.addAction(alertAction)
present(alertController, animated: true)
}
}
}
This VC is embedded in another viewcontroller.
When a button is tapped in the parent viewcontroller, startListening() is called. When the same button is hit again stopListening() is called.
The first time the speech recognition works just fine. on a second try I get this error (I guess it has to do with grammar loading?):
recognitionTask error = Optional("The operation couldn’t be completed. (kAFAssistantErrorDomain error 209.)")
and speech recognition doesn't works anymore. After 30 seconds I get the timeout error:
Optional(Error Domain=kAFAssistantErrorDomain Code=203 "Timeout" UserInfo={NSLocalizedDescription=Timeout, NSUnderlyingError=0x170446f90 {Error Domain=SiriSpeechErrorDomain Code=100 "(null)"}})
Original code is here SayWhat
What am I missing?
All I had to do was add recognitionRequest?.endAudio() when trying to stop listening:
func stopListening() {
guard isListening else {return}
audioEngine.stop()
audioEngine.inputNode?.removeTap(onBus: 0)
// Indicate that the audio source is finished and no more audio will be appended
recognitionRequest?.endAudio()
recognitionRequest = nil
recognitionTask = nil
isListening = false
}
Related
I want my app to recognize speech from the microphone and allow audio in the background to keep playing.
My app recognizes speech coming in through the microphone and converts it to text. When my app launches it shuts down any audio playing in the background.
Is it possible to let the background audio continue to play while my app listens for speech using the microphone?
Stripped down code:
import UIKit
import Speech
class ViewController: UIViewController {
public private(set) var isRecording = false
private var audioEngine: AVAudioEngine!
private var inputNode: AVAudioInputNode!
private var audioSession: AVAudioSession!
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
override func viewDidLoad() {
super.viewDidLoad()
}
override public func viewDidAppear(_ animated: Bool) {
checkPermissions()
startRecording()
isRecording.toggle()
}
private func startRecording() {
guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
handleError(withMessage: "Speech recognizer not available.")
return
}
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
recognitionRequest!.shouldReportPartialResults = true
recognizer.recognitionTask(with: recognitionRequest!) { (result, error) in
guard error == nil else { self.handleError(withMessage: error!.localizedDescription); return }
guard let result = result else { return }
print(result.bestTranscription.segments)
}
audioEngine = AVAudioEngine()
inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, _) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare()
do {
audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .spokenAudio, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
try audioEngine.start()
} catch {
handleError(withMessage: error.localizedDescription)
}
}
private func checkPermissions() {
SFSpeechRecognizer.requestAuthorization { authStatus in
DispatchQueue.main.async {
switch authStatus {
case .authorized: break
default: self.handlePermissionFailed()
}
}
}
}
private func handlePermissionFailed() {
// Present an alert asking the user to change their settings.
let ac = UIAlertController(title: "This app must have access to speech recognition to work.",
message: "Please consider updating your settings.",
preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Open settings", style: .default) { _ in
let url = URL(string: UIApplication.openSettingsURLString)!
UIApplication.shared.open(url)
})
ac.addAction(UIAlertAction(title: "Close", style: .cancel))
present(ac, animated: true)
}
private func handleError(withMessage message: String) {
// Present an alert.
let ac = UIAlertController(title: "An error occured", message: message, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
When I run my app and there is audio running in the background my app pauses the audio. I tried exiting my app and restarting the audio but when I return to my app it once again pauses the background audio. I would like the audio to keep playing while my app is using the microphone to listen.
I tried removing "options: .duckOthers" but it made no difference.
I believe what I want to do is possible. Shazam, for instance, can play a song on the speaker and simultaneously use the microphone to listen to it and identify it.
Try .playAndRecord instead of .record .
import UIKit
import AVFoundation
class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var video = AVCaptureVideoPreviewLayer()
#IBOutlet weak var square: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
//creates the session
let session = AVCaptureSession()
//Defines the capture device
guard let captureDevice = AVCaptureDevice.default(for: AVMediaType.video),
let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
session.addInput(input)
let output = AVCaptureMetadataOutput()
session.addOutput(output)
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
video = AVCaptureVideoPreviewLayer(session: session)
video.frame = view.layer.bounds
view.layer.addSublayer(video)
self.view.bringSubviewToFront(square)
session.startRunning()
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects
metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
if metadataObjects != nil && metadataObjects.count != 0 {
if let object = metadataObjects[0] as? AVMetadataMachineReadableCodeObject {
if object.type == AVMetadataObject.ObjectType.qr {
let alert = UIAlertController(title: "QR Code", message: object.stringValue,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Retake", style: .default,
handler: nil))
alert.addAction(UIAlertAction(title: "Copy", style: .default, handler: { (nil) in
UIPasteboard.general.string = object.stringValue
}))
present(alert, animated: true, completion: nil)
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
No errors print out and terminal is also empty. This is my updated version of some Swift 3 code that did work so don't understand why it won't work
Try this, working fine with me - using Swift 5.2. Modify the function found() with whatever you want with the returned string. Reference: qr scanner
import AVFoundation
import UIKit
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
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 = [.qr]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .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 metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
dismiss(animated: true)
}
func found(code: String) {
print(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
I am learning SWIFT and getting better. The barcode application I have works, but it keeps scanning the barcode endlessly, until I force a pop up screen 'Ok', 'Cancel' to accept or reject the scan. On research, I found very similar code (shown below). So I created a test project with just one view controller to try this code. It works great, but freezes as soon as the barcode is scanned. My intent is UNFREEZE the screen, accept and save this and allow to user to scan more barcodes. Any help is greatly appreciated. I tried stop scanning just under the line found(code:) but it didn't help unfreezing. I may be wrong, but I suspect the viewilldisapper never gets called here.
import AVFoundation
import UIKit
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
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.frame = view.layer.bounds
previewLayer.videoGravity = .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 metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
dismiss(animated: true)
}
func found(code: String) {
print(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
Edited code:
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
barcodeValue = stringValue
}
// dismiss(animated: true)
previewLayer.removeFromSuperlayer()
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)
let alertController = UIAlertController(title: "Barcode Scanned", message: barcodeValue!, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: {(alert: UIAlertAction!) in self.restartScan()}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler:nil))
present(alertController, animated: true, completion: nil)
//captureSession.startRunning()
}
func found(code: String) {
print(code)
}
func restartScan() {
captureSession.startRunning()
}
It's not a freeze , when the first barcode is detected you stop the process with captureSession.stopRunning() inside func metadataOutput(_ output: AVCaptureMetadataOutput you need this
previewLayer.removeFromSuperlayer()
instead of this
dismiss(animated: true)
as it's a layer not a vc to dismiss that you previously added in
view.layer.addSublayer(previewLayer)
Note: background of your view is black
In my application I need to read QA Code. For the first time it showing string in QR Code. But when second time I try to use it. it is getting stuck in captured photo mode, but the backing code flow is running.
import UIKit
import AVFoundation
class BarCodeViewController: UIViewController,AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
let appDelegat : AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.blackColor()
captureSession = AVCaptureSession()
let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
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: dispatch_get_main_queue())
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
previewLayer.frame = view.layer.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
view.layer.addSublayer(previewLayer);
captureSession.startRunning();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
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, handler: nil))
presentViewController(ac, animated: true, completion: nil)
captureSession = nil
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if (captureSession?.running == false) {
captureSession.startRunning();
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.running == true) {
captureSession.stopRunning();
}
}
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
foundCode(readableObject.stringValue);
}
}
func foundCode(code: String) {
var x = code.componentsSeparatedByString("-")
let productProfile = ProductDetailViewController(nibName: "ProductDetailViewController", bundle: nil)
productProfile.ProductCode = x[1]
dismissViewControllerAnimated(true, completion: nil)
appDelegat.centerContainer!.centerViewController = productProfile
}
override func prefersStatusBarHidden() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
}
I tried but cannot figure it out.. please help me in this
func foundCode(code: String) {
var x = code.componentsSeparatedByString("-")
let productProfile = ProductDetailViewController(nibName: "ProductDetailViewController", bundle: nil)
productProfile.ProductCode = x[1]
captureSession.stopRunning()
dismissViewControllerAnimated(true, completion: nil)
self.navigationController?.popViewControllerAnimated(false)
appDelegat.centerContainer!.centerViewController = productProfile
}
added self.navigationController?.popViewControllerAnimated(false)
I have a project with SpriteKit. I have seen the video of WWDC15 for ReplayKit.
I added ReplayKit to my project and i want record my GameScene and share it in Facebook , save in camera roll more...and more.
anything not working now but i added the functions and i organized all.
thanks!
Start Record
func startScreenRecording() {
// Do nothing if screen recording hasn't been enabled.
let sharedRecorder = RPScreenRecorder.sharedRecorder()
// Register as the recorder's delegate to handle errors.
sharedRecorder.delegate = self
sharedRecorder.startRecordingWithMicrophoneEnabled(true) { error in
if let error = error {
self.showScreenRecordingAlert(error.localizedDescription)
}
}
}
Stop Record
func stopScreenRecordingWithHandler(handler:(() -> Void)) {
let sharedRecorder = RPScreenRecorder.sharedRecorder()
sharedRecorder.stopRecordingWithHandler { (previewViewController: RPPreviewViewController?, error: NSError?) in
if let error = error {
// If an error has occurred, display an alert to the user.
self.showScreenRecordingAlert(error.localizedDescription)
return
}
if let previewViewController = previewViewController {
// Set delegate to handle view controller dismissal.
previewViewController.previewControllerDelegate = self
/*
Keep a reference to the `previewViewController` to
present when the user presses on preview button.
*/
self.presentViewController(previewViewController, animated: true, completion: nil)
}
handler()
}
}
Other Functions of ReplayKit
func showScreenRecordingAlert(message: String) {
// Pause the scene and un-pause after the alert returns.
GameScene().paused = true
// Show an alert notifying the user that there was an issue with starting or stopping the recorder.
let alertController = UIAlertController(title: "ReplayKit Error", message: message, preferredStyle: .Alert)
let alertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) { _ in
GameScene().paused = false
}
alertController.addAction(alertAction)
/*
`ReplayKit` event handlers may be called on a background queue. Ensure
this alert is presented on the main queue.
*/
dispatch_async(dispatch_get_main_queue()) {
self.view?.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
}
}
func discardRecording() {
// When we no longer need the `previewViewController`, tell `ReplayKit` to discard the recording and nil out our reference
RPScreenRecorder.sharedRecorder().discardRecordingWithHandler {
self.previewViewController = nil
}
}
// MARK: RPScreenRecorderDelegate
func screenRecorder(screenRecorder: RPScreenRecorder, didStopRecordingWithError error: NSError, previewViewController: RPPreviewViewController?) {
// Display the error the user to alert them that the recording failed.
showScreenRecordingAlert(error.localizedDescription)
/// Hold onto a reference of the `previewViewController` if not nil.
if previewViewController != nil {
self.previewViewController = previewViewController
}
}
// MARK: RPPreviewViewControllerDelegate
func previewControllerDidFinish(previewController: RPPreviewViewController) {
previewViewController?.dismissViewControllerAnimated(true, completion: nil)
}
Game Scene
import SpriteKit
import ReplayKit
class GameScene: SKScene , SKPhysicsContactDelegate{
func addRecordButton() {
RecordButton = SKSpriteNode(imageNamed:"Record-Off.png")
RecordButton.name = "RecordButton"
RecordButton.position = CGPoint(x: self.size.width/2 + 185, y: self.size.height/2 + 285)
RecordButton.size = CGSizeMake(32,32)
addChild(RecordButton)
}
func Record() {
let recorder = RPScreenRecorder.sharedRecorder()
if recorder.available{
RecordButton.texture = SKTexture(imageNamed: "Record-On.png")
print("Record")
} else {
RecordButton.texture = SKTexture(imageNamed: "Record-Off.png")
print("Stop Record")
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if {
if (RecordButton.containsPoint(location)){
Record()
}
}
}
class ViewController: UIViewController, RPScreenRecorderDelegate, RPPreviewViewControllerDelegate {
#IBOutlet weak var startRecordingButton: UIButton!
#IBOutlet weak var stopRecordingButton: UIButton!
#IBOutlet weak var activityView: UIActivityIndicatorView!
private let recorder = RPScreenRecorder.sharedRecorder()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if SIMULATOR {
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(self.showSimulatorWarning), userInfo: nil, repeats: false)
return
}
}
override func viewDidLoad() {
super.viewDidLoad()
recorder.delegate = self
activityView.hidden = true
buttonEnabledControl(recorder.recording)
}
#IBAction func startRecordingAction(sender: AnyObject) {
activityView.hidden = false
// start recording
recorder.startRecordingWithMicrophoneEnabled(true) { [unowned self] (error) in
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.activityView.hidden = true
}
if let error = error {
print("Failed start recording: \(error.localizedDescription)")
return
}
print("Start recording")
self.buttonEnabledControl(true)
}
}
#IBAction func stopRecordingAction(sender: AnyObject) {
activityView.hidden = false
//end recording
recorder.stopRecordingWithHandler({ [unowned self] (previewViewController, error) in
dispatch_async(dispatch_get_main_queue()) {
self.activityView.hidden = true
}
self.buttonEnabledControl(false)
if let error = error {
print("Failed stop recording: \(error.localizedDescription)")
return
}
print("Stop recording")
previewViewController?.previewControllerDelegate = self
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
// show preview vindow
self.presentViewController(previewViewController!, animated: true, completion: nil)
}
})
}
//MARK: - Helper
//control the enabled each button
private func buttonEnabledControl(isRecording: Bool) {
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
let enabledColor = UIColor(red: 0.0, green: 122.0/255.0, blue: 1.0, alpha: 1.0)
let disabledColor = UIColor.lightGrayColor()
if !self.recorder.available {
self.startRecordingButton.enabled = false
self.startRecordingButton.backgroundColor = disabledColor
self.stopRecordingButton.enabled = false
self.stopRecordingButton.backgroundColor = disabledColor
return
}
self.startRecordingButton.enabled = !isRecording
self.startRecordingButton.backgroundColor = isRecording ? disabledColor : enabledColor
self.stopRecordingButton.enabled = isRecording
self.stopRecordingButton.backgroundColor = isRecording ? enabledColor : disabledColor
}
}
func showSimulatorWarning() {
let actionOK = UIAlertAction(title: "OK", style: .Default, handler: nil)
// let actionCancel = UIAlertAction(title: "cancel", style: .Cancel, handler: nil)
let alert = UIAlertController(title: "ReplayKit不支持模拟器", message: "请使用真机运行这个Demo工程", preferredStyle: .Alert)
alert.addAction(actionOK)
// alert.addAction(actionCancel)
self.presentViewController(alert, animated: true, completion: nil)
}
func showSystemVersionWarning() {
let actionOK = UIAlertAction(title: "OK", style: .Default, handler: nil)
let alert = UIAlertController(title: nil, message: "系统版本需要是iOS9.0及以上才支持ReplayKit", preferredStyle: .Alert)
alert.addAction(actionOK)
self.presentViewController(alert, animated: true, completion: nil)
}
// MARK: - RPScreenRecorderDelegate
// called after stopping the recording
func screenRecorder(screenRecorder: RPScreenRecorder, didStopRecordingWithError error: NSError, previewViewController: RPPreviewViewController?) {
print("Stop Recording ...\n");
}
// called when the recorder availability has changed
func screenRecorderDidChangeAvailability(screenRecorder: RPScreenRecorder) {
let availability = screenRecorder.available
print("Availability: \(availability)\n");
}
// MARK: - RPPreviewViewControllerDelegate
// called when preview is finished
func previewControllerDidFinish(previewController: RPPreviewViewController) {
print("Preview finish");
dispatch_async(dispatch_get_main_queue()) {
[unowned previewController] in
// close preview window
previewController.dismissViewControllerAnimated(true, completion: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}