Objective-C Moving object along curved path - objective-c

I've looked at other SO answers to this quesiton, particularly Brad Larson's but I am still not getting any results. I'm not sure if I am approaching this the correct way. In my app, when a user touches the screen I capture the point and create an arc from a fixed point to that touch point. I then want to move a UIView along that arc.
Here's my code:
ViewController.m
//method to "shoot" object - KIP_Projectile creates the arc, KIP_Character creates the object I want to move along the arc
...
//get arc for trajectory
KIP_Projectile* vThisProjectile = [[KIP_Projectile alloc] initWithFrame:CGRectMake(51.0, fatElvisCenterPoint-30.0, touchPoint.x, 60.0)];
vThisProjectile.backgroundColor = [UIColor clearColor];
[self.view addSubview:vThisProjectile];
...
KIP_Character* thisChar = [[KIP_Character alloc] initWithFrame:CGRectMake(51, objCenterPoint-5, imgThisChar.size.width, imgThisChar.size.height)];
thisChar.backgroundColor = [UIColor clearColor];
thisChar.charID = charID;
thisChar.charType = 2;
thisChar.strCharType = #"Projectile";
thisChar.imgMyImage = imgThisChar;
thisChar.myArc = vThisProjectile;
[thisChar buildImage];
[thisChar traceArc];
in KIP_Projectile I build the arc using this code:
- (CGMutablePathRef) createArcPathFromBottomOfRect : (CGRect) rect : (CGFloat) arcHeight {
CGRect arcRect = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height - arcHeight, rect.size.width, arcHeight);
CGFloat arcRadius = (arcRect.size.height/2) + (pow(arcRect.size.width, 2) / (8*arcRect.size.height));
CGPoint arcCenter = CGPointMake(arcRect.origin.x + arcRect.size.width/2, arcRect.origin.y + arcRadius);
CGFloat angle = acos(arcRect.size.width / (2*arcRadius));
CGFloat startAngle = radians(180) + angle;
CGFloat endAngle = radians(360) - angle;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, arcCenter.x, arcCenter.y, arcRadius, startAngle, endAngle, 0);
return path;
}
- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
_myArcPath = [self createArcPathFromBottomOfRect:self.bounds:30.0];
CGContextSetLineWidth(currentContext, 1);
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(currentContext, red);
CGContextAddPath(currentContext, _myArcPath);
CGContextStrokePath(currentContext);
}
Works fine. The arc is displayed with a red stroke on the screen.
In KIP_Character, which has been passed it's relevant arc, I am using this code but getting no results.
- (void) traceArc {
CGMutablePathRef myArc = _myArc.myArcPath;
// Set up path movement
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
pathAnimation.path = myArc;
CGPathRelease(myArc);
[self.layer addAnimation:pathAnimation forKey:#"savingAnimation"];
}
Any help here would be appreciated.

Related

Implementing a clock face in UIView

I'm trying to implement a clock face in UIView - without much luck. The problem is that the hands on the clock face end up in totally the wrong position (i.e. pointing to the wrong time). I'm pretty sure that the logic of my code is correct - but clearly there's something significantly wrong.
My test app is an iOS Single View Application. ViewController.h looks like this:
#import <UIKit/UIKit.h>
#interface Clock : UIView {
UIColor* strokeColour;
UIColor* fillColour;
float strokeWidth;
NSDate* clockTime;
CGPoint clockCentre;
float clockRadius;
}
- (id)initWithCentre:(CGPoint)centre
radius:(float)radius
borderWidth:(float)width
facecolour:(UIColor*)fill
handcolour:(UIColor*)handFill
time:(NSDate*)time;
#end
#interface ViewController : UIViewController
#end
and ViewController.m looks like this:
#import "ViewController.h"
#implementation Clock
- (id)initWithCentre:(CGPoint)centre
radius:(float)radius
borderWidth:(float)width
facecolour:(UIColor*)fill
handcolour:(UIColor*)handFill
time:(NSDate*)time {
CGRect frame = CGRectMake(centre.x-radius,
centre.y-radius,
radius*2,
radius*2);
if (self = [super initWithFrame:frame]) {
// Initialization code
strokeColour = fill;
fillColour = handFill;
strokeWidth = width;
clockRadius = radius;
clockTime = time;
clockCentre.x = frame.size.width/2;
clockCentre.y = frame.size.height/2;
}
return self;
}
- (void)drawRect:(CGRect)rect {
double red, green, blue, alpha;
CGRect circleRect;
circleRect.origin.x = rect.origin.x+strokeWidth;
circleRect.origin.y = rect.origin.y+strokeWidth;
circleRect.size.width = rect.size.width - (strokeWidth*2);
circleRect.size.height = rect.size.height - (strokeWidth*2);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, strokeWidth);
[fillColour getRed:&red green:&green blue:&blue alpha:&alpha];
CGContextSetRGBFillColor(contextRef, red, green, blue, alpha);
[strokeColour getRed:&red green:&green blue:&blue alpha:&alpha];
CGContextSetRGBStrokeColor(contextRef, red, green, blue, alpha);
CGContextFillEllipseInRect(contextRef, circleRect);
CGContextStrokeEllipseInRect(contextRef, circleRect);
CGContextSetLineWidth(contextRef, strokeWidth);
[strokeColour getRed:&red green:&green blue:&blue alpha:&alpha];
CGContextSetRGBStrokeColor(contextRef, red, green, blue, alpha);
float hourAngle, minuteAngle, secondAngle, angle;
double endX, endY;
NSCalendar *cal = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [cal components:units fromDate:clockTime];
hourAngle = (components.hour /12.0) * M_PI * 2.0;
minuteAngle = (components.minute / 60.0) * M_PI * 2.0;
secondAngle = (components.second / 60.0) * M_PI * 2.0;
//minute hand
angle = minuteAngle;
endX = cos(angle) * (clockRadius*0.85) + clockCentre.x;
endY = sin(angle) * (clockRadius*0.85) + clockCentre.y;
CGContextMoveToPoint(contextRef,clockCentre.x,clockCentre.y);
CGContextAddLineToPoint(contextRef,endX,endY);
CGContextStrokePath(contextRef);
//hour hand
angle = hourAngle;
endX = cos(angle) * (clockRadius*0.65) + clockCentre.x;
endY = sin(angle) * (clockRadius*0.65) + clockCentre.y;
CGContextSetLineWidth(contextRef, strokeWidth*1.5);
CGContextMoveToPoint(contextRef,clockCentre.x,clockCentre.y);
CGContextAddLineToPoint(contextRef,endX,endY);
CGContextStrokePath(contextRef);
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (UIView*)drawClockFaceWithCentre:(CGPoint)centre
radius:(float)radius
time:(NSDate*)time
colour:(UIColor*)colour
backgroundColour:(UIColor*)bgcolour {
UIView* clock = [[Clock alloc]initWithCentre:centre
radius:radius
borderWidth:6.0
facecolour:bgcolour
handcolour:colour
time:time];
clock.backgroundColor = [UIColor clearColor];
return clock;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGPoint centre;
centre.x=200;
centre.y=200;
[self.view addSubview:[self drawClockFaceWithCentre:centre
radius:100
time:[NSDate date]
colour:[UIColor blackColor]
backgroundColour:[UIColor whiteColor]]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I'm sure it's something very simple that I've done wrong - a mistake in my maths. Can anyone see the bug (and any other suggestions for improvements that could be made would be welcome, bearing in mind (of course) that this is just a simple test harness).
As I wrote in the comment, consider dropping doing everything in drawRect and use layers. Also, instead of calculating trigonometry to draw correct path, consider just drawing hands at 12:00 and transform rotate them with correct angle. You could do it like this in init...
CAShapeLayer *faceLayer = [CAShapeLayer layer];
faceLayer.frame = self.bounds;
faceLayer.fillColor = [UIColor whiteColor].CGColor;
faceLayer.strokeColor = [UIColor blackColor].CGColor;
faceLayer.path = [UIBezierPath bezierPathWithOvalInRect:faceLayer.bounds].CGPath;
faceLayer.lineWidth = 3.0;
[self.layer addSublayer:faceLayer];
CGPoint middle = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [cal components:units fromDate:time];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:middle];
[path addLineToPoint:CGPointMake(middle.x, middle.y-radius*0.5)];
CGFloat hourAreaAngle = (2*M_PI)/12.0;
CAShapeLayer *hourLayer = [CAShapeLayer layer];
hourLayer.frame = self.bounds;
hourLayer.strokeColor = [UIColor redColor].CGColor;
hourLayer.lineWidth = 3.0;
hourLayer.path = path.CGPath;
[self.layer addSublayer:hourLayer];
hourLayer.transform = CATransform3DMakeRotation((components.hour/12.0*(M_PI*2.0))+(hourAreaAngle*(components.minute/60.0)), 0.0, 0.0, 1.0);
[path removeAllPoints];
[path moveToPoint:middle];
[path addLineToPoint:CGPointMake(middle.x, middle.y-radius*0.8)];
CAShapeLayer *minuteLayer = [CAShapeLayer layer];
minuteLayer.frame = self.bounds;
minuteLayer.strokeColor = [UIColor blueColor].CGColor;
minuteLayer.lineWidth = 2.0;
minuteLayer.path = path.CGPath;
[self.layer addSublayer:minuteLayer];
minuteLayer.transform = CATransform3DMakeRotation(components.minute/60.0*(M_PI*2.0), 0.0, 0.0, 1.0);
[path removeAllPoints];
[path moveToPoint:middle];
[path addLineToPoint:CGPointMake(middle.x, middle.y-radius)];
CAShapeLayer *secondsLayer = [CAShapeLayer layer];
secondsLayer.frame = self.bounds;
secondsLayer.strokeColor = [UIColor greenColor].CGColor;
secondsLayer.lineWidth = 1.0;
secondsLayer.path = path.CGPath;
[self.layer addSublayer:secondsLayer];
secondsLayer.transform = CATransform3DMakeRotation(components.second/60.0*(M_PI*2.0), 0.0, 0.0, 1.0);
Note I put my colours and line widths because I'm lazy and didn't want to check your code :P.
If you want to update hands later, just store hands layers as variables and transform rotate them again (just remember to first transform to identity!).

Detect tap on curved line?

I'm trying to draw a line with a specific width. I searched for examples online, but I only found examples using straight lines. I need curved lines. Also, I need to detect if the user touched within the line. Is it possible to achieve this using Objective C and Sprite Kit? If so can someone provide an example?
You can use UIBezierPath to create bezier curves (nice smooth curves). You can specify this path for a CAShapeLayer and add that as a sublayer to your view:
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 150)];
[path addCurveToPoint:CGPointMake(110, 150) controlPoint1:CGPointMake(40, 100) controlPoint2:CGPointMake(80, 100)];
[path addCurveToPoint:CGPointMake(210, 150) controlPoint1:CGPointMake(140, 200) controlPoint2:CGPointMake(170, 200)];
[path addCurveToPoint:CGPointMake(310, 150) controlPoint1:CGPointMake(250, 100) controlPoint2:CGPointMake(280, 100)];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.lineWidth = 10;
layer.strokeColor = [UIColor redColor].CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
layer.path = path.CGPath;
[self.view.layer addSublayer:layer];
If you want to randomize it a little, you can just randomize some of the curves. If you want some fuzziness, add some shadow. If you want the ends to be round, specify a rounded line cap:
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint point = CGPointMake(10, 100);
[path moveToPoint:point];
CGPoint controlPoint1;
CGPoint controlPoint2 = CGPointMake(point.x - 5.0 - arc4random_uniform(50), 150.0);
for (NSInteger i = 0; i < 5; i++) {
controlPoint1 = CGPointMake(point.x + (point.x - controlPoint2.x), 50.0);
point.x += 40.0 + arc4random_uniform(20);
controlPoint2 = CGPointMake(point.x - 5.0 - arc4random_uniform(50), 150.0);
[path addCurveToPoint:point controlPoint1:controlPoint1 controlPoint2:controlPoint2];
}
CAShapeLayer *layer = [CAShapeLayer layer];
layer.lineWidth = 5;
layer.strokeColor = [UIColor redColor].CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
layer.path = path.CGPath;
layer.shadowColor = [UIColor redColor].CGColor;
layer.shadowRadius = 2.0;
layer.shadowOpacity = 1.0;
layer.shadowOffset = CGSizeZero;
layer.lineCap = kCALineCapRound;
[self.view.layer addSublayer:layer];
If you want it to be even more irregular, break those beziers into smaller segments, but the idea would be the same. The only trick with conjoined bezier curves is that you want to make sure that the second control point of one curve is in line with the first control point of the next one, or else you end up with sharp discontinuities in the curves.
If you want to detect if and when a user taps on it, that's more complicated. But what you have to do is:
Make a snapshot of the image:
- (UIImage *)captureView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 1.0); // usually I'd use 0.0, but we'll use 1.0 here so that the tap point of the gesture matches the pixel of the snapshot
if ([view respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
BOOL success = [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
NSAssert(success, #"drawViewHierarchyInRect failed");
} else {
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
Get the color of the pixel at the coordinate that the user tapped by identifying the color of the pixel the user tapped.
- (void)handleTap:(UITapGestureRecognizer *)gesture
{
CGPoint point = [gesture locationInView:gesture.view];
CGFloat red, green, blue, alpha;
UIColor *color = [self image:self.image colorAtPoint:point];
[color getRed:&red green:&green blue:&blue alpha:&alpha];
if (green < 0.9 && blue < 0.9 && red > 0.9)
NSLog(#"tapped on curve");
else
NSLog(#"didn't tap on curve");
}
Where I adapted Apple's code for getting the pixel buffer in order to determine the color of the pixel the user tapped on was.
// adapted from https://developer.apple.com/library/mac/qa/qa1509/_index.html
- (UIColor *)image:(UIImage *)image colorAtPoint:(CGPoint)point
{
UIColor *color;
CGImageRef imageRef = image.CGImage;
// Create the bitmap context
CGContextRef context = [self createARGBBitmapContextForImage:imageRef];
NSAssert(context, #"error creating context");
// Get image width, height. We'll use the entire image.
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGRect rect = {{0,0},{width,height}};
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(context, rect, imageRef);
// Now we can get a pointer to the image data associated with the bitmap
// context.
uint8_t *data = CGBitmapContextGetData (context);
if (data != NULL) {
size_t offset = (NSInteger) point.y * 4 * width + (NSInteger) point.x * 4;
uint8_t alpha = data[offset];
uint8_t red = data[offset+1];
uint8_t green = data[offset+2];
uint8_t blue = data[offset+3];
color = [UIColor colorWithRed:red / 255.0 green:green / 255.0 blue:blue / 255.0 alpha:alpha / 255.0];
}
// When finished, release the context
CGContextRelease(context);
// Free image data memory for the context
if (data) {
free(data); // we used malloc in createARGBBitmapContextForImage, so free it
}
return color;
}
- (CGContextRef) createARGBBitmapContextForImage:(CGImageRef) inImage
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
size_t bitmapByteCount;
size_t bitmapBytesPerRow;
// Get image width, height. We'll use the entire image.
size_t pixelsWide = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage);
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
// Use the generic RGB color space.
colorSpace = CGColorSpaceCreateDeviceRGB(); // CGColorSpaceCreateDeviceWithName(kCGColorSpaceGenericRGB);
NSAssert(colorSpace, #"Error allocating color space");
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
bitmapData = malloc(bitmapByteCount);
NSAssert(bitmapData, #"Unable to allocate bitmap buffer");
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
(CGBitmapInfo)kCGImageAlphaPremultipliedFirst);
NSAssert(context, #"Context not created!");
// Make sure and release colorspace before returning
CGColorSpaceRelease( colorSpace );
return context;
}

objective-c create and stroke an arc with CG

I was following a Ray Wenderlich tutorial on creating arcs:
http://www.raywenderlich.com/33193/core-graphics-tutorial-arcs-and-paths
-what I am looking to do is begin at point a (a fixed point) and locate where the user touches the screen, if it is + point A, then I want to draw an arc to that point. Though I don't return any errors I also don't get the path to be stroked. Could someone review the code below and see what I am doing wrong?
#implementation KIP_Arc
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void) setArc {
//set the frame
float frameX = _startPoint.x;
float frameY = _startPoint.y;
float frameW = _endPoint.x;
float frameH = 50.0;
[self setFrame:CGRectMake(frameX, frameY, frameW, frameH)];
self.backgroundColor = [UIColor clearColor];
}
- (BOOL)isFlipped {
return YES;
}
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] set];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGRect arcRect = self.frame;
CGMutablePathRef arcPath = [self createArcPathFromBottomOfRect:arcRect:25.0];
CGContextAddPath(context, arcPath);
CGContextClip(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
CFRelease(arcPath);
}
- (CGMutablePathRef) createArcPathFromBottomOfRect : (CGRect) rect : (CGFloat) arcHeight {
CGRect arcRect = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height - arcHeight, rect.size.width, arcHeight);
CGFloat arcRadius = (arcRect.size.height/2) + (pow(arcRect.size.width, 2) / (8*arcRect.size.height));
CGPoint arcCenter = CGPointMake(arcRect.origin.x + arcRect.size.width/2, arcRect.origin.y + arcRadius);
CGFloat angle = acos(arcRect.size.width / (2*arcRadius));
CGFloat startAngle = radians(180) + angle;
CGFloat endAngle = radians(360) - angle;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, arcCenter.x, arcCenter.y, arcRadius, startAngle, endAngle, 0);
CGPathAddLineToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPathAddLineToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPathAddLineToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect));
return path;
}
static inline double radians (double degrees){
return degrees * M_PI/180;
}
CGContextClip, as a side effect, empties the current path. When you call CGContextFillPath immediately afterward, the current path is empty, so you fill nothing.
As its name implies, CGContextFillPath restricts itself to the context's current path. (If it didn't, its name would just be CGContextFill.) So, you don't need to clip.
Drop the CGContextClip call and just use CGContextFillPath.

