Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Keyboard hiding TextView
-
I have a view with a canvas size of small portrait (iPhone) containing a
TextView
in the lower half. When I enter text into the view the keyboard completely covers the TextView. It accepts text but it is not visbible. How do I make the canvas automatically shift upward in such a case? I thought that the iOS framework would do this for me. Thanks for your help! -
Automatic content inset adjustment can only shift the text within the limits of the TextView, which will not help in your case.
The approach I use is to implement
keyboard_frame_did_change
on the containing view to move the TextView back and forth to stay in view.Small problem there is that the keyboard frame provided to the method is in screen coordinates, and your view is not, and the
ui.convert_point
is somehow still messed up, giving wildly unpredictable results.@JonB has implemented a replacement method, but I have found that if your views are not transformed, simply walking up the view hierarchy to adjust the y value of the top of the keyboard to your view's coordinates works well.
-
And sample code:
def keyboard_frame_did_change(self, frame): kb_y = frame[1] if self.kb_y == 0: # move TextView back to orig pos else: dy = 0 view = self while view: dy += view.y view = view.superview kb_y -= dy # Move TextView to be all above kb_y
-
@mikael Thanks, Mikael! That was a very helpful hint. I've proceeded quite a bit with my implementation. I've created a view called
EnhancedView
which can serve as a drop-in replacement forui.View
in layouts that have at least oneui.TextView
orui.TextField
in them. See here. The view does the following:- It overwrited the method
keyboard_frame_did_change
to hook into the changes of the keyboard frame (as suggested by you). - Upon first call of the method it scans all subviews recursively to find all
ui.TextView
's andui.TextField
's. - The default
delegate
method of all these subviews is replaced by a delegate listening to the*_did_*_editing
hooks.- When editing is started in one of the subviews, this subview stores itself as current view in the
EnhancedView
. - When editing is finished the view removes itself again.
- When editing is started in one of the subviews, this subview stores itself as current view in the
- For each call to
keyboard_frame_did_change
theEnhancedView
checks if a) the keyboard is visible, b) it has an active editing subview and c) the subview is at least partially hidden by the keyboard. If so, it computes a delta y offset for itsbounds
attribute and thus moves itself upward. In all other cases it restores thebounds
to its original value (which isy=0
).
The only little problem left is that even the recursive scan of the delta offsets for the y coordinate does not yield the correct value on the iPad. For some reason the topmost view has an offset of
y=0
according to itsframe
. However, it is NOT at the top of screen but underneath the status bar which seems to have a height of 20 pixels. If I add those 20 extra pixels on the iPad it works fine. But on my iPhone, it actually shifts the the view to hight, although the status bar is visible there, too.Is there a way to distiguish between these cases? Thanks a lot!
- It overwrited the method
-
Is the problem tied to orientation or iPad/iPhone?
If former, you can check the dimensions.
If latter, use cclauss' script to find the model. -
@mikael For the time being I'm just checking the screen dimensions and add the offset when I find out that the app is running on an iPad. This is probably wrong but for my two cases it works.
It did, however, add some more functionality to my
EnhancedView
: In case the app is running on an iPhone which does not offer the hide keyboard key the view will automatically show aButtonItem
on the upper right which will callend_editing()
on the open view. After closing it will restore anyButtonItem
's previously replaced by the keyboardButtonItem
. See this screen shot. -
Looks very useful.
What assumptions are you making regarding the view, i.e. existence of a top bar where you can place the extra button? Was thinking about a further option where the app is fullscreen and you would maybe need to show an auxiliary keyboard key instead.
-
You can use ui.convert_point((0,0),rootview,None) to get the screen position of the top frame. However, this has long been broken. in my uicomponents library, I had a RootView class with a replacement for this function. Quite honestly, never been tested on iPhone, but you might find it useful.
Another thing you might try: use convert_point to get the relative y of your textview relative to the root view. This does work. Then simply move the rootview.frame up by that y value. Should always bring the component in question to the top of the screen. For extra credit, account for the content_offset to keep the current line at the top.
-
@JonB Thanks for the hint!
ui.convert_point((0,0),rootview,None)
works for me. Whatever was wrong with it was either fixed or does not seem to bother me in my context. :-) -
@mikael I don't make any assumptions but the existence of the title row which is usually shown when the view is presented using
View.present()
. Of course, there's no way to offer theButtonItem
when there's no title row, but then again there's no close button either.. -
@marcus67: To be clear, my comment was intended for the case where you e.g. want to publish your app on the App Store, and the title bar and the close button no longer make sense. Or you don't like them for esthetic reasons, and are willing to put up with the swipe to close.
-
@mikael Ok, now I get it. Sorry for being so slow. You're right. I haven't thought about this. I guess I would need to put the button somewhere else since I would definitely still need one until Apple finally decides to let the user dismiss the keyboard on the iPhone, too.
Is the title row actually a feature which would automatically be hidden when the app is run as an appstore app? Or will I still have control over it? -
Disclaimer: I have not published anything to App Store.
You probably have control over it, but what would the close button do? Thus I am guessing that if you publish a stand-alone app and you want the title bar, you use e.g. a NavigationView for it, or roll your own.
-
iirc convert point was broken in some orientations in fullscreen. I had a little tester that showed this behavior. i think i tested this in 2.0.
-
@JonB is this the beg reports that you are referring to? https://forum.omz-software.com/topic/2329/bug-list-for-beta-release-160037/1
-
I just ran comparisons between doing a manual walk up the view hierarchy and using ui.convert_point.
I got the same results regardless of orientation, full screen or not, title bar or not - but all on iPhone 6s Plus.
-
try this
https://github.com/jsbain/uicomponents/blob/master/fixed_convert_and_kbframe.pyOn ipad, the problem is that in fullscreen, using convert_point with None thinks 0,0 is at the physical bottom left corner of the screen. also ui.get_keyboard_frame returns nonsense values. things work normally if converting between two views, just not view to None
it seems my code to fix this issue is not working either though... so somewhere along the line the problem changed.