AVPlayer keeping in memory when the video is in loop - ios

I need to put a player in loop, but why when I add the
NotificationCenter.default.addObserver(forName:NSNotification.Name.AVPlayerItemDidPlayToEndTime,object: nil, queue:nil){
notification in
videoPlayer.seek(to: KCMTimeZero)
videoplayer.play()
}
}
My view stay in memory when i give dismiss in my viewController.
How i reproduce a video my memory goes to increasing all every time that I open the ViewController
Whithout this code it is removed with sucess.
I don't know what I have to do
Can you help me please?

There are three issues with your code:
By default, references are passed as strong into a block. To make sure they are not retained, use weak or unowned:
NotificationCenter.default.addObserver(forName:NSNotification.Name.AVPlayerItemdidPlayToEndTime,object: nil, queue:nil){
[weak videoPlayer] notification in
videoPlayer?.seek(to: KCMTimeZero)
videoplayer?.play()
}
Since iOS 9, observers do not need to be removed from the NotificationCenter unless you are using block observers (which you are). You should store the reference to the observer which is returned from NotificationCenter.addObserver:forName:object:queue:usingBlock::
self.observer = NotificationCenter.default.addObserver(...)
and in viewWillDissappear:
NotificationCenter.default.removeObserver(self.observer)
(alternatively, you could use a selector instead, as Chan Jing Hong pointed out; in that case, removing the observer is no longer necessary but might be needed depending on your app's logic)
The way you are registering for NSNotification.Name.AVPlayerItemdidPlayToEndTime, you will be notified whenever the playback of any AVPlayerItem reaches the end. To avoid potential problems, listen to the notification on the currently played item (by replacing object:nil with object: playerItem)

You should set self as the observer when adding to NotificationCenter.
NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachedEnd(_:)), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
This way, in your viewWillDisappear, you can do removeObserver()
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
}

Related

How to readd a notification observer

So I have about four notification observers added in a particular view controller, but I want to remove one in particular if a condition is met and then add it back for a different condition. I was able to remove the particular observer but I have had trouble adding it back. the other ones work except the one a removed and added back and I know it's working cos when the VC loads and all the notification observers are added but then it stops working after adding it back. here's my code:
// registered the observer in ViewDIdAppear and this registers my bluetooth device and whenever i tap on the bluetooth device button this function gets called
NotificationCenter.default.addObserver(
self, selector: #selector(Myclass.deviceTapped), name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
// i removed the observer here
if condition == false {
NotificationCenter.default.removeObserver(self, name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
}
// and then use this to add it back again
NotificationCenter.default.addObserver(
self, selector: #selector(Myclass.deviceTapped), name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
// and after adding this ^ back the deviceTapped function doesn't get called anymore
The thing to do in these situations is prove to yourself that this does normally work. That way, you can track down what you are doing that prevents it from working.
For example, perhaps self is going out of existence before any notifications have a chance to arrive...? Perhaps some other code, that you have not shown, is unregistering you again...? I've certainly experienced confusions like that! And they can be very hard to debug.
But whatever the explanation, you need first to assure yourself that unregistering for a notification and then re-registering for it is not a problem.
Here's a test app. It has three buttons: Register, Unregister, and Notify. Here's the code:
let notif : Notification.Name = .init("howdy")
#objc func gotNotified() {
print("got notified")
}
#IBAction func doRegister(_ sender: Any) {
NotificationCenter.default.addObserver(self, selector: #selector(gotNotified), name: notif, object: nil)
}
#IBAction func doUnregister(_ sender: Any) {
NotificationCenter.default.removeObserver(self, name: notif, object: nil)
}
#IBAction func doNotify(_ sender: Any) {
NotificationCenter.default.post(name: notif, object: nil)
}
So configure that with the buttons hooked up, and then:
Tap Register.
Tap Notify.
Tap Unregister.
Tap Notify.
Tap Register.
Tap Notify.
You will see that on 2 and 6, but not 4, we print to the console to prove we were notified — just as you would expect.

