Does the way CALayer generate its contents Affect its scale animation? - ios

#interface CircleRevealLayer : CALayer
#end
#implementation CircleRevealLayer
- (instancetype)init
{
if (self = [super init]) {
[self setNeedsDisplay];
[self displayIfNeeded];
}
return self;
}
- (void)display
{
self.contents = (__bridge id)([self maskImage].CGImage);
self.contentsGravity = kCAGravityCenter;
}
- (UIImage *)maskImage
{
static UIImage *mask = nil;
if (!mask) {
UIGraphicsBeginImageContext(CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT));
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(209.5, 368)];
[bezierPath addCurveToPoint: CGPointMake(207, 370.5) controlPoint1: CGPointMake(208.12, 368) controlPoint2: CGPointMake(207, 369.12)];
[bezierPath addCurveToPoint: CGPointMake(209.5, 373) controlPoint1: CGPointMake(207, 371.88) controlPoint2: CGPointMake(208.12, 373)];
[bezierPath addCurveToPoint: CGPointMake(212, 370.5) controlPoint1: CGPointMake(210.88, 373) controlPoint2: CGPointMake(212, 371.88)];
[bezierPath addCurveToPoint: CGPointMake(210.98, 368.49) controlPoint1: CGPointMake(212, 369.67) controlPoint2: CGPointMake(211.6, 368.94)];
[bezierPath addCurveToPoint: CGPointMake(209.5, 368) controlPoint1: CGPointMake(210.57, 368.18) controlPoint2: CGPointMake(210.05, 368)];
[bezierPath closePath];
[bezierPath moveToPoint: CGPointMake(411, 0)];
[bezierPath addCurveToPoint: CGPointMake(411, 736) controlPoint1: CGPointMake(411, 0) controlPoint2: CGPointMake(411, 736)];
[bezierPath addLineToPoint: CGPointMake(-3, 736)];
[bezierPath addLineToPoint: CGPointMake(-3, 0)];
[bezierPath addLineToPoint: CGPointMake(411, 0)];
[bezierPath addLineToPoint: CGPointMake(411, 0)];
[bezierPath closePath];
[UIColor.grayColor setFill];
[bezierPath fill];
mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//mask = [UIImage imageNamed:#"mask.PNG"];
}
return mask;
}
#end
#interface CircleRevealView : UIView
#end
#implementation CircleRevealView
+ (Class)layerClass{
return [CircleRevealLayer class];
}
#end
then, I create it like this:
_maskView = [[CircleRevealView alloc] initWithFrame:self.view.bounds];
_maskView.frame = self.view.bounds;
and add a scale animation:
POPSpringAnimation *springAnimation = [POPSpringAnimation animation];
springAnimation.property = [POPAnimatableProperty propertyWithName:kPOPViewScaleXY];
springAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake(80, 80)];
springAnimation.velocity=[NSValue valueWithCGPoint:CGPointMake(1, 1)]; // change of value units per second
springAnimation.springBounciness=20; // value between 0-20 default at 4
springAnimation.springSpeed=3;
springAnimation.name=#"scaleXY1";
springAnimation.delegate=self;
[_maskView pop_addAnimation:springAnimation forKey:#"scaleXY1"];
the mask.PNG is just a white image with a hole in the center.
when I use UIBezierPath to draw the contents and assign it to the layer, the scale animation seems to happen from the top-left corner of the layer;
but if I instead use a image , the scale animation seems to happen from the center of it.
what I want is to generate dynamic contents with Core Graphics and scale the layer like when I use mask.PNG
update:
UIBezierPath
mask.PNG
the mask.PNG is generated from the UIBezierPath code, with the PaintCode.

Related

How to create a multi colour progress view with different values for each colour

I've tried different ways and other resources on how to create a progress bar with 3 different colours and show progress according the values provided from the server but i can't find anything. How can i create a progress bar like this one below. I want to show the progress according to the values i can get from the backend which can change for different users. Here is the example json. Green -> "Won", Red -> "Lost" and darkGray -> "undecided"
"condition": {
"won": 2,
"lost": 3,
"undecided": 0
}
- (void)setCurrentProgressWithIndex1:(float)index1 index2:(float)index2 index3:(float)index3 {
_index1 = index1;
_index2 = index2;
_index3 = index3;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self drawProgressWithContext:ctx];
}
- (void)drawProgressWithContext:(CGContextRef)ctx {
float width = self.frame.size.width;
// index
float start_index = 0.0f;
if (_index1 != 0.0f) {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[bezierPath moveToPoint: CGPointMake(start_index, 0)];
[bezierPath addLineToPoint: CGPointMake(start_index, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index1, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index1, 0)];
[UIColor.redColor setFill];
[bezierPath fill];
start_index += width * _index1;
}
if (_index2 != 0.0f) {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[bezierPath moveToPoint: CGPointMake(start_index, 0)];
[bezierPath addLineToPoint: CGPointMake(start_index, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index2, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index2, 0)];
[UIColor.greenColor setFill];
[bezierPath fill];
start_index += width * _index2;
}
if (_index3 != 0.0f) {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[bezierPath moveToPoint: CGPointMake(start_index, 0)];
[bezierPath addLineToPoint: CGPointMake(start_index, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index3, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index3, 0)];
[UIColor.blueColor setFillx];
[bezierPath fill];
}
}
you can use this framework to get the output
https://github.com/minjoongkim/MJProgressView
another related framework
https://github.com/mac-gallagher/MultiProgressView
https://github.com/fxm90/GradientProgressBar

