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 из 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;

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


person Steve Wilford    schedule 21.08.2013    source источник


Ответы (2)


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

Затем вы также добавляете слой формы, но не как слой маски. Кроме того, убедитесь, что вы используете не 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 пикселя), а другая половина будет за пределами маски.

person Accatyyc    schedule 21.08.2013
comment
Это работает, но теперь текст метки закрыт слоем shape :( - person Steve Wilford; 21.08.2013
comment
ОК, мне удалось заставить его работать, применив маску к UIView и добавив UILabel в качестве подвида. Большое спасибо :) - person Steve Wilford; 21.08.2013
comment
StrokeLayer закрывает метку, поэтому я добавил это, чтобы исправить это shape.fillColor = [UIColor clearColor].CGColor - person name-it; 17.07.2014

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

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

Свифт 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")
    }
}
person rambossa    schedule 23.08.2017