hackiftekhar / IQKeyboardManager

Codeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more.
MIT License
16.45k stars 2.41k forks source link

UITextView Not Working Properly - 6.0.4 #1378

Closed rlaurb closed 6 years ago

rlaurb commented 6 years ago

Describe the bug I have a configuration where a UITextView is located near the bottom of a view. Using IQKeyboardManager, I have been unable to get it to display correctly when editing.

[NOTE: this is an unusual layout, but one which accurately simulates the requirements of my actual project.]

To Reproduce Steps to reproduce the behavior:

  1. Open the test app. Tap "Go to Detail"
  2. Tap on the ipsum-lorem text (preferably near the bottom)
  3. Observe that the cursor is hidden behind the keyboard.

Expected behavior I expected the view to offset such that the cursor would be visible above the keyboard and would then scroll and/or offset to keep the cursor in view as I type.

Screenshots -none- see sample app.

Demo Project Sample App - "TextView-Test" is attached.

TextView-Test.zip

Versions

Xcode: 9.4.1 (same behavior under 10.0ß2. Mac OS: 10.13.6ß4 Simulator/Device: 11.3 Simulator/Device Name: iPhone 8+ Library Version: 6.0.4

Additional context Add any other context about the problem here.

hackiftekhar commented 6 years ago

Thanks for the demo project. It will really help me to debug the issue.

hackiftekhar commented 6 years ago

I found that you have made ViewController's main view is UIScrollView in two controllers which is causing problem. It's because those scrollView doesn't have correct contentSize set. If you make those 2 UIScrollView as UIView then all works great.

rlaurb commented 6 years ago

Thank you for your help. However, when I followed your recommendations, I got everything working in the Test App, but not in my actual app.

What is now happening is that the TextView is scrolling TOO FAR, and the cursor is hidden under the Top Navigation bar.

The following two screenshots demonstrate the problem:

In Shot-1, I have not yet tapped on any of the text. In Shot-2, I tapped on the word ‘use’ in the first line of text. As you can see, the scrolling was excessive.

At the present time, I don’t know what the difference is; as far as I can tell, the “relevant” layout constraints are the same for both views, and the architecture is pretty similar, too.

Do you have any thoughts about where I should look next for the problem?

Cheers,

Rick

Cheers,

Rick

On Jul 9, 2018, at 11:26 AM, Mohd Iftekhar Qurashi notifications@github.com wrote:

I found that you have made ViewController's main view is UIScrollView in two controllers which is causing problem. It's because those scrollView doesn't have correct contentSize set. If you make those 2 UIScrollView as UIView then all works great.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/hackiftekhar/IQKeyboardManager/issues/1378#issuecomment-403538226, or mute the thread https://github.com/notifications/unsubscribe-auth/AGTdERJ2eVf61UXwQz5pEamIWgAPrsZDks5uE4RGgaJpZM4U_716.

hackiftekhar commented 6 years ago

I didn't found the screenshot. However I'm not sure if I recommend something to you because according to you same kind of configuration works with demo app but not with your actual app. I think you are the best person to differentiate both apps. There might be a very little chance that other third party libraries might affecting this library to work with your actual app?

rlaurb commented 6 years ago

I’m sorry that the screenshots didn’t arrive in my email.

I agree that there is a possibility that there is some sort of interaction with other libraries. I’m looking into that now. (And of course if I discover anything, I’ll let you know.)

On the chance that it might trigger some thought of yours (due to your intimate knowledge of how the library works), let me share one additional observation I made yesterday. In my actual app, tapping in the text view causes it to scroll about 40 or so pixels too far IN PORTRAIT MODE. In landscape mode, it works fine.

Does that suggest anything to you?

Cheers,

Rick

Sent from my iPad

On Jul 10, 2018, at 4:47 AM, Mohd Iftekhar Qurashi notifications@github.com wrote:

I didn't found the screenshot. However I'm not sure if I recommend something to you because according to you same kind of configuration works with demo app but not with your actual app. I think you are the best person to differentiate both apps. There might be a very little chance that other third party libraries might affecting this library to work with your actual app?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

hackiftekhar commented 6 years ago

You tried with iPhone X or any other phone?

rlaurb commented 6 years ago

The more time I spend on this problem, the more confused I get.

Here’s what I did…

I rebuilt both scenes in my storyboard (i.e., the host scene that contains the ContainerView and the scene that gets embedded in the container view) from scratch. (To rule out storyboard corruption.)

Then I ran the app again in the Simulator and saw the same results. This is Xcode 9.4.1, running iPhone 8Plus (11.3), iPhone 8 (11.4) and iPhone X (11.4). All the same result. The first two lines of text are completely hidden. Let me be more specific. On at least three different iPhone simulators, If the simulator is set to show the on-screen software keyboard, the text view scrolls to hide the first two lines of text. If there are three or more lines of text visible and I tap in the third line, the cursor is visible (although the top two lines are hidden). If the simulator is set to act like a hardware keyboard is present, tapping in the text view causes the display to shift up slightly, but otherwise to work in a functional fashion.

Then I loaded the application on a real iPad Air 2 (11.4) and it worked. The text field scrolled up to just below the top navigation bar, just as I’d otherwise suspect. So the next thing I tried was to run it on the Simulator’s iPad Air 2 (11.4), and that worked exactly like the real physical device.

Now I don’t know what to think. While the geometry of an iPad and an iPhone are different, it is hard for me to understand why IQKeyboardManager should behave so differently in the iPhone case than it does on iPad. But I have no reason to believe that I’m looking at an iPhone-specific Simulator error. Unfortunately I don’t have a real physical iPhone of my own to try it on…

Does anything come to mind?

Cheers,

Rick

Cheers,

Rick Aurbach 8233 Tulane Avenue St. Louis, MO 63132

eMail: rlaurb@icloud.com Phone: 314/721-7987

"We're here to put a dent in the universe" - Steve Jobs (1955-2011)

“Art should be like a holiday: something to give a man the opportunity to see things differently and to change his point of view.” - Paul Klee, painter (1879-1940)

"Elitism is the slur directed at merit by mediocrity." - Sydney J. Harris, journalist (1917-1986)

"Life is easy to chronicle, but bewildering to practice." - E.M. Forster (1879-1970)

"Cowardice asks the question, 'Is it safe?' Expediency asks the question, 'Is it politic?' Vanity asks the question, 'Is it popular?' But, conscience asks the question, 'Is it right?' And there comes a time when one must take a position that is neither safe, nor politic, nor popular but one must take it because one's conscience tells one that it is right." -Martin Luther King, Jr. (1929-1968)

"The saddest aspect of life right now is that science gathers knowledge faster than society gathers wisdom." -Isaac Asimov (1920-1992)

"If in the last few years you haven't discarded a major opinion or acquired a new one, check your pulse. You may be dead." - Gelett Burgess (1866-1951)

"You can judge your age by the amount of pain you feel when you come in contact with a new idea." - John Nuveen (1896-1968)

“Soon silence will have passed into legend. Man has turned his back on silence. Day after day he invents machines and devices that increase noise and distract humanity from the essence of life, contemplation, meditation. Tooting, howling, screeching, booming, crashing, whistling, grinding, and trilling bolster his ego.” -Jean Arp, artist and poet (1887-1948)

“Moral certainty is always a sign of cultural inferiority. The more uncivilized the man, the surer he is that he knows precisely what is right and what is wrong. All human progress, even in morals, has been the work of men who have doubted the current moral values, not of men who have whooped them up and tried to enforce them. The truly civilized man is always skeptical and tolerant, in this field as in all others. His culture is based on "I am not too sure.” - H.L. Mencken, writer, editor, and critic (1880-1956)

On Jul 10, 2018, at 1:57 PM, Mohd Iftekhar Qurashi notifications@github.com wrote:

You tried with iPhone X or any other phone?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/hackiftekhar/IQKeyboardManager/issues/1378#issuecomment-403930142, or mute the thread https://github.com/notifications/unsubscribe-auth/AGTdEVWK_ZtQxrn-SzGlcphyJzEInl2Eks5uFPkBgaJpZM4U_716.

rlaurb commented 6 years ago

I had another thought this morning.

I turned on the enableDebugging flag in IQKeyboardManager and ran it twice:

The first time was using the iPhone 8 Simulator (11.4). I ran my app and tapped on the first world of text in the problem textView. IQKeyboardManager hid the first two lines and generated debug output.

Then I did the same test again, using the iPad Air 2 Simulator (11.4). Same sequence of actions. This time IQKeyboardManager handled things as I would have expected. I also captured the debug output.

I have appended both outputs to the bottom of this email, in the hopes that you’ll see something that helps you understand what is going on.

Cheers,

Rick

iPhone 8 Simulator (11.4): IQKeyboardManager: Enabled 2018-07-11 11:44:19.165081-0500 Cooks-Memory[51457:7693324] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /Users/rlaurb/Library/Developer/CoreSimulator/Devices/791F28FF-F32D-43B7-809B-1B20693DBCE1/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles 2018-07-11 11:44:19.165426-0500 Cooks-Memory[51457:7693324] [MC] Reading from private effective user settings. IQKeyboardManager: UIKeyboard Size : (375.0, 258.0) IQKeyboardManager: ** keyboardWillShow started ** IQKeyboardManager: ** keyboardWillShow ended: 8.73795943334699e-06 seconds ** IQKeyboardManager: ** textFieldViewDidBeginEditing started ** IQKeyboardManager: ** addToolbarIfRequired() started ** IQKeyboardManager: Found 1 responder sibling(s) IQKeyboardManager: ** addToolbarIfRequired() ended: 0.00461480097146705 seconds ** IQKeyboardManager: ** textFieldViewDidBeginEditing ended: 0.0052358289831318 seconds ** IQKeyboardManager: ** adjustPosition() started ** IQKeyboardManager: Need to move: 229.0 IQKeyboardManager: Moving Upward IQKeyboardManager: Set <<UINavigationController: 0x7f9392882600> 0x00007f9392882600> origin to : (0.0, -229.0) IQKeyboardManager: ** adjustPosition() ended: 0.000716694048605859 seconds ** IQKeyboardManager: ** keyboardDidShow started ** IQKeyboardManager: ** keyboardDidShow ended: 5.08219818584621e-05 seconds ** IQKeyboardManager: UIKeyboard Size : (375.0, 302.0) IQKeyboardManager: ** keyboardWillShow started ** IQKeyboardManager: ** keyboardWillShow ended: 6.44460087642074e-05 seconds ** IQKeyboardManager: ** adjustPosition() started ** IQKeyboardManager: Need to move: 0.0 IQKeyboardManager: <<RLAUIObjects.BorderedTextView: 0x7f9397803a00; baseClass = UITextView; frame = (0 44; 343 348); text = 'We use an electric skille...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6080002453d0>; layer = <CALayer: 0x608000224dc0>; contentOffset: {0, 0}; contentSize: {343, 67}; adjustedContentInset: {0, 0, 0, 0}> 0x00007f9397803a00> Old UITextView.contentInset : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0) IQKeyboardManager: <<RLAUIObjects.BorderedTextView: 0x7f9397803a00; baseClass = UITextView; frame = (0 44; 343 348); text = 'We use an electric skille...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6080002453d0>; layer = <CALayer: 0x608000224dc0>; contentOffset: {0, 0}; contentSize: {343, 67}; adjustedContentInset: {0, 0, 8, 0}> 0x00007f9397803a00> Old UITextView.contentInset : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 8.0, right: 0.0) IQKeyboardManager: Moving Upward IQKeyboardManager: Set <<UINavigationController: 0x7f9392882600> 0x00007f9392882600> origin to : (0.0, -229.0) IQKeyboardManager: ** adjustPosition() ended: 0.00323512102477252 seconds ** IQKeyboardManager: ** keyboardDidShow started ** IQKeyboardManager: ** keyboardDidShow ended: 3.27419838868082e-05 seconds **


