Ответ 1
Не так давно я даже не знал, как произносить Безье, не говоря уже о том, как использовать пути Безье для создания произвольной формы. Вот что я узнал. Оказывается, они не так страшны, как кажется на первый взгляд.
Как нарисовать путь Безье в пользовательском представлении
Это основные шаги:
- Создайте контур желаемой формы.
- Разделите контурный контур на сегменты линий, дуг и кривых.
- Создайте этот путь программно.
- Нарисуйте путь либо в
drawRect
либо используяCAShapeLayer
.
Контур формы дизайна
Вы можете сделать что угодно, но в качестве примера я выбрал форму ниже. Это может быть всплывающая клавиша на клавиатуре.
Разделите путь на сегменты
Посмотрите на свой дизайн фигуры и разбейте его на более простые элементы линий (для прямых линий), дуг (для кругов и круглых углов) и кривых (для всего остального).
Вот как будет выглядеть наш пример дизайна:
- Черные - это отрезки
- Светло-голубые - дуговые сегменты
- Красные кривые
- Оранжевые точки - это контрольные точки для кривых
- Зеленые точки - это точки между отрезками пути
- Пунктирные линии показывают ограничивающий прямоугольник
- Синие цифры - это сегменты в том порядке, в котором они будут добавлены программно
Построить путь программно
Мы произвольно начнем в левом нижнем углу и будем работать по часовой стрелке. Я буду использовать сетку на изображении, чтобы получить значения x и y для точек. Я здесь жестко закодирую все, но, конечно, вы не сделаете этого в реальном проекте.
Основной процесс:
- Создайте новый
UIBezierPath
- Выберите начальную точку на пути с помощью
moveToPoint
- Добавить сегменты к пути
- строка:
addLineToPoint
- arc:
addArcWithCenter
- кривая:
addCurveToPoint
- строка:
- Закройте путь с помощью
closePath
Вот код для создания пути на изображении выше.
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath()
// starting point for the path (bottom left)
path.move(to: CGPoint(x: 2, y: 26))
// *********************
// ***** Left side *****
// *********************
// segment 1: line
path.addLine(to: CGPoint(x: 2, y: 15))
// segment 2: curve
path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
// segment 3: line
path.addLine(to: CGPoint(x: 0, y: 2))
// *********************
// ****** Top side *****
// *********************
// segment 4: arc
path.addArc(withCenter: CGPoint(x: 2, y: 2), // center point of circle
radius: 2, // this will make it meet our path line
startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
clockwise: true) // startAngle to endAngle goes in a clockwise direction
// segment 5: line
path.addLine(to: CGPoint(x: 8, y: 0))
// segment 6: arc
path.addArc(withCenter: CGPoint(x: 8, y: 2),
radius: 2,
startAngle: CGFloat(3*M_PI_2), // straight up
endAngle: CGFloat(0), // 0 radians = straight right
clockwise: true)
// *********************
// ***** Right side ****
// *********************
// segment 7: line
path.addLine(to: CGPoint(x: 10, y: 12))
// segment 8: curve
path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
// segment 9: line
path.addLine(to: CGPoint(x: 8, y: 26))
// *********************
// **** Bottom side ****
// *********************
// segment 10: line
path.close() // draws the final line to close the path
return path
}
Примечание. Часть приведенного выше кода можно уменьшить, добавив в одну команду строку и дугу (поскольку у дуги есть подразумеваемая начальная точка). Смотрите здесь для более подробной информации.
Нарисуй путь
Мы можем нарисовать путь либо в слое, либо в drawRect
.
Метод 1: Нарисуйте путь в слое
Наш пользовательский класс выглядит следующим образом. Мы добавляем наш путь Безье к новому CAShapeLayer
когда представление инициализируется.
import UIKit
class MyCustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
// Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
// The Bezier path that we made needs to be converted to
// a CGPath before it can be used on a layer.
shapeLayer.path = createBezierPath().cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 10, y: 10)
// add the new layer to our custom view
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
И создание нашего представления в контроллере вида, как это
override func viewDidLoad() {
super.viewDidLoad()
// create a new UIView and add it to the view controller
let myView = MyCustomView()
myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
myView.backgroundColor = UIColor.yellow
view.addSubview(myView)
}
Мы получаем...
Хм, это немного мало, потому что я жестко закодировал все числа в. Я могу увеличить размер пути, хотя, как это:
let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
Способ 2: рисовать путь в draw
Использование draw
медленнее, чем рисование на слое, поэтому этот метод не рекомендуется, если он вам не нужен.
Вот пересмотренный код для нашего пользовательского представления:
import UIKit
class MyCustomView: UIView {
override func draw(_ rect: CGRect) {
// create path (see previous code)
let path = createBezierPath()
// fill
let fillColor = UIColor.white
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blue
strokeColor.setStroke()
// Move the path to a new location
path.apply(CGAffineTransform(translationX: 10, y: 10))
// fill and stroke the path (always do these last)
path.fill()
path.stroke()
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
что дает нам тот же результат...
Дальнейшее обучение
Я очень рекомендую посмотреть на следующие материалы. Это то, что, наконец, сделало пути Безье понятными для меня. (И научил меня, как это произносить:/ˈbɛ zi eɪ/.)
- Думая как путь Безье (Все, что я когда-либо читал от этого автора, хорошо, и вдохновение для моего примера выше пришло отсюда.)
- Математика кодирования: Эпизод 19 - Кривые Безье (занимательные и наглядные иллюстрации)
- Кривые Безье (как они используются в графических приложениях)
- Кривые Безье (хорошее описание того, как получены математические формулы)