2016-11-03 3 views
0

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

Например это код для пользовательского UIView:

class BottomOverlayView: UIView { 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     drawCircle() 
    } 

    fileprivate func drawCircle(){ 

     let circleRadius: CGFloat = 80 
     let topMidRectangle = CGRect(x: 0, y: 0, width: circleRadius*2, height: circleRadius*2) 

     let circle: CAShapeLayer = CAShapeLayer() 
     circle.position = CGPoint(x: (frame.width/2)-circleRadius, y: 0-circleRadius) 
     circle.fillColor = UIColor.black.cgColor 
     circle.path = UIBezierPath(ovalIn: topMidRectangle).cgPath 
     circle.fillRule = kCAFillRuleEvenOdd 

     self.layer.mask = circle 
     self.clipsToBounds = true 
    } 
} 

Вот что я надеюсь достичь (светло-голубой является UIView, темно-синий фон):

What I want

Но вот что я получаю вместо этого. (Каждый раз, независимо от того, что я стараюсь)

What I get

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

EDIT: Вопрос, что это, предположительно, дубликат, я уже пытался и не смог получить работу. Возможно, я делал это неправильно или использовал его в неправильном контексте. Я не был знаком ни с одним из этих методов, а использование указателей показалось ему немного устаревшим. В принятом ответе гораздо лучше объясняется, как это можно реализовать с использованием гораздо более широко используемых UIBezierPaths, а также в контексте пользовательского UIView.

+0

Reilem - Он работает, если вы хотите использовать два пути Мэтта с четным правилом заполнения, чтобы нарисовать путь всего, что не замаскировано. Я лично считаю, что это противоречит интуиции, когда я могу просто нарисовать путь маски, но каждому свой. Во всяком случае, я обновил свой ответ обеими подходами. – Rob

ответ

2

Я предлагаю нарисовать путь для вашей маски, например. в Swift 3

// BottomOverlayView.swift 

import UIKit 

@IBDesignable 
class BottomOverlayView: UIView { 

    @IBInspectable 
    var radius: CGFloat = 100 { didSet { updateMask() } } 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     updateMask() 
    } 

    private func updateMask() { 
     let path = UIBezierPath() 
     path.move(to: bounds.origin) 
     let center = CGPoint(x: bounds.origin.x + bounds.size.width/2.0, y: bounds.origin.y) 
     path.addArc(withCenter: center, radius: radius, startAngle: .pi, endAngle: 0, clockwise: false) 
     path.addLine(to: CGPoint(x: bounds.origin.x + bounds.size.width, y: bounds.origin.y)) 
     path.addLine(to: CGPoint(x: bounds.origin.x + bounds.size.width, y: bounds.origin.y + bounds.size.height)) 
     path.addLine(to: CGPoint(x: bounds.origin.x, y: bounds.origin.y + bounds.size.height)) 
     path.close() 

     let mask = CAShapeLayer() 
     mask.path = path.cgPath 

     layer.mask = mask 
    } 
} 

Примечание, я отлажены это, чтобы установить маску в двух местах:

  • От layoutSubviews: Таким образом, если кадр изменения, например, в результате автоматического макета (или вручную изменив значение frame или что-то еще), он будет соответствующим образом обновляться; и

  • Если вы обновите radius: Таким образом, если вы используете это в раскадровке или если программным образом изменить радиус, это отразит это изменение.

Таким образом, вы можете наложить на половину высоты, светло-голубой BottomOverlayView на вершине темно-синего UIView, например, так:

enter image description here

Это дает:

enter image description here


Если вы хотите использовать «вырезать отверстие» метод, предложенный в дублирующем ответе, метод updateMask будет:

private func updateMask() { 
    let center = CGPoint(x: bounds.origin.x + bounds.size.width/2.0, y: bounds.origin.y) 

    let path = CGMutablePath() 
    path.addArc(center: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: false) 
    path.addRect(bounds) 

    let mask = CAShapeLayer() 
    mask.fillRule = kCAFillRuleEvenOdd 
    mask.path = path 

    layer.mask = mask 
} 

Я лично найти путь в пределах пути с четным нечетным правилом, немного счетчиком -Интуитивный.Где я могу (например, этот случай), я просто предпочитаю просто рисовать путь маски. Но если вам нужна маска с вырезанием, этот подход с четным четным заполнением может оказаться полезным.

+0

Большое вам спасибо за это! Очень хороший ответ на мой вопрос, я даже не знал, что пути могут быть использованы таким образом, поэтому спасибо, что указали на это. Я уже включил его в свой код, и похоже, все будет работать. Я положил это как принятый ответ. :) – Reilem

Смежные вопросы