how to smoothly move object on ellipse shape view - objective-c

Consider this image
How would I go about drawing a custom UIView that is literally just a ellipse. Would I just override the drawRect method? And can someone show me the code for dragging red ball on ecllips path?

Drawing the ball can be done in a custom drawRect if you want, or you could use CAShapeLayer:
UIView *ball = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
ball.userInteractionEnabled = TRUE;
CAShapeLayer *ballLayer = [CAShapeLayer layer];
ballLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:48 startAngle:0 endAngle:M_PI * 2.0 clockwise:YES].CGPath;
ballLayer.strokeColor = [UIColor blackColor].CGColor;
ballLayer.lineWidth = 0.5;
ballLayer.fillColor = [UIColor redColor].CGColor;
ballLayer.shadowColor = [UIColor blackColor].CGColor;
ballLayer.shadowRadius = 2;
ballLayer.shadowOpacity = 0.75;
ballLayer.shadowOffset = CGSizeZero;
[ball.layer addSublayer:ballLayer];
[self.view addSubview:ball];
Creating the ellipse could be done in a similar fashion.
Dragging the ball could be done with a gesture.
UIGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[ball addGestureRecognizer:pan];
Where:
- (void)handlePan:(UIPanGestureRecognizer *)gesture {
CGPoint translate = [gesture translationInView:gesture.view];
if (gesture.state == UIGestureRecognizerStateChanged) {
gesture.view.transform = CGAffineTransformMakeTranslation(translate.x, translate.y);
} else if (gesture.state == UIGestureRecognizerStateEnded) {
gesture.view.center = CGPointMake(gesture.view.center.x + translate.x, gesture.view.center.y + translate.y);
gesture.view.transform = CGAffineTransformIdentity;
}
}
You presumably would want to constrain the ball's movement to the ellipse, then you'd just adjust the translate coordinates accordingly. But hopefully this illustrates how to create a gesture recognizer, and move a UIView accordingly.

Related

UILabel inside UIView produces rounded corner with "square corner"

