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

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

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

enter image description here

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

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

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

enter image description here

  • черный отрезков
  • светло-голубые сегменты дуги
  • красные кривые
  • Оранжевые точки являются контрольными точками для кривых
  • зеленые точки-это точки между путями сегменты
  • пунктирные линии показывают прямоугольник
  • темно-синие числа-это сегменты в том порядке, в котором они будут добавлены программно

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

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

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

  1. создать новый UIBezierPath
  2. выберите начальную точку на пути с moveToPoint
  3. добавить сегменты в путь
    • строку: addLineToPoint
    • arc: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)

}

мы получаем...

enter image description here

Хм, это немного мало, потому что я жестко закодировал все цифры. Я могу масштабировать размер пути вверх, хотя, вот так:

let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath

enter image description here

Метод 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
    }
}

что дает нам тот же результат...

enter image description here

дальнейшего изучения

я действительно рекомендуем ознакомиться со следующими материалами. Это то, что наконец сделало пути Безье понятными для меня. (И научил меня произносить это: /bbɛ 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 для вашего пользовательского класса представления.


Да, вы должны переопределить drawrect, если хотите что-то нарисовать.Создание UIBezierPath можно сделать в любом месте, но чтобы нарисовать что-то, вы должны сделать это внутри drawrect метод

вы должны звонить setNeedsDisplay Если вы переопределите drawRect в подклассе UIView, который в основном представляет собой пользовательский вид, рисующий что-то на экране, например линии,изображение , прямоугольник.