AVPictureinPictureController - Can I pause pipController multiple times? - ios

I'm using Picture in Picture(PiP) feature in my application using AVPictureInPictureController and it's delegate methods.
I want to call saveClipfunc() multiple times when click pause icon of AVPictureInPictureController each time.
But play/pause icon is changed to larger rectangle icon when click once and after that, is not changed again (remains larger rectangle icon).
before click play/pause
after click play/pause
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, setPlaying playing: Bool) {
if !playing {
saveClipfunc()
}
} else {
}
}
this is delegate methods of AVPictureInPictureSampleBufferPlaybackDelegate. when paused, call saveClipfunc().
So Can I pause pipController multiple times?

Better approach would be propably observing if AVPlayer is paused and if PiP is simultaneously active. Here is an example:
Here are yor class variables, you propably initialize AVPlayer and AVPictureInPictureController somewhere on different place in your code, so take it just as an example:
var playerRateObserver: NSKeyValueObservation?
var pictureInPictureController = AVPictureInPictureController()
var avPlayer = AVPlayer()
Somewhere in your code assign observer to playerRateObserver variable:
let playerRateObserver = avPlayer.observe(\.rate, options: [.new, .initial], changeHandler: {(player, rate) in
if rate.newValue == 1 {
//"user pressed play"
}
if rate.newValue == 0 {
//user pressed stop
if pictureInPictureController.isPictureInPictureActive {
saveClipfunc()
}
}
})
Do not forget to deinitialize your observer at the end of you class / struct by adding deinit function:
deinit {
playerRateObserver?.invalidate()
}

Related

Dynamically change color of rows in a SwiftUI List

In a SwiftUI app I want to do the following and I am wondering how to tackle the problem. I hope someone can give me a hint.
I have a list with about seven or eight rows and below the list is a button. When I tap the button the app plays audio files (one for each row). This is already working.
And here is what I want to implement:
When I tap the button, I want each row to change color one after the other while the matching audio file is playing.
The sound files are handled by a class like the one below with an audioPlayerDidFinishPlaying method to detect when a file play has ended.
class AudioEngine: NSObject,ObservableObject,AVAudioPlayerDelegate {
#Published var isPlaying:Bool = false {
willSet {
if newValue == true {
playAudio()
}
}
}
....
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
....
}
}
An instance of AudioEngine is created with:
let soundManager = AudioEngine()
........
And this shows the view with the list and the button:
VStack {
// Display the list of items:
ListItemsView(sentenceList: sntncLst, moc: moc)
// Display the button to play all the items:
Button(action: {
if !soundManager.isPlaying {self.playAllAudio(sntncLst)}
else {soundManager.stopAudio()}
})
{
Image(uiImage: UIImage(named: "speaker.png")!)
.padding(.bottom, 17)
}
}
....
func playAllAudio(_ sndList:[AudioList]) {
for sentence in sndList {
soundManager.filesArray.append(sentence.soundFile!)
}
soundManager.serialPlayAudio()
}

AdMob - Get Notify when isReady property changes

I trying implement Rewarded Ad - Rewarded Ads New APIs (Beta). Video is load and isReady property is changing to true in a couple of seconds.
I have a button on which user press and Rewarded Video appear
This is function which is fire when user press on button
func presentRewardAd(from viewController: UIViewController) {
if rewardedAd.isReady {
rewardedAd.present(fromRootViewController: viewController, delegate: self)
}
}
The problem is
I want to hide button until video isReady == true, and when it's ready show button. So i want to get notify when rewardedAd.isReady is changing.
What i try so far:
class CustomRewardAd: GADRewardedAd {
private var observation: NSKeyValueObservation?
override init(adUnitID: String) {
super.init(adUnitID: adUnitID)
observation = observe(\.isReady, options: [.old, .new]) { object, change in
print("isReady changed from: \(change.oldValue!), updated to: \(change.newValue!)")
}
}
}
Also i tried this Using Key-Value Observing in Swift but same result.
But changeHandler never gets called. Am i doing something wrong?
Thanks!
I found solution, not ideal but it's works! Maybe this is can help someone in future.
When new rewarded request finishes, isReady property is set to true or false depends what response is.
private func createAndLoadRewardedAd() -> GADRewardedAd {
let rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
rewardedAd.load(GADRequest()) { [weak self] error in
guard let self = self else { return }
self.videoIsReady?(rewardedAd.isReady) // already set
}
return rewardedAd
}
You are welcome!

Need to switch SKScenes at applicationWillResignActive

I need to switch to my pause scene when I get applicationWillResignActive so the user cannot browse the background app screens without my timer running. I use the following code, but the switch to my pause scene doesn't happen until after I bring the app back into the foreground. What am I doing wrong?
func applicationWillResignActive(_ application: UIApplication) {
if let liveWindow = window {
if let viewController = liveWindow.rootViewController {
if let rotopathViewController = viewController as? RotopathViewController {
rotopathViewController.pause()
}
}
}
}
My view controller's pause function is simple.
func pause() {
game.score.pause()
rotopathView.presentScene(pausedScene)
}
and it just calls
func pause() {
elapsedTime += Int(round(Date().timeIntervalSince(intervalStartTime)))
isActive = false
}
If I move some code around and make sure that I set isActive = false after the presentScene call, there is no difference. Also, if I remove the isActive = false assignment, there is no difference.

