How to detect keyboard events on hardware keyboard on iPhone (iOS) - iphone

I have an app with a login page that has three fields for three different random characters. When the user leaves the last field, the soft keyboard disappears and the user can touch a "login" button on screen.
When there is a hardware keyboard (bluetooth or physical) attached, I'd like to be able to hit "enter" on it. However because the user is not in a field, I can't see how to detect this key being pressed.
Would anyone have advice on which class handles key press events? Presumably there is a delegate that I can use to receive these but my searches through the SDK haven't found anything.
Thanks

For iOS 7.0 or later, you can return UIKeyCommands for the keyCommands property from any UIResponder, such as UIViewController:
Objective-C
// In a view or view controller subclass:
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (NSArray *)keyCommands
{
return #[ [UIKeyCommand keyCommandWithInput:#"\r" modifierFlags:0 action:#selector(enterPressed)] ];
}
- (void)enterPressed
{
NSLog(#"Enter pressed");
}
Swift
// In a UIView/UIViewController subclass:
override var canBecomeFirstResponder: Bool {
true
}
override var keyCommands: [UIKeyCommand]? {
return [ UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(enterPressed)) ]
}
#objc func enterPressed() {
print("Enter pressed")
}

One way to accomplish this is to have a hidden extra (4th in your case) text field. Make it 1x1 px in size and transparent. Then make it the first responder when any of your other 3 text fields are not, and look for text change events in that hidden field to trigger your key input event.
You might also want to check the notification for a software keyboard appearing if you don't want it to stay visible as well.

As a followup to the response by #Patrick, here is how you do it in Xamarin.iOS:
public override bool CanBecomeFirstResponder
{
get { return true; }
}
public override UIKeyCommand[] KeyCommands
{
get
{
return new[]{ UIKeyCommand.Create((NSString)"\r", (UIKeyModifierFlags)0, new ObjCRuntime.Selector("enterPressed")) };
}
}
[Export("enterPressed")]
private void OnEnterPressed()
{
// Handle Enter Key
}

Related

How to prevent videoGravity changes in AVPlayerViewController

My memory is not working for me right now. I think I remember there was a way to prevent the video interface of AVPlayerViewController (or similar) from having the button which allows user to toggle between videoGravity settings, I think those are basically two;
.resizeAspect
.resizeAspectFill
User can also double tap on screen to toggle between these two.
What I'd like to do is force the video of a AVPlayerViewController to only use . .resizeAspect for .videoGravity. I think I remember there should be a way to do this with an easy boolean toggle somewhere, but cannot find it searching for 15 minutes.
Here's a very dirty way I was able to hide the button. Unfortunately, user can still use gestures (double tap, pinch) to zoom the video. The proper thing to do is a full custom view it seems.
e.g.
extension UIView {
var recursiveSubviews: [UIView] {
return subviews + subviews.flatMap { $0.recursiveSubviews }
}
}
class CustomPlayerViewController: AVPlayerViewController {
private func hideZoom() {
let zoomButton = view.recursiveSubviews.first(where: { $0.accessibilityLabel == "Zoom" })
zoomButton?.superview?.removeFromSuperview()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
hideZoom()
}
}

Can I set different actions when a keyboard is returned?

I searched everywhere and can't seem to find anything on this. For example I want to have more then one UITextField on the same screen. If I tap on one and type in information and click return it does an action. If you tap in the second one and return the keyboard it does a different action.
Any suggestions or help would be awesome thanks!
One option is to use tags. In the example below you can select the next textfield, and only on the last one do we login. You could use cases to do whatever you'd like
//automatically move to the next field
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
let next = textField.tag + 1
// Find next responder
if let nextField = view.viewWithTag(next) as? UITextField {
nextField.becomeFirstResponder()
} else {
// Not found, so remove keyboard.
textField.resignFirstResponder()
//do the final action to login
loginAction()
}
return false
}
Since iOS 9, you can connect the “Primary Action Triggered” event of your UITextField to an action method. So you can connect each text field's “Primary Action Triggered” event to a separate action method to give the text fields different actions.
See this answer for a demo: https://stackoverflow.com/a/26288292/77567
I realized I was really over thinking it.. Took another look and saw what I was trying to do was a lot easier then I thought.. Thanks everybody who helped! (just an example below)
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if field1.resignFirstResponder() {
print("yellow")
}
if field2.resignFirstResponder() {
print("blue")
}
return (true)
}