Objective C - Create UIImage from this PaintCode code

I am using PaintCode and it has generated some code to create a shape with text overlayed.
I want to use the code to parse a string and generate a UIImage that I can save to the camera roll or share on FB, but I am struggling to generate the UIImage.
Any help would be great!
Here is the code:
- (void)drawShareImageWithText: (NSString*)text;
{
//// Color Declarations
UIColor* questionText = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1];
//// Bezier Drawing
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(42.46, 0.09)];
[bezierPath addCurveToPoint: CGPointMake(53.37, 11) controlPoint1: CGPointMake(42.46, 0.09) controlPoint2: CGPointMake(47.66, 5.3)];
[bezierPath addLineToPoint: CGPointMake(255.6, 11)];
[bezierPath addCurveToPoint: CGPointMake(267.62, 11.92) controlPoint1: CGPointMake(261.76, 11) controlPoint2: CGPointMake(264.84, 11)];
[bezierPath addLineToPoint: CGPointMake(268.16, 12.05)];
[bezierPath addCurveToPoint: CGPointMake(275.95, 19.84) controlPoint1: CGPointMake(271.78, 13.37) controlPoint2: CGPointMake(274.63, 16.22)];
[bezierPath addCurveToPoint: CGPointMake(277, 32.4) controlPoint1: CGPointMake(277, 23.16) controlPoint2: CGPointMake(277, 26.24)];
[bezierPath addLineToPoint: CGPointMake(277, 148.6)];
[bezierPath addCurveToPoint: CGPointMake(276.08, 160.62) controlPoint1: CGPointMake(277, 154.76) controlPoint2: CGPointMake(277, 157.84)];
[bezierPath addLineToPoint: CGPointMake(275.95, 161.16)];
[bezierPath addCurveToPoint: CGPointMake(268.16, 168.95) controlPoint1: CGPointMake(274.63, 164.78) controlPoint2: CGPointMake(271.78, 167.63)];
[bezierPath addCurveToPoint: CGPointMake(255.6, 170) controlPoint1: CGPointMake(264.84, 170) controlPoint2: CGPointMake(261.76, 170)];
[bezierPath addLineToPoint: CGPointMake(21.4, 170)];
[bezierPath addCurveToPoint: CGPointMake(9.38, 169.08) controlPoint1: CGPointMake(15.24, 170) controlPoint2: CGPointMake(12.16, 170)];
[bezierPath addLineToPoint: CGPointMake(8.84, 168.95)];
[bezierPath addCurveToPoint: CGPointMake(1.05, 161.16) controlPoint1: CGPointMake(5.22, 167.63) controlPoint2: CGPointMake(2.37, 164.78)];
[bezierPath addCurveToPoint: CGPointMake(0, 148.6) controlPoint1: CGPointMake(0, 157.84) controlPoint2: CGPointMake(0, 154.76)];
[bezierPath addLineToPoint: CGPointMake(0, 32.4)];
[bezierPath addCurveToPoint: CGPointMake(0.92, 20.38) controlPoint1: CGPointMake(0, 26.24) controlPoint2: CGPointMake(0, 23.16)];
[bezierPath addLineToPoint: CGPointMake(1.05, 19.84)];
[bezierPath addCurveToPoint: CGPointMake(8.84, 12.05) controlPoint1: CGPointMake(2.37, 16.22) controlPoint2: CGPointMake(5.22, 13.37)];
[bezierPath addCurveToPoint: CGPointMake(21.4, 11) controlPoint1: CGPointMake(12.16, 11) controlPoint2: CGPointMake(15.24, 11)];
[bezierPath addLineToPoint: CGPointMake(31.54, 11)];
[bezierPath addCurveToPoint: CGPointMake(42.46, 0.09) controlPoint1: CGPointMake(37.25, 5.3) controlPoint2: CGPointMake(42.46, 0.09)];
[bezierPath closePath];
[StyleKit.Color1 setFill];
[bezierPath fill];
//// shareContent Drawing
CGRect shareContentRect = CGRectMake(15, 26, 247, 136.03);
NSMutableParagraphStyle* shareContentStyle = NSMutableParagraphStyle.defaultParagraphStyle.mutableCopy;
shareContentStyle.alignment = NSTextAlignmentCenter;
NSDictionary* shareContentFontAttributes = #{NSFontAttributeName: [UIFont fontWithName: #"Helvetica" size: 15], NSForegroundColorAttributeName: questionText, NSParagraphStyleAttributeName: shareContentStyle};
[text drawInRect: CGRectOffset(shareContentRect, 0, (CGRectGetHeight(shareContentRect) - [text boundingRectWithSize: shareContentRect.size options: NSStringDrawingUsesLineFragmentOrigin attributes: shareContentFontAttributes context: nil].size.height) / 2) withAttributes: shareContentFontAttributes];
}
All of this drawing code is generated under the expectation that it will be run within a graphics context created and managed by UIKit.
This is automatically the case within a UIView's drawRect method. But you want to generate a UIImage, so you need to setup a temporary graphics context just for that.
The general approach is as follows:
// specify size of the image
CGRect imageRect = CGRectMake(0,0,100,100);
// create the graphics context
UIGraphicsBeginImageContextWithOptions(imageRect.size,NO,0.0);
// insert calls to your drawing code here, to draw into the context
// ...
// save the image from the implicit context into an image
UIImage * finalImage = UIGraphicsGetImageFromCurrentImageContext();
// cleanup
UIGraphicsEndImageContext();
Erica Sadun's book on practical UIKit drawing is probably the best one-stop resource for this sort of thing.
Equivalent in Swift (language version 1.2):
// specify size of the image
let imageRect:CGRect = CGRectMake(0,0,100,100)
// create the graphics context
UIGraphicsBeginImageContextWithOptions(imageRect.size, false, 0)
// insert calls to your drawing code here, to draw into the context
// ...
// save the image from the implicit context into an image
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
// cleanup
UIGraphicsEndImageContext()
Swift 4+ Version:
// specify size of the image
let imageSize = CGSize(width: 100, height: 100)
// create the graphics context
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
// insert calls to your drawing code here, to draw into the context
// ...
// save the image from the implicit context into an image
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
// cleanup
UIGraphicsEndImageContext()

