iOS 8.x Swift MPRemoteCommandCenter - Disabling audio playback controls - ios

I'm currently experiencing either a bug or lack of understanding of Apple's Documentation.
Some background. I'm using AVPlayer to initiate a playback of audio. I'm using AVPlayer because it provides more precise controls over playback positioning (dropping into specific spots of a song).
When I implement the code below, the MPRemoteCommandCenter only displays one button (the play/pause button) and all other buttons (back and forward) are not present.
MPRemoteCommandCenter.sharedCommandCenter().playCommand.addTarget(self, action: "remoteCommandMute")
MPRemoteCommandCenter.sharedCommandCenter().pauseCommand.addTarget(self, action: "remoteCommandMute")
I can then toggle whether it's disabled or not just fine. According to Apple's documentation however you should be able to toggle the enabled property and it should hide the buttons entirely without having to add a target to them (at least from my understanding). Here is the reference to the documentation
If I do not include the .addTarget code to any of the buttons and simply follow the instructions from apple to disable said buttons, it does not work.
MPRemoteCommandCenter.sharedCommandCenter().previousTrackCommand.enabled = false
MPRemoteCommandCenter.sharedCommandCenter().nextTrackCommand.enabled = false
MPRemoteCommandCenter.sharedCommandCenter().playCommand.enabled = false
MPRemoteCommandCenter.sharedCommandCenter().pauseCommand.enabled = false
Based on this behavior I would expect that disabling all three buttons would in turn make them hidden however this is not the behavior at all in fact disabling them has no effect on them. You can still press them and they behave as if that code isn't present at all. However, if (as stated above) I add the .addTarget to the button it will become disabled if I set the .enabled flag to false.
Please correct my logic/understanding if this isn't the intended behavior but in my opinion this is absolutely backwards thinking on Apple's part. If my app doesn't support pausing forward or backward functionality the button shouldn't appear at all and disabling it shouldn't just turn it grey (like it currently does). It also shouldn't magically hide buttons if you don't add a target to them either.
On a side note, whenever I do add a target to the buttons they work just fine even if I don't want my app to have the forward and back capabilities.