Swift, for some UIViews to their overall controller when clicked

I have a DotBoss:UIViewController
There are a dozen UIView in the scene, Dot:UIView
(Some are direct subviews, some are further down.)
There are even container views in the chain between the highest controller and the Dot items.
The dozen Dot items know if they are tapped...
class Dot:UIView
{
private var tap:UITapGestureRecognizer? = nil
override func awakeFromNib()
{
tap = UITapGestureRecognizer(target:self,
action: #selector(SnapDot.handleTap(_:)))
self.addGestureRecognizer(tap!)
}
func handleTap(g:UITapGestureRecognizer)
{
print("user tapped on this particular Dot...")
}
}
I want DotBoss to know when one of the Dot is tapped.
class DotBoss:UIViewController
{
func oneDotWasClicked(d:Dot)
{
}
}
How to do this perfectly in Swift?
Note for anyone googling here, this bubbling extension is remarkably useful: https://blog.veloxdb.com/2016/05/12/bubbling-events-using-uiresponder-in-swift/
A Dot (UIView) and its ultimately controlling DotBoss (UIViewController) are both responders, and are links along the responder chain. Therefore, a Dot can call a method in its DotBoss by walking the responder chain until it comes to the DotBoss:
func handleTap(g:UITapGestureRecognizer) {
// ... other stuff can go here if necessary ...
var r : UIResponder = self
repeat { r = r.nextResponder()! } while !(r is DotBoss)
(r as! DotBoss).oneDotWasClicked(self)
}

Select next NSTextField with Tab key in Swift

Is there a way to change the responder or select another textfield by pressing tab on the keyboard, in Swift?
Notes:
It's for a fill in the blank type application.
My VC creates a list of Words [Word], and each of those words has its own WordView - word.wordView. The WordView is what is displayed. WordView is a child of NSTextField.
I tried to override keydown but it doesn't allow me to type anything in the text view.
You have to connect your textField nextKeyView to the next textField through the IB or programmatically:
textField1.nextKeyView = textField2
Assuming you want to go from textView1 to textView2. First set the delegate:
self.textView1.delegate = self
Then implement the delegate method:
func textView(textView: NSTextView, doCommandBySelector commandSelector: Selector) -> Bool {
if commandSelector == "insertTab:" && textView == self.textView1 {
self.window.makeFirstResponder(self.textView2)
return true
}
return false
}
If you want some control over how your field tabs or moves with arrow keys between fields in Swift, you can add this to your delegate along with some move meaningful code to do the actual moving like move next by finding the control on the superview visibly displayed below or just to the right of the control and can accept focus.
public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
switch commandSelector {
case #selector(NSResponder.insertTab(_:)), #selector(NSResponder.moveDown(_:)):
// Move to the next field
Swift.print("Move next")
return true
case #selector(NSResponder.moveUp(_:)):
// Move to the previous field
Swift.print("Move previous")
return true
default:
return false
}
return false // I didn't do anything
}
I had the same or a similar problem, in that I wanted to use an NSTextView field, to allow multiple lines of text to be entered, but it was the sort of field where entering a tab character would make no sense. I found an easy fix for this: NSTextView has an instance property of isFieldEditor, which is set to false by default; simply set this to true, and tabs will now skip to the next field.

iPhone: Change Keyboard language programmatically