Do I have to remove in built observers in swift?

So I have a login page that has a video looping in the background and I have done this with the following code:
videoPlayer.play()
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: videoPlayer.currentItem, queue: .main) { [weak self] _ in
self?.videoPlayer?.seek(to: CMTime.zero)
self?.videoPlayer?.play()
}
Question is do I have to remove this observer at deinit or something and if so how do I go about removing the .AVPlayerItemDidPlayToEndTime observer. Not sure of the syntax when it comes to removing these built in observers.
Swift automatically deinitializes built in observables when the controller is dismissed - just use the following function.
deinit {
// Release all resources
// perform the deinitialization
}
there is also a similar question asked here.
Swift deinit

Player swift crash app

I have add Player-swift in my project to play video but when i pop that controller app getting crash with following log
Terminating app due to uncaught exception 'NSRangeException', reason:
'Cannot remove an observer for the key
path "rate" from because it is not
registered as an observer.'
any one have any idea? i have used this player https://github.com/piemonte/Player
Thanks in advance !
Do not forget to unsubsribe from observing some property. For example if you subscribed for observing rate then to remove observing use
player.removeObserver(observer, forKeyPath: #keyPath(AVPlayer.rate))
First: check KVO basics.
Second: in objective-C you would the removing observers code into try-catch block and live happy. The apple's guide tell the same:
Asking to be removed as an observer if not already registered as one
results in an NSRangeException. You either call
removeObserver:forKeyPath:context: exactly once for the corresponding
call to addObserver:forKeyPath:options:context:, or if that is not
feasible in your app, place the removeObserver:forKeyPath:context:
call inside a try/catch block to process the potential exception.
In swift there’s no KVO API call you can make to ask, “Is X observing key path Y of object Z?” There is some ways to workaround it.
Also check the one of the reasons of the crashes when removing the observer. Here is the quote:
"It" refers to the observer. -removeObserver:forKeyPath: raises this
exception if told to remove an object that isn't currently registered
as an observer. So what's happening is that a table view is trying to
unregister as an observer from one of your objects that it
unfortunately didn't previouly register as an observer for.
The usual cause of this is that you have a property that isn't KVO-
compliant. Something accesses your 'foo' property and registers as an
observer of that property, and also as an observer of the object
that's the property's current value; you change the value of 'foo'
without letting anyone know; the observer then later decides to stop
observing, gets your 'foo' property, and removes itself as an observer
of that object. But it's no longer the same object that it registered
as an observer for...
Take IBOutlet of Your UIView for Example
#IBOutlet var videoView:UIView!
var player:AVPlayer!
func buttonPressed()
{
let videoURL = URL(fileURLWithPath: "your File Path")
player = AVPlayer(url: videoURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspect
playerLayer.frame = videoView.bounds
videoView.layer.addSublayer(playerLayer)
player.play()
player.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd(_:));, name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
}
#objc func playerItemDidReachEnd(_ notification: Notification?)
{
let p = notification?.object as? AVPlayerItem
p?.seek(to: kCMTimeZero)
}
override func viewWillDisappear(_ animated: Bool)
{
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
}

Is it possible to detect screenshot in background for iOS? [duplicate]

The app Snapchat, on the App Store, is an app that lets you share pictures with a self-destruct on them. You can only view the pics for X seconds. If you attempt to take a screenshot while the picture is showing using the home-power key combo, it will tell the sender you tried to take a screenshot.
What part of the SDK lets you detect that the user is taking a screenshot? I did not know this was possible.
As of iOS 7 the other answers are no longer true. Apple has made it so touchesCancelled:withEvent: is no longer called when the user takes a screenshot.
This would effectively break Snapchat entirely, so a couple betas in a new solution was added. Now, the solution is as simple as using NSNotificationCenter to add an observer to UIApplicationUserDidTakeScreenshotNotification.
Here's an example:
Objective C
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
// executes after screenshot
}];
Swift
NotificationCenter.default.addObserver(
forName: UIApplication.userDidTakeScreenshotNotification,
object: nil,
queue: .main) { notification in
//executes after screenshot
}
I found the answer!! Taking a screenshot interrupts any touches that are on the screen. This is why snapchat requires holding to see the picture. Reference: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat
Heres how to do in Swift with closures:
func detectScreenShot(action: () -> ()) {
let mainQueue = NSOperationQueue.mainQueue()
NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationUserDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
detectScreenShot { () -> () in
print("User took a screen shot")
}
Swift 4.2
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
This is included as a standard function in:
https://github.com/goktugyil/EZSwiftExtensions
Disclaimer: Its my repo
Swift 4+
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: OperationQueue.main) { notification in
//you can do anything you want here.
}
by using this observer you can find out when user takes a screenshot, but you can not prevent him.
Latest SWIFT 3:
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
In viewDidLoad, call this function
detectScreenShot { () -> () in
print("User took a screen shot")
}
However,
NotificationCenter.default.addObserver(self, selector: #selector(test), name: .UIApplicationUserDidTakeScreenshot, object: nil)
func test() {
//do stuff here
}
works totally fine. I don't see any points of mainQueue...
Looks like there are no direct way to do this to detect if user has tapped on home + power button. As per this, it was possible earlier by using darwin notification, but it doesn't work any more. Since snapchat is already doing it, my guess is that they are checking the iPhone photo album to detect if there is a new picture got added in between this 10 seconds, and in someway they are comparing with the current image displayed. May be some image processing is done for this comparison. Just a thought, probably you can try to expand this to make it work. Check this for more details.
Edit:
Looks like they might be detecting the UITouch cancel event(Screen capture cancels touches) and showing this error message to the user as per this blog: How to detect screenshots on iOS (like SnapChat)
In that case you can use – touchesCancelled:withEvent: method to sense the UITouch cancellation to detect this. You can remove the image in this delegate method and show an appropriate alert to the user.
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
NSLog(#"Touches cancelled");
[self.imageView removeFromSuperView]; //and show an alert to the user
}
Swift 4 Examples
Example #1 using closure
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot,
object: nil,
queue: OperationQueue.main) { notification in
print("\(notification) that a screenshot was taken!")
}
Example #2 with selector
NotificationCenter.default.addObserver(self,
selector: #selector(screenshotTaken),
name: .UIApplicationUserDidTakeScreenshot,
object: nil)
#objc func screenshotTaken() {
print("Screenshot taken!")
}

