Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ios How to add an inputAccessoryView?

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:

  1. [self.tableView becomeFirstResponder];

  2. [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!

like image 703
Josh Avatar asked Nov 08 '25 06:11

Josh


2 Answers

Add input accessory to your textField or textView rather than to pure UIView.

self.mytextField.inputAccessoryView = view;
like image 163
MadNik Avatar answered Nov 09 '25 22:11

MadNik


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.

like image 32
Eugene Avatar answered Nov 09 '25 20:11

Eugene



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!