I am trying to provide a different language support on my iOS 5.x application whenever native Keyboard is opened. Provide this language in native keyboard programmatically. Could someone guide me how can i support it? I saw a carbon framework, but looks like its for Mac apps.
Thanks.
You can do it starting from iOS 7 on a per UIResponder basis.
There is textInputMode property in UIResponder class. It is readonly, but the documentation says:
The text input mode identifies the language and keyboard displayed
when this responder is active.
For responders, the system normally displays a keyboard that is based
on the user’s language preferences. You can redefine this property and
use it to return a different text input mode in cases where you want a
responder to use a specific keyboard. The user can still change the
keyboard while the responder is active, but switching away to another
responder and then back restores the keyboard you specified.
In my project I created a subclass of UITextField and defined a new property called userDefinedKeyboardLanguage. I also overrode above mentioned textInputMode method. It looks similar to the following:
- (UITextInputMode *) textInputMode {
for (UITextInputMode *tim in [UITextInputMode activeInputModes]) {
if ([[Utilities langFromLocale:userDefinedKeyboardLanguage] isEqualToString:[Utilities langFromLocale:tim.primaryLanguage]]) return tim;
}
return [super textInputMode];
}
I have also a custom method +(NSString *)langFromLocale:(NSString *)locale in my Utilities class which looks like this:
+ (NSString *)langFromLocale:(NSString *)locale {
NSRange r = [locale rangeOfString:#"_"];
if (r.length == 0) r.location = locale.length;
NSRange r2 = [locale rangeOfString:#"-"];
if (r2.length == 0) r2.location = locale.length;
return [[locale substringToIndex:MIN(r.location, r2.location)] lowercaseString];
}
Now my custom textfield class can change the keyboard input language simply by setting userDefinedKeyboardLanguage property to the desired language.
I know it is old question, but here it is my way to change keyboard language.
Thank to #the4kman for the mark: this way can change current keyboard only to those which were added in Settings.
Swift 3:
class CustomTextField: UITextField {
private func getKeyboardLanguage() -> String? {
return "en" // here you can choose keyboard any way you need
}
override var textInputMode: UITextInputMode? {
if let language = getKeyboardLanguage() {
for tim in UITextInputMode.activeInputModes {
if tim.primaryLanguage!.contains(language) {
return tim
}
}
}
return super.textInputMode
}
}
No this is not possible - user can only change their language in the settings.
However you can give the user an "English" keyboard if you choose (or ask them their preference)
you do this using: UIKeyboardTypeASCIICapable
You can change keyboard directly on the keyboard by pressing "globe icon" on the bottom row.
First, you have to enable those language for input in Settings. Then pressing the globe button on the keyboard would toggle between those languages.
All great answers. But in Swift you can override methods and variables defined in parent class (UIResponder) in an extension. So it's not necessary to subclass. And it's nice to replace the for loop with a more swift like one-liner.
public extension UITextField {
override var textInputMode: UITextInputMode? {
return UITextInputMode.activeInputModes.filter { $0.primaryLanguage == "emoji" }.first ?? super.textInputMode
}
}
Instead of "emoji" probably will be something like Utils.currentAppLanguage
Declaration:
#IBOutlet var yourTextField:cTextField!;
Use:
yourTextField.languageCode = "ru";
Class cTextField:
class cTextField: UITextField {
// ru, en, ....
var languageCode:String?{
didSet{
if self.isFirstResponder{
self.resignFirstResponder();
self.becomeFirstResponder();
}
}
}
override var textInputMode: UITextInputMode?{
if let language_code = self.languageCode{
for keyboard in UITextInputMode.activeInputModes{
if let language = keyboard.primaryLanguage{
let locale = Locale.init(identifier: language);
if locale.languageCode == language_code{
return keyboard;
}
}
}
}
return super.textInputMode;
}
}
Swift 5 implementation:
public extension UITextField
{
// ⚠️ Prefer english keyboards
//
override var textInputMode: UITextInputMode?
{
let locale = Locale(identifier: "en_US") // your preferred locale
return
UITextInputMode.activeInputModes.first(where: { $0.primaryLanguage == locale.languageCode })
??
super.textInputMode
}
}
This seems to work to change, for instance, to a Greek keyboard:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"el", nil]
forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];