I'm in need of an UIImage from a Masked CALayer. This is the function I use:
- (UIImage *)imageFromLayer:(CALayer *)layer
{
UIGraphicsBeginImageContext([layer frame].size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
The problem is that the mask isn't maintained.
This is the completed code:
CAShapeLayer * layerRight= [CAShapeLayer layer];
layerRight.path = elasticoRight;
im2.layer.mask = layerRight;
CAShapeLayer * layerLeft= [CAShapeLayer layer];
layerLeft.path = elasticoLeft;
im3.layer.mask = layerLeft;
[viewImage.layer addSublayer:im2.layer];
[viewImage.layer addSublayer:im3.layer];
UIImage *image_result = [self imageFromLayer:viewImage.layer];
If I visualize the viewImage, the result is correct, but if I try to obtain the image relative to the layer, the masks are lost.
I've solved.
Now i obtaining the image mask and use CGContextClipToMask.
CGRect rect = CGRectMake(0, 0, 1024, 768);
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0);
{
[[UIColor blackColor] setFill];
UIRectFill(rect);
[[UIColor whiteColor] setFill];
UIBezierPath *leftPath = [UIBezierPath bezierPath];
// Set the starting point of the shape.
CGPoint p1 = [(NSValue *)[leftPoints objectAtIndex:0] CGPointValue];
[leftPath moveToPoint:CGPointMake(p1.x, p1.y)];
for (uint i=1; i<leftPoints.count; i++)
{
CGPoint p = [(NSValue *)[leftPoints objectAtIndex:i] CGPointValue];
[leftPath addLineToPoint:CGPointMake(p.x, p.y)];
}
[leftPath closePath];
[leftPath fill];
}
UIImage *mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
{
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
[im_senza drawAtPoint:CGPointZero];
}
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Related
I am using the ios default camera in my application. I would like to change something the edit view that shows after the user takes a photo.Normally, it shows a rectangle to crop, but I would like it to show a circle how would I do this.
Here is the solution which might help you to create crop overlay:-
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([navigationController.viewControllers count] == 3)
{
CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
UIView *plCropOverlay = [[[viewController.view.subviews objectAtIndex:1]subviews] objectAtIndex:0];
plCropOverlay.hidden = YES;
int position = 0;
if (screenHeight == 568)
{
position = 124;
}
else
{
position = 80;
}
CAShapeLayer *circleLayer = [CAShapeLayer layer];
UIBezierPath *path2 = [UIBezierPath bezierPathWithOvalInRect:
CGRectMake(0.0f, position, 320.0f, 320.0f)];
[path2 setUsesEvenOddFillRule:YES];
[circleLayer setPath:[path2 CGPath]];
[circleLayer setFillColor:[[UIColor clearColor] CGColor]];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 320, screenHeight-72) cornerRadius:0];
[path appendPath:path2];
[path setUsesEvenOddFillRule:YES];
CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = path.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor blackColor].CGColor;
fillLayer.opacity = 0.8;
[viewController.view.layer addSublayer:fillLayer];
UILabel *moveLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, 320, 50)];
[moveLabel setText:#"Move and Scale"];
[moveLabel setTextAlignment:NSTextAlignmentCenter];
[moveLabel setTextColor:[UIColor whiteColor]];
[viewController.view addSubview:moveLabel];
}
}
In one of my application I need to split UIImage into multiple parts. The following was the code I am using to split. Here my problem is I am unable to load the image view by adding the image to UIImageView.
- (void)viewDidLoad
{
UIImage* image = [UIImage imageNamed:#"monalisa.png"];
NSMutableArray* splitImages = [self splitImageIntoRects:(__bridge CGImageRef)(image)];
printf("\n count; %d",[splitImages count]);
CALayer *layer = [splitImages objectAtIndex:5];
CGImageRef imgRef = (__bridge CGImageRef)(layer.contents);
UIImage *img = [[UIImage alloc] initWithCGImage:imgRef];
UIImageView* imageview = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
imageview.image = img;
imageview.backgroundColor = [UIColor redColor];
[self.view addSubview:imageview];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (NSMutableArray*)splitImageIntoRects:(CGImageRef)anImage
{
CGSize imageSize = CGSizeMake(CGImageGetWidth(anImage), CGImageGetHeight(anImage));
NSMutableArray *splitLayers = [NSMutableArray array];
int kXSlices = 3;
int kYSlices = 3;
for(int x = 0;x < kXSlices;x++) {
for(int y = 0;y < kYSlices;y++) {
CGRect frame = CGRectMake((imageSize.width / kXSlices) * x,
(imageSize.height / kYSlices) * y,
(imageSize.width / kXSlices),
(imageSize.height / kYSlices));
CALayer *layer = [CALayer layer];
layer.frame = frame;
CGImageRef subimage = CGImageCreateWithImageInRect(anImage, frame);
layer.contents = (__bridge id)subimage;
[splitLayers addObject:layer];
}
}
return splitLayers;
}
And the output is like follows,
Try This:
- (void)viewDidLoad
{
[super viewDidLoad];
[self getSplitImagesFromImage:[UIImage imageNamed:#"Image1.png"] withRow:4 withColumn:4];
}
-(NSMutableArray *)getSplitImagesFromImage:(UIImage *)image withRow:(NSInteger)rows withColumn:(NSInteger)columns
{
NSMutableArray *aMutArrImages = [NSMutableArray array];
CGSize imageSize = image.size;
CGFloat xPos = 0.0, yPos = 0.0;
CGFloat width = imageSize.width/rows;
CGFloat height = imageSize.height/columns;
for (int aIntY = 0; aIntY < columns; aIntY++)
{
xPos = 0.0;
for (int aIntX = 0; aIntX < rows; aIntX++)
{
CGRect rect = CGRectMake(xPos, yPos, width, height);
CGImageRef cImage = CGImageCreateWithImageInRect([image CGImage], rect);
UIImage *aImgRef = [[UIImage alloc] initWithCGImage:cImage];
UIImageView *aImgView = [[UIImageView alloc] initWithFrame:CGRectMake(aIntX*width, aIntY*height, width, height)];
[aImgView setImage:aImgRef];
[aImgView.layer setBorderColor:[[UIColor blackColor] CGColor]];
[aImgView.layer setBorderWidth:1.0];
[self.view addSubview:aImgView];
[aMutArrImages addObject:aImgRef];
xPos += width;
}
yPos += height;
}
return aMutArrImages;
}
for more info see this and you can also download demo from here.
We can enhance more the Yasika Patel Answer. Below function will give you exact peice of image which fits to your view.
- (void)splitImage :(UIImage *)image withColums:(int)columns WithRows: (int)rows ViewToIntegrate : (UIView *)view
{
CGSize imageSize = _imgSplit.image.size;
CGFloat xPos = 0.0, yPos = 0.0;
CGFloat width = imageSize.width/rows;
CGFloat height = imageSize.height/columns;
for (int aIntY = 0; aIntY < columns; aIntY++)
{
xPos = 0.0;
for (int aIntX = 0; aIntX < rows; aIntX++)
{
CGRect rect = CGRectMake(xPos, yPos, width, height);
CGImageRef cImage = CGImageCreateWithImageInRect([ image CGImage], rect);
UIImage *aImgRef = [[UIImage alloc] initWithCGImage:cImage];
UIImageView *aImgView = [[UIImageView alloc] initWithFrame:CGRectMake(aIntX*(view.frame.size.width/rows), aIntY*( view.frame.size.height/columns), view.frame.size.width/rows, view.frame.size.height/columns)];
[aImgView setImage:aImgRef];
[aImgView.layer setBorderColor:[[UIColor blackColor] CGColor]];
[aImgView.layer setBorderWidth:0.5];
[view addSubview:aImgView];
xPos += width;
}
yPos += height;
}
[self.view addSubview:view];
}
This will give you the image in 9parts . here you just need to pass the row and colums.
I'm trying to take an NSGradient and save it as an image in RubyMotion, but I can't get it to work. This is the code I have so far:
gradient = NSGradient.alloc.initWithColors(colors,
atLocations: locations.to_pointer(:double),
colorSpace: NSColorSpace.genericRGBColorSpace
)
size = Size(width, height)
image = NSImage.imageWithSize(size, flipped: false, drawingHandler: lambda do |rect|
gradient.drawInRect(rect, angle: angle)
true
end)
data = image.TIFFRepresentation
data.writeToFile('output.tif', atomically: false)
It runs without error, but the file that is saved is blank and there is no image data. Can anyone help point me in the right direction?
I don’t know about RubyMotion, but here’s how to do it in Objective-C:
NSGradient *grad = [[NSGradient alloc] initWithStartingColor:[NSColor redColor]
endingColor:[NSColor blueColor]];
NSRect rect = CGRectMake(0.0, 0.0, 50.0, 50.0);
NSImage *image = [[NSImage alloc] initWithSize:rect.size];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:rect];
[image lockFocus];
[grad drawInBezierPath:path angle:0.0];
NSBitmapImageRep *imgRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect];
NSData *data = [imgRep representationUsingType:NSPNGFileType properties:nil];
[image unlockFocus];
[data writeToFile: #"/path/to/file.png" atomically:NO];
In case you want to know how it works in Swift 5:
extension NSImage {
convenience init?(gradientColors: [NSColor], imageSize: NSSize) {
guard let gradient = NSGradient(colors: gradientColors) else { return nil }
let rect = NSRect(origin: CGPoint.zero, size: imageSize)
self.init(size: rect.size)
let path = NSBezierPath(rect: rect)
self.lockFocus()
gradient.draw(in: path, angle: 0.0)
self.unlockFocus()
}
}
I'm drawing a UIBezierPath on a UIScrollView I have made an animation that draws the path from start to end point but this is not the animation that I want.
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:startPoints];
[linePath addLineToPoint:endPoints;
//shape layer for the line
CAShapeLayer *line = [CAShapeLayer layer];
line.path = [linePath CGPath];
// line.fillColor = [[UIColor blackColor] CGColor];
line.strokeColor = [[colors objectAtIndex:i] CGColor];
line.lineWidth = 5;
// line.contents = (id)[[UIImage imageNamed:#"Mask.png"] CGImage];
// line.contentsGravity = kCAGravityCenter;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
pathAnimation.repeatCount = HUGE_VAL;
[line addAnimation:pathAnimation forKey:#"strokeEnd"];
I have tried adding a contents to the shape layer but I'm bad at animations. The effect I want to achieve is the same animation as "slide to unlock" has, or a path that pulses.
I've tried to do the same thing as the answer from slide-to-unlock but can't seem to manage
I ended up dooing this:
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:startPoints];
[linePath addLineToPoint:endPoints];
//gradient layer for the line
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(0, 0, 150.0, 1.0);
gradient.cornerRadius = 5.0f;
gradient.startPoint = CGPointMake(0.0, 0.5);
gradient.endPoint = CGPointMake(1.0, 0.5);
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor],(id)[[UIColor whiteColor] CGColor],(id)[[UIColor blueColor] CGColor],(id)[[UIColor clearColor] CGColor], nil];
[scrollViewContent.layer addSublayer:gradient];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = [linePath CGPath];
anim.rotationMode = kCAAnimationRotateAuto;
anim.repeatCount = 0;
anim.duration = 1;
[gradient addAnimation:anim forKey:#"gradient"];
I have a simple CALayer subclass (BoxLayer) with this drawLayer method:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
NSLog(#"drawLayer");
NSGraphicsContext *nsGraphicsContext;
nsGraphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx
flipped:NO];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:nsGraphicsContext];
// ...Draw content using NS APIs...
NSPoint origin = { 21,21 };
NSRect rect;
rect.origin = origin;
rect.size.width = 128;
rect.size.height = 128;
NSBezierPath * path;
path = [NSBezierPath bezierPathWithRect:rect];
[path setLineWidth:4];
[[NSColor whiteColor] set];
[path fill];
[[NSColor grayColor] set];
[path stroke];
[NSGraphicsContext restoreGraphicsState];
}
I then have this awakeFromNib in my NSView subclass:
- (void)awakeFromNib {
CALayer* rootLayer = [CALayer layer];
[self setLayer:rootLayer];
[self setWantsLayer:YES];
box1 = [CALayer layer];
box1.bounds = CGRectMake(0, 0, 70, 30);
box1.position = CGPointMake(80, 80);
box1.cornerRadius = 10;
box1.borderColor = CGColorCreateGenericRGB(255, 0, 0, 1);
box1.borderWidth = 1.5;
[rootLayer addSublayer:box1];
box2 = [BoxLayer layer];
[box2 setDelegate:box2];
[box2 setNeedsDisplay];
[rootLayer addSublayer:box2];
}
My drawLayer is never called though, but why not?
Thank you
Possibly because the frame of your layer seems to be equal to CGRectZero, so the OS might think it's invisible so it doesn't need to draw it.
On a side note, why are going the complicated way of setting the layer's delegate to itself and implementing drawLayer:inContext: instead of using drawInContext: directly?