How to draw an open ended triangle with a specific angle in an UIView?

I'm stumped here and need some assistance with this. For testing purposes I have a UIView that I draw into via drawRect (code below). I'm able to draw a circle with an specific angle and such but it's filled and closed. I need it to be open and stroked. I'm currently using UIBezierPath to do this but is there better way to do this? I can draw the open ended triangle like I want in UIBezierPath but I can't specify the angle that I need it to be (code below also). Any assistance with this would be much appreciated. Thanks.
The picture below is how I would like it to look when I draw it with a specific angle.
#import "AngleView.h"
#define DEGREES_TO_RADIANS(degrees) ((M_PI * degrees)/ 180)
#implementation AngleView
-(void)drawRect:(CGRect)rect {
UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointZero
radius:50
startAngle:0
endAngle:DEGREES_TO_RADIANS(45)
clockwise:NO];
[aPath fill];
}
#end
and here is how I can draw it without a specific angle.
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(86.5, 33.5)];
[bezierPath addLineToPoint: CGPointMake(60.5, 62.5)];
[bezierPath addLineToPoint: CGPointMake(87.5, 62.5)];
[[UIColor blackColor] setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Instead of trying draw one single line you can draw two separate originating from same point and you can give angle to one of them.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, 119.5); //here you can set originating point of line
CGContextRotateCTM(context, -45 * M_PI / 180); //Change Angle here -45
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(0, 0)];
[bezierPath addCurveToPoint: CGPointMake(39, 0) controlPoint1: CGPointMake(39, 0) controlPoint2: CGPointMake(39, 0)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CGContextRestoreGState(context);
//Second UIBezierPath with 0 angle
context = UIGraphicsGetCurrentContext();
UIBezierPath* bezier2Path = UIBezierPath.bezierPath;
[bezier2Path moveToPoint: CGPointMake(0, 119.5)];
[bezier2Path addCurveToPoint: CGPointMake(38.5, 119.5) controlPoint1: CGPointMake(38.5, 119.5) controlPoint2: CGPointMake(38.5, 119.5)];
[UIColor.blackColor setStroke];
bezier2Path.lineWidth = 1;
[bezier2Path stroke];
CGContextRestoreGState(context);
}

