Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crazy rounded rect UIBezierPath behavior on iOS 7. What is the deal?

The simple UIView below draws a rounded rectangle. When I pass a corner radius of 65 or below it rounds correctly, but 66 and above and it generates a perfect circle! What is going on here? It should only show a circle when the corner radius is equal to 1/2 the frame width, but it seems that it is drawing a circle when the radius is about 1/3rd, no matter what the size of the view is. This behavior appears on iOS 7. On iOS 6 I get expected behavior.

#import "ViewController.h"
@interface MyView : UIView
@end

@implementation MyView
-(void)drawRect:(CGRect)rect {
  UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 200, 200) cornerRadius:65];
  CGContextRef c = UIGraphicsGetCurrentContext();
  CGContextAddPath(c, path.CGPath);
  [[UIColor redColor] set];
  CGContextStrokePath(c);
}
@end

@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  MyView *v = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
  [self.view addSubview:v];
}
@end

enter image description hereenter image description here

enter image description here

like image 314
Steveo Avatar asked Nov 22 '22 16:11

Steveo


1 Answers

This will probably never be fixed. Here is why.

The math to calculate if you can make a squircle is: radius * magicMultiplier * 2. If the result is longer than the side, it can't make a squircle so it makes a circle.

The magicMultiplier is required because to make it look like a squircle, the bezier curve needs to start from a longer distance than the radius. The magic multiplier provides that extra distance.

From my research and playing around with the bezier function, I believe the magic multiplier might be something around 1.0 + 8.0 / 15.0 = 1.533.

So 66*(1+8/15)*2 is 202.4 which is longer than the shortest side (200), thus it makes it a circle.

However! 65*(1+8/15)*2 is 199.33 which is smaller than 200, so it squircles it correctly.

Possible solutions

  1. Code your own bezier curve function (or get one online)
  2. Use the view's layer.cornerRadius to achieve the same thing since Apple doesn't clamp the corner radius here.
layer.cornerCurve = .continuous
layer.cornerRadius = min(radius, min(bounds.width, bounds.height)/2.0)
// You might want to clamp it yourself

Bear in mind that draw(in ctx) doesn't work with layer.maskedCorners. So you can't use SnapshotTesting with those.

like image 85
iGerms Avatar answered Nov 25 '22 04:11

iGerms