8

I have a Bluetooth foot switch that's basically a wireless keyboard. One pedal sends the up arrow key, the other sends the down arrow key. I want to be able to execute my own code in my iPad app when one of the pedals is pressed. The maker of the pedal tells me I should create a UITextField, and adopt the UIKeyInput protocol in the containing UIView and use the beginningOfDocument and endOfDocument methods to execute my code. I did this, but no matter what I do, none of the UIKeyInput or UITextInput methods get called. Can anyone walk me through this, or direct me to a tutorial on something similar to this? Is there an easier way to do this?

Thanks for your help.

Here's my .h:

#import <UIKit/UIKit.h>

@interface Pedal_ProtocolViewController : UIViewController <UIKeyInput, UITextInput>{
UITextField *myTextField;
}
@property (nonatomic, retain) IBOutlet UITextField *myTextField;
@end

And here's my .m:

#import "Pedal_ProtocolViewController.h"

@implementation Pedal_ProtocolViewController

@synthesize myTextField;

- (void)dealloc
{
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
[myTextField canBecomeFirstResponder];
[myTextField becomeFirstResponder];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return YES;
}

#pragma mark -
#pragma mark UIKeyInput Protocol Methods

- (BOOL)hasText {
    return NO;
}

- (void)insertText:(NSString *)theText {
}

- (void)deleteBackward {
}

- (BOOL)canBecomeFirstResponder {
    return YES; 
}

#pragma mark -
#pragma mark UITextInput Protocol Methods

