1

I have a very intriguing obstacle to overcome. I am trying to display the live contents of a UIView in another, separate UIView.

What I am trying to accomplish is very similar to Mission Control in Mac OS X. In Mission Control, there are large views in the center, displaying the desktop or an application. Above that, there are small views that can be reorganized. These small views display a live preview of their corresponding app. The preview is instant, and the framerate is exact. Ultimately, I am trying to recreate this effect, as cheaply as possible.

I have tried many possible solutions, and the one shown here is as close as I have gotten. It works, however the - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx method isn't called on every change. My solution was to call [cloneView setNeedsDisplay] using a CADisplayLink, so it is called on every screen refresh. It is very near my goal, but the framerate is extremely low. I think that [CALayer renderInContext:] is much too slow.

If it is possible to have two CALayers render the same source, that would be golden. However, I am not sure how to approach this. Luckily, this is simply a concept app and isn't destined for the App Store, so I can make use of the private APIs. I have looked into IOSurface and Quartz contexts, but I haven't been able to solve this puzzle so far. Any input would be greatly appreciated!

Community
  • 1
  • 1
eswick
  • 519
  • 4
  • 16
  • Is there a reason you can't just duplicate the view and update both using the same method you are updating the orignal? – Chris Truman Jul 30 '13 at 18:25
  • Yes, there is. Each view is extremely complex, and duplicating one would take an extremely large amount of memory and time. A much more practical and efficient solution would be to simply preview it instead. OS X does it perfectly, so it must somehow be possible. – eswick Jul 30 '13 at 22:43
  • The Mission Control implementation may be using private undocumented APIs for OSX. The APIs may not be available on iOS. OSX also may be able to renderInContext at a speed that is much higher on a Mac, while iOS looks laggy. – Chris Truman Jul 30 '13 at 23:01
  • While this is true, as I said, I have full access to all private APIs used in iOS, which includes almost all APIs used on OS X. The functionality more than likely is available somewhere. iOS is much more powerful than it seems to most, and I believe this is possible. I just need a nudge in the right direction. While renderInContext is obviously faster on OS X, I doubt OS X uses renderInContext. There must be another solution. – eswick Jul 30 '13 at 23:11

1 Answers1

2

iOS and OSX are actually mostly the same underneath at the lowest level. (However, when you get higher up the stack iOS is actually largely more advanced than OSX as it is newer and had a fresh start)

However, in this case they both use the same thing (I believe). You'll notice something about Mission Control. It isolates "windows" rather than views. On iOS each UIWindow has a ".contentID" property and CALayerHost can use to make the render server share the render context between the 2 of them (2 layers that is).

So my advice is to make your views separate UIWindows and get native mirroring for free-(ish). (In my experience the CALayerHost takes over the target layers place with the render server and so if both the CALayerHost and the window are visible the window won't be anymore, only the layer host will be (which the way they are used on OSX and iOS isn't a problem).)

So if you are after true mirroring, 2 copies of it, you'll need to resort to the sort of thing you were thinking about.

1 Option for this is to create a UIView subclass that uses https://github.com/yyfrankyy/iOS5.1-Framework-Headers/blob/master/UIKit.framework/UIView-Rendering.h#L12 this UIView private method to get an IOSurface for a target view and then using a CADisplayLink once per second get and draw the surface.

Another option which may work (I'm not sure as I don't know your setup or desired effect) is possibly just to use a CAReplicatorLayer which displays a mirror of a CALayer using the same backing store (very fast and efficient + public stable API).

Sorry I couldn't give you a fixed, "this is the answer reply", but hopefully I've given you enough ideas and possibilities to get started. I've also included some links to things you might find useful to read.

What's the magic behind CAReplicatorLayer?

http://aptogo.co.uk/2011/08/no-fuss-reflections/

http://iphonedevwiki.net/index.php/SBAppContextHostManager

http://iphonedevwiki.net/index.php/SBAppContextHostView

http://iphonedevwiki.net/index.php/CALayerHost

http://iky1e.tumblr.com/post/33109276151/recreating-remote-views-ios5

http://iky1e.tumblr.com/post/14886675036/current-projects-understanding-ios-app-rendering

Community
  • 1
  • 1
Kyle Howells
  • 3,008
  • 1
  • 25
  • 35
  • Perfect, exactly what I was looking for, just a nudge in the right direction. I do have a question; When you say each window has a .contentID property, do you mean a ._contextId property? I assume so. This is what I tried earlier, with the CALayerHosts, however I found that the context ID of a UIWindow that is a subview of another UIWindow have the same context ID. I think I've found out why that happened, and I should be able to get something working. This is extremely valuable information! You have done truly amazing work. – eswick Jul 31 '13 at 15:52
  • UIWindows should NOT be subviews of other UIWindows. UIWindow's are the containers which hold UIView's they have no superview. If you are adding them to other windows then yes they will have the same contextID as only their parent actually has one and it'll just report that. – Kyle Howells Jul 31 '13 at 23:00