Swift Broadcast Replaykit Stop Recording - ios

I have this code for running the BroadCast now I need one Button in App to stop broadcast without going to Notification Centre is that possible.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
UIScreen.main.addObserver(self, forKeyPath: "captured", options: .new, context: nil)
}
func addRPkitVw() {
let broadcastPickerView = RPSystemBroadcastPickerView(frame: CGRect(x: (holderVw.frame.width / 2) - 19, y: 0, width: 38, height: 38))
holderVw.addSubview(broadcastPickerView)
broadcastPickerView.backgroundColor = .clear
broadcastPickerView.showsMicrophoneButton = true
}

I'm facing same issue, but have you tried with finishBroadcastWithError in RPBroadcastSampleHandler. It's temporary solution, cus there is error popup

You have to pass a message to our Extension Whenever you need to stop the recording. Now in your Upload Broadcast Extension when you get the message just call the finishBroadcastWithError function and pass your own error type.
Example:- Recording successfully stoped etc.

Related

Putting loading animation over VNDocumentViewController Swift

Is it possible to put a loading animation over the VNDocumentViewController? As in, when the user presses the Save button, is there a way for me to somehow indicate that the Vision is processing the image and hasn't frozen? Right now, in my app, there is a long pause between the user pressing Save and the actual image being processed.Here is an example from another post of what I'm trying to create
Here is one example of adding a loading indicator using UIActivityIndicatorView().
startAnimating() to start the animation and stopAnimation() to stop the animation.
iOS - Display a progress indicator at the center of the screen rather than the view
guard let topWindow = UIApplication.shared.windows.last else {return}
let overlayView = UIView(frame: topWindow.bounds)
overlayView.backgroundColor = UIColor.clear
topWindow.addSubview(overlayView)
let hudView = UIActivityIndicatorView()
hudView.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
overlayView.addSubview(hudView)
hudView.center = overlayView.center
hudView.startAnimating()
Alternatively, you could look into using Cocoapod MBProgressHud
https://cocoapods.org/pods/MBProgressHUD
There's a way you can extend a class in Swift that captures this problem well. The idea is you want a UIActivityIndicator in your VNDocumentCameraViewController. But we'd like that to be a part of every version of this we use. We could simply embed the DocumentVC's view into our current view and superimpose a UIActivityIndicator above it in the view stack, but that's pretty hacky. Here's a quick way we can extend any class and solve this problem
import VisionKit
import UIKit
extension VNDocumentCameraViewController {
private struct LoadingContainer {
static var loadingIndicator = UIActivityIndicatorView()
}
var loadingIndicator: UIActivityIndicatorView {
return LoadingContainer.loadingIndicator
}
func animateLoadingIndicator() {
if loadingIndicator.superview == nil {
view.addSubview(loadingIndicator)
//Setup your constraints through your favorite method
//This constrains it to the very center of the controller
loadingIndicator.frame = CGRect(
x: view.frame.width / 2.0,
y: view.frame.height / 2.0,
width: 20,
height: 20)
//Setup additional state like color/etc here
loadingIndicator.color = .white
}
loadingIndicator.startAnimating()
}
func stopAnimatingLoadingIndicator() {
loadingIndicator.stopAnimating()
}
}
The place we can call these functions are in the delegate methods for VNDocumentCameraViewController that you implement in your presenting ViewController:
func documentCameraViewController(
_ controller: VNDocumentCameraViewController,
didFinishWith scan: VNDocumentCameraScan
) {
controller.animateLoadingIndicator()
}

Using multiple AVAudioSequencer with one or more AVAudioUnitSampler

