iOS Support External Keyboard Without Displaying One - ios

Given a UIViewController, I would like to receive text input only from the external keyboard. Think UIKeyCommand but for any character (not just 'modified' ones).
However, when I try to implement that using UIKeyInput, it seems that iOS desperately wants to display a keyboard if there is no external one connected.
Is there any way to circumvent that? Specifically, to have the options to receive keystrokes from the keyboard if, and only if, one is connected?

After fiddling with a iPad for an hour, I finally have a good solution for this in swift. The other methods are weak or use 3rd party software. The reason why UIKeyboardWillShowNotification is getting fired even when an external keyboard is being used for an iPad is the shortcut bar existing. In order to disable the shortcut bar, do this:
let item : UITextInputAssistantItem = textField.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
This covers most cases of what you need, but UIKeyboardWillShowNotification can still be fired if someone plugs their keyboard in at certain points of use. If you have the screen adjust, you can't afford any case for the user to experience this. Plus, you might want the shortcut bar for some reason. Regardless of what your desires are, this covers all cases of an external keyboard being used:
Add to viewDidAppear
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(LoginViewController.keyboardWillShow), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(LoginViewController.keyboardWillHide), name: UIKeyboardWillHideNotification, object: nil)
whenever you leave the view add this to anything that makes you leave
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
also add it to the deinit{} method to be thourough.
Now use these functions:
func keyboardWillShow(notification: NSNotification) {
// This code is an alternate way of checking for keyboard
var userInfo: [NSObject: AnyObject] = notification.userInfo!
let firstFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue
let secondFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue
let firstRect = firstFrame.CGRectValue()
let secondRect = secondFrame.CGRectValue()
let diff = abs(firstRect.origin.y - secondRect.origin.y)
let isFirstBigger = firstRect.origin.y > secondRect.origin.y
if firstRect != secondRect && diff != 55 {
if !isFirstBigger {
//animateViewToDefaultPosition()
} else {
//animateViewToPositionWhenKeyboardActive()
}
}
}
func keyboardWillHide() {
//animateViewToDefaultPosition()
}
The 55 is the height of the shortcut bar. You can remove it's functionality if you don't have one. The !isFirstBigger is used to check for when they unhook the keyboard and hook it back in during text field editing. It is also important that diff != 55 during that check because with a shortcut bar this would be the case when you did not want to animate the screen.
This is by far the best method I have seen after scouring Stack Overflow. If anyone finds a bug in the functionality, let me know, but I am confident it will take care of the pesky shortcut bar external keyboard issues. I hope this helps everyone else out there confused by all this!

Related

Swift: Listen to physical button up/down events on iOS device

I would like to perform one action when the user presses either volume button, and another when they stop pressing it, similar to what I can do by overriding touchesBegan() and touchesEnded.
I'm aware I can list to the volume level on change like so:
NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)
#objc func volumeChanged(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let volumeChangeType = userInfo["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String {
// do something here, such as a switch based off of "volumeChangeType"
}
}
}
However, once the user has turned the volume up or down all the way, events are no longer fired. Also, no event is fired when the user stops pressing the button. This makes sense, because I'm actually listening to a volume change event, not a volume button press event.
Is there a way to listen to physical button presses in iOS?
Take a look at this GitHub Repo which looks like it provides everything that you're asking for.

Disable smart invert in launch screen

In my app, I have a dark launch screen and, hence, I would like to disable "smart" invert for the launch screen.
In a similar question, I found, that in any view, I can just do:
accessibilityIgnoresInvertColors = true
However, I obviously cannot get a hold of my launch screen's view.
Is there some hidden .plist setting, or any other steps I can take?
call below function in your willFinishLaunchingWithOptions of your AppDelegate :
func appAppearanceConfig(){
NotificationCenter.default
.addObserver(forName: UIAccessibility.invertColorsStatusDidChangeNotification,
object: nil,
queue: OperationQueue.main) {_ in
if UIAccessibility.isInvertColorsEnabled {
UIView.appearance().accessibilityIgnoresInvertColors = true
}
}
}

Why doesn't my iOS (Swift) app properly recognize some external display devices?

