There's been a bit of recent interest in building "real" apps using ECL/iOS. While the `eclffi' sample code in the ecl-iphone-builder repo included examples of how to create/call Objective-C classes/methods from Lisp, it was missing the glue to go the other way, which was necessary to implement callbacks in Lisp. I've pushed an update to github which adds this functionality.
As an example I created a subclass of UIButton
called UIButtonCB
with one instance var called clickHandler
, which can be set to a
Lisp function pointer. The UIButtonCB
class wires the
UIControlEventTouchUpInside
event to the specified handler function.
@implementation UIButtonCB - (id) initWithFrame: (CGRect) aFrame { [super initWithFrame: aFrame]; clickHandler = Cnil; [self addTarget: self action: @selector(clicked:) forControlEvents: UIControlEventTouchUpInside]; return self; } - (void) setClickHandler: (cl_object) fun { register_cb(clickHandler, fun); } - (void) clicked: (id) sender { if (Cnil != clickHandler) { cl_funcall(2, clickHandler, ecl_foreign_data_ref_elt(&sender,ECL_FFI_POINTER_VOID)); } } - (void) dealloc { remove_cb(clickHandler); clickHandler = Cnil; [super dealloc]; }
The call to register_cb()
ensures that the callback function is
protected from GC, and remove_cb()
makes the function GC'able
again. UIButtonCB
can now be wrapped in Lisp with the following FFI
glue:
(defun set-click-handler (view fun) (when fun (c-fficall ((view :pointer-void) ((make-callback-function fun) :object)) :void "[#0 setClickHandler: #1];"))) (defun make-button (&rest init-view-args &key title title-color icon on-click (enabled t) &allow-other-keys) (let ((view (make-view-instance "UIButtonCB" init-view-args))) (set-click-handler view on-click) (when icon (set-image view icon)) (when title (set-title view title)) (when title-color (set-title-color view title-color)) (set-enabled view enabled) view))
Here's an example of the Lisp code to create a button with an on-click callback, which updates a label:
(let ((n 0) (label (make-label "" :frame '(10.0 50.0 300.0 35.0)))) (add-subview (key-window) label) (add-subview (key-window) (make-button :frame '(140 230 40 20) :title "click" :background-color (color-argb 1 0.5 0.5 0.5) :on-click (lambda (button) (declare (ignore button)) (set-text label (format nil "~d click~:p" (incf n)))))))
Obviously, this isn't a very sophisticated approach, but it's a start. It requires subclassing each UIControl or UIView to add the vars to store the Lisp callbacks. I'm sure there's ample room for abstracting or automating some of this boilerplate Objective C code.