I have an UIView which I created and set background color to white. This view contains UILabel, which is a class called BubbleView. (Sorry I cannot add a picture because you need reputation 10+ :(
PROBLEM:
1. The following code produces a gray Label with rounded corner with gray-border square corner tips. This is because the UIView produces the square corner tips. The UILabel is rounded. Please note that I already set the background of UIView to white.
2. My text string of the UILabel is hiding behind UIView, so it is not displayed.
I'd love to show you pictures, but I am new and I cannot add pictures until I get to 10+ reputations.
http://i.stack.imgur.com/CdRjy.png
http://i.stack.imgur.com/zCdCV.png
Here is my code for setting the text and the view:
BubbleView:
- (void)drawRect:(CGRect)rect
{
const CGFloat boxWidth = self.bubbleWidth;
const CGFloat boxHeight = self.bubbleHeight;
NSLog(#"text, width, height: %#, %f, %f", self.text, self.bubbleWidth, self.bubbleHeight);
CGRect boxRect = CGRectMake(
roundf(self.bounds.size.width - boxWidth) / 2.0f,
roundf(self.bounds.size.height - boxHeight) / 2.0f,
boxWidth,
boxHeight);
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:boxRect cornerRadius:14.0f];
[[UIColor colorWithWhite:0.3f alpha:0.8f] setFill];
[roundedRect fill];
NSDictionary *attributes = #{
NSFontAttributeName : [UIFont systemFontOfSize:16.0f],
NSForegroundColorAttributeName : [UIColor whiteColor]
};
CGPoint textPoint = CGPointMake(
self.center.x+boxWidth/2,
self.center.y+boxHeight/2);
NSLog(#"text point origin: %f, %f", textPoint.x, textPoint.y);
[self.text drawAtPoint:textPoint withAttributes:attributes];
}
Main View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
[self setText];
}
-(void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self setText];
}
- (void) setText
{
NSString *textR = #"I need this text to show up on autolayout so that i could continue working";
UIFont* font = [UIFont fontWithName:#"HelveticaNeue" size:14.0f];
CGSize constraint = CGSizeMake(250,9999);
CGRect textRect = [textR boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil];
BubbleView *hostView = [[BubbleView alloc] initWithFrame:CGRectMake(20.0f, 160.0f, textRect.size.width+20, textRect.size.height+20)];
hostView.bubbleWidth = textRect.size.width+20;
hostView.bubbleHeight = textRect.size.height+20;
hostView.text = textR;
hostView.layer.cornerRadius = 10.0f;
self.view.layer.masksToBounds = TRUE;
[hostView drawRect:textRect];
hostView.backgroundColor = [UIColor whiteColor];
self.detailsView = hostView;
//self.detailsView.backgroundColor = [UIColor whiteColor];
NSLog(#"size: %f, %f", textRect.size.width, textRect.size.height);
NSLog(#"origin: %f, %f - size: %f, %f, backgroundColor: #%#", self.detailsView.frame.origin.x, self.detailsView.frame.origin.y, self.detailsView.frame.size.width, self.detailsView.frame.size.height, self.detailsView.backgroundColor);
[self.view addSubview:self.detailsView];
self.hostSays.text = textR;
self.hostSays.textColor = [UIColor blueColor];
[self.view layoutSubviews];
}
SOLUTION (ONLY 1 PART):
OK so I managed to solve half of my problems. I had to add the following code in my BubbleView class (inside - (id)initWithFrame:(CGRect)frame). This got rid of the square angles! (I think Wain was the one who suggested this but I might've misunderstood him)...
[self setOpaque:NO];
[self setBackgroundColor:[UIColor clearColor]];
So...still have the other part 2 of problem to solve and I'm hoping someone has run into this issue before!
Set the background colour of the view and add the label as a subview. Set the frame to get your required padding. Do not implement drawRect.
Now, the view will draw the background colour and the label automatically and the label will draw the text (with its background colour and border settings).
I know when I create custom buttons I need to setMasksToBounds
+ (void) addBorderToButtons:(UIButton *) btn
{
// Round button corners
CALayer *btnLayer = [btn layer];
[btnLayer setMasksToBounds:YES];
[btnLayer setCornerRadius:15.0f];
// Apply a 1 pixel, black border around Buy Button
[btnLayer setBorderWidth:1.5f];
[btnLayer setBorderColor:[[UIColor blackColor] CGColor]];
}
Setting this changes
To this
If you want to save your coding approach you strongly need to add [super drawRect:rect] in your drawRect: method
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
YOUR CODE
}
In this case you will see your text in UILabel.
Also you should not call drawRect: directly. It will be called automatically in runtime:
BubbleView *hostView =
[[BubbleView alloc] initWithFrame:CGRectMake(20.0f,
160.0f,
textRect.size.width+20,
textRect.size.height+20)];
hostView.bubbleWidth = textRect.size.width+20;
hostView.bubbleHeight = textRect.size.height+20;
hostView.text = textR;
// hostView.layer.cornerRadius = 10.0f;
// self.view.layer.masksToBounds = TRUE;
// [hostView drawRect:textRect];
// hostView.backgroundColor = [UIColor whiteColor];
self.detailsView = hostView;

Make area around masked region transparent with CALayers?

I'm trying to crop an image to an irregular shape, but I need to make the area that was removed transparent.
Inside subclass of UIView
CALayer *myLayer = [CALayer layer];
CAShapeLayer *mask = [CAShapeLayer layer];
myLayer.frame = self.bounds;
myLayer.contents = (id)[self.picture CGImage];
mask.path = path;
myLayer.mask = mask;
[self.layer addSublayer:myLayer];
This will crop the image appropriately, but the background color of the view is white and therefore still visible. I've tried making the other layers transparent, but it has not worked.
(self and subview both refer to same view)
[self layer].backgroundColor = [UIColor clearColor].CGColor //no change
[self layer].opacity = 0; //makes entire view transparent
subView.backgroundColor = [UIColor clearColor]; // entire view becomes transparent
Is it possible to create the effect I'm trying to achieve?
I tried the same thing, here is the exact code:
// In KMViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
KMView *view = [[KMView alloc] initWithFrame:frame];
view.center = self.view.center;
[self.view addSubview:view];
}
// In KMView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// self.backgroundColor = [UIColor clearColor];
CALayer *layer = [CALayer layer];
CAShapeLayer *mask = [CAShapeLayer layer];
layer.frame = self.bounds;
UIImage *image = [UIImage imageNamed:#"orange"];
layer.contents = (id)image.CGImage;
CGPathRef path = CGPathCreateWithEllipseInRect(frame, NULL);
mask.path = path;
CGPathRelease(path);
layer.mask = mask;
[self.layer addSublayer:layer];
}
return self;
}
It worked for me as expected. The background was transparent. Then I tried adding
self.backgroundColor = [UIColor clearColor];
to the beginning of the code and nothing changed, the image was showing on a transparent background clipped to the path.
I think the problem might be somewhere else.

CALayer shadow while scrolling UITableView

I have added a shadow to a UITableView (which covers a third of the screen sfrom the bottom - see attached screenshot) using the following in a UIView Category:
- (void) addShadow {
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
self.layer.masksToBounds = NO;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 1;
self.layer.shadowOffset = CGSizeMake(-5,-5);
self.layer.shadowRadius = 20;
self.layer.shadowPath = path.CGPath;
self.layer.shouldRasterize = YES;
}
It appears as expected, but when I scroll it up, the shadow scrolls up too. Also, the table scrolls beyond its upper bound. Can you suggest what is wrong here? if I comment self.layer.masksToBounds = NO;, the shadow disappears, but the table scrolling is as expected. Hence, the problem lies somewhere around masksToBounds perhaps.
I solved it by putting an identical view underneath, just for the shadow. Not a clean solution ... hence I am still open to answers. My code is as follows:
- (UIView*) addShadow {
UIView* backView = [[UIView alloc] initWithFrame:self.frame];
UIBezierPath *path = [UIBezierPath bezierPathWithRect:backView.bounds];
backView.layer.masksToBounds = NO;
backView.layer.shadowColor = [UIColor blackColor].CGColor;
backView.layer.shadowOpacity = 1;
backView.layer.shadowOffset = CGSizeMake(-5,-5);
backView.layer.shadowRadius = 20;
backView.layer.shadowPath = path.CGPath;
backView.layer.shouldRasterize = YES;
[self.superview addSubview:backView];
[self.superview bringSubviewToFront:self];
return backView;
}
(void) removeShadow {
self.layer.masksToBounds = YES;
self.layer.shadowColor = nil;
self.layer.shadowOpacity = 0;
self.layer.shadowOffset = CGSizeMake(0,0);
self.layer.shadowRadius = 0;
}

Using a CALayer as a mask to a backing layer of a GLKView blocks touch events?

I'm trying to use a CAShapeLayer as a mask to the backing layer of a GLKView like so:
CGRect circleFrame = CGRectMake(0, 0, 800, 800);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:circleFrame];
CGPoint circleAnchor = CGPointMake(CGRectGetMidX(circleFrame) / CGRectGetMaxX(circleFrame),CGRectGetMidY(circleFrame) / CGRectGetMaxY(circleFrame));
shapeLayerMask = [[CAShapeLayer alloc] init];
shapeLayerMask.path = path.CGPath;
shapeLayerMask.anchorPoint = circleAnchor;
shapeLayerMask.frame = CGRectMake(0, 0,800, 800);
shapeLayerMask.fillColor = [UIColor blackColor].CGColor;
shapeLayerMask.backgroundColor = [UIColor clearColor].CGColor;
shapeLayerMask.opacity = 1.0f;
shapeLayerMask.hidden = NO;
self.view.layer.mask = shapeLayerMask;
But as soon as i do this, all my touch events stop firing. No more touchesBegan, touchesEnded, etc. Anyone knows why? Thanks in advance!

