I am trying to replicate Facebook Messenger App, where there is a UITextView attached to the top of the keyboard.
Due to the nature of this app I need my view to be attached, instead of manually scrolling up and down a ScrollView when the keyboard appears.
This can be achieved by using a inputAccessoryView.
I read the docs on it here.
The documentation is very brief and says:
"This property is typically used to attach an accessory view to the system-supplied keyboard that is presented for UITextField and UITextView objects.
The value of this read-only property is nil. If you want to attach custom controls to a system-supplied input view (such as the system keyboard) or to a custom input view (one you provide in the inputView property), redeclare this property as read-write in a UIResponder subclass.
You can then use this property to manage a custom accessory view. When the receiver becomes the first responder, the responder infrastructure attaches the accessory view to the appropriate input view before displaying it."
I have tried declaring a property
@interface CommentViewController ()
@property (nonatomic, readwrite, retain) UIView *inputAccessoryView;
@end
And then setting it:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 20, 320, 100)];
[view setBackgroundColor:[UIColor greenColor]];
self.inputAccessoryView = view;
}
Then I have tried calling both of these:
[self.tableView becomeFirstResponder];
[view becomeFirstResponder];
Nothing happens. What am I doing wrong?
*Note - Extra information: I am using a UITableViewController that I want to have a UIView attached as an inputAccessoryView. Once I get the view working then I will add in a UITextView and more, but this is mainly an example.
Any help is greatly appreciated!
Add input accessory to your textField or textView rather than to pure UIView.
self.mytextField.inputAccessoryView = view;
The inputAccessoryView is a property of the UIResponder class. It allows you to define a custom input accessory view to display when the receiver becomes the first responder. Usually an instance of UIToolBar should be set as the accessory view.
A toolbar sample:
MYInputAccessoryToolbar.h
typedef void (^MYInputAccessoryToolbarDidDoneTap)(id activeItem);
@interface MYInputAccessoryToolbar : UIToolbar
@property (nonatomic, copy) MYInputAccessoryToolbarDidDoneTap didDoneTapBlock;
+ (instancetype)toolbarWithInputItems:(NSArray *)items;
- (instancetype)initWithInputItems:(NSArray *)items;
- (void)addInputItem:(id)item;
- (void)goToNextItem;
- (void)goToPrevItem;
@end
MYInputAccessoryToolbar.m
@interface MYInputAccessoryToolbar ()
@property (strong, nonatomic) UIBarButtonItem *nextButton;
@property (strong, nonatomic) UIBarButtonItem *prevButton;
@property (strong, nonatomic) UIBarButtonItem *doneButton;
@property (nonatomic, copy) NSMutableArray *inputItems;
@property (nonatomic) NSInteger activeItemIndex;
@property (nonatomic) id activeItem;
@end
@implementation MYInputAccessoryToolbar
+ (instancetype)toolbarWithInputItems:(NSArray *)items {
return [[self alloc] initWithInputItems:items];
}
#pragma mark - Initializations
- (instancetype)init {
self = [super init];
if (self) {
_inputItems = [NSMutableArray new];
_prevButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:101 target:self action:@selector(prevButtonTaped)];
_nextButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:102 target:self action:@selector(nextButtonTaped)];
_doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleBordered target:self action:@selector(doneButtonTaped)];
[_doneButton setTitleTextAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:17]} forState:UIControlStateNormal];
UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixedSpace.width = 20.0f;
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
NSArray<UIBarButtonItem *> *barButtons = @[_prevButton, fixedSpace, _nextButton, flexSpace, _doneButton];
[self sizeToFit];
self.items = barButtons;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(itemDidBeginEditing:)
name:UITextFieldTextDidBeginEditingNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(itemDidBeginEditing:)
name:UITextViewTextDidBeginEditingNotification
object:nil];
}
return self;
}
- (instancetype) initWithInputItems:(NSArray *)items {
self = [self init];
for (id item in items) {
[self addInputItem:item];
}
return self;
}
#pragma mark - Accessors
- (void)addInputItem:(id)item {
if ([item respondsToSelector:@selector(setInputAccessoryView:)]) {
[item setInputAccessoryView:self];
}
[_inputItems addObject:item];
}
#pragma mark - Actions
- (void)itemDidBeginEditing:(NSNotification *)noticifation {
NSInteger itemIndex = [_inputItems indexOfObject:noticifation.object];
if (itemIndex != NSNotFound && _activeItem != noticifation.object) {
_activeItemIndex = itemIndex;
_activeItem = noticifation.object;
[self activeItemChanged];
}
}
- (void)activeItemChanged {
_prevButton.enabled = _activeItemIndex != 0;
_nextButton.enabled = _activeItemIndex != _inputItems.count - 1;
}
- (void)prevButtonTaped {
[self goToPrevItem];
}
- (void)nextButtonTaped {
[self goToNextItem];
}
- (void)goToNextItem {
[_inputItems[_activeItemIndex + 1] becomeFirstResponder];
}
- (void)goToPrevItem {
[_inputItems[_activeItemIndex - 1] becomeFirstResponder];
}
- (void)doneButtonTaped {
if (_didDoneTapBlock) {
_didDoneTapBlock(_activeItem);
}
[_activeItem resignFirstResponder];
}
#pragma mark - Dealloc
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
}
@end
Now assuming that we have a set of text field fields and text views we could use them to initialize an instance of our toolbar.
MYInputAccessoryToolbar *accessoryToolbar = [MYInputAccessoryToolbar toolbarWithInputItems:@[_passwordCurrentField, _passwordNewField, _passwordVerifyField]];
And then each of these fields will have a custom accessory view like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With