Remove loop AVPlayer Observer in UIPageViewController

Hi guys I am having a problem with UIPageViewController and Notifications.
I have a page UIPageVewController with a array of pages, so in these pages I have a AVplayer playing in loop as bellow:
func loopVideo(videoPlayer:AVPlayer){
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil){
[weak videoPlayer] notification in
videoPlayer?.seek(to: kCMTimeZero)
videoPlayer?.play()
}
}
The problem is when I change page with scroll the notifications from another pages change my current video playing AVPlayer. I put a print inside the notification and I can see calling the other pages notifications. I don't Know what I have to do?
I tried remove the notification in the viewDidDisappear using NotificationCenter.default.removeObserver(self) but didn't work.
Can you help me?
Thanks
NotificationCenter.default.removeObserver(self) won't work here as you never added yourself as a target.
Instead keep a reference to your notification and remove it. I think it should look something like this:
var notificationObserver:NSObjectProtocol?
func loopVideo(videoPlayer:AVPlayer){
self.notificationObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil){
[weak videoPlayer] notification in
videoPlayer?.seek(to: kCMTimeZero)
videoPlayer?.play()
}
}
func removeObserver() {
NotificationCenter.default.removeObserver(self.notificationObserver)
}
You can simply do a check when your notification is received.
Check if the notification object as AVPlayerItem is the same as the visible views player playerItem, videoPlayer.currentItem
Or simply check if the AVPlayerItem in the notification is the same as the yourCustomView.playerItem
EDIT:
I see your object is nil, it should be the AVPlayerItem. Check this thread.

Resources