iPad Air 2 Simulator (11.4): IQKeyboardManager: Enabled 2018-07-11 11:47:09.654715-0500 Cooks-Memory[51785:7708284] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /Users/rlaurb/Library/Developer/CoreSimulator/Devices/04BAE4FC-752B-4CE8-B211-CC6E6985A045/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles 2018-07-11 11:47:09.655067-0500 Cooks-Memory[51785:7708284] [MC] Reading from private effective user settings. IQKeyboardManager: UIKeyboard Size : (768.0, 313.0) IQKeyboardManager: ** keyboardWillShow started ** IQKeyboardManager: ** keyboardWillShow ended: 7.5900461524725e-06 seconds ** IQKeyboardManager: ** textFieldViewDidBeginEditing started ** IQKeyboardManager: ** addToolbarIfRequired() started ** IQKeyboardManager: Found 1 responder sibling(s) IQKeyboardManager: ** addToolbarIfRequired() ended: 0.00338510703295469 seconds ** IQKeyboardManager: ** textFieldViewDidBeginEditing ended: 0.00385373900644481 seconds ** IQKeyboardManager: ** adjustPosition() started ** IQKeyboardManager: Need to move: 185.0 IQKeyboardManager: <<RLAUIObjects.BorderedTextView: 0x7ff9e4057200; baseClass = UITextView; frame = (0 44; 415.5 705); text = 'We use an electric skille...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60800025b450>; layer = <CALayer: 0x6080002331e0>; contentOffset: {0, 0}; contentSize: {415.5, 50}; adjustedContentInset: {0, 0, 0, 0}> 0x00007ff9e4057200> Old UITextView.contentInset : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0) IQKeyboardManager: <<RLAUIObjects.BorderedTextView: 0x7ff9e4057200; baseClass = UITextView; frame = (0 44; 415.5 705); text = 'We use an electric skille...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60800025b450>; layer = <CALayer: 0x6080002331e0>; contentOffset: {0, 0}; contentSize: {415.5, 50}; adjustedContentInset: {0, 0, 63, 0}> 0x00007ff9e4057200> Old UITextView.contentInset : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 63.0, right: 0.0) IQKeyboardManager: Moving Upward IQKeyboardManager: Set <<Cooks_Memory.RCPDetailViewController: 0x7ff9e185f690> 0x00007ff9e185f690> origin to : (0.0, -185.0) IQKeyboardManager: ** adjustPosition() ended: 0.0013938769698143 seconds ** IQKeyboardManager: ** keyboardDidShow started ** IQKeyboardManager: ** keyboardDidShow ended: 3.94780072383583e-05 seconds ** IQKeyboardManager: UIKeyboard Size : (768.0, 357.0) IQKeyboardManager: ** keyboardWillShow started ** IQKeyboardManager: ** keyboardWillShow ended: 6.09079725109041e-05 seconds ** IQKeyboardManager: ** adjustPosition() started ** IQKeyboardManager: Need to move: 0.0 IQKeyboardManager: <<RLAUIObjects.BorderedTextView: 0x7ff9e4057200; baseClass = UITextView; frame = (0 44; 415.5 705); text = 'We use an electric skille...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60800025b450>; layer = <CALayer: 0x6080002331e0>; contentOffset: {0, 0}; contentSize: {415.5, 50}; adjustedContentInset: {0, 0, 63, 0}> 0x00007ff9e4057200> Old UITextView.contentInset : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 63.0, right: 0.0) IQKeyboardManager: <<RLAUIObjects.BorderedTextView: 0x7ff9e4057200; baseClass = UITextView; frame = (0 44; 415.5 705); text = 'We use an electric skille...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60800025b450>; layer = <CALayer: 0x6080002331e0>; contentOffset: {0, 0}; contentSize: {415.5, 50}; adjustedContentInset: {0, 0, 107, 0}> 0x00007ff9e4057200> Old UITextView.contentInset : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 107.0, right: 0.0) IQKeyboardManager: Moving Upward IQKeyboardManager: Set <<Cooks_Memory.RCPDetailViewController: 0x7ff9e185f690> 0x00007ff9e185f690> origin to : (0.0, -185.0) IQKeyboardManager: ** adjustPosition() ended: 0.000585903995670378 seconds ** IQKeyboardManager: ** keyboardDidShow started ** IQKeyboardManager: ** keyboardDidShow ended: 3.15289944410324e-05 seconds **


