Package: pythonista-gestures
gesturesGestures wrapper for iOSGestures for the Pythonista iOS appThis is a convenience class for enabling gestures, including drag and drop
support, in Pythonista UI applications. Main intent here has been to make
them Python friendly, hiding all the Objective-C details.Run the file on its own to see a demo of the supported gestures.InstallationCopy fromGitHub, orpip install pythonista-gestureswithstash.Versions:1.2 - Adds drag and drop support.1.1 - Adds distance parameters to swipe gestures.1.0 - First version released to PyPi.
Breaks backwards compatibility in syntax, adds multi-recognizer coordination,
and removes force press support.UsageFor example, do something when user swipes left on a Label:import gestures

def swipe_handler(data):
    print(f‘I was swiped left, starting from {data.location}')
 
label = ui.Label()
gestures.swipe(label, swipe_handler, direction=gestures.LEFT)Your handler method gets onedataargument that always contains the
attributes described below. Individual gestures may provide more
information; see the API documentation for the methods used to add different
gestures.recognizer- (ObjC) recognizer objectview- (Pythonista) view that was gestured atlocation- Location of the gesture as aui.Pointwithxandyattributesstate- State of gesture recognition; one ofgestures.POSSIBLE/BEGAN/RECOGNIZED/CHANGED/ENDED/CANCELLED/FAILEDbegan,changed,ended,failed- convenience boolean properties to
check for these statesnumber_of_touches- Number of touches recognizedFor continuous gestures, check fordata.beganordata.endedin the handler
if you are just interested that a pinch or a force press happened.All of the gesture-adding methods return an object that can be used
to remove or disable the gesture as needed, see the API. You can also remove
all gestures from a view withremove_all_gestures(view).Drag and dropThis module supports dragging and dropping both within a Pythonista app and
between Pythonista and another app (only possible on iPads). These two cases
are handled differently:For in-app drops, Apple method of relaying objects is skipped completely,
and you can refer toanyPython object to be dropped to the target view.For cross-app drops, we have to conform to Apple method of managing data.
Currently only plain text and image drops are supported, in either direction.It is also good to note thatui.TextFieldandui.TextViewviews natively
act as receivers for both in-app and cross-app plain text drag and drop.View is set to be a sender for a drap and drop operation with thedragfunction. Drag starts with a long press, and can end in any view that has been
set as a receiver with thedropfunction. Views show the readiness to receive
data with a green "plus" sign. You can accept only specific types of data;
incompatible drop targets show a grey "forbidden" sign.Following example covers setting up an in-app drag and drop operation between
two labels. To repeat, in the in-app case, the simple string could replaced by
any Python object of any complexity, passed by reference:drag(sender_label, "Important data")

drop(receiver_label,
    lambda data, sender, receiver: setattr(receiver, 'text', data),
    accept=str)See the documentation for the two functions for details.Fine-tuning gesture recognitionBy default only one gesture recognizer will be successful. You can prioritize
one over the other by using thebeforemethod of the returned object.
For example, the following ensures that the swipe always has a chance to happen
first:panner = pan(view, pan_handler)
swiper = swipe(view, swipe_handler, direction=RIGHT)
swiper.before(panner)(For your convenience, there is also a similaraftermethod.)You can also allow gestures to be recognized simultaneously using thetogether_withmethod. For example, the following enables simultaneous panning
and zooming (pinching):panner = pan(view, pan_handler)
pincher = pinch(view, pinch_handler)
panner.together_with(pincher)Using lambdasIf there in existing method that you just want to trigger with a gesture,
often you do not need to create an extra handler function.
This works best with the discretetapandswipegestures where we do not
need to worry with the state of the gesture.tap(label, lambda _: setattr(label, 'text', 'Tapped'))The example below triggers some kind of a database refresh when a long press is
detected on a button.
Anything more complicated than this is probably worth creating a separate
function.long_press(button, lambda data: db.refresh() if data.began else None)Pythonista app-closing gestureWhen you use thehide_title_bar=Trueattribute withpresent, you close
the app with the 2-finger-swipe-down gesture. This gesture can be
disabled with:gestures.disable_swipe_to_close(view)where theviewmust be the one youpresent.You can also replace the close gesture with another, by providing the
"magic"closestring as the gesture handler. For example,
if you feel that tapping with two thumbs is more convenient in two-handed
phone use:gestures.tap(view, 'close', number_of_touches_required=2)Other detailsAdding a gesture to a view automatically setstouch_enabled=Truefor that
view, to avoid counter-intuitive situations where adding a gesture
recognizer to e.g. ui.Label produces no results.It can be hard to add gestures to ui.ScrollView, ui.TextView and the like,
because they have complex multi-view structures and gestures already in
place.APIFunctionsGesturesGesture managementDrag and dropFunctionsGESTUREStap(view, action,number_of_taps_required=None, number_of_touches_required=None)Callactionwhen a tap gesture is recognized for theview.Additional parameters:number_of_taps_required- Set if more than one tap is required for
the gesture to be recognized.number_of_touches_required- Set if more than one finger is
required for the gesture to be recognized.doubletap(view, action,number_of_touches_required=None)Convenience method that callstapwith a 2-tap requirement.long_press(view, action,number_of_taps_required=None,number_of_touches_required=None,minimum_press_duration=None,allowable_movement=None)Callactionwhen a long press gesture is recognized for theview. Note that this is a continuous gesture; you might want to
check fordata.changedordata.endedto get the desired results.Additional parameters:number_of_taps_required- Set if more than one tap is required for
the gesture to be recognized.number_of_touches_required- Set if more than one finger is
required for the gesture to be recognized.minimum_press_duration- Set to change the default 0.5-second
recognition treshold.allowable_movement- Set to change the default 10 point maximum
distance allowed for the gesture to be recognized.pan(view, action,minimum_number_of_touches=None,maximum_number_of_touches=None)Callactionwhen a pan gesture is recognized for theview.
This is a continuous gesture.Additional parameters:minimum_number_of_touches- Set to control the gesture recognition.maximum_number_of_touches- Set to control the gesture recognition.Handleractionreceives the following gesture-specific attributes
in thedataargument:translation- Translation from the starting point of the gesture
as aui.Pointwithxandyattributes.velocity- Current velocity of the pan gesture as points per
second (aui.Pointwithxandyattributes).edge_pan(view, action, edges)Callactionwhen a pan gesture starting from the edge is
recognized for theview. This is a continuous gesture.edgesmust be set to one ofgestures.EDGE_NONE/EDGE_TOP/EDGE_LEFT/EDGE_BOTTOM/EDGE_RIGHT /EDGE_ALL. If you want to recognize pans from different edges,
you have to set up separate recognizers with separate calls to this
method.Handleractionreceives the same gesture-specific attributes in
thedataargument as pan gestures, seepan.pinch(view, action)Callactionwhen a pinch gesture is recognized for theview.
This is a continuous gesture.Handleractionreceives the following gesture-specific attributes
in thedataargument:scale- Relative to the distance of the fingers as opposed to when
the touch first started.velocity- Current velocity of the pinch gesture as scale
per second.rotation(view, action)Callactionwhen a rotation gesture is recognized for theview.
This is a continuous gesture.Handleractionreceives the following gesture-specific attributes
in thedataargument:rotation- Rotation in radians, relative to the position of the
fingers when the touch first started.velocity- Current velocity of the rotation gesture as radians
per second.swipe(view, action,direction=None,number_of_touches_required=None,min_distance=None,max_distance=None)Callactionwhen a swipe gesture is recognized for theview.Additional parameters:direction- Direction of the swipe to be recognized. Either one ofgestures.RIGHT/LEFT/UP/DOWN, or a list of multiple directions.number_of_touches_required- Set if you need to change the minimum
number of touches required.min_distance- Minimum distance the swipe gesture must travel in
order to be recognized. Default is 50.
This uses an undocumented recognizer attribute.max_distance- Maximum distance the swipe gesture can travel in
order to still be recognized. Default is a very large number.
This uses an undocumented recognizer attribute.If set to recognize swipes to multiple directions, the handler
does not receive any indication of the direction of the swipe. Add
multiple recognizers if you need to differentiate between the
directions.GESTURE MANAGEMENTdisable(handler)Disable a recognizer temporarily.enable(handler)Enable a disabled gesture recognizer. There is no error if the
recognizer is already enabled.remove(view, handler)Remove the recognizer from the view permanently.remove_all_gestures(view)Remove all gesture recognizers from a view.disable_swipe_to_close(view)Utility class method that will disable the two-finger-swipe-down
gesture used in Pythonista to end the program when in full screen
view (hide_title_barset toTrue).Returns a tuple of the actual ObjC view and dismiss target.replace_close_gesture(view, recognizer_class)DRAG AND DROPdrag(view, payload, allow_others=False)Sets theviewto be the sender in a drag and drop operation. Dragging
starts with a long press.For within-app drag and drop,payloadcan be anything, and it is passed
by reference.If thepayloadis a text string or aui.Image, it can be dragged
(copied) to another app (on iPad).
There is also built-in support for dropping text to anyui.TextFieldorui.TextView.Ifpayloadis a function, it is called at the time when the drag starts.
The function receives one argument, the sendingview, and must return the
data to be dragged.Additional parameters:allow_others- Set to True if other gestures attached to the view
should be prioritized over the dragging.drop(view, action, accept=None)Sets theviewas a drop target, calling theactionfunction with
dropped data.Additional parameters:accept- Control which data will be accepted for dropping. Simplest
option is to provide an accepted Python type likedictorui.Label.For cross-app drops, only two types are currently supported:strfor
plain text, andui.Imagefor images.For in-app drops, theacceptargument can also be a function that will
be called when a drag enters the view. Function gets same parameters
as the main handler, and should return False if the view should not accept
the drop.actionfunction has to have this signature:def handle_drop(data, sender, receiver):
      ...Arguments of theactionfunction are:data- The dragged data.sender- Source view of the drag and drop. This isNonefor drags
between apps.receiver- Same asview.