Search code examples
pythonpythonista

Create auto-complete TextField in Pythonista 3


I want to create an auto-complete TextField.

I mean - when you type something in the field and see a prompt list below. The prompt list is an array with possible values. The best way to explain it show a similar picture.

Auto-complete example

I already have some experience in Pythonista 3 but it was not UI programming experience.

I understand this is complex and that maybe I should use an additional View and Delegate mechanism but I don't have any idea how to start. I have already spent several days in Google looking for a solution, but I can't, in the context of Pythonista.

Has anybody done this? Or could someone provide useful links for reading?


Solution

  • A drop-down list can be created in Pythonista using a TableView. TableViews are really just single-column lists, they’re not just for tables.

    So the steps would be:

    1. Create a tableview.
    2. Align it with your text field.
    3. Hide the tableview until typing starts.
    4. Update the tableview’s item list with the auto-completion options whenever the typing changes.
    5. Potentially hide the tableview again when typing ends.

    You can hide any view by setting its .hidden property to True.

    You can do things when typing starts in a TextField by creating a delegate object that implements textfield_did_change.

    You set a TableView to have a list of items by giving the TableView a data_source, probably an implementation of ui.ListDataSource. Whenever the items property on the data source changes, the list of options will also automatically change.

    You can react to the user choosing an option from the TableView by setting an action on the TableView’s delegate.

    The documentation for TableView, TextField, ListDataSource, delegates, and actions, can be found at Pythonista’s Native GUI for iOS documentation.

    Here is a basic example:

    # documentation at http://omz-software.com/pythonista/docs/ios/ui.html
    import ui
    
    # the phoneField delegate will respond whenever there is typing
    # the delegate object will combine phoneField delegate, tableview delegate, and data source, so that it can share data
    class AutoCompleter(ui.ListDataSource):
        def textfield_did_change(self, textfield):
            dropDown.hidden = False
            # an arbitrary list of autocomplete options
            # you will have a function that creates this list
            options = [textfield.text + x for x in textfield.text]
    
            # setting the items property automatically updates the list
            self.items = options
            # size the dropdown for up to five options
            dropDown.height = min(dropDown.row_height * len(options), 5*dropDown.row_height)
    
        def textfield_did_end_editing(self, textfield):
            #done editing, so hide and clear the dropdown
            dropDown.hidden = True
            self.items = []
    
            # this is also where you might act on the entered data
            pass
    
        def optionWasSelected(self, sender):
            phoneField.text = self.items[self.selected_row]
            phoneField.end_editing()
    
    autocompleter = AutoCompleter(items=[])
    autocompleter.action = autocompleter.optionWasSelected
    
    # a TextField for phone number input
    phoneField = ui.TextField()
    phoneField.delegate = autocompleter
    phoneField.keyboard_type = ui.KEYBOARD_PHONE_PAD
    phoneField.clear_button_mode = 'while_editing'
    
    # the drop-down menu is basically a list of items, which in Pythonista is a TableView
    dropDown = ui.TableView()
    dropDown.delegate = autocompleter
    dropDown.data_source = autocompleter
    # hide the dropdown until typing starts
    dropDown.hidden = True
    
    # create interface
    mainView = ui.View()
    mainView.add_subview(phoneField)
    mainView.add_subview(dropDown)
    
    # present the interface before aligning fields, so as to have the window size available
    mainView.present()
    
    # center text field
    phoneField.width = mainView.width*.67
    phoneField.height = 40
    
    phoneField.x = mainView.width/2 - phoneField.width/2
    phoneField.y = mainView.height/3 - phoneField.height/2
    
    # align the dropdown with the phoneField
    dropDown.x = phoneField.x
    dropDown.y = phoneField.y + phoneField.height
    dropDown.width = phoneField.width
    dropDown.row_height = phoneField.height
    

    On my iPhone, this code creates an interface that looks like this:

    Sample code output on iPhone 8