Aligning Background Within a CALayer - iphone

I'm trying to create a UIImage from an image context and set that as the contents of a CALayer (the image will end up split up and spread across multiple CALayers). The only problem is, it doesn't seem to be aligning correctly - it's offset by 30 pixels or so.
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
number = 1;
background = [UIImage imageNamed:#"numberBackground.png"];
// draw images
UIGraphicsBeginImageContext(CGSizeMake(background.size.width, background.size.height));
[background drawAtPoint:CGPointMake(0, 0)];
NSString *numberString = [[NSString alloc] initWithString:[NSString stringWithFormat:#"%d", number]];
[[UIColor whiteColor] set];
[numberString drawAtPoint:CGPointMake(0, 0) withFont:[UIFont fontWithName:#"HelveticaNeue" size:128]];
UIImage *front = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
// top layer
topLayer = [CALayer layer];
[topLayer setContents:(id)front.CGImage];
[topLayer setContentsGravity:#"kCAGravityTop"];
[topLayer setFrame:CGRectMake(0, 0, background.size.width, background.size.height / 2)];
[topLayer setAnchorPoint:CGPointMake(0, 1)];
[topLayer setPosition:CGPointMake(0, background.size.height / 2)];
[topLayer setMasksToBounds:NO];
[topLayer setBackgroundColor:[UIColor lightGrayColor].CGColor];
[self.layer addSublayer:topLayer];
}
return self;
}
I don't have enough reputation points to post an image yet, but here's how the layer renders, as well as how it should render and the full image: link text

Found my mistake - setContentsGravity should take the built-in string constant kCAGravityTop, not my NSString literal #"kCAGravityTop".

Related

Face detection is not working properly on resized images specially in Device, why?

Here is the code I am using to detect face from an Image:
- (void)detectFaces:(UIImageView *)photo
{
CIImage *coreImage = [CIImage imageWithCGImage:photo.image.CGImage];
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
context:nil
options:[NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh
forKey:CIDetectorAccuracy]];
NSArray* features = [detector featuresInImage:coreImage];
for(CIFaceFeature* faceFeature in features)
{
NSLog(#"self.view %#",NSStringFromCGRect(self.view.frame));
NSLog(#"self.view %#",NSStringFromCGRect(self.view.bounds));
NSLog(#"self.vounds %#",NSStringFromCGRect(faceFeature.bounds));
CGFloat faceWidth = faceFeature.bounds.size.width;
UIView* faceView = [[UIView alloc] initWithFrame:faceFeature.bounds];
faceView.layer.borderWidth = 1;
faceView.layer.borderColor = [[UIColor redColor] CGColor];
[self.view addSubview:faceView];
if(faceFeature.hasLeftEyePosition)
{
UIView* leftEyeView = [[UIView alloc] initWithFrame:CGRectMake(faceFeature.leftEyePosition.x-faceWidth*0.15, faceFeature.leftEyePosition.y-faceWidth*0.15, faceWidth*0.3, faceWidth*0.3)];
[leftEyeView setBackgroundColor:[[UIColor blueColor] colorWithAlphaComponent:0.3]];
[leftEyeView setCenter:faceFeature.leftEyePosition];
leftEyeView.layer.cornerRadius = faceWidth*0.15;
[self.view addSubview:leftEyeView];
}
if(faceFeature.hasRightEyePosition)
{
UIView* leftEye = [[UIView alloc] initWithFrame:CGRectMake(faceFeature.rightEyePosition.x-faceWidth*0.15, faceFeature.rightEyePosition.y-faceWidth*0.15, faceWidth*0.3, faceWidth*0.3)];
[leftEye setBackgroundColor:[[UIColor blueColor] colorWithAlphaComponent:0.3]];
[leftEye setCenter:faceFeature.rightEyePosition];
leftEye.layer.cornerRadius = faceWidth*0.15;
[self.view addSubview:leftEye];
}
if(faceFeature.hasMouthPosition)
{
UIView* mouth = [[UIView alloc] initWithFrame:CGRectMake(faceFeature.mouthPosition.x-faceWidth*0.2, faceFeature.mouthPosition.y-faceWidth*0.2, faceWidth*0.4, faceWidth*0.4)];
[mouth setBackgroundColor:[[UIColor greenColor] colorWithAlphaComponent:0.3]];
[mouth setCenter:faceFeature.mouthPosition];
mouth.layer.cornerRadius = faceWidth*0.2;
[self.view addSubview:mouth];
}
}
}
This is code that I have used to resize an image:
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
and finally I am calling detect face method like this:
UIImageView *inputImage = [[UIImageView alloc] initWithImage:[self imageWithImage:[UIImage imageNamed:#"facedetectionpic.jpg"] scaledToSize:CGSizeMake(320, 460)]];
[self.view addSubview:inputImage];
[inputImage setTransform:CGAffineTransformMakeScale(1, -1)];
[self.view setTransform:CGAffineTransformMakeScale(1, -1)];
[self performSelectorInBackground:#selector(detectFaces:) withObject:inputImage];
It is working properly in Simulator but not in device. Can anyone please help me on this.
Simulator:
Device:
When I have changed options in UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); to UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0); it started working even in device. Solved the issue.
Change options in UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); to
UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0); it will work.

Draw in image programmatically

I'm having trouble with my bingo application,I tried drawing a circle programmatically whenever a number is picked in my number generator.
I tried this code, that draws the circle in image then save it to the documentsDirectory. I also have a load implementation that I load it in the view when I call it.
//draw
-(void)draw
{
UIImage *image = [UIImage imageNamed:#"GeneralBingoResult.png"];
UIImage *imageWithCircle1 = [self imageByDrawingCircleOnImage1:image];
// save it to documents
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"Output.png"];
NSData *imageData = UIImagePNGRepresentation(imageWithCircle1);
[imageData writeToFile:filePath atomically:YES];
NSLog(#"Saved new image to %#", filePath);
UIImage *image1 = [self loadImage];
[imageToDisplay setImage:image1];
}
//draws the circle in the image
- (UIImage *)imageByDrawingCircleOnImage1:(UIImage *)image
{
// begin a graphics context of sufficient size
UIGraphicsBeginImageContext(image.size);
// draw original image into the context
[image drawAtPoint:CGPointZero];
// get the context for CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// set stroking color and draw circle
[[UIColor redColor] setStroke];
// make circle rect 5 px from border
CGRect circleRect = CGRectMake(420,40,
90,
90);
circleRect = CGRectInset(circleRect, 5, 5);
// draw circle
CGContextStrokeEllipseInRect(ctx, circleRect);
// make image out of bitmap context
UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
// free the context
UIGraphicsEndImageContext();
return retImage;
}
//loads from doc directory
- (UIImage*)loadImage
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithString: #"Output.png"] ];
UIImage* image = [UIImage imageWithContentsOfFile:path];
return image;
}
I successfully draw a circle in my image, but my problem is when I save the image with the circle in documentsDirectory I want to be able to load the saved image and draw with that image again. Or rather how am I going to implement it like a bingo app, like this:
Example:
First,Number 7 is picked in the number generator.
Output:
Next, number 55 is picked. It adds another circle to the number.
Output:
By the way, I'm using a UIScrollview. And I am implementing it in the ScrollViewDidEndScrolling. Thanks.
I also tried this code, but it only shows one circle every time the UIScrollView stops.
- (void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollview{
if ([[images objectAtIndex:index] intValue] == 1){
[circle setFrame:CGRectMake(420,40,90,90)];
[self.view addSubview:circle];
}else if([[images objectAtIndex:index] intValue] == 2){
[circle setFrame:CGRectMake(460,40,90,90)];
[self.view addSubview:circle];
}
}
Your easiest option is to create an image in photoshop or other similar program which is the required size and which has a transparent background. Save this as a .png file. Then, when you want to add the circles over the board, you just have to add a new UIImageView over the correct location in the grid:
UIImageView *circle;
circle = [[UIImageView alloc] initWithFrame:CGRectMake(xLocation, yLocation, myCircleWidth, myCircleHeight)];
circle.image = [UIImage imageNamed:#"myCircle.png"];
[self.view addSubview:circle];
An alternate option is to create a view that inherits from UIView. You can add methods to this class which allow you to set locations as circled. Then inside the drawRect code you just draw circles over all the locations which have been marked.
- (void)drawRect:(CGRect)rect {
// Circle your marked locations here
}
The final step in this case is to add your new view over your original view which contains the BINGO board.
EDIT:
Here is extended sample code for doing a view overlay. First, OverlayView.h:
#interface OverlayView : UIView {
}
- (void)clearPoints;
- (void)addPointX:(int)whichX Y:(int)whichY;
#end
Note that I use the C++ vector here, so this is in OverlayView.mm file:
#import "OverlayView.h"
#import <UIKit/UIKit.h>
#include <vector>
#implementation OverlayView
std::vector<int> points;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
[self setOpaque:NO];
[self setUserInteractionEnabled:false];
}
return self;
}
- (void)drawRect:(CGRect)rect {
if (points.size() == 0)
return;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 15.0);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0, 0.5);
for (int x = 0; x < points.size(); x++)
CGContextStrokeEllipseInRect(context, CGRectMake(points[x]%width-resolution/2,
points[x]/width-resolution/2,
resolution, resolution));
}
- (void)dealloc {
//printf("Deallocating OverlayView\n");
[super dealloc];
}
- (void)clearPoints {
points.resize(0);
[self setNeedsDisplay];
}
- (void)addPointX:(int)whichX Y:(int)whichY {
points.push_back(whichX+whichY*width);
[self setNeedsDisplay];
printf("Adding point (%d,%d)\n", whichX, whichY);
}
You just need to add this view inside the same view as your board. Call the addPoint function to add a circle centered at that point. You'll need to defined the resolution and width of your view for yourself.
Just paste it over top of your bingo board via UIImageView at the correct coordinates (The bingo boards are all the same size, I assume? Should be easy to calculate). Or am I missing what you are trying to do?
Just give this a try
UIImage *image1 = [UIImage imageNamed:#"image1.png"];
UIImage *image2 = [UIImage imageNamed:#"image2.png"];
CGSize newSize = CGSizeMake(width, height);
UIGraphicsBeginImageContext( newSize );
[image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
[image2 drawInRect:CGRectMake(0,0,newSize.width,newSize.height) blendMode:kCGBlendModeNormal alpha:0.8];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
If you want animations like bingo app than you can use Cocos2D framework for building your app.

Load entire image and use CATiledLayer

I'm not sure I did understand that very well in the Apple doc. I'm considering using CATiledLayer to display a JPEG image. However, I only have an entire JPEG file at my disposal and no small tiles. Is it still possible to use a CATiledLayer and let it "tile" the JPEG?
Thanks!
No. You will have to tile them yourself, unfortunately. The WWDC 2010 videos on Core Animation discuss how to do this and in their sample code, they demonstrate how to use a CATileLayer when the tiles already exist.
Correction
I meant to say watch the Scroll View session. It's session 104 "Designing Apps with Scroll Views"
You may use This for making CATileLayer with image and Zoom functionality in scrollview.
- (void)viewDidLoad
{
[super viewDidLoad];
// You do not need to and must not autorelease [NSBundle mainBundle]
NSString *path = [[NSBundle mainBundle] pathForResource:#"img" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
// CGRect pageRect = CGRectMake(0, 0, 1600, 2400);
CGRect pageRect = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self;
tiledLayer.tileSize = CGSizeMake(256.0, 256.0);
tiledLayer.levelsOfDetail = 5; // was 1000, which makes no sense. Each level of detail is a power of 2.
tiledLayer.levelsOfDetailBias = 5; // was 1000, which also makes no sense.
// Do not change the tiledLayer.frame.
tiledLayer.bounds = pageRect; // I think you meant bounds instead of frame.
// Flip the image vertically.
tiledLayer.transform = CATransform3DMakeScale(1.0f, -1.0F, 1.0f);
myContentView = [[UIView alloc] initWithFrame:CGRectMake(500,400,image.size.width,image.size.height)];
[myContentView.layer addSublayer:tiledLayer];
// UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(10, 10, 300, 400)];
scrollView.backgroundColor = [UIColor whiteColor];
// [scrollView setContentSize:CGSizeMake(image.size.width,image.size.height)];
[scrollView setContentSize:image.size];
scrollView.maximumZoomScale = 32; // = 2 ^ 5
scrollView.delegate = self;
// scrollView.contentSize = pageRect.size;
[scrollView addSubview:myContentView];
[self.view addSubview:scrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return myContentView;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"img" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
CGRect imageRect = CGRectMake (0.0, 0.0, image.size.width, image.size.height);
CGContextDrawImage (context, imageRect, [image CGImage]);
}
The easier way is to just downscale the image until it fits (I think devices support up to 2044x2044 or so). You can create subimages of a CGImage with CGImageCreateWithImageInRect()

iPhone: CATiledLayer/UIScrollView wont scroll after zooming and only zooms to anchor point

Here is the problem... I am using CA Tiled Layer to display a large jpg. The view loads okay, and when I go to scroll around, it works fine. However, as soon as I zoom in or out once, it scrolls to the top left (to the anchor point) and will not scroll at all. The zooming works fine, but I just cannot scroll.
Here is my code:
#import <QuartzCore/QuartzCore.h>
#import "PracticeViewController.h"
#implementation practiceViewController
//#synthesize image;
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"H-5" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
CGRect pageRect = CGRectMake(0, 0, image.size.width, image.size.height);
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.anchorPoint = CGPointMake(0.0f, 1.0f);
tiledLayer.delegate = self;
tiledLayer.tileSize = CGSizeMake(1000, 1000);
tiledLayer.levelsOfDetail = 6;
tiledLayer.levelsOfDetailBias = 0;
tiledLayer.bounds = pageRect;
tiledLayer.transform = CATransform3DMakeScale(1.0f, -1.0f, 0.3f);
myContentView = [[UIView alloc] initWithFrame:self.view.bounds];
[myContentView.layer addSublayer:tiledLayer];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.delegate = self;
scrollView.contentSize = pageRect.size;
scrollView.minimumZoomScale = .2;
scrollView.maximumZoomScale = 1;
[scrollView addSubview:myContentView];
[self.view addSubview:scrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return myContentView;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"H-5" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
CGRect imageRect = CGRectMake (0.0, 0.0, image.size.width, image.size.height);
CGContextDrawImage (ctx, imageRect, [image CGImage]);
}
#end
See the above listed question

iPhone SDK: Rendering a CGLayer into an image object

I am trying to add a curved border around an image downloaded and to be displayed in a UITableViewCell.
In the large view (ie one image on the screen) I have the following:
productImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:product.image]];
[productImageView setAlpha:0.4];
productImageView.frame = CGRectMake(10.0, 30.0, 128.0, 128.0);
CALayer *roundedlayer = [productImageView layer];
[roundedlayer setMasksToBounds:YES];
[roundedlayer setCornerRadius:7.0];
[roundedlayer setBorderWidth:2.0];
[roundedlayer setBorderColor:[[UIColor darkGrayColor] CGColor]];
[self addSubview:productImageView];
In the table view cell, to get it to scroll fast, an image needs to be drawn in the drawRect method of a UIView which is then added to a custom cell.
so in drawRect
- (void)drawRect:(CGRect)rect {
...
point = CGPointMake(boundsX + LEFT_COLUMN_OFFSET, UPPER_ROW_TOP);
//CALayer *roundedlayer = [productImageView layer];
//[roundedlayer setMasksToBounds:YES];
//[roundedlayer setCornerRadius:7.0];
//[roundedlayer setBorderWidth:2.0];
//[roundedlayer setBorderColor:[[UIColor darkGrayColor] CGColor]];
//[productImageView drawRect:CGRectMake(boundsX + LEFT_COLUMN_OFFSET, UPPER_ROW_TOP, IMAGE_WIDTH, IMAGE_HEIGHT)];
//
[productImageView.image drawInRect:CGRectMake(boundsX + LEFT_COLUMN_OFFSET, UPPER_ROW_TOP, IMAGE_WIDTH, IMAGE_HEIGHT)];
So this works well, but if I remove the comment and try to show the rounded CA layer the scrolling goes really slow.
To fix this I suppose I would have to render this image context into a different image object, and store this in an array, then set this image as something like:
productImageView.image = (UIImage*)[imageArray objectAtIndex:indexPath.row];
My question is "How do I render this layer into an image?"
TIA.
This is what I got to work well.
- (UIImage *)roundedImage:(UIImage*)originalImage
{
CGRect bounds = CGRectMake(0.0, 0.0, originalImage.size.width, originalImage.size.height);
UIImageView *imageView = [[UIImageView alloc] initWithImage:originalImage];
CALayer *layer = imageView.layer;
imageView.frame = bounds;
[layer setMasksToBounds:YES];
[layer setCornerRadius:7.0];
[layer setBorderWidth:2.0];
[layer setBorderColor:[[UIColor darkGrayColor] CGColor]];
UIGraphicsBeginImageContext(bounds.size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *anImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[imageView release];
return anImage;
}
Then to scale the image, I found this in the lazy loading example:
#define kAppIconHeight 48
CGSize itemSize = CGSizeMake(kAppIconHeight, kAppIconHeight);
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
self.appRecord.appIcon = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You are on the right track with your own comment.
- (UIImage *)roundedImage:(UIImage*)originalImage;
{
CGRect bounds = originalImage.bounds;
CGImageRef theImage = originalImage.CGImage;
CALayer *roundedlayer = [CALayer layer];
roundedlayer.position = CGPointMake(0.0f,0.0f);
roundedlayer.bounds = bounds;
[roundedlayer setMasksToBounds:YES];
[roundedlayer setCornerRadius:7.0];
[roundedlayer setBorderWidth:2.0];
[roundedlayer setBorderColor:[[UIColor darkGrayColor] CGColor]];
roundedlayer.contents = theImage;
UIGraphicsBeginImageContext(bounds.size); // creates a new context and pushes it on the stack
CGContextBeginTransparencyLayerWithRect(UIGraphicsGetCurrentContext(), bounds, NULL);
CGContextClearRect(UIGraphicsGetCurrentContext(), bounds);
[roundedlayer drawInContext:UIGraphicsGetCurrentContext()];
UIImage *anImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext(); //releases new context and removes from stack
return anImage;
}
I would pre-render these and store them in your image array so they are not calculated during your drawRect, but set in your
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
method.