UIBezierPath é uma classe do UIKit que encapsula um caminho vetorial para gráficos 2D no iOS. Ela combina a descrição geométrica do contorno com capacidades de desenho, permitindo criar linhas, curvas Bezier, arcos e retângulos. Ao contrário do CGPath, o UIBezierPath contém seus próprios métodos de desenho que funcionam diretamente no contexto gráfico atual. De acordo com Apple Developer Documentation, 2026, o UIBezierPath suporta todas as operações padrão do Quartz 2D em um invólucro orientado a objetos.
Pontos principais
UIBezierPath é uma classe do framework UIKit que herda de NSObject e implementa NSCopying, NSSecureCoding. Ela fornece uma API Objective-C/Swift de alto nível para construir e desenhar caminhos vetoriais. Internamente, o UIBezierPath armazena uma referência ao CGPathRef — um objeto do Core Graphics — mas adiciona métodos convenientes para desenho, modificação de geometria e gerenciamento de estilos.
A principal vantagem do UIBezierPath sobre o uso direto do CGPath são os métodos de desenho integrados. Chamar fill() ou stroke() em uma instância do UIBezierPath configura automaticamente o contexto e desenha o caminho. A classe também suporta as propriedades lineWidth, lineCapStyle, lineJoinStyle, miterLimit e flatness, permitindo controlar a aparência sem um objeto separado para armazenar estilos.
O UIBezierPath é usado na maioria dos aplicativos iOS para desenho personalizado dentro de UIView: desde ícones simples até gráficos animados complexos. O método draw(_:) da classe UIView é o local principal onde os desenvolvedores criam e desenham o UIBezierPath. A classe é totalmente compatível com Core Animation e pode ser usada para animação de caminhos através de CAShapeLayer e CABasicAnimation.
O método move(to:) move o ponto inicial do caminho para as coordenadas especificadas sem desenhar. Esta é uma chamada obrigatória antes de construir qualquer segmento. addLine(to:) adiciona um segmento reto da posição atual até o ponto indicado. O método close() fecha o contorno desenhando uma linha do último ponto até o primeiro ponto do último move(to:).
UIBezierPath fornece vários inicializadores convenientes para formas prontas. init(rect:) cria um retângulo, init(roundedRect:cornerRadius:) cria um retângulo com cantos arredondados. O parâmetro UIRectCorner permite arredondar apenas os cantos selecionados, como topLeft e bottomRight, deixando os demais retos. Isso é amplamente usado para criar botões e cartões personalizados.
init(ovalIn:) cria uma elipse inscrita no retângulo especificado. Se o retângulo for um quadrado, a elipse se torna um círculo. init(roundedRect:byRoundingCorners:cornerRadii:) é a opção mais flexível, permitindo definir raios diferentes para cada canto. Este inicializador foi introduzido no iOS 6 e continua sendo o padrão para criar contêineres arredondados.
let path = UIBezierPath()
path.move(to: CGPoint(x: 50, y: 50))
path.addLine(to: CGPoint(x: 200, y: 50))
path.addLine(to: CGPoint(x: 200, y: 150))
path.addLine(to: CGPoint(x: 125, y: 200))
path.addLine(to: CGPoint(x: 50, y: 150))
path.close()
addQuadCurve(to:controlPoint:) adiciona uma curva Bezier quadrática com um ponto de controle. Este tipo de curva é usado para curvaturas suaves simples quando se deseja minimizar os cálculos. Uma curva quadrática é sempre uma parábola e está garantidamente dentro do triângulo formado pelos pontos inicial, de controle e final.
addCurve(to:controlPoint1:controlPoint2:) adiciona uma curva Bezier cúbica com dois pontos de controle. As curvas cúbicas são usadas em todos os editores vetoriais modernos e fontes TrueType. Dois pontos de controle fornecem mais graus de liberdade para um controle preciso da forma. Este método é recomendado para construir contornos orgânicos e complexos.
Ao construir curvas, é importante manter o princípio de continuidade — a suavidade no ponto de conexão dos segmentos. Para continuidade C1 (tangente), o ponto de controle da próxima curva deve estar na mesma linha que o ponto de controle da anterior. Para continuidade C2 (curvatura), é necessária uma coordenação mais complexa, que é fornecida automaticamente no CAShapeLayer ao animar entre dois UIBezierPath com o mesmo número de pontos de controle.
| Método | Tipo | Pontos de controle | Grau |
|---|---|---|---|
| addQuadCurve | Bezier quadrática | 1 | 2 |
| addCurve | Bezier cúbica | 2 | 3 |
| addArc | Arco | — | — |
addArc(withCenter:radius:startAngle:endAngle:clockwise:) adiciona um arco circular com os parâmetros especificados. Os ângulos são indicados em radianos em relação ao centro do círculo. O ângulo zero corresponde à direção para a direita (3 horas em um mostrador de relógio). O parâmetro clockwise determina a direção: true para sentido horário, false para anti-horário. Os arcos são essenciais para gráficos de pizza, velocímetros e anéis de progresso interativos.
Para uma circunferência completa, use startAngle = 0 e endAngle = .pi * 2. Para criar um setor (como parte de um gráfico de pizza), use addArc seguido de addLine(to:) ao centro e close(). O UIBezierPath não tem um método addCircle embutido — um círculo é criado via init(ovalIn:) com um retângulo quadrado ou via addArc com um ângulo completo.
O método addArc(withCenter:radius:startAngle:endAngle:clockwise:) executa automaticamente move(to:) para o ponto inicial do arco se a posição atual não coincidir. Se a posição atual for diferente, uma linha reta é desenhada entre ela e o início do arco. Este comportamento difere do Core Graphics, onde um move(to:) explícito é necessário antes de cada novo contorno.
let center = CGPoint(x: 150, y: 150)
let slice = UIBezierPath()
slice.move(to: center)
slice.addArc(
withCenter: center,
radius: 100,
startAngle: 0,
endAngle: .pi * 0.75,
clockwise: true
)
slice.close()
fill() preenche o interior do caminho com a cor fillColor atual ou a cor definida no contexto gráfico através de setFill(). Para caminhos com autointerseções, a regra de preenchimento é determinada pela propriedade usesEvenOddFillRule. Se for true, a regra par-ímpar é usada: um ponto é considerado dentro da forma se um raio a partir dele cruzar o contorno um número ímpar de vezes.
stroke() desenha o contorno do caminho com as configurações atuais de lineWidth, lineCapStyle e lineJoinStyle. lineCapStyle define a forma das extremidades dos segmentos abertos: .butt (corte reto), .round (semicircular), .square (quadrado com saliência). lineJoinStyle define a forma das junções dos cantos: .miter (agudo), .round (arredondado), .bevel (chanfrado).
Para linhas tracejadas, usa-se a propriedade setLineDash(_:count:phase:). Ela aceita um array de comprimentos alternados de traços e espaços, o número de elementos e a fase inicial. A renderização do tracejado é realizada durante a rasterização do caminho — padrões complexos não aumentam a complexidade geométrica do contorno. O CAShapeLayer suporta animação da propriedade strokeEnd para um efeito de “desenho” do início ao fim.
override func draw(_ rect: CGRect) {
let path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 200, height: 200))
UIColor.blue.setFill()
UIColor.black.setStroke()
path.lineWidth = 4
path.fill()
path.stroke()
}
Vamos criar um indicador de progresso personalizado em forma de anel usando UIBezierPath. O anel é construído a partir de dois arcos do mesmo raio com cores de traçado diferentes. O primeiro arco é um círculo completo em cinza (fundo), o segundo é um segmento que mostra o progresso. Para animação, são usados CAShapeLayer e CABasicAnimation.
class RingView: UIView {
private let progressLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = bounds.width * 0.4
let lineWidth: CGFloat = 12
let backgroundRing = UIBezierPath()
backgroundRing.addArc(withCenter: center, radius: radius,
startAngle: -.pi / 2, endAngle: .pi * 1.5, clockwise: true)
progressLayer.path = backgroundRing.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.lineWidth = lineWidth
progressLayer.lineCap = .round
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeEnd = 0
layer.addSublayer(progressLayer)
}
func animateProgress(to value: CGFloat) {
CABasicAnimation(keyPath: "strokeEnd").apply {
$0.toValue = value
$0.duration = 1.5
$0.timingFunction = CAMediaTimingFunction(name: .easeOut)
progressLayer.add($0, forKey: nil)
}
progressLayer.strokeEnd = value
}
}
Neste exemplo, UIBezierPath é usado para criar um contorno circular, que é então passado para CAShapeLayer através da propriedade cgPath. A animação de strokeEnd de 0 a 1 visualiza o preenchimento do anel. Esta abordagem é usada em indicadores de carregamento, rastreadores de fitness e widgets de progresso. O método layoutSubviews garante a reconstrução do caminho quando o tamanho da View muda.
lineCap = .round adiciona extremidades arredondadas ao arco, dando ao indicador uma aparência mais cuidada. A propriedade strokeEnd é uma propriedade animável do CAShapeLayer que não requer redesenhar todo o contorno a cada quadro. A animação é executada inteiramente na GPU através do Core Animation, garantindo 60 FPS sem carga na CPU.
Perguntas frequentes
UIBezierPath é um invólucro orientado a objetos em torno do CGPath que contém métodos fill() e stroke() embutidos. CGPath é um objeto C imutável do Core Graphics que requer desenho explícito através de CGContext. UIBezierPath é mais conveniente para UIKit, enquanto CGPath é mais eficiente para reutilização.
Use init(roundedRect:byRoundingCorners:cornerRadii:) com os parâmetros .topLeft e .topRight para roundedRect. Este é o único método do UIBezierPath que permite arredondar seletivamente cantos sem criar um caminho composto complexo.
A propriedade cgPath retorna um CGPathRef imutável que pode ser passado para CAShapeLayer ou usado com cores e espessuras diferentes. Modificações no UIBezierPath (movimento de pontos) não afetam o cgPath obtido anteriormente até que seja explicitamente atualizado.
Verifique usesEvenOddFillRule — se o contorno tiver autointerseções, a regra par-ímpar pode deixar áreas não preenchidas. Defina usesEvenOddFillRule = false para a regra de preenchimento padrão non-zero winding.
Use CABasicAnimation com o caminho chave “path” no CAShapeLayer. Ambos os estados do caminho devem conter o mesmo número de segmentos e pontos de controle — caso contrário, a animação será irregular ou falhará.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também