Context:
I am trying to play multiple MIDI sequences in an iOS app using AVFoundation. The tracks are loaded in MIDI files and I have successfully managed to play them one by one if I load them in a AVAudioSequencer. I also have an AVAudioUnitSampler object which is connected in an AVAudioEngine and it successfully plays the selected instrument from the loaded sound bank in the sampler.
The setup works correctly if I create a new AVAudioSequencer each time I play a sound. However, if I would like to reuse a sequencer after it's finished, it sounds like it's not using the sampler's instrument.
I suspect when I create the AVAudioSequencer objects they are automatically connected to the AVAudioEngine but only the last object will get the connected to the sampler.
I've tried to manually connect the destinationAudioUnit of the tracks in the sequencer to the sampler, but then it doesn't play a sound at all. I also tried to make multiple samplers and connect them all to the engine, but that didn't work either.
My main question is: What is the proper way of using multiple AVAudioSequencer objects with one AVAudioUnitSampler? Or do I need to create a sampler for each sequencer and connect them somehow?
Here's a very basic playground example of two sequencers. When I run it, sequencer B successfully plays the sound through the sampler, but A is not using the instrument.
import UIKit
import PlaygroundSupport
import AVFoundation
class MyViewController : UIViewController {
let buttonA = UIButton(type: .system), buttonB = UIButton(type: .system)
let engine = AVAudioEngine()
lazy var sequencerA = AVAudioSequencer(audioEngine: engine)
lazy var sequencerB = AVAudioSequencer(audioEngine: engine)
let sampler = AVAudioUnitSampler()
// UI setup
override func loadView() {
let view = UIView()
view.backgroundColor = .white
buttonA.setTitle("Sequencer A", for: .normal)
buttonB.setTitle("Sequencer B", for: .normal)
buttonA.addTarget(self, action: #selector(playSequencerA), for: .touchUpInside)
buttonB.addTarget(self, action: #selector(playSequencerB), for: .touchUpInside)
view.addSubview(buttonA)
view.addSubview(buttonB)
buttonA.frame = CGRect(x: 150, y: 200, width: 100, height: 100)
buttonB.frame = CGRect(x: 150, y: 300, width: 100, height: 100)
self.view = view
}
// Sound engine setup
override func viewDidLoad() {
engine.attach(sampler)
engine.connect(sampler, to: engine.mainMixerNode, format: nil)
let soundBankPath = playgroundSharedDataDirectory.appendingPathComponent("gs_instruments.dls")
let midiA = playgroundSharedDataDirectory.appendingPathComponent("sfx_a.mid")
let midiB = playgroundSharedDataDirectory.appendingPathComponent("sfx_b.mid")
try! sampler.loadSoundBankInstrument(at: soundBankPath, program: 11, bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB))
try! sequencerA.load(from: midiA, options: [])
try! sequencerB.load(from: midiB, options: [])
try! engine.start()
}
#objc public func playSequencerA() { play(sequencerA) }
#objc public func playSequencerB() { play(sequencerB) }
func play(_ sequencer: AVAudioSequencer) {
if sequencer.isPlaying { sequencer.stop() }
sequencer.currentPositionInBeats = 0
try! sequencer.start()
}
}
PlaygroundPage.current.liveView = MyViewController()
Edit:
After some additional experiments I suspect that the AVAudioEngine cannot have more than one AVAudioSequencer instance (or I'm still doing something wrong). As a workaround, I have created a separate AVAudioEngine object for each MIDI file that I need to play and they all have their own sampler and sequencer inputs, which plays the sounds just fine. I'm pretty sure this solution is not optimal, so I would be glad for any tips about a better setup.

UITextView publisher doesn't publish a signal when a user taps a keyboard

Apple says:
Combine also provides a built-in publisher for any
property that’s compliant with Key-Value Observing.
So, I have an instance of UITextView inside of a view controller view which should emit a signal via publisher when a user taps a keyboard but it doesn't happens. Below a snipet of code which explains my way of subscribing to a publisher
class MyViewController : UIViewController {
var t = UITextView(frame: .init(x: 0, y: 0, width: 100, height: 20))
override func viewDidLoad() {
super.viewDidLoad()
t.publisher(for: \UITextView.text)
.receive(on: RunLoop.main)
.sink { (str) in
print(str)
}
view.addSubview(t)
}
}
You did not store the subscription a.k.a. AnyCancellable. Simply do this:
class MyViewController : UIViewController {
var t = UITextView(frame: .init(x: 0, y: 0, width: 100, height: 20))
var set = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
t.publisher(for: \UITextView.text)
.receive(on: RunLoop.main)
.sink { (str) in
print(str)
}.store(in: &set)
view.addSubview(t)
}
}
In addition to making sure you store your subscription so it does not get removed by memory management, take heed of the fact that much of UIKit is not KVO-compliant, and that when things do work in KVO, they're typically the result of happy accidents.
This means you probably don't want to rely on KVO to retrieve user input unless you're prepared to continue testing the results of this in a lot of different scenarios for each minor iOS revision.
Instead, you're probably stuck relying on UITextViewDelegate methods like textDidChange(UITextView) for this kind of information — at least for now. I'd be shocked if we didn't have any kind of Combine binding options for UIKit this time next year.

How can I replace the inputAccessoryView with another one unnoticed by the user?

I have requirements, that it is necessary to show one inputAccessoryView for the software keyboard and another for the hardware keyboard. I made a separate manager which includes NotificationCenter, after receiving the notification I calculate the software or hardware is being used at the moment. After that I try to replace the first inputAccessoryView with the one that should be used with the hardware keyboard, but I have a flicker when I call textView.reloadInputViews(), I tried it without animation, but it does not help. Please tell me how can I replace inputAccessoryView not noticeable for the user? Thank you.
code example
#objc private func keyboardWillShow(_ notification: Notification) {
// test
if KeyboardManager.shared.isHardwareKeyboard {
let redView = UIView()
redView.backgroundColor = .red
redView.frame = CGRect(x: 0, y: 0, width: 320, height: 100)
textView?.inputAccessoryView = redView
UIView.performWithoutAnimation {
self.textView?.reloadInputViews()
}
}
}
Update: Of course, the best option would be to know which keyboard is used and add only the inputAccessoryView that is needed at the moment, but unfortunately Apple does not allow it.