Custom shaped MPMoviePlayerController

How can I achieve custom shaped movie in IOS?
I have a UIBezierPath and CAShapeLayer. I would like to display movie in that shape.
I'm looking for the effect like skew - http://i57.tinypic.com/16020ps.jpg
I'm using QR code reader and there I get the boundaries(UIBezierPath) of the found qr code. I want to be able to display the video over the qr code to achieve AR effect.
Thanks for the help!
You can do this by creating a mask layer on top of the MPMoviePlayerController view.
I just tested this by creating a simple single view project. I added the QuartzCore and MediaPlayer frameworks.
This is the code from ViewController.m:
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <MediaPlayer/MediaPlayer.h>
#interface ViewController ()
#property (nonatomic, strong) MPMoviePlayerController *movieController;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *moviePath = [[NSBundle mainBundle] pathForResource:#"myMovie" ofType:#"mov"];
NSURL *url = nil;
if (moviePath != nil) {
url = [NSURL fileURLWithPath:moviePath];
}
self.movieController = [[MPMoviePlayerController alloc] initWithContentURL:url];
self.movieController.view.frame = CGRectMake(50, 50, 400, 400);
[self.view addSubview:self.movieController.view];
[self.movieController play];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.movieController.view.bounds;
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(118.81, 189.76)];
[bezierPath addCurveToPoint: CGPointMake(188.77, 37.51) controlPoint1: CGPointMake(118.81, 189.76) controlPoint2: CGPointMake(138.99, 36.06)];
[bezierPath addCurveToPoint: CGPointMake(255.06, 158.26) controlPoint1: CGPointMake(238.56, 38.96) controlPoint2: CGPointMake(226.1, 78.56)];
[bezierPath addCurveToPoint: CGPointMake(369.21, 200.26) controlPoint1: CGPointMake(284.02, 237.96) controlPoint2: CGPointMake(314.4, 134.08)];
[bezierPath addCurveToPoint: CGPointMake(269.79, 300.02) controlPoint1: CGPointMake(424.02, 266.44) controlPoint2: CGPointMake(319.71, 329.77)];
[bezierPath addCurveToPoint: CGPointMake(151.95, 300.02) controlPoint1: CGPointMake(219.86, 270.26) controlPoint2: CGPointMake(202.68, 327.08)];
[bezierPath addCurveToPoint: CGPointMake(15.71, 284.27) controlPoint1: CGPointMake(101.22, 272.95) controlPoint2: CGPointMake(-0.1, 369.78)];
[bezierPath addCurveToPoint: CGPointMake(118.81, 189.76) controlPoint1: CGPointMake(31.51, 198.75) controlPoint2: CGPointMake(118.81, 189.76)];
[bezierPath closePath];
maskLayer.fillColor = [[UIColor whiteColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [bezierPath CGPath];
self.movieController.view.layer.mask = maskLayer;
}
#end
(The bezier path is a hat shape created quickly in PaintCode, so it's a bit odd.)

Masking UIImage in TableViewCell

I have an UITableView where each cell is a picture, name and button, just like the favorites tab in the phone app in iOS 7. And, just like it is in the phone app, I want the image in each cell to be a circle, rather then a square.
Using a program called PaintCode, I have generated a UIBezierPath which I wanted to place over the cell.imageView in order to make the image look like it's a circle. Here is that code
//// Color Declarations
UIColor* fillColor = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0];
UIColor* fillColor2 = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1];
//// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(36.36, 26.36)];
[bezierPath addCurveToPoint: CGPointMake(30.5, 40.5) controlPoint1: CGPointMake(32.45, 30.26) controlPoint2: CGPointMake(30.5, 35.38)];
[bezierPath addLineToPoint: CGPointMake(30.5, 20.5)];
[bezierPath addLineToPoint: CGPointMake(50.5, 20.5)];
[bezierPath addCurveToPoint: CGPointMake(36.36, 26.36) controlPoint1: CGPointMake(45.38, 20.5) controlPoint2: CGPointMake(40.26, 22.45)];
[bezierPath closePath];
[bezierPath moveToPoint: CGPointMake(70.5, 60.5)];
[bezierPath addLineToPoint: CGPointMake(50.5, 60.5)];
[bezierPath addCurveToPoint: CGPointMake(64.64, 54.64) controlPoint1: CGPointMake(55.62, 60.5) controlPoint2: CGPointMake(60.74, 58.55)];
[bezierPath addCurveToPoint: CGPointMake(70.5, 40.5) controlPoint1: CGPointMake(68.55, 50.74) controlPoint2: CGPointMake(70.5, 45.62)];
[bezierPath addCurveToPoint: CGPointMake(64.64, 26.36) controlPoint1: CGPointMake(70.5, 35.38) controlPoint2: CGPointMake(68.55, 30.26)];
[bezierPath addCurveToPoint: CGPointMake(50.5, 20.5) controlPoint1: CGPointMake(60.74, 22.45) controlPoint2: CGPointMake(55.62, 20.5)];
[bezierPath addLineToPoint: CGPointMake(70.5, 20.5)];
[bezierPath addLineToPoint: CGPointMake(70.5, 60.5)];
[bezierPath closePath];
[bezierPath moveToPoint: CGPointMake(36.36, 54.64)];
[bezierPath addCurveToPoint: CGPointMake(50.5, 60.5) controlPoint1: CGPointMake(40.26, 58.55) controlPoint2: CGPointMake(45.38, 60.5)];
[bezierPath addLineToPoint: CGPointMake(30.5, 60.5)];
[bezierPath addLineToPoint: CGPointMake(30.5, 40.5)];
[bezierPath addCurveToPoint: CGPointMake(36.36, 54.64) controlPoint1: CGPointMake(30.5, 45.62) controlPoint2: CGPointMake(32.45, 50.74)];
[bezierPath closePath];
[fillColor2 setFill];
[bezierPath fill];
[fillColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Simply put, I don't know how to use this code to apply this mask to the cell's image view. Any suggestions?
I think you can accomplish this more easily by just editing the cornerRadius property of the UIImageView's layer (after importing QuartzCore), and then setting masksToBounds to true. Check out this answer: How to crop UIImage on oval shape or circle shape?

Resources