views:

499

answers:

2

I am attempting to create a "marching ants" (an animated dashed line) selection indicator around UIView that contains a UIImageView subview. Drawing the dashed line around the perimeter of the UIView is trivial using core graphics:

-(void) drawSelectedMarquee{
     CGContextRef c=UIGraphicsGetCurrentContext();
 CGContextSetStrokeColorWithColor(c, [[UIColor whiteColor] CGColor] );
 CGRect f=self.bounds;
 CGContextSetLineWidth(c, 2.0);
 CGFloat dashes[]={1,2};
 CGContextSetLineDash(c, 0, dashes, 2);
 CGContextBeginPath(c);
 CGContextAddRect(c, f);
 CGContextStrokePath(c);
}

However, I need to scale (and rotate) the view using affine transforms so that the UIImageView inside can be scaled and rotated:

-(void) scaleByX:(CGFloat) xScale andY:(CGFloat) yScale{
 [UIView beginAnimations:nil context:nil];
 [UIView setAnimationDuration:0.1];
 [UIView setAnimationBeginsFromCurrentState:YES];
 self.transform=CGAffineTransformScale(self.transform, xScale, yScale);
 [UIView commitAnimations];
}

However, because core graphics draws in relative "user space points" which apparently are scaled by the transform as well, scaling the view causes the dashed line to scale in proportion. So, as the view scale smaller, the line grows thinner and the dashes closer together and has the view scales larger, the line grows thicker and the dashes father apart.

As an interface element, the selection marquee should remain of fixed width with fixed sized dashes. I've tried just rotating and scaling the UIImageView subview of the main view but that causes the UIImageView to scale out of the superview.

I can think of some brute force methods to properly draw the marquee. I've could track the absolute size of the view and then recalculate the end user space units every time it redraws or I create a new view with selection marquee every time the UIImageView. But those solutions are inelegant and bug prone.

I think I've missed something in documentation about how to draw and transform graphics in UIViews.

[Update: I've decided that using marching ants isn't useful on the iPhone's small screen so I've adopted another method. However I'm still interested in the solution to this particular problem.]

A: 

I think you can inverse the transform to do what you want. If i'm wrong on that you should be able to construct the opposite transform if you know your scale and translation values.

Another way would be to draw onto layers instead of drawing directly into the view and only scale the layer.

In many apps (like adobe illustrator) the marquee rectangle is actually a transparent window infront of main window.

+2  A: 

I don't think you've missed anything in the documentation, but rather you may have missed the ideas of what scaling provides. If you scale the contents of a view or layer, it will resize everything in the contents. This is actually desirable. It makes it easy to scale without having to manage all of the subview scaling yourself. What it sounds like you need is to add an overlay layer in front of your view.

I'm not sure how you are causing your view to resize, but when your view loads, add an overlay Core Animation layer that you can resize as your view scales. Notice I used the word resize rather than scale when referring to the overlay layer.

To make this work correctly you have to make sure that the overlay layer is a sibling to the view you are scaling rather than a child otherwise it will get scaled as well.

Interestingly, you can use a CAShapeLayer and take advantage of its lineDashPattern property which takes an array of floats to specify a dash pattern that can outline the CGRect you specify. This pattern can then be animated using the lineDashPhase property and a CABasicAnimation giving it the marching ants effect. I can elaborate on this if it interests you.

UPDATE: I went ahead and wrote a blog post on getting the marching ants effect working using Core Animation. Sorry, I didn't have time to try to address your problem, but the blog post might help point you in the right direction.

Matt Long
I'll look into the Core Animation solution. The problem I hit with using overlapping views or layers is that the frame of the view containing the imageview has to grow and shrink when the image scales. This is also true of any independent views used as overlays. Syncing the frames with scale of the image produces noticeable artifacts. Hopefully, the CA layer will not have that problem.
TechZen
I'm going to go ahead an accept this as the answer because I don't think it's possible to do exactly what I originally wanted to. This answer provides gobs of very useful information in the same problem space.
TechZen