Рисование 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 ответов

Решение

Не так давно я даже не знал, как произносить Безье, не говоря уже о том, как использовать пути Безье для создания произвольной формы. Вот что я узнал. Оказывается, они не так страшны, как кажется на первый взгляд.

Как нарисовать путь Безье в пользовательском представлении

Это основные шаги:

  1. Создайте контур желаемой формы.
  2. Разделите контурный контур на сегменты линий, дуг и кривых.
  3. Создайте этот путь программно.
  4. Нарисуйте путь либо в drawRect или используя CAShapeLayer,

Контур формы дизайна

Вы можете сделать что угодно, но в качестве примера я выбрал форму ниже. Это может быть всплывающая клавиша на клавиатуре.

Разделите путь на сегменты

Посмотрите на свой дизайн фигуры и разбейте его на более простые элементы линий (для прямых линий), дуг (для кругов и круглых углов) и кривых (для всего остального).

Вот как будет выглядеть наш пример дизайна:

  • Черные - это отрезки
  • Светло-голубые являются дуговыми сегментами
  • Красные кривые
  • Оранжевые точки - контрольные точки для кривых
  • Зеленые точки - это точки между отрезками пути
  • Пунктирные линии показывают ограничивающий прямоугольник
  • Синие цифры - это сегменты в том порядке, в котором они будут добавлены программно

Построить путь программно

Мы произвольно начнем в левом нижнем углу и будем работать по часовой стрелке. Я буду использовать сетку на изображении, чтобы получить значения x и y для точек. Я здесь все жестко закодирую, но, конечно, вы не сделаете этого в реальном проекте.

Основной процесс:

  1. Создать новый UIBezierPath
  2. Выберите начальную точку на пути с moveToPoint
  3. Добавить сегменты к пути
    • линия: addLineToPoint
    • дуга: addArcWithCenter
    • кривая: addCurveToPoint
  4. Закройте путь с 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ɪ/.)

Было бы проще, если бы вы использовали 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];
 }
Другие вопросы по тегам