Рисование UIBezierPath на сгенерированном коде UIView
У меня есть UIView
добавлено в коде во время выполнения.
Я хочу нарисовать UIBezierPath
в нем, но значит ли это, что я должен переопределить drawRect
для UIView?
Или есть другой способ рисования к нему на заказ UIView
?
Вот код для генерации UIView
:
UIView* shapeView = [[UIView alloc]initWithFrame:CGRectMake(xOrigin,yOrigin+(i*MENU_BLOCK_FRAME_HEIGHT), self.shapeScroll.frame.size.width, MENU_BLOCK_FRAME_HEIGHT)];
shapeView.clipsToBounds = YES;
А вот функция для создания и возврата UIBezierPath
:
- (UIBezierPath*)createPath
{
UIBezierPath* path = [[UIBezierPath alloc]init];
[path moveToPoint:CGPointMake(100.0, 50.0)];
[path addLineToPoint:CGPointMake(200.0,50.0)];
[path addLineToPoint:CGPointMake(200.0, 200.0)];
[path addLineToPoint:CGPointMake(100.0, 200.0)];
[path closePath];
return path;
}
6 ответов
Не так давно я даже не знал, как произносить Безье, не говоря уже о том, как использовать пути Безье для создания произвольной формы. Вот что я узнал. Оказывается, они не так страшны, как кажется на первый взгляд.
Как нарисовать путь Безье в пользовательском представлении
Это основные шаги:
- Создайте контур желаемой формы.
- Разделите контурный контур на сегменты линий, дуг и кривых.
- Создайте этот путь программно.
- Нарисуйте путь либо в
drawRect
или используяCAShapeLayer
,
Контур формы дизайна
Вы можете сделать что угодно, но в качестве примера я выбрал форму ниже. Это может быть всплывающая клавиша на клавиатуре.
Разделите путь на сегменты
Посмотрите на свой дизайн фигуры и разбейте его на более простые элементы линий (для прямых линий), дуг (для кругов и круглых углов) и кривых (для всего остального).
Вот как будет выглядеть наш пример дизайна:
- Черные - это отрезки
- Светло-голубые являются дуговыми сегментами
- Красные кривые
- Оранжевые точки - контрольные точки для кривых
- Зеленые точки - это точки между отрезками пути
- Пунктирные линии показывают ограничивающий прямоугольник
- Синие цифры - это сегменты в том порядке, в котором они будут добавлены программно
Построить путь программно
Мы произвольно начнем в левом нижнем углу и будем работать по часовой стрелке. Я буду использовать сетку на изображении, чтобы получить значения x и y для точек. Я здесь все жестко закодирую, но, конечно, вы не сделаете этого в реальном проекте.
Основной процесс:
- Создать новый
UIBezierPath
- Выберите начальную точку на пути с
moveToPoint
- Добавить сегменты к пути
- линия:
addLineToPoint
- дуга:
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 - Кривые Безье (интересные и наглядные иллюстрации)
- Кривые Безье (как они используются в графических приложениях)
- Кривые Безье (хорошее описание того, как получены математические формулы)
Было бы проще, если бы вы использовали CAShapeLayer
, как это:
CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];
И установить его path
:
[shapeView setPath:[self createPath].CGPath];
Наконец добавьте это:
[[self.view layer] addSublayer:shapeView];
Вы можете использовать CAShapeLayer
сделать это.
Как это...
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [self createPath].CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc...
shapeLayer.lineWidth = 2.0; //etc...
shapeLayer.position = CGPointMake(100, 100); //etc...
[self.layer addSublayer:shapeLayer];
Это затем добавит и нарисует путь без необходимости переопределения drawRect
,
Есть несколько способов выполнить то, что вы хотите. Больше всего я видел: переопределить drawRect, нарисовать вашу фигуру в CAShapeLayer и затем добавить ее в качестве подслоя в ваше представление, или нарисовать ваш путь в другом контексте, сохранить его в виде изображения и затем добавить в свой Посмотреть.
Все это разумный выбор, и какой из них лучше всего зависит от многих других факторов, таких как то, будете ли вы постоянно добавлять фигуры, как часто это называется и т. Д.
Как отмечалось в других постерах, использование слоя с фигурами - это хороший способ.
Слои формы a, вероятно, дадут вам лучшую производительность, чем переопределение drawRect.
Если вы хотите нарисовать свой путь самостоятельно, тогда да, вам нужно переопределить drawRect для вашего пользовательского класса представления.
Да, вы должны переопределить рисование, если хотите что-то нарисовать. Создание UIBezierPath можно сделать где угодно, но чтобы нарисовать что-то, вы должны сделать это внутри drawrect
метод
Вы должны звонить setNeedsDisplay
если вы переопределите drawRect в подклассе UIView, который в основном представляет собой пользовательский вид, рисующий что-то на экране, например, линии, изображение, прямоугольник.
Рисуя UIBezierPath на коде, созданном UIView, вы можете использовать события касания UIView, как показано ниже. Создайте глобальную переменную для начальной точки касания и конечной точки касания, как показано ниже:
CGPoint startingPoint;
CGPoint endingPoint;
А затем нарисуйте UIBezierPath, используя UIView Touchevents, как показано ниже:
/*Touch Start*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
startingPoint = [touch locationInView:self];
}
/*Touch End*/
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
/*Touch Move*/
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
endingPoint = [touch locationInView:self];
[self makeLineLayer:self.layer lineFromPointA:startingPoint
toPointB:endingPoint];
}
/*Draw UIBezierPath*/
-(void)makeLineLayer:(CALayer *)layer lineFromPointA:(CGPoint)pointA
toPointB:(CGPoint)pointB
{
CAShapeLayer *line = [CAShapeLayer layer];
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint: pointA];// Start Point
[linePath addLineToPoint:pointB];//End Point
line.path=linePath.CGPath;
line.fillColor = nil;
line.opacity = 2.0;
line.lineWidth = 4.0;
line.strokeColor = [UIColor redColor].CGColor;
[layer addSublayer:line];
}