viewDidLayoutSubviews & UIApplicationDidBecomeActive notification - ios

In my viewDidLoad, I add code to follow UIApplication.didBecomeActive notification as follows:
NotificationCenter.default.addObserver(self,
selector: #selector(ViewController.applicationDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: UIApplication.shared)
The question is whether it is a given that once the notification is subscribed in viewDidLoad, the notification callback will not fire before viewDidLayoutSubviews() is called? It so happens on all my iOS devices across all iOS versions but it seems to be the source of bug if this is not true. Trying to figure out sources of all unreproducible bugs, not sure if this can be one of them.

Related

How to know when MPMusicPlayerController changes playing item naturally

I am using the MPMusicPlayerController to create a music player in my app. I have got it all working great except for one small issue:
When the songs changes naturally - one song finishes and the next starts from the set queue - the notification MPMusicPlayerControllerNowPlayingItemDidChange doesn't appear to be called.
At the moment I am utilising both the MPMusicPlayerControllerNowPlayingItemDidChange and MPMusicPlayerControllerPlaybackStateDidChange notifications. These cover playing, pausing, shuffle, repeat, next, previous etc. When the notifications are hit I then refresh the screen based on the MPMusicPlayerController to show the new song, artist or different button icons required. Neither of these are called though when a song finishes and the next one automatically starts playing - this means that the title and artist of the previous song is left until the user reloads the screen or interacts with the audio controls which is not good user experience.
Short of regularly checking whether the current name matches the playing name I don't know how to update this in the normal flow of the app.
NotificationCenter.default.addObserver(
forName: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange,
object: musicPlayerController,
queue: nil) { _ in
// Update view
}
The answer to this turns out to be very simple but also difficult to spot if you're not looking in the right place.
Before we add our observers we need to begin generating the playback notifications:
musicPlayerController.beginGeneratingPlaybackNotifications()
NotificationCenter.default.addObserver(self,
selector: #selector(refreshView),
name: .MPMusicPlayerControllerPlaybackStateDidChange,
object: musicPlayerController)
NotificationCenter.default.addObserver(self,
selector: #selector(refreshView),
name: .MPMusicPlayerControllerNowPlayingItemDidChange,
object: musicPlayerController)
We also need to remember to end generating them when we leave (deallocate) the view:
deinit {
NotificationCenter.default.removeObserver(self, name: .MPMusicPlayerControllerPlaybackStateDidChange, object: nil)
NotificationCenter.default.removeObserver(self, name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
musicPlayerController.endGeneratingPlaybackNotifications()
}
The confusion came from the musicMediaPlayer returning a number of notifications even without this which didn't point to the fact we weren't observing all the notifications that were being fired.
Note: It is worth noting that as of the time of writing this it was in discussion whether there was a need to manually remove observers - I have included it here for answer completeness.

Block screen recoding but allow HDMI screen mirroring Swift 4

I need to stop Screen recoding, however I need to allow video sharing through HDMI.
I know the captured notification is usually used for this, but I cant find a way to separate out these two things.
UIScreen.main.addObserver(self, forKeyPath: "captured", options: .new, context: nil)
The above triggers notifications when screen recording is started / stopped. But also triggers when an external display is connected / disconnected (though I'm finding it to be intermittent here).
I have tried using the following to detect if a screen within the UIScreen is mirrored or just to check the screen count. Recordings show only one screen in the count, and mirrored screens should show a count of 2
recordingLabel.text = "count \(UIScreen.screens.count)"
for screen in UIScreen.screens {
if screen.mirrored != nil {
recordingLabel.text = "Mirrored - count \(UIScreen.screens.count)"
}
}
But again this is intermittent. Most of the time the count does not change on connecting a HDMI screen, but does change to 2 on disconnecting.
I have also found using UIScreen notifications work for connected / disconnected. But there seems to be a race condition happening with the captured notification that I still need to handle recordings.
NotificationCenter.default.addObserver(self, selector: #selector(connected), name: UIScreen.didConnectNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(disconnected), name: UIScreen.didDisconnectNotification, object: nil)
Is there any way to detect the type of recording or prevent the race from happening when both types of notifications trigger?
EDIT :
I have since found
NotificationCenter.default.addObserver(self, selector: #selector(captureChanged(notification:)), name: UIScreen.capturedDidChangeNotification, object: nil)
Which works better with the connection notifications, but it still triggers before I get a connected notification. I don't really want to add a timer to detect if the didConnect notification triggers after the captureChange, but that might be the only way

How to prevent SpriteKit game from crashing when Notification Center is opened

I am having a weird issue with a SpriteKit game. The app crashes when a user opens Notification center or Control Center. I want the worldNode layer to be paused and the menuNode layer to be presented when the applicationWillResignActive is called, which is working fine when the home button is pressed. I've tried pausing my game from within the AppDelegate functions applicationWillResignActive and I've tried pausing when applicationDidBecomeActive is called if the game has started. I've tried using the NotificationCenter approach from within the ViewController both work when the Home Button/Home Gesture is used. How Should I handle this? Or is this just a bug in iOS 11?
I have this in the viewDidLoad method of my ViewController
NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
Then each one of those selectors
It crashes on both if trying to present the menu when the appDidBecomeActive and appWillResignActive
#objc func appDidBecomeActive() {
print("appDidBecomeActive")
if GameScene.sharedInstance != nil {
print("presentingMenu")
GameScene.sharedInstance.presentMenuNode()
}
}
#objc func appWillResignActive() {
print("appWillResignActive")
if GameScene.sharedInstance != nil {
print("presentingMenu")
GameScene.sharedInstance.presentMenuNode()
}
}
I feel like I may be trying to approach this the wrong way, but what I don't understand is why does it work when the Home button/Home gesture is fired?
Edit:
After more testing I found that everything works as expected when running on iOS 10 devices. However when I run the DemoBots app that apple provides from their sample code it doesn't crash on iOS 11 and basically does what I want to do, so there has got to be a better way to handle the transitions of the game state, any input is appreciated.

UIKeyboardWillChangeFrame Notification not called with emoji keyboard

First I had a UIViewController listenning for the UIKeyboardWillShow notification to adjust the screen for the keyboard. But every time I changed to emoji keyboard, the notification wasn't called.
So, I changed to UIKeyboardWillChangeFrame notification like this
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
It seems to work fine if I just change to emoji by tapping keyboard type.
However, if I press and hold keyboard type to select (my keyboard have more than one language) and select the emoji keyboard, the notification is not fired.
Anyone had something like this before? Any suggestions?
This is a bug in iOS 11, but there is a hacky temporary solution:
You can listen language mode changes:
NotificationCenter.default.addObserver(self, selector: #selector(inputModeDidChange(_:)), name: .UITextInputCurrentInputModeDidChange, object: nil)
And check for emoji:
if([[UITextInputMode currentInputMode].primaryLanguage isEqualToString:#"emoji"]) // layout again

NSNotificationCenter not working with Custom Keyboard

I am developing a custom keyboard using Swift. I want to get UIDeviceOrientationDidChangeNotification when Orientation changes; for that purpose, I am using:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "changeOrientation:", name: UIDeviceOrientationDidChangeNotification, object: nil)
func changeOrientation(notification: NSNotification) { }
It works with a simple app, but doesn't work with custom keyboard extension.
To anyone new to Extensions
Select your extension target, Select 'play' it will ask you to chose an app to run. Select an appropriate app.
Then you are running with the debugger attached to your extension and can use breakpoints.
-Dan
What did you put in your changeOrientation function ?
Breakpoints aren't working for me in the keyboard swift file.

Resources