Drop Shadow behind ui.view()
Ask and he shall receive. It's not perfect, yet. There are some clipping problems I can't figure out.
# coding: utf-8 from objc_util import * import ui UIColor = ObjCClass('UIColor') def Color(red=0, green=0, blue=0, alpha=1): return UIColor.colorWithRed_green_blue_alpha_(red, green, blue, alpha) class ShadowView (ui.View): def __init__(self, *args, **kwargs): super(ShadowView, self).__init__() self.pntr = ObjCInstance(self) self.pntr.layer().setMasksToBounds_(False) ## Go ahead and do this. @property def corner_radius(self): return self.pntr.layer().cornerRadius() @corner_radius.setter def corner_radius(self, val): self.pntr.layer().setCornerRadius_(val) @property def border_color(self): return self.pntr.layer().borderColor() @border_color.setter def border_color(self, color): self.pntr.layer().setBorderColor_(Color(*color).CGColor()) @property def border_width(self): return self.pntr.layer().borderWidth() @border_width.setter def border_width(self, val): self.pntr.layer().setBorderWidth_(val) @property def opacity(self): return self.pntr.layer().opacity() @opacity.setter def opacity(self, val): self.pntr.layer().setOpacity_(value) @property def hidden(swlf): return self.pntr.layer().hidden() @hidden.setter def hidden(self, val): self.pntr.layer().setHidden_(val) @property def masks_to_bounds(self): return self.pntr.layer().masksToBounds() @masks_to_bounds.setter def masks_to_bounds(self, val): self.pntr.layer().setMasksToBounds_(val) @property def mask(self): return self.pntr.layer().mask() @mask.setter def mask(self, new_mask): self.pntr.layer().setMask_(new_mask) @property def double_sided(self): return self.pntr.layer().doubleSided() @double_sided.setter def double_sided(self, val): self.pntr.layer().setDoubleSided_(val) @property def shadow_opacity(self): return self.pntr.layer().shadowOpacity() @shadow_opacity.setter def shadow_opacity(self, val): self.pntr.layer().setShadowOpacity_(val) @property def shadow_radius(self): return self.pntr.layer().shadowRadius() @shadow_radius.setter def shadow_radius(self, val): self.pntr.layer().setShadowRadius_(val) @property def shadow_offset(self): return self.pntr.layer().shadowOffset() @shadow_offset.setter def shadow_offset(self, offset): ## offset should be a tuple, but I'll take a CGSize if isinstance(offset, CGSize): self.pntr.layer().setShadowOffset_(offset) elif isinstance(offset, tuple): self.pntr.layer().setShadowOffset_(CGSize(*offset)) else: raise TypeError("Cannot use type %s. Use CGSize or tuple" % type(offset)) @property def shadow_color(self): return self.pntr.layer().shadowColor() @shadow_color.setter def shadow_color(self, color): if isinstance(color, UIColor.CGColor()): self.pntr.layer().setShadowColor_(color) elif isinstance(color, tuple) and len(color) == 4: self.pntr.layer().setShadowColor_(Color(*color).CGColor()) else: raise ValueError('Cannot use type %s. Use UIColor or tuple' % type(color)) @property def shadow_path(self): return self.pntr.layer().shadowPath() @shadow_path.setter def shadow_path(self, path): self.pntr.layer().setShadowPath_(path) @property def style(self): return self.pntr.layer().style() @style.setter def style(self, style): self.pntr.layer().setStyle_(style) if __name__ == '__main__': view = ui.View(frame=(0,0,500,500)) box = ShadowView(frame=(0,0,100,100)) view.background_color = 'white' box.background_color = 'red' box.center = view.center view.add_subview(box) box.masks_to_bounds = False box.corner_radius = 6. box.border_color = (0,1,0) box.border_width = 6 box.shadow_radius = 10 box.shadow_offset = (0,0) box.shadow_opacity = 1 view.present('sheet')
I tested most of it, but there may still be some funny-ness.
@blmacbeth , ok thanks. I will try it. Maybe I am wrong, but doesn't your call to super need to be like
super(ShadowView, self).init(self, *args, **kwargs)
If not, then I would love to know why not.
dgelessus last edited by
@Phuket2 Since you've already passed
selfas an argument to
super, you already get (bound) methods instead of normal functions, so you don't need to pass
selfas the first parameter to
__init__. Though you are right, the
**kwargsdo need to be passed (if you want
ui.Viewto interpret them, which I assume is the case). For
ui.Viewthe docs say that you don't need to call
super().__init__()at all, perhaps everything important is already handled by
@Phuket2 Ha! You caught me. That may be a problem… it should look like:
super(ShadowView, self).__init__(*args, **kwargs)
I must have been in a zone to miss that. There is no need to repeat
super(ShadowView, self)returns an instance of the superclass, which will pass
@dgelessus , thanks. I tend not to call super anymore. I normally call the class init method. I think I did that because I was doing multiple inheritance and was having a hard time following what was going on 😭, to this day I still don't know the most correct way.
I am pretty sure I have checked this theory of not having to pass on params to ui.View, I know it's in the docs. As far as I can see it does not work. Maybe if you have no init. Not sure that should cancel new, but maybe it's been written to work that way. So many combinations, I get confused 😱
@blmacbeth , lol. I understand. I thought you had some special trick up your sleeve 😝
@Phuket2 I wish I have a cool trick up my sleeve! But to comment on your response about not calling
super: it is best practice to call
superwhen subclassing. This is because it allows you to refactor your code with less changes made afterwards.
For example: let's say I someday make a kick-ass version of
ui.Viewlook like children's toys. So, you want to incorporate the new view classes into your old code. The way you have, you would need to change every instance of
ui.View.__init__(...). By calling
superyou no longer have to do all that tedious work. It makes life slightly easier.
I'm not saying that what your doing is wrong; I have plenty of classes that do the same thing. But o have started using
superbecause of the refactoring issue.
Now, this could all be a load of bullshit I am feeding you, so go look it up yourself and let me know if I'm correct! 😛
@blmacbeth , hmmm, I am a little on the drunk side now 🎉😎 almost 11:30pm here now. So maybe I should wait until tomorrow to look it up 😱
But I started getting a problem when inheriting from multiple classes. Gets confusing about which base class is being called ( it did for me anyway). That's when I started using the implicit calls to base class's init methods instead. But I see your point though. I haven't managed to write anything significant enough in Python were it's been a problem to refactor 😭
Still trying though
JonB last edited by
re needing to call View.init, see
If you do not implement your own init, you do not need to call super init.
If you have an init, you do need to call the View.init
dgelessus last edited by
From what I've heard, you need to use
super()for some aspects of multiple inheritance to work properly. I know almost nothing about the details of multiple inheritance on Python though, so I can't tell you why exactly that is and what would break otherwise. Mostly because multiple inheritance is not needed very often.