Применение радиуса угла к определенному углу UIView внутри раскадровки не работает для всех углов - PullRequest
3 голосов
/ 30 мая 2019

Я сделал специальный класс для этого.Но это работает только для верхнего левого угла, а не для остальных:

@IBDesignable
public class RoundedView: UIView {

    @IBInspectable public var topLeft: Bool = false
    @IBInspectable public var topRight: Bool = false
    @IBInspectable public var bottomLeft: Bool = false
    @IBInspectable public var bottomRight: Bool = false
    @IBInspectable public var cornerRadius: Int = 0

    public override func awakeFromNib() {
        var options = UIRectCorner()

        if topLeft { options =  options.union(.topLeft) }
        if topRight { options =  options.union(.topRight) }
        if bottomLeft { options =  options.union(.bottomLeft) }
        if bottomRight { options =  options.union(.bottomRight) }

        let path = UIBezierPath(roundedRect:self.bounds,
                                byRoundingCorners:options,
                                cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))

        let maskLayer = CAShapeLayer()

        maskLayer.path = path.cgPath
        self.layer.mask = maskLayer
    }
}

Вот как это выглядит при работе в симуляторе, и я применяю радиус угла для всех 4 углов:

enter image description here

Что здесь происходит?TopLeft работает, TopRight немного, а нижние углы совсем нет.Я в замешательстве.

1 Ответ

5 голосов
/ 30 мая 2019

Вы должны обновить маску в переопределении layoutSubviews. Когда ограничения обновляют frame представления, вызывается layoutSubviews, так что это правильное место для обновления маски.


В качестве отступления, я бы не рекомендовал использовать awakeFromNib для настройки представления. Он прекрасно работает, если вы используете раскадровку, но программно созданные представления не будут вызывать это. Если вы хотите настроить представление при его создании, лучше поместить код в какой-то частный метод настройки, который вызывается init(frame:) и init(coder:). Таким образом, он работает как для раскадровок, так и для программно созданных представлений.

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


Таким образом:

@IBDesignable
public class RoundedView: UIView {

    @IBInspectable public var topLeft: Bool = false      { didSet { setNeedsLayout() } }
    @IBInspectable public var topRight: Bool = false     { didSet { setNeedsLayout() } }
    @IBInspectable public var bottomLeft: Bool = false   { didSet { setNeedsLayout() } }
    @IBInspectable public var bottomRight: Bool = false  { didSet { setNeedsLayout() } }
    @IBInspectable public var cornerRadius: CGFloat = 0  { didSet { setNeedsLayout() } }

    public override func layoutSubviews() {
        super.layoutSubviews()

        var options = UIRectCorner()

        if topLeft     { options.formUnion(.topLeft) }
        if topRight    { options.formUnion(.topRight) }
        if bottomLeft  { options.formUnion(.bottomLeft) }
        if bottomRight { options.formUnion(.bottomRight) }

        let path = UIBezierPath(roundedRect: bounds,
                                byRoundingCorners: options,
                                cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))

        let maskLayer = CAShapeLayer()

        maskLayer.path = path.cgPath
        layer.mask = maskLayer
    }
}

Или, в качестве альтернативы, если нацелена на iOS 11 и более поздние версии, мы можем позволить CoreAnimation выполнить округление:

@IBDesignable
public class RoundedView: UIView {

    @IBInspectable public var topLeft: Bool = false      { didSet { updateCorners() } }
    @IBInspectable public var topRight: Bool = false     { didSet { updateCorners() } }
    @IBInspectable public var bottomLeft: Bool = false   { didSet { updateCorners() } }
    @IBInspectable public var bottomRight: Bool = false  { didSet { updateCorners() } }
    @IBInspectable public var cornerRadius: CGFloat = 0  { didSet { updateCorners() } }

    public override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        updateCorners()
    }

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

private extension RoundedView {
    func updateCorners() {
        var corners = CACornerMask()

        if topLeft     { corners.formUnion(.layerMinXMinYCorner) }
        if topRight    { corners.formUnion(.layerMaxXMinYCorner) }
        if bottomLeft  { corners.formUnion(.layerMinXMaxYCorner) }
        if bottomRight { corners.formUnion(.layerMaxXMaxYCorner) }

        layer.maskedCorners = corners
        layer.cornerRadius = cornerRadius
    }
}

Хорошая вещь в этом последнем подходе заключается в том, что CoreAnimation будет соблюдать округление нашего угла, если мы получим анимацию frame представления:

enter image description here

Если вы используете вышеупомянутый подход layoutSubviews, вам придется управлять анимацией вручную (например, с помощью CADisplayLink).

...