Inspired by @DoinStuffMobile request for a Joystick-like control for the scene module, I created a very basic joystick using the objc_util module. I’ve gotten it working to use custom actions, pass a few kwargs in, and such things without crashing.
I was curious as to if there’s a way to wrap the objc_instance of the Joystick with the ui module similar to how @omz has with the other ui views?
Also, I need suggestions! What should I change to be more efficient, elegant, pythonic, etc? What are some things you guys think I should add?
Code is kinda long:
from objc_util import *
import ui
class Joystick (ui.View):
def __new__(cls,**kwargs):
inst=create_objc_class(
'UIJoystick',
ObjCClass('UIControl'),
methods=[Joystick.beginTrackingWithTouch_withEvent_,Joystick.continueTrackingWithTouch_withEvent_,Joystick.endTrackingWithTouch_withEvent_,Joystick.action]
).new()
Joystick.__init__(inst,**kwargs)
return inst
def __init__(self,**kwargs):
for key,val in kwargs.items():
if key =='action':
self.customAction=val
elif eval(f'self.{key}()').__class__ != self.aabbbbbbb.__class__:
key=key[0].upper()+key[1:]
if key == 'Frame':
val=CGRect(CGPoint(val[0],val[1]),CGSize(val[2],val[3]))
elif key == 'BackgroundColor':
val=ObjCClass('UIColor').alloc().initWithRed_green_blue_alpha_(val[0],val[1],val[2],val[3])
eval(f'self.set{key}_(val)')
#can add more kwargs
self.radius=self.size().height/2
self._setCornerRadius_(self.radius)
stick=ui.View(bg_color='#acacac')
stick.touch_enabled=False
stick.frame=(
0,
0,
self.size().width,
self.size().height)
self.originalStickCenter=stick.center
stick.corner_radius=self.size().width/2
self.addSubview_(stick.objc_instance)
self.layer().setBorderColor_(ObjCClass('UIColor').alloc().initWithRed_green_blue_alpha_(0.67, 0.67, 0.67,1.0).CGColor)
self.layer().setBorderWidth_(1)
return
@staticmethod
def beginTrackingWithTouch_withEvent_(_self,_cmd,touch,event):
#called when a touch enters
#control's bounds
return True
@staticmethod
def continueTrackingWithTouch_withEvent_(_self,_cmd,touch,event):
#called when a touch event is
#updated. like dragging
#add code
self=ObjCInstance(_self)
touchLoc=ObjCInstance(touch).preciseLocationInView_(self)
stick=self.subviews()[0]
touchVec=CGVector(
touchLoc.x-self.radius,
touchLoc.y-self.radius)
touchVecMagn=(touchVec.dx**2 + touchVec.dy**2)**.5
unitTouchVec=CGVector(
touchVec.dx/touchVecMagn,
touchVec.dy/touchVecMagn)
if touchVecMagn < self.radius and touchLoc.x < self.size().width and touchLoc.y < self.size().height:
newLoc=touchLoc
else:
newLoc=CGPoint(
self.radius*unitTouchVec.dx+self.radius,
self.radius*unitTouchVec.dy+self.radius)
stick.setCenter_(newLoc)
self.touchVec=touchVec
self.sendAction_to_forEvent_('action',self,None)
return True
@staticmethod
def endTrackingWithTouch_withEvent_(_self,_cmd,touch,event):
#called when touch ends
#add code
touch=ObjCInstance(touch)
self=ObjCInstance(_self)
stick=self.subviews()[0]
stick.setCenter_(CGPoint(self.originalStickCenter.x,self.originalStickCenter.y))
return True
@staticmethod
def action(_self,_cmd):
self=ObjCInstance(_self)
self.customAction.__call__(ObjCInstance(_self))
return
if __name__=='__main__':
x,y=ui.get_screen_size()
root=ui.View(
frame=(0,0,x,y),
bg_color='white')
def closeRoot(sender):
root.close()
return
closeButton=ui.Button(
frame=(50,75,24,24),
image=ui.Image('iob:close_24'),
tint_color='#ff0000',
action=closeRoot
)
def moveButton(sender):
btn=sender.superview().subviews()[1]
btn.setCenter_(CGPoint(btn.center().x+sender.touchVec.dx//10,btn.center().y+sender.touchVec.dy//10))
return
j=Joystick(
frame=(x//2-125,y//2-200,250,250),
action=moveButton
)
root.objc_instance.addSubview_(j)
root.add_subview(closeButton)
root.present('popover',hide_title_bar=True)