cocoa - Why is an empty drawRect interfering with a simple animation? -
i'm trying put own take on cocoa's nspathcontrol
.
in bid figure out best way handle expand-contract animation when mouse on component in control, put simple sample app. things going - mouse or out of view containing path components , you'd simple animation using following code:
// code belongs pathview (not pathcomponentview) override func mouseentered(theevent: nsevent) { nsanimationcontext.runanimationgroup({ (ctx) -> void in self.animatetoproportions([cgfloat](arrayliteral: 0.05, 0.45, 0.45, 0.05)) }, completionhandler: { () -> void in // }) } // animating subviews func animatetoproportions(proportions: [cgfloat]) { let height = self.bounds.height let width = self.bounds.width var xoffset: cgfloat = 0 (i, proportion) in enumerate(proportions) { let subview = self.subviews[i] as! nsview let newframe = cgrect(x: xoffset, y: 0, width: width * proportion, height: height) subview.animator().frame = newframe xoffset = subview.frame.maxx } }
as next step in control's development, instead of using nsview
instances path components started using own nsview
subclass:
class pathcomponentview: nsview { override func drawrect(dirtyrect: nsrect) { super.drawrect(dirtyrect) } }
although identical nsview
, when use class in animation, animation no longer it's supposed - frames set subviews pretty ignored, , whole effect ugly. if comment out drawrect:
method of subclass things return normal. can explain what's going wrong? why presence of drawrect:
interfering animation?
i've put demo project on github page.
you've broken rules here, , when break rules, behavior becomes undefined.
in layer backed view, must not modify layer directly. see documentation wantslayer
(which how specify it's layer-backed):
in layer-backed view, drawing done view cached underlying layer object. cached content can manipulated in ways more performant redrawing view contents explicitly. appkit automatically creates underlying layer object (using makebackinglayer method) , handles caching of view’s content. if wantsupdatelayer method returns no, should not interact underlying layer object directly. instead, use methods of class make changes view , layer. if wantsupdatelayer returns yes, acceptable (and appropriate) modify layer in view’s updatelayer method.
you make call:
subview.layer?.backgroundcolor = color.cgcolor
that's "interact[ing] underlying layer object directly." you're not allowed that. when there's no drawrect
, appkit skips trying redraw view , uses core animation animate what's there, gives expect (you're getting lucky there; it's not promised work).
but when sees implemented drawrect
(it knows did, doesn't know what's inside), has call perform custom drawing. it's responsibility make sure drawrect
fills in every pixel of rectangle. can't rely on backing layer (that's cache). draw nothing, you're seeing default background gray intermixed cached color. there's no promise of pixels updated correctly once we've gone down road.
if want manipulate layer, want "layer-hosting view" not "layer-backed view." can replacing appkit's cache layer own custom layer.
let subview = showmetheproblem ? pathcomponentview() : nsview() addsubview(subview) subview.layer = calayer() // use our layer, not appkit's caching layer. subview.layer?.backgroundcolor = color.cgcolor
while doesn't matter in case, keep in mind layer-backed view , layer-hosting view have different performance characteristics. in layer-backed view, appkit caching drawrect
results caching layer, , applying animations on automatically (this allows existing appkit code written before calayer
nice opt-in performance improvements without changing much). in layer-hosting view, system more ios. don't magical caching. there's layer, , can use core animation on it.
Comments
Post a Comment