iOS: Table cell footer dropshadow

I have a tableview with only a few rows. So instead of displaying a bunch of blanks I added a blank UIView to the tableview.footer. However I would like the last cell to cast a dropshadow on the UIView. How would I achieve this? Here is my current code.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *emptyView = [[UIView alloc] initWithFrame:CGRectZero];
CALayer *layer = [emptyView layer];
[layer setShadowOffset:CGSizeMake(0, 1)];
[layer setShadowColor:[[UIColor darkGrayColor] CGColor]];
[layer setShadowRadius:8.0];
[layer setShadowOpacity:0.8];
self.tableView.tableFooterView = emptyView;
}
EDIT:
It is adding the UIView to the footer but not creating the dropshadow. I'm not sure the layer is the best approach for this or even correct for this type of thing.
It is probably because you set the frame to CGRectZero.
The zero rectangle is equivalent to CGRectMake(0,0,0,0).
A shadow of a zero rect (x=0, y=0, width=0, height=0) won't show at all.
Try giving it a proper frame size and you will see the difference.
Check out this for ref as well: https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html
I ended up using
shadowBackgroundView.layer.shadowOpacity = 0.3;
shadowBackgroundView.layer.shadowRadius = 2;
shadowBackgroundView.layer.shadowColor = [[UIColor blackColor] CGColor];
shadowBackgroundView.layer.shadowOffset = CGSizeMake(0.0, 1.0);
CGPathRef shadowPath = [UIBezierPath bezierPathWithRoundedRect: shadowBackgroundView.bounds
byRoundingCorners: UIRectCornerAllCorners
cornerRadii: CGSizeMake(PageCellBackgroundRadius, PageCellBackgroundRadius)].CGPath;
shadowBackgroundView.layer.shadowPath = shadowPath;
shadowBackgroundView.layer.shouldRasterize = YES;
[self addSubview: shadowBackgroundView];
In cellForRowAtIndexPath: function:
// drop shadow
cell.layer.shadowOpacity = 1.0;
cell.layer.shadowRadius = 1.7;
cell.layer.shadowColor = [UIColor blackColor].CGColor;
cell.layer.shadowOffset = CGSizeMake(0.0, 0.0);