hackiftekhar/IQKeyboardManager

Sibling responders not detected when textfields are placed in custom views in same viewcontroller

7vikram7 opened this issue · 7 comments

Hi,
Thank you for the excellent library. I have stumbled upon one case while creating a form.
screen shot 2015-01-28 at 11 01 55 pm
All the 6 marked in red boxes are separate custom views which are added to the viewcontroller. Notice that the next previous buttons did not appear for this case. For all other forms where the text fields are on the same scrollview/viewcontroller and not on custom views, the next/previous buttons appear and work just fine.

Upon further investigation, I found that the responderSiblings() function is not able to find any siblings for the textfields on these custom views. I also tried replacing responderSiblings() with deepResponderViews() but that too didnt work.
Hence as a temporary fix, I have made a small change in the IQKeyboardManager.m file where if a textfield from this particular viewcontroller comes up, in the responderViews() function, instead of fetching sibling responders from "textFields = [_textFieldView responderSiblings];" I pass an "array of responders", with responders i know exist on the viewcontroller for the textfield.

Have i missed something obvious over here?
Or will the responderSiblings()/deepResponderViews() fuctions have to be improved to fetch all siblings on the viewcontroller?
Or we can just facilitate an option to pass "sibling responders array" to textfields?

In general, if you're putting textFields on different views, then you are saying 'Keep them separate and do not disturb each other'. If you're putting textField on same view then you're saying 'Keep the relation between them'. Next/Previous buttons follows the same concept.

Ok sounds correct, Thanks.
But I had this requirement to create lots of similar forms which had a lot of similar components which i can reuse. Hence, I have created custom views for such components.
Others too might have this requirement if they are using custom views for reuse sake in large projects.

Lets see if I can put an elegant solution in place. Like set a property for textfields which if set, will use a different deepResponderViews() function or maybe can come with a provision to set an array of sibling responder view for the textfield.

The logic behind deepResponderView() is, it find all responderView in it's subview and their lower hierarchy.
You should check addToolbarIfRequired() method, which find the textField's superTableView or superCollectionView. If tableView or collectionView is found then using deepResponderView() method to get all responderView's in it's lower hierarchy, if not found then using responderSiblings method to get all sibling responderView's.
For a workaround, you can create a FakeCustomView class and put it above all your original customised view. Then you should also add a condition with tableView and collectionView for FakeCustomisedView in addToolbarIfRequired() method.

What I did was, I created a reference for an UIView in the IQKeyboardManager class, named "responderSuperView"(this would be the reference of the view, whose subview responder views I wanted to have a relation ).

Then, whenever i wanted to keep relation between responder views which are not located on the same view; I would set the "responderSuperView" to that view (which is usually a viewcontroller.view having 2 or more custom views which further have responder views on them)

IQKeyboardManager *iQManager = [IQKeyboardManager sharedManager];
iQManager.responderSuperView = self.view;

Then in the IQKeyboard manager class, in the place where you have check superview to get responder views, I added these 4 lines in the beginning

//If superview for responders is manually set
if (_responderSuperView) {
    return [_responderSuperView deepResponderViews];
}
//If there is a tableView in view's hierarchy, then fetching all it's subview that responds. No sorting for tableView, it's by subView position.
else if (tableView)  //     //   (Enhancement ID: #22)
{
    return [tableView deepResponderViews];
}
//Otherwise fetching all the siblings
else

In viewwilldisappear() of the viewcontroller, i set the "responderSuperView" back to nil.

IQKeyboardManager *iQManager = [IQKeyboardManager sharedManager];
iQManager.responderSuperView = nil;

So far it is working just fine. Do you see any probable loophole in what i have done?

If you are not aware of 'how IQKeyboardManager works under the hood' then you can go with your proposed simple solution. If you are setting responderSuperView = nil in viewWillDisappear then you should set responderSuperview in viewDidAppear or viewWillAppear (Not in viewDidLoad).

If you are aware of IQKeyboardManager functional architecture then let me know I can suggest you a more better way, otherwise your proposed solution is also fine.

I cannot say I am fully aware of the functional architecture, but I have had a look at quiet a lot of code. If you have a better solution, you can suggest it to me. I would be happy to try it out.

  1. Create a UIView subclass, just say CustomView.
  2. Create a property named superCustomView in 'IQUIView+Hierarchy.h' and define it's getter method in .m file(Just like other implementation for UIScrollView/UITableView/UICollectionView).
  3. Now in IQKeyboardManager.m, where we are checking for superTableView/superCollectionView, please add another check for superCustomView.
  4. Open storyboard and change UIView class to cusomView where you need(In your case self.view) to check all deepResponderView.

That's all. Hope you understand my suggested way, it's standard approach.
But at all, note that your workaround is also fine.