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.
Moving the keyboard input text view to be visible?
-
Is there any way to force whatever text view is currently receiving keyboard input into view, if it is covered up by the keyboard? So, for example, there's a text input field at the bottom of a form, user taps in it to begin entering information, but the keyboard pops up and covers the text field, so they can type, but can't see what they're typing.
In many iOS apps, the view slides up to make sure the input field is visible...is that kind of thing possible in Pythonista? Perhaps someone has a clever way to display a proxy text view to get the input and then send the value to the view which was tapped?
-
@shinyformica Quick and dirty code of "put your textview in a scroll_view"
import ui class my_View(ui.View): def __init__(self): self.frame = (0,0,400,600) sv = ui.ScrollView(name='sv') sv.border_width = 1 sv.frame = (10,10,self.width-20,self.height-20) self.original_height = sv.height self.add_subview(sv) tv = ui.TextView() tv.frame = (0,0,sv.width,sv.height) tv.text = '' for i in range(0,50): tv.text = tv.text + str(i) + '\n' sv.add_subview(tv) tv.delegate = self def textview_did_begin_editing(self, textview): self['sv'].height = self.original_height-120 def textview_did_end_editing(self, textview): self['sv'].height = self.original_height v = my_View() v.present('sheet')
-
Yeah, I thought about putting the whole thing inside a scrollview which only is there for this purpose...I just might do that.
Trouble is, I'm not actually in control, necessarily, of the location/contents of widgets in the view that the user might tap on to enter text. Those views will be constructed by other people, and displayed in this main view...so when the keyboard pops up it could be that the text box would have to be placed in a scrollview dynamically, or hard to scroll to for other reasons. I'll think on it, but thanks for the sample code!
I should actually just resize the whole main view when the keyboard shows up...but I don't yet have a global way to access and connect to the keyboard notifications:
UIKeyboardWillShowNotification
UIKeyboardDidShowNotification
UIKeyboardWillHideNotification
UIKeyboardDidHideNotification
UIKeyboardWillChangeFrameNotification
UIKeyboardDidChangeFrameNotificationI got pointed to good info on that front by you and @JonB, just haven't had a chance to actually try and build something that hooks into the notification center.
-
Custom Views have the following:
def keyboard_frame_will_change(self, frame): # Called when the on-screen keyboard appears/disappears # Note: The frame is in screen coordinates. pass def keyboard_frame_did_change(self, frame): # Called when the on-screen keyboard appears/disappears # Note: The frame is in screen coordinates. pass
You then have to search through the view heirarchy for textfield or textview that are editing... which is easiest done by using the textfield delegates and setting an attribute. or, you can check whether the objc_instance is first responder, i think.
then you determine the location of the textfield, and set the scrollview content_inset to the y location of the textfield.
-
@shinyformica As you can test with this little script, the keyboard_frame_will/did_change is only called when the keyboard is displayed or dismissed. Thus, if you change of field while the keyboard is already displayed, these functions will not been called and you will not see which field has really the focus.
import ui from objc_util import * class my_View(ui.View): def __init__(self): self.frame = (0,0,400,600) tv1 = ui.TextView(name='tv1') tv1.frame = (10,10,180,300) tv1.text = '' for i in range(0,50): tv1.text = tv1.text + str(i) + '\n' self.add_subview(tv1) tv2 = ui.TextView(name='tv2') tv2.frame = (210,10,180,300) tv2.text = '' for i in range(0,50): tv2.text = tv2.text + str(i) + '\n' self.add_subview(tv2) def search_editing(self): for subv in self.subviews: if type(subv) is ui.TextView: subvo = ObjCInstance(subv) #print(dir(subvo)) #return if subvo.isFirstResponder(): print(subv.name) def keyboard_frame_will_change(self, frame): # Called when the on-screen keyboard appears/disappears # Note: The frame is in screen coordinates. print('keyboard_frame_will_change',frame) self.search_editing() def keyboard_frame_did_change(self, frame): # Called when the on-screen keyboard appears/disappears # Note: The frame is in screen coordinates. self.search_editing() pass print('keyboard_frame_did_change',frame) v = my_View() v.present('sheet')
-
Once again, had I just looked at the docs more closely, I would have seen those custom View methods are available...that will definitely do what I need @cvp, so thanks!
-
Good point about this only getting called when keyboard is first shown. Ideally, you would attach textfield did_begin_editing delegates that scroll the textfield to the top of the scroll view. Even if you don't own those, you could basically store the original delegate within your custom delegate, and then call the original delegate methods after you scroll into place.