hackiftekhar commented 6 years ago

It shouldn't capture UINavigationController objects to move. It should capture UIViewController's object instead.

rlaurb commented 6 years ago

Okay, you’ve identified that in the failing case, the root controller should be RCPDetailViewController, but it is UINavigationController instead.

This turned out to the key to identifying and solving the problem. THANK YOU!

If I’m reading adjustPosition() correctly, rootController is set at the top of the function using let rootController = textFieldView.parentContainerViewController(),

and (since it is a let variable, is never changed.

So I used the Xcode View Hierarchy debugger to examine the view hierarchy of the app in both the iPad and iPhone cases. In the iPad case, the hierarchy for the TextView has one NavigationController in it, while in the iPhone case, it has TWO. I understand this difference as follows:

The view hierarchy looks like this:

TabController SplitViewController Master: NavigationController root: RCPMasterViewController Detail: NavigationController root: RCPDetailViewController RCPDetail2ViewController (associated with ContainerView) [TextView sits here]

in the iPad case, Master and Detail are side-by-side.Tapping an item in the Master selects data to be displayed in the Detail, but since the detail is already visible, it is not pushed onto the master NavigationController’s stack. There is only one Navigation Controller in the detail’s view hierarchy.

In the iPhone case, Master and Detail are NOT side-by-side. Tapping an item in the Master selects data to be displayed in the Detail, AND PUSHES THE DETAIL CONTROLLER ONTO THE MASTER’S Stack. Since the Detail view ALSO has a NavigationController, this means there are TWO NavigationController’s in the Detail View’s view-hierarchy.

[NOTE: The Detail view needs its own NavigationController because it also needs to push and pop additional views.]

In this latter case, parentContainerViewController() [in IQUIView+Hierarchy] is selecting the detail view’s NavigationController instead of the detail view’s root.

So, I took a look at IQUIView+Hierarchy:parentContainerViewController().

At the top of the function is the code:

    if let /*var*/ navController = matchController?.navigationController {  // Changed 'var' to 'let' - RLA - 07/12/2018 - #1378

    // RLA - 07/12/2018 - #1378

// while let parentNav = navController.navigationController { // navController = parentNav // }

        var parentController : UIViewController = navController

        while let parent = parentController.parent,
            (parent.isKind(of: UINavigationController.self) == false &&
                parent.isKind(of: UITabBarController.self) == false &&
                parent.isKind(of: UISplitViewController.self) == false) {

                    parentController = parent
        }

        if navController == parentController {
            parentContainerViewController = navController.topViewController
        } else {
            parentContainerViewController = parentController
        }
    }

But it looks like the lines I’ve commented out are meant to do the same thing as the logic that is beneath them. In any case, those commented lines are clearly responsible for the fact that this function returns a NavigationController in the case where there are two on the stack.

And removing them fixes the problem.

Cheers,

Rick

On Jul 12, 2018, at 9:13 AM, Mohd Iftekhar Qurashi notifications@github.com wrote:

It shouldn't capture UINavigationController objects to move. It should capture UIViewController's object instead.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/hackiftekhar/IQKeyboardManager/issues/1378#issuecomment-404526530, or mute the thread https://github.com/notifications/unsubscribe-auth/AGTdEXJ_4uHfsmx68WIQEAGCg2NHkd2cks5uF1mNgaJpZM4U_716.

hackiftekhar commented 6 years ago

Can I reproduce same issue with your demo app or it's only reproducible with your main app?

rlaurb commented 6 years ago

I took a look at my test app, and it duplicates a critical part of the problem, although the text offset isn’t as obviously wrong as with my real app.

I’ve made the model a bit more complicated (by adding a TabController) — the link below is to a copy of the new test app from my Dropbox.

What DOES occur in the test app is that there are TWO NavigationControllers in the View Hierarchy of the TextView and IQUIView+Hierarchy:parentContainerViewController() returns a NavigationController, rather than the appropriate ViewController. (The NavigationController is the one closest in the hierarchy to the TextView.)

I am just guessing here, but looking at IQUIView+Hierarchy:parentContainerViewController(), it seems to me that you neglected to remove the first while clause when you replaced it by the second (more complex) one. Regardless of whether this guess is correct, it is clear to me that the problematic while clause guarantees that parentContainerViewController() will always return a NavigationController in the case where there are two NavigationControllers in the view hierarchy without some other ViewController in between them.

Revised Test App: https://www.dropbox.com/s/4gb7grfw75mhzpm/TextView-Test.zip?dl=0

Cheers,

Rick

On Jul 13, 2018, at 12:18 AM, Mohd Iftekhar Qurashi notifications@github.com wrote:

Can I reproduce same issue with your demo app or it's only reproducible with your main app?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/hackiftekhar/IQKeyboardManager/issues/1378#issuecomment-404728263, or mute the thread https://github.com/notifications/unsubscribe-auth/AGTdEVcAF7Zn_yfpGg-LmCFmvTc9LE4mks5uGC3BgaJpZM4U_716.

hackiftekhar commented 6 years ago

Thanks, I'll take a look of new demo.

hackiftekhar commented 6 years ago

Just an update, I fixed the textView hiding first 2 lines behind navigation bar issue and released a new version which is 6.1.1. I also fixed an issue where small textView is at the near of tab bar or at the bottom and it get's too much distance from bottom.

hackiftekhar commented 6 years ago

Now please try new version and let me know if everything works or what remaining issues you are getting right now?

rlaurb commented 6 years ago

Thank you!

While I’ve only made a brief test so far, your fix works perfectly. I will continue to test it, of course, but it certainly appears to work in my application. Thank you again.

By the way, if you’d like to see your library at work, I’d be happy to include you in the Field Test group for Cook’s Memory (the app in which I encountered the problem). Let me know if you’d like that.) [And there is no commitment; the app is completely free — no up-front charge, no in-app purchases, no in-app ads.]

Cheers,

Rick

Cheers,

Rick Aurbach

On Jul 18, 2018, at 10:14 AM, Mohd Iftekhar Qurashi notifications@github.com wrote:

Now please try new version and let me know if everything works or what remaining issues you are getting right now?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/hackiftekhar/IQKeyboardManager/issues/1378#issuecomment-405967126, or mute the thread https://github.com/notifications/unsubscribe-auth/AGTdEbIiyFRqpjpsO1-TTz7IvgCqxyTCks5uH1C6gaJpZM4U_716.