2

I have a toolbar which I need to use when editing text, and when not.

In previous apps, I've moved the toolbar manually (listening for notifications, etc.)

But I want to use inputAccessoryView... so in my viewDidLoad, I do

for (/*myTextFields*/) {
   textField.inputAccessoryView = keyboardToolbar;
}
[self.view addSubView:keyboardToolbar];

Which works fine, the toolbar appears, I click on a text field, toolbar slides up - all good. But when I then hide the keyboard, inputAccessoryView drags my toolbar off the screen. Is there any way to tell the inputAcessoryView where it's fixed location is? - Or do I have to go back to my old way of listening for notifications etc...?

vrwim
  • 9,459
  • 11
  • 57
  • 107
Alex Coplan
  • 12,581
  • 17
  • 72
  • 135
  • I've been trying to figure out a way to do this exact thing (in my case, I'm trying to duplicate the iOS messages UX). Using the accessory view _seems_ like a good idea because it handles animations for you. I guess I'm back to notifications again... – Kevin Cantwell Apr 05 '13 at 16:05

2 Answers2

2

I solved this by listening for Notifications and moving the toolbar up... oh well.

Something like this does the job:

- (void)viewWillAppear:(BOOL)animated 
{
    [super viewWillAppear:animated];
    /* Listen for keyboard */
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification 
{
    [keyboardToolbar setItems:itemSetFull animated:YES];
    /* Move the toolbar to above the keyboard */
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];
    CGRect frame = self.keyboardToolbar.frame;
    frame.origin.y = self.view.frame.size.height - 210.0;
    self.keyboardToolbar.frame = frame;
    [UIView commitAnimations];
}

- (void)keyboardWillHide:(NSNotification *)notification 
{
    [keyboardToolbar setItems:itemSetSmall animated:YES];
    /* Move the toolbar back to bottom of the screen */
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];
    CGRect frame = self.keyboardToolbar.frame;
    frame.origin.y = self.view.frame.size.height - frame.size.height;
    self.keyboardToolbar.frame = frame;
    [UIView commitAnimations];
}

I guess input accessory view is literally just meant for something stuck on top of the keyboard :)

Alex Coplan
  • 12,581
  • 17
  • 72
  • 135
  • 2
    Right. The reason your original approach behaved the way it did was that a view cannot have multiple superviews. as soon as the keyboard came up for one of your text fields, it added the input accessory view to the keyboard view, which automatically removes it from its original superview if it has one. It doesn't keep track of where it used to be, so for the behavior you want your solution here is necessary. I think the sms app does this too; it has a persistent bar that appears above the keyboard when the keyboard is visible, but is anchored to the bottom when the keyboard is dismissed. – Kevlar Aug 03 '11 at 17:49
1

I've figured it out recently, and it seems few people have. So, I would like to direct you to this answer: https://stackoverflow.com/a/24855095/299711, which I will just copy below:

Assign your UIToolbar to a property in your view controller:

@property (strong, nonatomic) UIToolbar *inputAccessoryToolbar;

In your top view controller, add these methods:

- (BOOL)canBecomeFirstResponder{

    return YES;

}

- (UIView *)inputAccessoryView{

    return self.inputAccessoryToolbar;

}

And then (optionally, as it usually shouldn't be necessary), whenever the keyboard gets hidden, just call:

[self becomeFirstResponder];

That way, your inputAccessoryToolbar will be both your view controller's and your text view's input accessory view.

Community
  • 1
  • 1
arik
  • 23,480
  • 35
  • 91
  • 147
  • 3
    This generates a retain cycle, and the UIViewController is never released. Memory leak – Gui Moura Oct 03 '14 at 04:11
  • I used the exact code that you pasted on your answer. The only difference is that my AccessoryView is instantiated on the Storyboard, and I hold a weak reference to an IBOutlet. – Gui Moura Oct 03 '14 at 17:39
  • That actually sounds like something that could cause the problem, because an input accessory view needs strong references. Perhaps you could try creating another, strongly referenced toolbar in the view controller, and then setting that one as the input accessory view just to check if that's what's causing the problem? – arik Oct 03 '14 at 18:17
  • Yeah, I tried that. I created a single Custom View with a strong reference and a UITextView subview and assigned it to the UIViewController inputAccessoryView. Reference is retained, and UIViewController is never dealloc'd. You can reproduce that, if you create a simple Navigation App, and the UIViewController with the inputAccessoryView is the second one. When you pop it, it never calls -dealloc – Gui Moura Oct 03 '14 at 19:08
  • If you have some spare time, please try that, and let me know if it's not just me... It's bugging me. – Gui Moura Oct 03 '14 at 19:11
  • Wait, are you saying that the text view is both the view controller's accessory view, and has itself as the accessory view? Perhaps we should continue in a chat. – arik Oct 03 '14 at 23:58
  • sure, where would you prefer to chat? you can email/imessage me at sprint.stack [at] icloud.com Let's see if we can find out a solution for this. I noticed it works fine for iOS7 but not in iOS8 – Gui Moura Oct 04 '14 at 02:00
  • I've sent you an email a few hours ago. – arik Oct 04 '14 at 16:47