2

Whenever keyboard appears in the project , I can find that by adding observer to UIKeyboardWillShowNotification.

But I want to get access to the tool bar that appears on top of the keyboard globally from not from every viewcontroller

Background:

I am using IQKeyboardManager and I am setting buttons on top of the keyboard using it.

IQKeyboardManager doesn't make the tool bar available for external access.

So I'm wondering if there's any way to get access to the keyboard whenever it appears and iterate the items of toolbar that's on top of the keyboard.

Any tips are welcome

Update:

The problem comes from the source code in the POD.

IQUIView+IQKeyboardToolbar.m

  {
  //Flexible space
   [items addObject:[[self class] flexibleBarButtonItem]];
   
   //Title button
   toolbar.titleBarButton.title = titleText;
   if (@available(iOS 11.0, *)) {}
   else
   {
       toolbar.titleBarButton.customView.frame = CGRectZero;
   }

   [items addObject:toolbar.titleBarButton];
   
   //Flexible space
   [items addObject:[[self class] flexibleBarButtonItem]];
    }

Because of the above code in their pod

even though there's no title text in tool bar , it's still being added as an item on the toolbar and so when we turn on the accessibility the empty title button is read as Dimmed button.

I have the fix

   if(titleText){
   //Flexible space
   [items addObject:[[self class] flexibleBarButtonItem]];
   
   //Title button
   toolbar.titleBarButton.title = titleText;
   if (@available(iOS 11.0, *)) {}
   else
   {
       toolbar.titleBarButton.customView.frame = CGRectZero;
   }

   [items addObject:toolbar.titleBarButton];
   
   //Flexible space
   [items addObject:[[self class] flexibleBarButtonItem]];
   }else{
   [items addObject:[[self class] flexibleBarButtonItem]];
   [items addObject:[[self class] flexibleBarButtonItem]];
   [items addObject:[[self class] flexibleBarButtonItem]];

   }

But we can't fix their pod from our side.

So I'm wondering if I could get access of the the tool bar above keyboard , then I can effectively remove the empty item and add the items back into the tool bar again and this would fix my problem.

13
  • Have you looked at inputAccessoryView? Commented Sep 28, 2023 at 22:17
  • Yes but I can't go and assign for every text field in the project Commented Sep 29, 2023 at 5:27
  • Please update your question with some relevant code. Also clearly explain why you need to access the toolbar above the keyboard. You state that you set buttons using IQKeyboardManager. If you are already setting those buttons, why do you need to get access to those buttons when the keyboard appears? Commented Oct 1, 2023 at 5:10
  • I haven't tested this, but it looks to me like you can use something like IQKeyboardManager.shared.textFieldView.keyboardToolbar to access the accessory views. You'll probably need to unwrap some optional values in there. It's nice that you can just look at the IQKeyboardManager code (some of it here: github.com/hackiftekhar/IQKeyboardManager/blob/master/…). Commented Oct 1, 2023 at 18:16
  • Another option is to traverse the view hierarchy, but I wouldn't really recommend it. BTW, IMO, there is no "canonical answer" for this. Typically, I would argue against doing something like this globally. (And certainly you should keep this code out of the AppDelegate!) But I could maybe see writing some kind of "keyboard service" in the app, that does exist at a global level, and handles this sort of thing. Commented Oct 1, 2023 at 18:19

3 Answers 3

1
+500

I strongly urge you to work the patched framework into your build environment since you already know the fix to the framework. Doing so avoids the need to change any of your app's code.

However, the following code could be used as a temporary workaround until the framework is fixed (if ever) by the framework author. The following makes it simple to get access to the inputAccessoryView of any text field or text view in your app each time the text field or text view becomes first responder.

Add these two lines to your app delegate's didFinishLaunchingWithOptions method:

Objective-C:

[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(beginEditingHandler:) name:UITextFieldTextDidBeginEditingNotification object:nil];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(beginEditingHandler:) name:UITextViewTextDidBeginEditingNotification object:nil];

Swift:

NotificationCenter.default.addObserver(self, selector: #selector(beginEditingHandler), name: UITextField.textDidBeginEditingNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(beginEditingHandler), name: UITextView.textDidBeginEditingNotification, object: nil)

And add this method to your app delegate class:

Objective-C:

- (void)beginEditingHandler:(NSNotification *)notification {
    UIResponder *responder = notification.object;
    UIView *inputAccessoryView = responder.inputAccessoryView;
    if (inputAccessoryView) {
        // Process the inputAccessoryView as needed
    }
}

Swift:

@objc func beginEditingHandler(_ notification: NSNotification) {
    if let responder = notification.object as? UIResponder, let inputAccessoryView = responder.inputAccessoryView {
        // Process the inputAccessoryView as needed
    }
}

I'm not familiar with the framework you are using so I don't know how to get the toolbar from the input accessory view. It could be as simple as trying to cast inputAccessoryView as? UIToolbar or you might have to see if there is a subview of inputAccessoryView that is a UIToolbar.

Sign up to request clarification or add additional context in comments.

Comments

0

The way I see it, you have an external dependency which you cannot change (because it will take a while to upstream your fix into their official release) but you have a fix you want in their code right now.

So one way to workaround it, whilst staying within your working practices, is to have an Xcode build script phase in your project which 'patches' the files of the Pod before compilation. See the command /usr/bin/diff and /usr/bin/patch for creating and applying patches.

Comments

0

Since you dont want to do it via inputAccessoryView then you can do it via UIKeyboardWillShowNotification observer and get the UIKeyboardFrameEndUserInfoKey from the notification's userInfo dictionary to get the frame of the keyboard and From there you can get the frame of the input accessory view and do any modifications on it.

c'mon, it's not that hard

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // to listen for notification when keyboard appears
    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector: @selector(handleNotification:) 
        name:UIKeyboardWillShowNotification 
        object: nil];
}

// when keyboard appears to handle it
- (void)handleNotification:(NSNotification *)notification {
    // notification's userInfo dictionary to get frame of the keyboard
    CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    
    // get input accessory view from current first responder
    UIView *inputAccessoryView = [[UIApplication sharedApplication] keyWindow].firstResponder.inputAccessoryView;
    
    // now its accessed, make changes
    inputAccessoryView.backgroundColor = [UIColor greenColor];
}
    // remove observer
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

7 Comments

So can you explain me how do you get the access of inputaccessoryview instance now
@Shajo updated. Just make sure to handle any potential edge cases, like if there is no first responder active or if the input accessory view is not present for some reason
UIApplication keyWindow has been deprecated since iOS 13 due to the support of scenes.
@HangarRash we can still use UIApplication.shared.connectedScenes to loop through window scenes, or if you have some workaround
@AfzalK. But which scene (and window) do you choose? On an iPad you can easily have multiple scenes open in an app.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.