SceneKit crash after dismissing VC

Project: https://github.com/cgranthovey/SCNViewTest
The app has 2 VCs.
When I segue to the 2nd VC I set up the SCNScene with the code below.
override func viewDidLoad() {
super.viewDidLoad()
mySCNView.autoenablesDefaultLighting = true
mySCNView.allowsCameraControl = true
mySCNView.backgroundColor = UIColor.black
}
override func viewWillAppear(_ animated: Bool) {
let pyramid = SCNNode()
pyramid.geometry = SCNPyramid(width: 1, height: 2, length: 1)
pyramid.geometry?.firstMaterial?.diffuse.contents = UIColor.purple
pyramid.geometry?.firstMaterial?.diffuse.contents = UIColor.white
pyramid.position = SCNVector3(0, 0, 0)
pyramid.eulerAngles = SCNVector3(x: 0, y: 10, z: 180 * Float.pi)
let shapeScene = SCNScene()
shapeScene.rootNode.addChildNode(pyramid)
mySCNView.scene = shapeScene
}
If I then flick the image and have it spin, and while it's spinning I press the back button
#IBAction func backBtnPress(_ sender: AnyObject){
self.navigationController?.popViewController(animated: true)
}
I get a crash.
Thread 1: EXC_BAD_ACCESS (code=1, address=0x101f2210)
However if I wait for the spinning to stop and then press the back button there is no crash. Any ideas what's going on or how to fix this?
Per my suggestion, after you expanded the stack trace you see an interesting piece of information. Specifically, the code causing the crash seems related to the inertia system of the SCNView's camera controller.
Attempting to stop the inertia before exiting the scene's view controller appears to mitigate the issue in my brief testing. In my experience with iOS 11, this crash only ever happened on the simulator for me, but I was using modal presentations. I think this is likely a bug in SCNCameraController.
#IBAction func backBtnPress(_ sender: AnyObject){
chariotView.defaultCameraController.stopInertia()
self.navigationController?.popViewController(animated: true)
}

Resources