- (NSString *)textInRange:(UITextRange *)range {
    return @"";
}
- (void)replaceRange:(UITextRange *)range withText:(NSString *)text {
}
- (void) setSelectedTextRange: (UITextRange *) range {
}
- (UITextRange *) markedTextRange {
    return nil;
}
- (NSDictionary *) markedTextStyle {
    return nil;
}
- (void) setMarkedTextStyle: (NSDictionary *) style {
}
- (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange {
}
- (void) unmarkText {
}
- (UITextPosition *) endOfDocument {
    //DOWN KEY

    NSLog(@"Down");
    return nil;
}
- (UITextPosition *) beginningOfDocument {
    //UP KEY

    NSLog(@"UP");
    return nil;
}
- (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition{
    return nil;
}
- (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset{
    return nil;
}
- (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset {
    return nil;
}
- (NSComparisonResult) comparePosition: (UITextPosition *)position toPosition: (UITextPosition *)other {
    return NSOrderedSame;
}
- (NSInteger) offsetFromPosition: (UITextPosition *)from toPosition: (UITextPosition *)toPosition {
    return 0;
}
- (void) setInputDelegate: (id <UITextInputDelegate>) delegate {
}
- (id <UITextInputDelegate>) inputDelegate {
    return nil;
}
- (id <UITextInputTokenizer>) tokenizer {
    return nil;
}
- (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction {
    return nil;
}
- (UITextRange *) characterRangeByExtendingPosition: (UITextPosition *) position inDirection: (UITextLayoutDirection) direction {
    return nil;
}
- (UITextWritingDirection) baseWritingDirectionForPosition: (UITextPosition *)position inDirection: (UITextStorageDirection)direction {
    return 0;
}
- (void) setBaseWritingDirection: (UITextWritingDirection)writingDirection forRange:(UITextRange *)range {
}
- (CGRect) firstRectForRange: (UITextRange *) range {
    return CGRectZero;
}
- (CGRect) caretRectForPosition: (UITextPosition *) position  {
    return CGRectZero;
}
- (UITextPosition *) closestPositionToPoint: (CGPoint)point {
    return nil;
}
- (UITextPosition *) closestPositionToPoint: (CGPoint)point withinRange: (UITextRange *) range {
    return nil;
}
- (UITextRange *) characterRangeAtPoint: (CGPoint)point {
    return nil;
}
- (UITextRange *) selectedTextRange {
    return [[UITextRange alloc]init];
}

@end
fredwardo
  • 143
  • 1
  • 1
  • 8
  • I have the same problem now, did you ever figure this out? – Chris Brandsma Sep 29 '11 at 01:19
  • 1
    There's some useful tidbits to be had in the [DCIntrospect project](https://github.com/0xced/DCIntrospect/blob/80425e50bbd7f779a42f009b93b0c1ef006946e9/DCIntrospect/DCIntrospect.m) over on Github. Those guys're using some neat selection tracking to distinguish between up, down, left, right, and all four with either shift and option, as well. All up, a pretty damn impressive effort, I thought. – Michael Tyson Sep 05 '12 at 19:03

3 Answers3

6

You have adopted the UIKeyInput in the UIViewController. Note the inheritance definition you entered:

@interface Pedal_ProtocolViewController : UIViewController <UIKeyInput, UITextInput>{

You've said here "This is a view controller that implements UIKeyInput and UITextInput." These two protocols apply to UIResponder subclasses such as UIView and subclasses or UIView. UIViewController is not one such class perhaps not the best class to handle text input.

View controllers manage views. They are not views themselves.

You can (instead of text input protocols) just use a hidden text field (such as the one you already have). Just create a subclass of NSObject that implements a delegate for the text field, and assign it as the delegate of the text field. Then, in -viewDidAppear:, call -becomeFirstResponder on the text field to focus on the field. You can probably use some hack to hide the keyboard.

This approach was commonly used in games and game-supporting libraries to show the software keyboard. It even works on iOS 3.1.3 and earlier (which is not a problem for you, considering that you are developing for the iPad).


In case you will keep that design (handling input in the view controller), then this may be required and make it work.

-(BOOL)canBecomeFirstResponder
{
    return YES;
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [self becomeFirstResponder];
}

Do consider using the UITextField and a delegate for handling input, or implementing the above two functions and the UIKeyInput protocol in a subclass of UIView.

Also note you are not required to conform to UITextInput just to get keypresses; UIKeyInput is enough.


Additional note: if you decide to subclass UIView (which, along with using a hidden UITextField, is what I do; I haven't tried subclassing UIViewController to get keyboard input), you will want to add -becomeFirstResponder to -awakeFromNib instead:

-(void)awakeFromNib
{
    [super awakeFromNib];
    [self becomeFirstResponder];
}

That's if you're loading the UIViewController (and hence the UIView) from a nib. Not doing that? Try adding that in -initWithFrame::

-(id)initWithFrame:(CGFrame)frame
{
    self = [super initWithFrame:frame];
    if(!self)
        return nil;

    [self becomeFirstResponder];
    return self;
}

Alternatively, in UIViewController's viewDidLoad:

    // ...
    [self.view becomeFirstResponder];
    // ...

There's obviously loads of ways you can do this. ;)

Ivan Vučica
  • 9,529
  • 9
  • 60
  • 111
  • 1
    Actually, `UIViewController` is a subclass of `NSResponder`. http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIViewController_Class/Reference/Reference.html – Alex Sep 30 '11 at 17:03
  • UIResponder. Well, my bad :) I'm not sure it should become the first responder however. But it looks like that's what the question asker did not do. I'll update my answer. – Ivan Vučica Sep 30 '11 at 21:02
  • The catch, though, is that I don't believe that a view controller can become the first responder, so you probably will still need to subclass `UIView`, but you'll also explicitly have to make it the first responder to trigger the keyboard functionality. – Alex Sep 30 '11 at 21:42
  • When I saw your comment, that's the first thing that came through my mind, "a view controller probably cannot be a first responder". But, I think that the only check UIKit does is asking a `UIResponder` whether it can become a first responder. Without testing, I believe the above solution (overriding `-canBecomeFirstResponder`, which is needed for `UIView` as well) should be correct. Also, slightly updating the answer. – Ivan Vučica Oct 01 '11 at 08:43
  • 3
    Yeah, I'm enough of a nerd I went and tested it. I couldn't get the view controller to become first responder, but a UIView subclass worked as expected. – Alex Oct 01 '11 at 15:01
  • I was able to get the view controller to become a first responder by adding the canBeFirstResponder and calling becomeFirstResponder in the viewDidLoad class. I can receive text just fine via the UIKeyInput protocol. – Cthutu Aug 01 '13 at 18:32
2

in IOS 7 it is easy. In previous versions - not official. Can be removed from App Store.

-(NSArray * ) keyCommands {
    if ([[[UIDevice currentDevice] systemVersion] intValue] <7) return nil;
    UIKeyCommand *upArrow = [UIKeyCommand keyCommandWithInput: UIKeyInputUpArrow modifierFlags: 0 action: @selector(upArrow:)];
    UIKeyCommand *downArrow = [UIKeyCommand keyCommandWithInput: UIKeyInputDownArrow modifierFlags: 0 action: @selector(downArrow:)];
    UIKeyCommand *leftArrow = [UIKeyCommand keyCommandWithInput: UIKeyInputLeftArrow modifierFlags: 0 action: @selector(leftArrow:)];
    UIKeyCommand *rightArrow = [UIKeyCommand keyCommandWithInput: UIKeyInputRightArrow modifierFlags: 0 action: @selector(rightArrow:)];
    UIKeyCommand *escapeCom = [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(escapeChar:)];
    return [[NSArray alloc] initWithObjects: upArrow, downArrow, leftArrow, rightArrow, escapeCom, nil];
}

It works for me, hope will be useful for you. Added version check to avoid usage in non-iOS7 devices.

ETech
  • 1,613
  • 16
  • 17
2

See the solution I came up with for responding to arrow keys from a foot pedal:

How can I respond to external keyboard arrow keys?

Community
  • 1
  • 1
colincameron
  • 2,696
  • 4
  • 23
  • 46