So I have an odd issue and my google-fu utterly fails to even provide me the basis of where to start investigating, so even useful keywords to search on may be of use.
I have an iOS application written in swift. I have a model hooked up to receive notifications about external displays. On some adaptors, I'm able to properly detect and respond to the presence of an external display and programatically switch it out to be something other than a mirror (see code block below). But with another adaptor, instead of just 'magically' becoming a second screen, I'm asked to 'trust' the external device, and it simply mirrors the device screen. Not the intended design at all.
func addSecondScreen(screen: UIScreen){
self.externalWindow = UIWindow.init(frame: screen.bounds)
self.externalWindow!.screen = screen
self.externalWindow!.rootViewController = self.externalVC
self.externalWindow!.isHidden = false;
}
#objc func handleScreenDidConnectNotification( _ notification: NSNotification){
let newScreen = notification.object as! UIScreen
if(self.externalWindow == nil){
addSecondScreen(screen: newScreen)
}
}
#objc func handleScreenDidDisconnectNotification( _ notification: NSNotification){
if let externalWindow = self.externalWindow{
externalWindow.isHidden = true
self.externalWindow = nil
}
}
The worst issue here is that because I'm connecting to an external display to do this, I can't even run this code through the debugger to find out what is going on. I don't know where to even begin.
Any ideas?
Edit:
Thanks to someone pointing out wifi debugging, I can tell you my notifications are firing off, but they're both firing at the same time, one after the other, when the external adaptor is disconnected.

Here Maps iOS SDK issue (Swift3, Xcode 8) has a nil currentPosition

I am using Here Maps SDK in my project in Swift 3 (in Xcode 8 for iOS 10) to grab the currentPosition from NMAPositioningManager. However, while isActive is true for the NMAPositioningManager, the currentPosition is always nil. I have a valid position and valid credentials H(ERE_MAPS_APPID, HERE_MAPS_APP_CODE, and HERE_MAPS_LICENSE_KEY).
I checked the SDKDemo with Xcode 8 for iOS 10, which runs fine, but their code is in Objective-C. Any comment would be appreciated.
if let posMan : NMAPositioningManager = NMAPositioningManager.shared() {
if let currPos = posMan.currentPosition{
if let geoCoordCenter = currPos.coordinates {
self.mapView.setGeoCenter(geoCoordCenter, with: NMAMapAnimation.none)
}
}
}
Make sure positioning is started (I guess you did, but it's not shown in your snipped, so pasting it here again for reference):
NMAPositioningManager.shared().startPositioning()
Then normally it takes some time till your position is available, so you can't query dirctly PositionManager. I suggest to register for the position update callbacks:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.positionDidUpdate), name: NSNotification.Name.NMAPositioningManagerDidUpdatePosition, object: NMAPositioningManager.shared())
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.didLosePosition), name: NSNotification.Name.NMAPositioningManagerDidLosePosition, object: NMAPositioningManager.shared())
Then do in the callback with the position whatever you want:
func positionDidUpdate(){
if let p = NMAPositioningManager.shared().currentPosition {
mapView.setGeoCenter(p.coordinates, with: NMAMapAnimation.bow)
}
}
func didLosePosition(){
print("Position lost")
}
Let me know if it's still not working for you like this, and I'll add more complete code.

Getting the currently active keyboard language as NSLocale

I already found a lot of approaches for this but no working solution. Here's what I tried and didn't work.
(1) Simply calling primaryLanguage()
UITextInputMode().primaryLanguage
→ always returns nil :-/
(2) Subscribing to UITextInputCurrentInputModeDidChangeNotification notifications
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "changeInputMode:", name: UITextInputCurrentInputModeDidChangeNotification, object: nil)
}
func changeInputMode(sender : NSNotification) {
...?!
}
The notification is getting triggered but it is unclear how I can extract the current language information from the notification.
(3) Using activeInputModes()
let localeIdentifier = UITextInputMode.activeInputModes().first as? UITextInputMode
var locale:NSLocale = NSLocale(localeIdentifier: localeIdentifier.primaryLanguage!)
println("Input Mode Language \(localeIdentifier.primaryLanguage!)")
This always provides the same array of all available keyboards, no information on the actually active one.
How do I get the NSLocale of the currently active keyboard?
You can access the primaryLanguage from every textfield by accessing the textfields textInputMode like that:
var language = textfield.textInputMode?.primaryLanguage

Resources