1

I have a UIView with a transparent background, and some buttons. I would like to capture the drawing of the view, shrink it, and redraw (mirror) it elsewhere on the screen. (On top of another view.) The buttons can change, so it isn't static.

What would be the best way to do this?

3 Answers3

4

Check a nice sample code http://saveme-dot-txt.blogspot.com/2011/06/dynamic-view-reflection-using.html following WWDC sessions. It uses

CAReplicatorLayer

for reflection, pretty easy to implement and looks really smooth and impressive.

A-Live
  • 8,904
  • 2
  • 39
  • 74
3

The general idea will be to get a UIView's layer to draw itself into a context and then grab a UIImage out of it.

UIGraphicsBeginImageContext(view.frame.size);

[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

You will also need to #import <QuartzCore/QuartzCore.h>

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
Paul.s
  • 38,494
  • 5
  • 70
  • 88
  • So then I would scale the UIImage and draw it where I want it? And would it preserve the transparency? Thanks. –  Dec 30 '11 at 04:13
  • Yes and Yes it should preserve the transparency. – Paul.s Dec 30 '11 at 04:15
  • Would it be possible to use a `CGContext` directly, without the overhead if a UIImage? –  Dec 30 '11 at 04:44
  • Do you have issues? or are you trying to prematurely optimize? – Paul.s Dec 30 '11 at 04:46
  • I'd probably go for the simplest thing that works, until it no longer works (in your case until you can prove it is slowing you down enough) then go fix it. – Paul.s Dec 30 '11 at 04:55
  • Okay, I think this would work, except for `renderInContext` doesn't capture everything. (Such as `UIButton`s.) See my new question: http://stackoverflow.com/questions/8860490/capturing-image-of-uiview-renderincontext-not-working-properly –  Jan 14 '12 at 06:03
  • Okay, so the problem actually isn't `renderInContext`, but instead is my implementation. I wasn't updating my second view. See my new question: http://stackoverflow.com/q/8865207/542687 –  Jan 14 '12 at 20:42
0

If you don't really need to capture the drawing (from what you describe, it seems unlikely that you need an image), create another instance of the view and apply a transform. Something like...

UIView* original [UIView alloc] initWithFrame:originalFrame];
UIView* copy = [[UIView alloc] initWithFrame:copyFrame];

// Scale down 50% and rotate 45 degrees
//
CGAffineTransform t = CGAffineTransformMakeScale(0.5, 0.5);
copy.transform = CGAffineTransformRotate(t, M_PI_4);

[someSuperView addSubview:original];
[someSuperView addSubview:copy];

// release, etc.

I added the rotation just to show that you can do a variety of different things with transformations.

nloko
  • 696
  • 4
  • 11
  • And then how do I draw the contents of the original `UIView` to the smaller one? –  Dec 30 '11 at 04:39
  • If it is an instance of the same class, wouldn't it draw the same once added to the superview? – nloko Dec 30 '11 at 04:45
  • But how are the two `UIView`s connected? I don't see why `copy` would draw the contents of `original` in your sample code. –  Dec 30 '11 at 04:53
  • Recreating a view hierarchy is surely more expensive then `... [getting an] image based on the contents of the current bitmap-based graphics context` – Paul.s Dec 30 '11 at 04:54
  • @Jay, the connection is that they are the same class ie. `UIView` or it could be some subclass of `UIView` (maybe `UIBallView`, or something) Therefore, since they are the same class, they share the same drawing logic and will draw the same. – nloko Dec 30 '11 at 04:58
  • @nloko But if I tapped a button in the `original` view, it would not result in that action being drawn in the `copy` view. This method would work if the `original` view was static, but it is not. –  Dec 30 '11 at 05:01
  • @Jay, yes, the views would need to be synchronized in some way whenever the `original` view (or any of its subviews) was redrawn. I think running with either of the approaches mentioned here could work for you. – nloko Dec 30 '11 at 05:33
  • @Paul.s: On what grounds? Copying and potentially scaling thousands of pixels is an expensive operation; well-optimized original drawing in both places may very well be cheaper. – Peter Hosey Dec 30 '11 at 06:06
  • Why use a transform rather than simply change the frame? – Peter Hosey Dec 30 '11 at 06:08
  • For this to work you would also need to have all your subviews auto-resizing masks set correctly. – Paul.s Dec 30 '11 at 06:10
  • @PeterHosey I guess it depends on how complicated the view hierarchy is. By the sounds of it there is a least one transparent container view and then several buttons which all need to be allocated, initialised and who knows what other code has been chucked in the mix, + surely the net result will be the same size backing store that the `UIImage` is created from? I'm no expert so all of that could well be all a lot cheaper than an image, would be interesting to know your thoughts. – Paul.s Dec 30 '11 at 06:17
  • @Paul.s: Creating the views only needs to happen once, whereas copying one view's image to the other would need to happen for every drawing pass. Moreover, the layer-backed nature of views in Cocoa Touch means that you wouldn't need to redraw all of the views most of the time. – Peter Hosey Dec 30 '11 at 06:54
  • @nloko In your example, Would I need to transform all of the buttons in the `copy` `UIView`, or would just transforming the view be good enough? –  Dec 30 '11 at 23:18