Нарисуйте iOS 7-стиль squircle программно
Я пытаюсь найти способ нарисовать значок iOS 7 в стиле "squircle" программно, используя основную графику. Я не спрашиваю, Как нарисовать закругленный прямоугольник. Закругление-это суперэллипс:
который немного отличается от обычного закругленного прямоугольника:
Это точная формула доступна. Однако я не могу понять, как это сделать, используя, например, CGPath, не говоря уже о том, чтобы заполнить его и легко изменить его размер. Все это при полной точности формулы.
4 ответов
цитата из Википедии: Superellipse
для n = 1/2, в частности, каждая из четырех дуг является квадратичная кривая Безье определяется двумя осями; в результате каждая дуга является сегментом параболы.
так почему бы не попытаться приблизить окружность, используя кривые Безье? Обе кривые (Безье и окружность) определяются параметрическими уравнениями.
Класс UIBezierPath есть способ: addCurveToPoint:controlPoint1:controlPoint2:
добавляет кубическую кривую Безье к пути приемника.
Примечание: использование addQuadCurveToPoint:controlPoint:
способ дает худшие результаты - проверено.
я использовал этот метод и вот что получилось в результате:
red line
- скругленный прямоугольник, blue line
- прямоугольник из четырех кривых Безье
если этот результат интересует-код чертежа ниже.
Примечание: Для достижения более точного соответствия Кривая Безье может потребоваться для изменения координат четырех corner points
(теперь они соответствуют углы прямоугольника, в который вписана фигура).
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
//set rect size for draw
float rectSize = 275.;
CGRect rectangle = CGRectMake(CGRectGetMidX(rect) - rectSize/2, CGRectGetMidY(rect) - rectSize/2, rectSize, rectSize);
//Rounded rectangle
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
UIBezierPath* roundedPath = [UIBezierPath bezierPathWithRoundedRect:rectangle cornerRadius:rectSize/4.7];
[roundedPath stroke];
//Rectangle from Fours Bezier Curves
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
UIBezierPath *bezierCurvePath = [UIBezierPath bezierPath];
//set coner points
CGPoint topLPoint = CGPointMake(CGRectGetMinX(rectangle), CGRectGetMinY(rectangle));
CGPoint topRPoint = CGPointMake(CGRectGetMaxX(rectangle), CGRectGetMinY(rectangle));
CGPoint botLPoint = CGPointMake(CGRectGetMinX(rectangle), CGRectGetMaxY(rectangle));
CGPoint botRPoint = CGPointMake(CGRectGetMaxX(rectangle), CGRectGetMaxY(rectangle));
//set start-end points
CGPoint midRPoint = CGPointMake(CGRectGetMaxX(rectangle), CGRectGetMidY(rectangle));
CGPoint botMPoint = CGPointMake(CGRectGetMidX(rectangle), CGRectGetMaxY(rectangle));
CGPoint topMPoint = CGPointMake(CGRectGetMidX(rectangle), CGRectGetMinY(rectangle));
CGPoint midLPoint = CGPointMake(CGRectGetMinX(rectangle), CGRectGetMidY(rectangle));
//Four Bezier Curve
[bezierCurvePath moveToPoint:midLPoint];
[bezierCurvePath addCurveToPoint:topMPoint controlPoint1:topLPoint controlPoint2:topLPoint];
[bezierCurvePath moveToPoint:midLPoint];
[bezierCurvePath addCurveToPoint:botMPoint controlPoint1:botLPoint controlPoint2:botLPoint];
[bezierCurvePath moveToPoint:midRPoint];
[bezierCurvePath addCurveToPoint:topMPoint controlPoint1:topRPoint controlPoint2:topRPoint];
[bezierCurvePath moveToPoint:midRPoint];
[bezierCurvePath addCurveToPoint:botMPoint controlPoint1:botRPoint controlPoint2:botRPoint];
[bezierCurvePath stroke];
CGContextRestoreGState(context);
заполненная версия принятого ответа, также портированная на Swift:
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else {
return
}
context.saveGState()
let rect = self.bounds
let rectSize: CGFloat = rect.width
let rectangle = CGRect(x: rect.midX - rectSize / 2, y: rect.midY - rectSize / 2, width: rectSize, height: rectSize)
let topLPoint = CGPoint(x: rectangle.minX, y: rectangle.minY)
let topRPoint = CGPoint(x: rectangle.maxX, y: rectangle.minY)
let botLPoint = CGPoint(x: rectangle.minX, y: rectangle.maxY)
let botRPoint = CGPoint(x: rectangle.maxX, y: rectangle.maxY)
let midRPoint = CGPoint(x: rectangle.maxX, y: rectangle.midY)
let botMPoint = CGPoint(x: rectangle.midX, y: rectangle.maxY)
let topMPoint = CGPoint(x: rectangle.midX, y: rectangle.minY)
let midLPoint = CGPoint(x: rectangle.minX, y: rectangle.midY)
let bezierCurvePath = UIBezierPath()
bezierCurvePath.move(to: midLPoint)
bezierCurvePath.addCurve(to: topMPoint, controlPoint1: topLPoint, controlPoint2: topLPoint)
bezierCurvePath.addCurve(to: midRPoint, controlPoint1: topRPoint, controlPoint2: topRPoint)
bezierCurvePath.addCurve(to: botMPoint, controlPoint1: botRPoint, controlPoint2: botRPoint)
bezierCurvePath.addCurve(to: midLPoint, controlPoint1: botLPoint, controlPoint2: botLPoint)
context.setFillColor(UIColor.lightGray.cgColor)
bezierCurvePath.fill()
context.restoreGState()
}
идеально подходит для использования в UIView подкласс.
Это не отличный ответ, так как он не доходит до сердца того, что вы спрашиваете, как нарисовать суперэллипс** программно, но вы можете:
- загрузите SVG для формы значка iOS7 здесь:http://dribbble.com/shots/1127699-iOS-7-icon-shape-PSD
- импортируйте его в свой проект Xcode
- добавить PocketSVG в свой проект:https://github.com/arielelkin/PocketSVG
-
загрузить SVG, и преобразуйте его в UIBezierPath, оттуда вы можете масштабировать и преобразовывать, как вам нравится:
PocketSVG *myVectorDrawing = [[PocketSVG alloc] initFromSVGFileNamed:@"iOS_7_icon_shape"]; UIBezierPath *myBezierPath = myVectorDrawing.bezier; // Apply your transforms here: [myBezierPath applyTransform:CGAffineTransformMakeScale(2.5, 2.5)]; [myBezierPath applyTransform:CGAffineTransformMakeTranslation(10, 50)]; CAShapeLayer *myShapeLayer = [CAShapeLayer layer]; myShapeLayer.path = myBezierPath.CGPath; myShapeLayer.strokeColor = [[UIColor redColor] CGColor]; myShapeLayer.lineWidth = 2; myShapeLayer.fillColor = [[UIColor clearColor] CGColor]; [self.view.layer addSublayer:myShapeLayer];
* * возможно, стоит отметить, что форма все равно не может быть точной суперэллипсой:http://i.imgur.com/l0ljVRo.png
Это было бы довольно легко сделать в OpenGL ES с шейдером. Просто нарисуйте квадрат и передайте x и y как атрибуты вершины. В шейдере фрагментов подключите x и y к уравнению. Если результат
Если вы хотите использовать CGPath, я думаю, что ключ должен параметризовать x и y в терминах t, который идет от 0 до 2π. Затем оцените x и y через регулярные промежутки времени. Я постараюсь разберусь в этом и в свободное время, но моя математика немного заржавела.
кстати, я уверен, что Apple не используя эту формулу. См. ссылку, опубликованную @millimoose: http://blog.mikeswanson.com/post/62341902567/unleashing-genetic-algorithms-on-the-ios-7-icon