2

I have a function that continuously takes screenshots of an UI element to then draw it itself. The UI element can change, so I take a screenshot in very short intervals to not have the second drawing lag behind (please don't question this and just assume that redrawing it is the right way. The use case is a bit more complicated).

However, taking the screenshot and invalidating the previous drawing to redraw it is quite an expensive operation, and most often not needed as the UI element doesn't update that often. Is there a way to detect when a UI element changes in such a way that it needs redrawing, including when it happens to one of its subviews? One solution would be to copy the state and the states of all its descendents and then check that, but that doesn't seem like a good solution either. iOS must know internally when it needs to redraw/update the views, is there any way to hook into this? Note that I tagged this UIKit and Core-Animation, I suppose the way to go for this is Core-Animation, but I'm open for a solution that uses either of these.

JustSid
  • 25,168
  • 7
  • 79
  • 97
  • I'm *very* curious why exactly this is the right way. But, you said don't ask, so… Are you able to subclass that UI element and "watch" an instance of the subclass instead? – ravron Feb 08 '15 at 01:17
  • @RileyAvron Sadly not. I can in one instance, but the others are system provided `UIButton` instances. I could redo the whole UI hierarchy I guess, but that seems like a lot of overkill. – JustSid Feb 08 '15 at 01:19
  • @RileyAvron It is the *right* way because it is a popover with a fake blur, mostly because there is a clip path applied to it to soften the blur and allow some transparency. Now, transparency and the GPU don't mix well, so that incurs a lot of offscreen rendering. The solution is to take a screenshot of the background and use that as a solid background and have onscreen rendering with 60 FPS. – JustSid Feb 08 '15 at 01:24
  • @RileyAvron I only need to update a small portion of the large screenshot though, mainly the part is clipped away and visible underneath the popover. So I'm okay with a solution that only notifies me when some specific views change. Although I would love a really general solution that can be reused. – JustSid Feb 08 '15 at 01:25
  • Ha, my curiosity is sated. I've got an idea, but I'm working up a test project to make sure it'll actually work before I just brazenly post an answer. – ravron Feb 08 '15 at 01:26
  • @RileyAvron Awesome, that sounds promising! :) – JustSid Feb 08 '15 at 01:27
  • 1
    Why don't you try to listen layout/display changes in UI components via KVO? But if you do so, you may need to create a super class first and then subclass your ui classes. For instance; http://stackoverflow.com/questions/4874288/use-key-value-observing-to-get-a-kvo-callback-on-a-uiviews-frame/19687115#19687115 – Ozgur Vatansever Feb 08 '15 at 01:29
  • @ozgurv Because listening to every possible UI change is nearly impossible. For one, because UIKit doesn't support KVO and some things simply don't work, and for other because there are a number of things that can change and adding myself as a subscriber for a whole view hierarchy with special case for all possible subclasses to encounter is pretty fragile. – JustSid Feb 08 '15 at 01:33
  • We should say "not fully" compliant :) – Ozgur Vatansever Feb 08 '15 at 01:35
  • @ozgurv True. I know a lot of things work most of the time, but I also know that a lot of things simply don't work in some weird edge cases. I don't need a totally general solution either I have to say, but the less specialized the better since that would make re-using it a lot easier and better fitted to possible UI changes in the future. – JustSid Feb 08 '15 at 01:37
  • Lots of fun experimenting, but, unfortunately, I made little progress. I first tried a little KVO, similar to what @ozgurv suggested, and then ended up using message forwarding to get between a UISlider and its layer. As of yet, though, I'm not seeing any behavior you could use as a hook to know when the view's rendering has changed. I'm looking into `CADisplayLink` now. Sorry to get your hopes up. I'll let you know if I have any insights. – ravron Feb 08 '15 at 02:53
  • @RileyAvron I've been playing around as well. I'm currently looking into either hooking `CABackingStoreInvalidate()` or swizzling `[CALayer setNeedsDisplayInRect:]`, both seem wrong in their own way. – JustSid Feb 08 '15 at 03:03

0 Answers0