Create a porthole animation transition on the iPhone? - iphone

Can anyone recommend how I might be able to create a view transition that looks like a circle expanding from the center of the view?
Any pointers would be helpful, or maybe a link to some similar code, even in a different language.

If you want to do that for images.. then you could use a mask and then make it bigger
Here is how you do it for images
CGRect bounds = CGRectMake(0.0f, 0.0f, 100, 100);
// Create a new path
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
// Add circle to path
CGPathAddEllipseInRect(path, NULL, bounds);
CGContextAddPath(context, path);
// Clip to the circle and draw the image
CGContextClip(context);
[image drawInRect:bounds]; //HOW WOULD YOU CLIP A VIEW INSTEAD OF AN IMAGE?
CFRelease(path);

Perhaps fill the screen with black, and then remove the black using a EllipseInRect that starts at the center point of the animation, and slowly increases in size and then deletes another segment, which loops until it's completed or hits an upper limit.
The smoothness of the animation would be controlled by how much the size of the Rect (which the Ellipse is then created inside of) increases every x amount of time.
The speed of the animation would be controlled by how often you increase the size of the rect.
Check out CoreGraphics if you haven't already. It's pretty powerful.

Related

How to create bubbles & arrows in iPhone help page about using buttons?

Most of the apps today provides the tutorial that teaches the user to how to use the buttons in the app. This help page is normally in black color with a little alpha value (so only the background will semi-visible) with a bubble box that contains the text to define controls and the arrow which points to the corresponding component.
Here is the sample image.. (From the app MyThings)
This is the normal page
And if you swipe from bottom to top, the help view will appear like..
Here is my doubts:
Which one is best to create the images & text in this help view? Drawing the images & texts using Core Graphics or by simply putting an UIImageView with a png image? Which one is efficient?
There is no problem in implementing a UIImageView with a ready-made png image. From my knowledge, the problem here is the image file size and the loading time. If we consider the drawing method, my mind things about the following problems.
Drawing a rectangle is so easy. (Refer here). But what about drawing a rectangle with a curved corners?? Is there any function available that handle this case?
Then the arrow, that points the corresponding component.. How can we find the exact point that should be pointed? (till iPhone 4S, there is no problem I hope, iPhone 5 has different height)
How to draw this pointers from the particular position in the rectangle?
Any ideas?
Just confused!!
Drawing the bubbles & arrows with CG is better, IMHO. It will work even if you change completely the app (if you draw them correctly pointing to the center of the button, for instance). With images, you'll need to have several copies for the different displays resolutions and scales. Also, you'll need to update the arrows if you change anything.
I don't see any performance drawback. Both methods will draw the bubbles very fast. Also, think that you can cache the CG generated images for future use.
See these questions to know how to draw bubbles:
How to draw a “speech bubble” on an iPhone?
How to make a Thought Bubble using core graphic in iPhone
It seems logical to use the center of the button each bubble point to. Your drawing methods needs to know where to point the arrow and the current orientation (if the app rotates). It should take into account other bubbles, to avoid overlap. You can divide the space in rows and columns and assign free space to each bubble.
For better user experience, those bubbles should not consume taps. If you tap a button when the bubbles are visible, the intended action should be performed (instead of just hiding the bubbles and require a second tap).
in your case i would refer resizable and reusable images.I also have many overlay screens in my app and i ended up wit generating 5 6 generic items arrows, label background and rectangular background image for the uilabel.
i know drawing rectangular is easy but it might be just overload sometimes.
if you want to change the direction of those arrows you can apply layer transformation to a UIImageview as following
arrowIamgeView.transform = CGAffineTransformMakeRotation(-M_PI / 20);
and for rounded corners of rectangles i assume you could user textfields with a certain background color and set their layers' cornerradius property.
Finally I did it!!
From my one-day experience, I come to know that there is three ways to handle this situation.
Simply creating needed images as png files and just use UIImageView to show. (But remember, you should have a same image with different resolutions. This will increase your app size).
Second way of doing is, Showing the bubbles (or maybe a rectangle) by creating labels, text fields, etc.. with a arrow image. You may simply transform the image to change the pointing place of the arrow. (As, Ilker Baltaci said in the previous answer).
Third way is, by Core Graphics. You can draw whatever you want here. As of my knowledge, increasing the app's size or increasing the memory consume by initializing/allocating/retaining labels & text fields, we can try this by Core Graphics.
I've three views where I want to implement this help screen facility. For just three screen, I can use any of the three method, because, there is no much difference related to performance if we use it for little-needed situaions. But I tried with the third way, because, I really don't knew anything about Core Graphics. So its just for learning..
Problems that I faced:
The arrow should point the center of the button(or maybe top of the button). So finding this exact location is complex. I have used this feature only in 3 pages in my app. Each page contains maximum of 4 icons to describe. So I just hard coded the values (My another luck is, the app does not support landscape mode). But, if you want to use this feature for so many pages, you should define some arrays and a method that finds the center of icons by calculating its center with reference to, their origin, height & width, blah, blah..
The another problem is, you should ensure that the bubbles are not overlapping anywhere. Here also, we need a global method that finds the location for each bubble. The size of the bubble depends on, the text size that is going to be placed inside it. This is more complex problem and for just 3 screens, I'm not going to define a global method with hundreds of calculations. So I again just hard coded the origin point, height & width of each bubble with reference to the self.view
Last but not least.. The Arrow!! The height of arrow may vary with the bubble's place. The width of the bubble also may vary with height. Also, you should know the side & points of the bubble from which the arrow goes to the button. If you already fixed the places of the bubbles in your mind, these are not at all a problem for you.. :P
Now let's come the coding part,
- (void) createRect:(CGRect)rect xPoint:(float)x yPoint:(float)y ofHeight:(float)height ofWidth:(float)width toPointX:(float)toX toPointY:(float)toY withString:(NSString *)helpString inDirection:(NSString *)direction
{
float distance = 5.0;
float widthOfLine = 5.0;
float arrowLength = 15.0;
//Get current context
CGContextRef context = UIGraphicsGetCurrentContext();
//Set width of border & colors of bubble
CGContextSetLineWidth(context, widthOfLine);
CGContextSetStrokeColorWithColor(context, [[UIColor darkGrayColor] CGColor]);
CGContextSetFillColorWithColor(context, [[UIColor colorWithRed:0 green:0 blue:0 alpha:0.9] CGColor]);
CGContextBeginPath(context);
CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x+width, y);
CGContextAddQuadCurveToPoint(context, x+width, y, x+width+distance, y+distance);
CGContextAddLineToPoint(context, x+width+distance, y+distance+height);
CGContextAddQuadCurveToPoint(context, x+width+distance, y+distance+height, x+width, y+distance+height+distance);
CGContextAddLineToPoint(context, x, y+distance+height+distance);
CGContextAddQuadCurveToPoint(context, x, y+distance+height+distance, x-distance, y+distance+height);
CGContextAddLineToPoint(context, x-distance, y+distance);
CGContextAddQuadCurveToPoint(context, x-distance, y+distance, x, y);
CGContextDrawPath(context, kCGPathFillStroke);
//Draw curvely arrow from bubble to button (but without arrow mark)
CGContextBeginPath(context);
CGContextSetLineWidth(context, 5.0);
CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);
CGPoint startPoint = CGPointMake(x+(width/2.0), y+distance+distance+height);
CGPoint endPoint = CGPointMake(toX, toY);
CGContextMoveToPoint(context, startPoint.x, startPoint.y+5.0);
if ([direction isEqualToString:#"left"])
{
CGContextAddCurveToPoint(context, startPoint.x, startPoint.y+5.0, endPoint.x, endPoint.y, toX-10, toY);
}
else
{
CGContextAddCurveToPoint(context, startPoint.x, startPoint.y+5.0, endPoint.x, endPoint.y, toX+10, toY);
}
CGContextStrokePath(context);
//Draw the arrow mark
CGContextBeginPath(context);
CGContextSetLineWidth(context, 5.0);
CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);
if ([direction isEqualToString:#"left"])
{
CGContextMoveToPoint(context, toX-10.0, toY-arrowLength);
CGContextAddLineToPoint(context, toX-10.0, toY);
CGContextAddLineToPoint(context, toX-10.0+arrowLength, toY);
}
else
{
CGContextMoveToPoint(context, toX+10.0, toY-arrowLength);
CGContextAddLineToPoint(context, toX+10.0, toY);
CGContextAddLineToPoint(context, toX+10.0-arrowLength, toY);
}
CGContextStrokePath(context);
.......
.......
}
You can call this method from the drawRect: method like this..
[self createRect:rect xPoint:30.0 yPoint:250.0 ofHeight:100.0 ofWidth:100.0 toPointX:48.0 toPointY:430.0 withString:#"xxxxxxx" inDirection:#"left"];
[self createRect:rect xPoint:160.0 yPoint:100.0 ofHeight:100.0 ofWidth:100.0 toPointX:260.0 toPointY:420.0 withString:#"yyyyyyy" inDirection:#"right"];
And the final image will be like this..
I skipped triangle shape arrow, because this arrow type gives a handwriting effect.
Thanks to #djromero and Ilker Baltaci for your valuable answers.
My next confusion is about Drawing Texts!!!!! :P

Remove shape from UIView to create transparency

Here's a sample view I have right now:
Ideally, I'd like to take a "chunk" out of the top, so any views underneath are now visible through that removed area (e.g. transparency).
I've tried creating a path, and then using CGContextClip to attempt a clip, but it doesn't seem to be clipping the shape like intended. Any ideas of how to do this, or even if it's possible at all?
Typically to do something like this, one would make a UIView with a transparent background color, and then draw the "background" manually through CoreGraphics. For instance, to make a view that is essentially a circle with a black background, you could do something like this in the drawRect method:
- (void)drawRect:(CGRect)dirtyRect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 0, 0, 0, 1);
CGContextFillElipseInRect(context, self.bounds);
}
For something more complicated than a simple circle, you should use CGContextBeginPath() in conjunction with the CGContextMoveToPoint() and CGContextAddLineToPoint() functions. This will allow you to make a transparent view with any opaque shape that you want for a background.
EDIT: To clip a background image to a certain path, you could do something like this:
CGContextSaveGState(context);
CGImageRef image = [[UIImage imageNamed:#"backgroundImage.png"] CGImage];
CGContextBeginPath(context);
// add your shape to the path
CGContextClipToPath(context);
CGContextDrawImage(context, self.bounds, image);
CGContextRestoreGState(context);
Obviously for repeating patterns you could use more than one call to CGContextDrawImage, but this is basically what needs to be done. Like I said above, you could use basic line drawing functions to add lines, rectangles, circles, and anything else to your path.
Use a [UIColor clearColor] in you V to make it transparent, you will probably have to set the view opaque to NO as well.

How to reset the context to the original rectangle after clipping it for drawing?

I try to draw a sequence of pattern images (different repeated patterns in one view).
So what I did is this, in a loop:
CGContextRef context = UIGraphicsGetCurrentContext();
// clip to the drawing rectangle to draw the pattern for this portion of the view
CGContextClipToRect(context, drawingRect);
// the first call here works fine... but for the next nothing will be drawn
CGContextDrawTiledImage(context, CGRectMake(0, 0, 2, 31), [img CGImage]);
I think that after I've clipped the context to draw the pattern in the specific rectangle, I cut out a snippet from the big canvas and the next time, my canvas is gone. can't cut out another snippet. So I must reset that clipping somehow in order to be able to draw another pattern again somewhere else?
Edit: In the documentation I found this:
CGContextClip: "... Therefore, to
re-enlarge the paintable area by
restoring the clipping path to a prior
state, you must save the graphics
state before you clip and restore the
graphics state after you’ve completed
any clipped drawing. ..."
Well then, how to store the graphics state before clipping and how to restore it?
The functions you are looking for are:
CGContextSaveGState(context);
and
CGContextRestoreGState(context);

iPhone: How to use CGContextConcatCTM for saving a transformed image properly?

I am making an iPhone application that loads an image from the camera, and then the user can select a second image from the library, move/scale/rotate that second image, and then save the result. I use two UIImageViews in IB as placeholders, and then apply transformations while touching/pinching.
The problem comes when I have to save both images together. I use a rect of the size of the first image and pass it to UIGraphicsBeginImageContext. Then I tried to use CGContextConcatCTM but I can't understand how it works:
CGRect rect = CGRectMake(0, 0, img1.size.width, img1.size.height); // img1 from camera
UIGraphicsBeginImageContext(rect.size); // Start drawing
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextClearRect(ctx, rect); // Clear whole thing
[img1 drawAtPoint:CGPointZero]; // Draw background image at 0,0
CGContextConcatCTM(ctx, img2.transform); // Apply the transformations of the 2nd image
But what do I need to do next? What information is being held in the img2.transform matrix? The documentation for CGContextConcatCTM doesn't help me that much unfortunately..
Right now I'm trying to solve it by calculating the points and the angle using trigonometry (with the help of this answer), but since the transformation is there, there has to be an easier and more elgant way to do this, right?
Take a look at this excellent answer, you need to create a bitmapped/image context, draw to it, and get the resultant image out. You can then save that. iOS UIImagePickerController result image orientation after upload

Quartz2D and Clipping - Optimize this drawing routine?

I'm new to Quartz2d so I hope this is an easy question to answer.
I have a scrollview whose content frame is 1024x768. Within this I push a custom view (with frame 1024x768) that uses Quartz2d to do some drawing. The idea is, I have a large area that the user can scroll around to see different parts.
The problem I'm having is, when I scroll on the device I get memory warning errors or terrible scrolling performance. This is no doubt because I am re-drawing the entire 1024x768 field. I thought that setting the clipping path to the UIScrollView offset would help.. and it does.. until i scroll.. in which case the view does not redraw until scrolling stops. telling the view to draw during scrollViewDidScroll also results in bad performance
can anyone tell me how i can optimize my drawing routine?
i have two images.. one of which is getting masked, and both being painted to the screen (one on top of the other)
- (void)drawRect:(CGRect)dirtyRect {
CGContextRef myContext = UIGraphicsGetCurrentContext();
// get our offset (where we've scrolled to)
CGPoint offset = scrollView.contentOffset;
// clip to our offset.. uncomment for good performance but terrible scrolling
//CGContextClipToRect(myContext, CGRectMake(offset.x, offset.y, 480, 320));
// make an image from the mask context
CGImageRef aMaskImage = CGBitmapContextCreateImage (myMaskContext);
// mask the first image
CGImageRef aImage = CGImageCreateWithMask(myImageRef1, aMaskImage);
CGContextDrawImage(myContext, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), myImageRef1);
CGContextDrawImage(myContext, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), aImage);
CGImageRelease(aImage);
CGImageRelease(aMaskImage);
}
thanks in advance for any help you can provide!
Generally to optimize scrolling big stuff you'd look into CATiledLayer.
I assume somewhere you call setNeedsDisplay. You need to call setNeedsDisplayInRect. Make sure to only pass the rect that changed.