Adding shadow to UICollectionViewCell slows down scrolling

I am able to successfully add shadow to UICollectionViewCell by adding the following code to
cellForItemAtIndexPath method
cell.image.layer.masksToBounds = NO;
cell.image.moviePlayerView.layer.shadowColor = [UIColor darkGrayColor].CGColor;
cell.image.moviePlayerView.self.layer.shadowOffset = CGSizeMake(0, 5);
cell.image.moviePlayerView.self.layer.shadowRadius = 2;
cell.image.moviePlayerView.layer.shadowOpacity = 0.3;
but it makes the scrolling really slow. I understand its adding shadow to the cell when its scrolling but can not figure out the solution. :/
I figured out the solution and hope this would be helpful to others. Instead of adding shadow separately, now I am merging existing image with shadow image and using the result image.
UIImage* image = [image imageWithFileShadow];
- (UIImage *)imageWithFileShadow {
static CGFloat pointScale = -1.0f;
if (pointScale < 0.0f) {
pointScale = [[UIScreen mainScreen] scale];
}
CGFloat imageWidth = (int)self.size.width;
CGFloat imageHeight = (int)self.size.height;
UIImage * shadowImage = [[UIImage imageNamed:#"FileShadowResizable"] resizableImageWithCapInsets:UIEdgeInsetsMake(20.0f, 24.0f, 29.0f, 24.0f)];
UIGraphicsBeginImageContextWithOptions(self.size, NO, pointScale);
[shadowImage drawInRect:CGRectMake(0.0f, 0.0f, imageWidth, imageHeight) blendMode:kCGBlendModeNormal alpha:1.0f];
[self drawInRect:CGRectMake(14.0f, 8.0f, self.size.width - 28.0f, self.size.height - 29.0f)];
UIImage * mergedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return mergedImage;
}

ios Help Screen semi-transparent overlay

Wondering if anyone can shed some light on how to create a semi-transparent Help screen on an ipad or iphone app? Something similar to Googles app.
I have a splitViewController and I want to overlay a see-through black overlay on top of the splitViewController where I can show Help information about each item you can click in the splitViewController.
I followed http://cocoawithlove.com/2009/04/showing-message-over-iphone-keyboard.html which gets me the overlay, but I am now adding clickable UIViews (eg a close button to dismiss the overlay). The issue is when I setup touchesEnded:withEvent I cannot seem to convert clicks from the main LoadingView to closeButton to see what was actually clicked?
See the touches ended at the end of the code
================================================================
#import "HelpView.h"
#import <QuartzCore/QuartzCore.h>
//
CGPathRef NewPathWithRoundRect(CGRect rect, CGFloat cornerRadius)
{
//
// Create the boundary path
//
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL,
rect.origin.x,
rect.origin.y + rect.size.height - cornerRadius);
// Top left corner
CGPathAddArcToPoint(path, NULL,
rect.origin.x,
rect.origin.y,
rect.origin.x + rect.size.width,
rect.origin.y,
cornerRadius);
// Top right corner
CGPathAddArcToPoint(path, NULL,
rect.origin.x + rect.size.width,
rect.origin.y,
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height,
cornerRadius);
// Bottom right corner
CGPathAddArcToPoint(path, NULL,
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height,
rect.origin.x,
rect.origin.y + rect.size.height,
cornerRadius);
// Bottom left corner
CGPathAddArcToPoint(path, NULL,
rect.origin.x,
rect.origin.y + rect.size.height,
rect.origin.x,
rect.origin.y,
cornerRadius);
// Close the path at the rounded rect
CGPathCloseSubpath(path);
return path;
}
#implementation HelpView
#synthesize closeButton = _closeButton;
- (id)loadingViewInView:(UIView *)aSuperview
{
HelpView *loadingView =
[[HelpView alloc] initWithFrame:[aSuperview bounds]];
if (!loadingView)
{
return nil;
}
loadingView.opaque = NO;
loadingView.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[aSuperview addSubview:loadingView];
const CGFloat DEFAULT_LABEL_WIDTH = 100.0;
const CGFloat DEFAULT_LABEL_HEIGHT = 40.0;
CGRect labelFrame = CGRectMake(0, 0, DEFAULT_LABEL_WIDTH, DEFAULT_LABEL_HEIGHT);
UILabel *loadingLabel = [[UILabel alloc] initWithFrame:labelFrame];
loadingLabel.text = NSLocalizedString(#"Close", nil);
loadingLabel.textColor = [UIColor whiteColor];
loadingLabel.backgroundColor = [UIColor clearColor];
loadingLabel.textAlignment = UITextAlignmentCenter;
loadingLabel.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
loadingLabel.autoresizingMask =
UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
// Background for Close button
CALayer *closeButtonBackground = [CALayer layer];
closeButtonBackground.opacity = 0.3;
closeButtonBackground.borderColor = [UIColor whiteColor].CGColor;
closeButtonBackground.borderWidth = 2.0;
closeButtonBackground.cornerRadius = 5.0;
closeButtonBackground.backgroundColor = [UIColor blackColor].CGColor;
// Finish setting up frame for Close button
labelFrame.origin.x = 0;
labelFrame.origin.y = 0;
loadingLabel.frame = labelFrame;
closeButtonBackground.frame = labelFrame;
CGRect viewRect = CGRectMake(loadingView.frame.size.width -150 ,loadingView.frame.size.height - 85,100,40);
self.closeButton = [[UIView alloc] initWithFrame:viewRect];
self.closeButton.userInteractionEnabled = YES;
self.userInteractionEnabled = NO;
[loadingView addSubview:self.closeButton];
[self.closeButton.layer addSublayer:closeButtonBackground];
[self.closeButton addSubview:loadingLabel];
// Set up the fade-in animation
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[[aSuperview layer] addAnimation:animation forKey:#"layerAnimation"];
//[loadingView createGestureRecognizer];
return loadingView;
}
- (void)removeView
{
UIView *aSuperview = [self superview];
[super removeFromSuperview];
// Set up the animation
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[[aSuperview layer] addAnimation:animation forKey:#"layerAnimation"];
}
- (void)drawRect:(CGRect)rect
{
rect.size.height -= 1;
rect.size.width -= 1;
const CGFloat RECT_PADDING = 0.0;
rect = CGRectInset(rect, RECT_PADDING, RECT_PADDING);
const CGFloat ROUND_RECT_CORNER_RADIUS = 0.0;
CGPathRef roundRectPath = NewPathWithRoundRect(rect, ROUND_RECT_CORNER_RADIUS);
CGContextRef context = UIGraphicsGetCurrentContext();
const CGFloat BACKGROUND_OPACITY = 0.85;
CGContextSetRGBFillColor(context, 0, 0, 0, BACKGROUND_OPACITY);
CGContextAddPath(context, roundRectPath);
CGContextFillPath(context);
const CGFloat STROKE_OPACITY = 0.25;
CGContextSetRGBStrokeColor(context, 1, 1, 1, STROKE_OPACITY);
CGContextAddPath(context, roundRectPath);
CGContextStrokePath(context);
CGPathRelease(roundRectPath);
}
- (void)handleSingleDoubleTap:(UITapGestureRecognizer *)sender {
NSLog(#"");
CGPoint tapPoint = [sender locationInView:sender.view];
CALayer* layerThatWasTapped = [self.layer hitTest:tapPoint];
CGPoint convertedPoint = [layerThatWasTapped convertPoint:layerThatWasTapped.position toLayer:self.closeButton.layer];
BOOL myBool = [self.closeButton.layer containsPoint:convertedPoint];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView: self];
CGPoint convertedPoint = [self convertPoint:point toView:[self.closeButton superview]];
if (CGRectContainsPoint(self.closeButton.bounds, convertedPoint)) {
NSLog (#"YAY!");
}
} // touchesEnded
Solved this with NSNotifications. Something like this... its a little static... should be more dynamic.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(eventHandler:)
name:#"SomeButtonClicked"
object:nil ];
-(void)eventHandler: (NSNotification *) notification
{
NSString *buttonWhichWasClicked = [[notification userInfo] objectForKey:#"buttonName"];
NSLog(#" %#", buttonWhichWasClicked);
if ([buttonWhichWasClicked isEqualToString:#"close"]){
[self removeView];
}
//Tell other buttons to dim/remove themselves as a new one was selected
}
- (void)removeView
{
//code to remove view
}