Маскированный CALayer в iOS

Я пытаюсь создать ярлык (или любой другой вид в этом отношении) с одним закругленным углом и штрихом/границей. Я могу достичь первого, используя следующий код:

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.label.bounds
                                      byRoundingCorners:UIRectCornerBottomRight
                                            cornerRadii:CGSizeMake(16.0f, 16.0f)];

CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = self.label.bounds;
shape.path = maskPath.CGPath;

self.label.layer.mask = shape;

Что отлично подходит для закругленного угла, но использование следующего кода не влияет на ход так, как я хочу. Вместо того, чтобы создать черный (или что-то из backgroundColor of self.label установлено) квадратная.

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.label.bounds
                                      byRoundingCorners:UIRectCornerBottomRight
                                            cornerRadii:CGSizeMake(16.0f, 16.0f)];

CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = self.label.bounds;
shape.path = maskPath.CGPath;

// Add stroke
shape.borderWidth = 1.0f;
shape.borderColor = [UIColor whiteColor].CGColor;

self.label.backgroundColor = [UIColor blackColor];
self.label.layer.mask = shape;

Любые предложения о том, как я могу применить произвольный цветной штрих, следующий за маскированным путем?

Ответ 1

Вы на правильном пути с слоем формы. Но у вас должно быть два разных слоя. Сначала слой маски, как в первом примере, который маскирует ваше представление (отсекает области, которые вы не хотите видеть)

Затем вы добавляете слой формы, но не как слой маски. Кроме того, убедитесь, что не используйте borderWidth и borderColor, но выполните штрих.

//
// Create your mask first
//
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.label.bounds
                                      byRoundingCorners:UIRectCornerBottomRight
                                            cornerRadii:CGSizeMake(16.0f, 16.0f)];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.label.bounds;
maskLayer.path = maskPath.CGPath;
self.label.layer.mask = maskLayer;    

//
// And then create the outline layer
//
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = self.label.bounds;
shape.path = maskPath.CGPath;
shape.lineWidth = 3.0f;
shape.strokeColor = [UIColor whiteColor].CGColor;
shape.fillColor = [UIColor clearColor].CGColor;
[self.label.layer addSublayer:shape];

Имейте в виду, что ваш путь слоя инсульта должен быть внутри (меньше) пути к маске. В противном случае поглаженный путь будет замаскирован слоем маски. Я установил lineWith в 3, что делает его таким, чтобы вы могли видеть половину ширины (1,5 px), а другая половина будет вне маски.

Ответ 2

Если вы подклассом CALayer, вы можете создать его с помощью маски, которую хотите, а также переопределить layoutSubLayers, чтобы включить рамку, которую вы хотите в этой маске.

Мог бы сделать это несколько способов. Ниже я делаю это, используя path данной маски и присваивая это свойству классу, которое будет использоваться для построения новой границы в layoutSubLayers. Существует потенциал, что этот метод можно было бы назвать несколько раз, поэтому я также задал логическое значение для отслеживания этого. (Также можно назначить границу как свойство класса и удалять/повторно добавлять каждый раз. На данный момент я использую проверку bool.

Swift 3:

class CustomLayer: CALayer {

    private var path: CGPath?
    private var borderSet: Bool = false

    init(maskLayer: CAShapeLayer) {
        super.init()
        self.path = maskLayer.path
        self.frame = maskLayer.frame
        self.bounds = maskLayer.bounds
        self.mask = maskLayer
    }

    override func layoutSublayers() {

        let newBorder = CAShapeLayer()

        newBorder.lineWidth = 12
        newBorder.path = self.path
        newBorder.strokeColor = UIColor.black.cgColor
        newBorder.fillColor = nil


        if(!borderSet) {
            self.addSublayer(newBorder)
            self.borderSet = true
        }

    }

    required override init(layer: Any) {
        super.init(layer: layer)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}