Reputation: 830
I'm developing a messaging app and would like the predictive keyboard in iOS 8 to recognize that the user who is composing a message is replying to the previous message.
So I want to be able to feed a string to the keyboard to provide it context for prediction. So if a user is asked a question that can be interpreted as polar (yes / no), then the predictive keyboard should have the Yes | No | Maybe
Is this available to developers?
Note that I'm not talking about a custom keyboard, just feeding the standard keyboard some context for it's predictions. I'm also not concerned with actually customizing the quick type replies as in this question. I just want the keyboard to know what it's typing about.
Upvotes: 14
Views: 9332
Reputation: 2431
Strictly inputting suggestions to the default keyboard is not possible. However, if you want to give the equivalent experience for the user, I would hide the iOS suggestion bar using myTextView.autocorrectionType = UITextAutocorrectionTypeNo;
and then replace that view with my own custom view that mimics the suggestion view until. Once the user types a character or selects an option, then hide the custom suggestion view and re-enable the iOS suggestion bar.
I Subclassed UIInputView just for this (the transparency and the transition is a little off, but everything else works pretty well).
#import <UIKit/UIKit.h>
@protocol SuggestionViewDelegate <NSObject>
@required
- (void)suggestionSelected:(NSString *)suggestion;
@end
@interface SuggestionView : UIInputView
- (instancetype)init;
- (instancetype)initWithFrame:(CGRect)frame;
/**
* The list of suggestions being displayed.
* The array contains 0-3 strings.
*
* @return Array of NSString's representing the current suggested strings
*/
- (NSArray *)suggestions;
/**
* Add a suggestion to display in the view.
* If there are already maxSuggestionCount suggestions, the added suggestion will push one of them out.
* If there are already maxSuggestionCount suggestions and the input is 'nil' then the last suggestion will be removed.
*
* @param suggestion String to suggest to the user
*/
- (void)addSuggestion:(NSString *)suggestion;
/**
* Removes the suggestion from the list of displayed suggestions.
* If the string is not in the set then there is no change made.
*
* @param suggestion NSString to remove from the suggested strings
*/
- (void)removeSuggestion:(NSString *)suggestion;
/**
* Takes in either NSArray or NSSet and replaces 'suggestions' with the input.
* Only the first three arguments are recognized.
* Objects should be strings. Undefined behavior otherwise.
*
* @param suggestions NSArray or NSSet with 0-3 NSStrings
*/
- (void)setSuggestions:(NSObject *)suggestions;
@property (weak) id <SuggestionViewDelegate> delegate;
/**
* The maximum number of suggestions allowed. Default is 3.
*/
@property (nonatomic) NSInteger maxSuggestionCount;
@end
#import "SuggestionView.h"
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
@implementation SuggestionView {
NSMutableOrderedSet *_suggestions;
NSMutableArray *_suggestionButtons;
}
- (instancetype)init {
self = [self initWithFrame:CGRectMake(0.0f, 0.0f, kScreenWidth, 36.0f)];
if (self) {
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame inputViewStyle:UIInputViewStyleKeyboard];
if (self) {
_suggestions = [[NSMutableOrderedSet alloc] initWithCapacity:3];
self.maxSuggestionCount = 3;
_suggestionButtons = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.04f];
}
return self;
}
#pragma mark - Modifying Suggestions
- (void)addSuggestion:(NSString *)suggestion {
if (suggestion) {
[_suggestions addObject:suggestion];
}
while (_suggestions.count > self.maxSuggestionCount) {
[_suggestions removeObjectAtIndex:self.maxSuggestionCount];
}
}
- (void)removeSuggestion:(NSString *)suggestion {
[_suggestions removeObject:suggestion];
}
- (void)setSuggestions:(NSObject *)suggestions {
if ([suggestions respondsToSelector:@selector(countByEnumeratingWithState:objects:count:)]) {
[_suggestions removeAllObjects];
for (NSString *suggestion in (NSArray *)suggestions) {
if (_suggestions.count < self.maxSuggestionCount) {
[_suggestions addObject:suggestion];
} else {
break;
}
}
}
}
- (NSArray *)suggestions {
NSMutableArray *suggestionsArray = [[NSMutableArray alloc] initWithCapacity:_suggestions.count];
for (NSString *suggestion in _suggestions) {
[suggestionsArray addObject:suggestion];
}
return suggestionsArray;
}
#pragma mark - Visual Layout of Suggestions
- (void)layoutSubviews {
[self layoutSuggestions];
}
- (void)layoutSuggestions {
for (UIView *subview in _suggestionButtons) {
[subview removeFromSuperview];
}
[_suggestionButtons removeAllObjects];
for (int i = 0; i < _suggestions.count; i++) {
NSString *suggestion = _suggestions[i];
UIButton *suggestionButton = [[UIButton alloc] initWithFrame:CGRectMake(i * self.bounds.size.width/_suggestions.count, 0.0f, self.bounds.size.width/_suggestions.count, self.bounds.size.height)];
[suggestionButton setTitle:suggestion forState:UIControlStateNormal];
suggestionButton.titleLabel.adjustsFontSizeToFitWidth = YES;
suggestionButton.titleLabel.textAlignment = NSTextAlignmentCenter;
[suggestionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[suggestionButton addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:suggestionButton];
if (i > 0) {
UIView *whiteLine = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.5f, self.bounds.size.height)];
whiteLine.backgroundColor = [UIColor whiteColor];
[suggestionButton addSubview:whiteLine];
}
[_suggestionButtons addObject:suggestionButton];
}
}
#pragma mark - Selecting a Suggestion
- (void)buttonTouched:(UIButton *)button {
NSTimeInterval animationDuration = 0.09f;
[UIView animateWithDuration:animationDuration animations:^{
[button setBackgroundColor:[UIColor whiteColor]];
if ([self.delegate respondsToSelector:@selector(suggestionSelected:)]) {
[self performSelector:@selector(suggestionSelected:) withObject:button.currentTitle afterDelay:animationDuration * 0.9f];
}
[button performSelector:@selector(setBackgroundColor:) withObject:[UIColor clearColor] afterDelay:animationDuration];
}];
}
- (void)suggestionSelected:(NSString *)suggestion {
if ([self.delegate respondsToSelector:@selector(suggestionSelected:)]) {
[self.delegate suggestionSelected:suggestion];
}
}
@end
To implement this into a UITextField
or UITextView
that you've already subclassed, import the SuggestionView and implement the SuggestionViewDelegate. Then, in the UITextFieldDelegate
(or UITextViewDelegate
) methods, add:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if ([textField isEqual:self.messageTextField]) {
if (self.suggestionView.suggestions.count > 0 && textField.text.length == 0) {
textField.inputAccessoryView = self.suggestionView;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
[textField reloadInputViews];
}
}
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([textField isEqual:self.messageTextField]) {
if (string.length > 0) {
[self removeSuggestionView];
}
}
return YES;
}
- (void)removeSuggestionView {
self.messageTextField.inputAccessoryView = nil;
[self.messageTextField setInputAccessoryView:nil];
self.messageTextField.autocorrectionType = UITextAutocorrectionTypeYes;
[self.messageTextField reloadInputViews];
[self.messageTextField performSelector:@selector(resignFirstResponder) withObject:self afterDelay:0.0f];
[self.messageTextField performSelector:@selector(becomeFirstResponder) withObject:self afterDelay:0.0f];
}
Then, implement the SuggestionViewDelegate
:
- (void)suggestionSelected:(NSString *)suggestion {
[self.messageTextField setText:[NSString stringWithFormat:@"%@%@ ", self.messageTextField.text, suggestion]];
[self removeSuggestionView];
}
While this isn't a perfect solution, it does create the desired effect.
Upvotes: 16
Reputation: 1331
As there's now way to find any further information on how to do this on the Official Apple Documentation, I guess it's impossible. However you have two options :
You can also think about a totally different UX. For eg. if your user is likely to choose one of your suggestions, then only present these suggestions and keep a little button to enter a custom input (and dismiss the keyboard here). If the user is likely to enter a custom input, refer to both above alternatives.
Upvotes: 4