So after experimenting with the different available button types and actions, it seems that the wording in the documentation could be better. In order to "disable and hide" a button one simply needs to add a target to the desired button. For example, likeCommand.addTarget disables the previous pause/play and forward buttons and hides them. However, if I set the playCommand.enabled to false, it will display said button as greyed out.
Edit: To address not actually sharing the code to which I'm referring...
import UIKit
import PlaygroundSupport
import MediaPlayer
class MyViewController : UIViewController {
private let remote: MPRemoteCommandCenter = MPRemoteCommandCenter.shared()
override func loadView() {
remote.playCommand.addTarget(self, action: #selector(playPause))
remote.playCommand.isEnabled = false
}
#objc private func playPause() {
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
This demonstrates the issue outlined such that toggling the isEnabled property does not do like the documentation states

Related

What is the reset(_:) method

I am learning iOS app development, and I came across a line which confused me:
Also add a call from reset(_:) so it works when you reset the app
But I can't find a reset(_:) function anywhere, both in ViewController and AppDelegate. Do I have to create the function, or is it something different?
So let's add the additional infos:
Your quote is from Intro to App Development with Swift, Apple's iBook., chapter 17.6 Polishing the Interface, in Disabling sliders subpart.
You missed the previous part:
#IBAction func reset(_ sender: AnyObject) {​} Open the Connections inspector. You’ll see that the button has been
connected to the Touch Up Inside event. This is the standard event
used ​for most buttons. Your reset button will set the value of each
slider to 1 and the isOn property of each switch to false. Add that
code to the new action method.
So your quote was talking about that method that you should have added previously on ViewController.swift in the chapter 17.5 Reset Button.

Buttons in iOS app don't respond to touches around the edge of the screen

I'm relatively new to iOS development, but I'm having a go at working on some open source code for an old game that used to be pretty popular (Eden World Builder)
I've made quite a lot of progress in cleaning up the codebase, making small changes. But there's an issue I can't seem to fix. Every button in the game will not respond to taps around the edges of the screen. If a button is in the corner of the screen, you will have to tap towards the bottom of the button.
I've tried to move the buttons away from the edges, and they work, but that isn't practical for use. So there's something preventing the edges of the screen from registering button taps for some reason, and it doesn't seem to be anything to do with the button target areas themselves.
One thing I've noticed: This game is currently on the App Store, even though it hasn't been updated since 2015. In the App Store version (Which is built from the same code that I have) the issue doesn't occur. It must be something to do with building it in a newer version of Xcode, right?
Any assistance would be very helpful, this has been frustrating me for weeks now. Thanks
The answer to your first question is most likely either that your button is outside its parent view, or a gesture recognizer is interfering.
If a button extends beyond its parent views boundaries (or any of its higher parents' boundaries), it will still be visible as long as the parent doesn't have clipping enabled. The result is that you will still see the button, but it will only respond when touching the parts that are inside the parent view. You can find this visually by using Xcode View UI Hierarchy found in the Debug Navigator.
If it is gesture recognizers that interfere with your button, there are several solutions that might work. Several are described in the link you got from #Anbu in the comment.
The answer to your second question is that old apps are linked against old frameworks. Even if they run on the latest iOS version, they still pull in older versions of the framework, causing them to (mostly) continue work as before. This is done to keep compatibility with legacy code.
Try adding this to viewDidAppear
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let window = view.window,
let recognizers = window.gestureRecognizers {
recognizers.forEach { r in
r.delaysTouchesBegan = false
r.cancelsTouchesInView = false
r.isEnabled = false
}
}
}

Find out if VoiceOver is enabled and set VoiceOver Focus on Apple Watch

I'm currently trying to make my game become more accessible by adding VoiceOver support. Everything is working fine on iOS, but I have some struggle with the watchOS Version. I need a way to find out, if VoiceOver is currently enabled to remove certain images based questions in the game. So ist there anything like:
UIAccessibilityIsVoiceOverRunning()
in WatchKit?
And also, is it possible to move the accessibility focus to a certain element? Something comparable to:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, view);
Thanks, Klemens
To check if it's running
let isVoiceOverOn: Bool = WKAccessibilityIsVoiceOverRunning()
if isVoiceOverOn {
// do some VoiceOver stuff
} else {
// do some stuff that does not make sense for VoiceOver
}
To find out when VoiceOver starts and stops observe:
WKAccessibilityVoiceOverStatusChanged

How to show list of keyboards

I have created my own custom keyboard and I want to show list of keyboards view as Apple does. How can I reach this?
Yes it's possible since iOS 10
If you have an UIButton called nextKeyboardButton just add that in your viewDidLoad function.
nextKeyboardButton.addTarget(self, action: #selector(handleInputModeList(from:with:)), for: .allTouchEvents)
handleInputModeList is part of UIInputViewController
This is not possible.
Apple documentation for developing custom keyboards says (emphasis mine):
To ask the system to switch to another keyboard, call the
advanceToNextInputMode method of the UIInputViewController class. The
system picks the appropriate “next” keyboard; there is no API to
obtain a list of enabled keyboards or for picking a particular
keyboard to switch to.
Note that you must provide a way to get to the next keyboard using the above method.

Selector not called on selecting menu item after force touch

I have this code, for responding to a menu item being tapped after a force touch:
class InterfaceController: WKInterfaceController {
override init() {
super.init()
self.addMenuItemWithItemIcon(WKMenuItemIcon.Pause, title: "Pause", action: Selector("test"))
}
func test() {
print("test")
}
}
When I force touch, the menu appears. When I tap the "Pause" button, test() is not called.
Any ideas why this might be?
Solved.
Though its the result of a silly mistake (aren't most problems?), I think this is something a lot of people will run into, so I'll keep it here with my answer.
I enabled force touch on the simulator, so I could show the menu. When I'm tapping again on the button, force touch is still enabled, so I'm force touching, thus dismissing the menu.
Solution: Disable force touch before tapping a menu button.
I wanted to comment with my thanks for this solution but I don't have the rep so I'll begin with, THANKS!
However I can also add an addendum to the solution by pointing out the shortcut keys are really handy here for switching between force press pressures.
⇧⌘1 - for shallow pressure (tap)
⇧⌘2 - for deep pressure (force press)
Make sure the iOS Simulator (watch) is focussed and you'll be good to go.
I'd like to add a small detail to make the SIM procedure more explicit because I actually saw this question and answer before and didn't perform it correctly because it wasn't totally obvious (to me at least).
Press ⇧⌘1 for all regular presses.
Press ⇧⌘2 before you use force touch.
Tricky part: After the menu item appears press ⇧⌘1 again before dismissing the menu item!

Resources