Remove buffering activity indicator from AVPlayerViewController

I'm implementing an iOS video player using AVPlayerViewController with custom playback controls (i.e., the showsPlaybackControls property is defined as NO). This seems to work properly in most cases, the only issue I'm seeing is that I would like to use a custom activity indicator with the player as well, but it seems that AVPlayerViewController shows a default activity indicator while buffering the video at some points.
Is there a way to remove this default activity indicator view from AVPlayerViewController?
The image shows what I'm describing, the controls at the bottom are custom and overlaid on top of the player, but the activity indicator is not.
I made an extension of AVPlayerViewController that exposes the internal activity indicator. Here you go, with all the Swift 3 sexiness:
import AVKit
extension AVPlayerViewController {
/// Activity indicator contained nested inside the controller's view.
var activityIndicator: UIActivityIndicatorView? {
// Indicator is extracted by traversing the subviews of the controller's `view` property.
// `AVPlayerViewController`'s view contains a private `AVLoadingIndicatorView` that
// holds an instance of `UIActivityIndicatorView` as a subview.
let nestedSubviews: [UIView] = view.subviews
.flatMap { [$0] + $0.subviews }
.flatMap { [$0] + $0.subviews }
.flatMap { [$0] + $0.subviews }
return nestedSubviews.filter { $0 is UIActivityIndicatorView }.first as? UIActivityIndicatorView
}
/// Indicating whether the built-in activity indicator is hidden or not.
var isActivityIndicatorHidden: Bool {
set {
activityIndicator?.alpha = newValue ? 0 : 1
}
get {
return activityIndicator?.alpha == 0
}
}
}
With this, you can either easily style the UIActivityIndicatorView or just hide it all together, e.g.:
playerViewController.isActivityIndicatorHidden = true
I also searched for this solution and the way I managed to make it is hide video player view controllers view once I started playing video and when video is ready to play I show it again.
private func playVideo() {
videoPlayer?.play()
self.addLoader()
videoPlayerController.view.hidden = true
videoPlayer?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: nil)
}
public override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if (object?.isEqual(videoPlayer) == true && keyPath == "status") {
self.removeLoader()
videoPlayerController.view.hidden = false
}
}

automatically update label in Swift by checking the status

I'm new to programming with iOS and Swift. I have a label that should automatically be updated when the status of the StreamingKit Framework changes.
Currently the label only changes when I press a different button (it calls the self.updateView() function again), but I want it to happen automatically when the status has changed, not by pressing a different button.
Here is the code:
func updateView() {
dispatch_async(dispatch_get_main_queue()) {
if let label = self.bufferingLabel, let button = self.playerButton{
if let audioPlayer = self.player{
if(audioPlayer.state == STKAudioPlayerStateBuffering)
{
self.playerState = "Loading"
button.setImage(self.image1, forState: UIControlState.Normal)
}
else(audioPlayer.state == STKAudioPlayerStatePlaying)
{
self.playerState = self.defaultPlayerState
self.playerState = "Playing"
button.setImage(self.image1, forState: UIControlState.Normal)
}
label.text = self.playerState!
}
}
So when I press this button, it gives the first state (which is loading), but after that is done (status changed), the label should change to play, but it doesn't.
#IBAction func pressStart(sender: AnyObject) {
//declare path to streaming
let filePath = "http://mp3.streampower.be/mnm-high.mp3"
if player == nil {
self.player = STKAudioPlayer()
self.player?.delegate = self
}
if let audioPlayer = player{
if (audioPlayer.state == STKAudioPlayerStateReady) ||
(audioPlayer.state == STKAudioPlayerStateStopped){
audioPlayer.play(filePath)
}else{
audioPlayer.stop()
}
}
self.updateView()
}
Some of the delegates I tried as well
func audioPlayer(audioPlayer: STKAudioPlayer!, didStartPlayingQueueItemId queueItemId: NSObject!) {
self.updateView()
}
func audioPlayer(audioPlayer: STKAudioPlayer!, didFinishBufferingSourceWithQueueItemId queueItemId: NSObject!) {
self.updateView()
}
func audioPlayer(audioPlayer: STKAudioPlayer!, stateChanged state: STKAudioPlayerState, previousState: STKAudioPlayerState) {
self.updateView()
}
You should look and use the delegate property of the STKAudioPlayer.
I'm not familiar with it, but generally in the iOS world, that's how you receive events from an object (the object delegates some stuff to the object of your choice).
You can use NSNotifications to send a signal whenever the streaming kit changes. Then you can add an observer to your UI view controller that will trigger a function whenever it receives a signal.
If you want to try out functional reactive programming you can also use The Reactive Cocoa framework to do a similar signal observer pattern. And finally, there is plain old key-value observation. One of these options should be able to help you.

Resources