Создать прямоугольник с двумя закругленными углами в Swift?

Мне нужно создать прямоугольник, который имеет только два закругленных угла в быстром (код Objective C также хорошо).

На данный момент мой код создает два прямоугольника с

CGPathCreateWithRoundedRect(CGRectMake(0, 0, 30, 60), 5, 5, nil);

а также

CGPathCreateWithRoundedRect(CGRectMake(0, 0, 30, 60), 0, 0, nil);

и объединение их (чтобы иметь два прямых угла и два закругленных), но я не доволен кодом и уверен, что должны быть гораздо лучшие способы сделать это.

Я новичок в iOS и графической разработке и Swift.

16 ответов

Решение

В Swift 2.3 вы можете сделать это

let maskPath = UIBezierPath(roundedRect: anyView.bounds,
            byRoundingCorners: [.BottomLeft, .BottomRight],
            cornerRadii: CGSize(width: 10.0, height: 10.0))

let shape = CAShapeLayer()
shape.path = maskPath.CGPath
view.layer.mask = shape

В Objective-C вы можете использовать UIBezierPath метод класса

bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:

пример реализации-

// set the corner radius to the specified corners of the passed container
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
    UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
                                                  byRoundingCorners:corners
                                                        cornerRadii:CGSizeMake(10.0, 10.0)];
    CAShapeLayer *shape = [[CAShapeLayer alloc] init];
    [shape setPath:rounded.CGPath];
    view.layer.mask = shape;
}

и вызвать вышеупомянутый метод как

[self setMaskTo:anyView byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight];

Swift 4+, iOS 11+

Если у вас уже есть UIView названный myView упоминается как IBOutletпопробуйте добавить следующие две строки в ViewDidLoad() или где это загружается:

myView.layer.cornerRadius = 10
myView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]

Вы можете изменить массив [] в любой комбинации MinX, MinY, MaxX, а также MaxY выбрать нужные углы. Приведенный выше пример закругляет нижние два угла.

Это просто еще один подход, может быть немного проще в зависимости от вашего дизайна.

Вот быстрое расширение Swift 3, которое вы можете использовать для округления и опциональных границ.

Примечание: если вы используете autolayout, вам может потребоваться вызвать это в одном из обратных вызовов жизненного цикла, таких как viewDidLayoutSubviews или же layoutSubviews после того, как вид был ограничен.

import UIKit

extension UIView {

    /**
     Rounds the given set of corners to the specified radius

     - parameter corners: Corners to round
     - parameter radius:  Radius to round to
     */
    func round(corners: UIRectCorner, radius: CGFloat) {
        _ = _round(corners: corners, radius: radius)
    }

    /**
     Rounds the given set of corners to the specified radius with a border

     - parameter corners:     Corners to round
     - parameter radius:      Radius to round to
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func round(corners: UIRectCorner, radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        let mask = _round(corners: corners, radius: radius)
        addBorder(mask: mask, borderColor: borderColor, borderWidth: borderWidth)
    }

    /**
     Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border

     - parameter diameter:    The view's diameter
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func fullyRound(diameter: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        layer.masksToBounds = true
        layer.cornerRadius = diameter / 2
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor;
    }

}

private extension UIView {

    @discardableResult func _round(corners: UIRectCorner, radius: CGFloat) -> CAShapeLayer {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
        return mask
    }

    func addBorder(mask: CAShapeLayer, borderColor: UIColor, borderWidth: CGFloat) {
        let borderLayer = CAShapeLayer()
        borderLayer.path = mask.path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.lineWidth = borderWidth
        borderLayer.frame = bounds
        layer.addSublayer(borderLayer)
    }

}

Swift 3 - Полезно UIView Расширение, когда нужно округлить определенные углы некоторых видов:

extension UIView {
  func round(corners: UIRectCorner, radius: CGFloat) {
    let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
    let mask = CAShapeLayer()
    mask.path = path.cgPath
    self.layer.mask = mask
  }
}

тогда просто используйте это так:

someView.round(corners: [.topLeft, .topRight], radius: 5)

Основываясь на превосходном ответе Санджея, я написал быстрое расширение CALayer для Swift 2.3, на случай, если вам понадобится сделать что-то вроде "только по нескольким углам" более одного раза.

extension CALayer {
  func roundCorners(corners: UIRectCorner, radius: CGFloat) {
    let maskPath = UIBezierPath(roundedRect: bounds,
                                byRoundingCorners: corners,
                                cornerRadii: CGSize(width: radius, height: radius))

    let shape = CAShapeLayer()
    shape.path = maskPath.CGPath
    mask = shape
  }
}

Использование:

myView.layer.roundCorners([.TopLeft, .TopRight], radius: myCornerRadius)

Swift 3.0 (в этом примере границы получены из представления, а не из слоя. Использование границ из представления заставит этот код работать с представлениями в UITableViewCell.):

func roundCorners(corners: UIRectCorner, radius: CGFloat, viewBounds: CGRect) {

    let maskPath = UIBezierPath(roundedRect: viewBounds,
                                byRoundingCorners: corners,
                                cornerRadii: CGSize(width: radius, height: radius))

    let shape = CAShapeLayer()
    shape.path = maskPath.cgPath
    mask = shape
}

Использование:

myView.layer.roundCorners(corners: [.topLeft, .topRight], radius: myCornerRadius, viewBounds: bounds)

iOS 11+ Только | Вы можете проверить статистику использования iOS здесь

объяснение

Так как CACornerMask rawValue является UInt Вы теперь, когда CACornerMask rawValue - сумма каждого CACornerMask.Element RawValue

Более конкретно:

  • Верхний левый (layerMinXMinYCorner) = 1
  • В правом верхнем углу (layerMaxXMinYCorner) = 2
  • Нижний левый (layerMinXMaxYCorner) = 4
  • Нижний правый (layerMaxXMaxYCorner) = 8

Так, например, если вы хотите верхний левый и верхний правый углы, вы можете просто набрать CACornerMask(rawValue: 3),


пример

Ниже простое расширение UIView

extension UIView {
    enum Corner:Int {
        case bottomRight = 0,
        topRight,
        bottomLeft,
        topLeft
    }

    private func parseCorner(corner: Corner) -> CACornerMask.Element {
        let corners: [CACornerMask.Element] = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
        return corners[corner.rawValue]
    }

    private func createMask(corners: [Corner]) -> UInt {
        return corners.reduce(0, { (a, b) -> UInt in
            return a + parseCorner(corner: b).rawValue
        })
    }

    func roundCorners(corners: [Corner], amount: CGFloat = 5) {
        layer.cornerRadius = amount
        let maskedCorners: CACornerMask = CACornerMask(rawValue: createMask(corners: corners))
        layer.maskedCorners = maskedCorners
    }
}

Вы можете использовать это как:

let myRect = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
myRect.roundCorners(corners: [.topRight, .topLeft])

2017...

@IBDesignable
class RoundedEnds: UIView {

    override func layoutSubviews() { setup() } // "layoutSubviews" is best

    func setup() {
        let r = self.bounds.size.height / 2
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius:r)
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
    }
}

Только для некоторых углов, просто измените на:

roundedRect: self.bounds,
  byRoundingCorners: [.topLeft, .topRight],
  cornerRadii: CGSize(width: r, height: r)

Обратите внимание, что, как обычно, в Swift было много небольших изменений, например, использование заглавных букв в константах и ​​т. Д.

Вот что вы делаете в Swift 2.0

var maskPath = UIBezierPath(roundedRect: anyView.bounds,
        byRoundingCorners: [.BottomLeft, .BottomRight],
        cornerRadii: CGSize(width: 10.0, height: 10.0))

Свифт 4:

    let maskPath = UIBezierPath(roundedRect: view.bounds,
                                byRoundingCorners: [.allCorners],
                                cornerRadii: CGSize(width: 10.0, height: 10.0))

    let shape = CAShapeLayer()
    shape.path = maskPath.cgPath
    cell.layer.mask = shape


    return cell

Обновлен ответ iWasRobbed для работы с версией Swift 3.0 GM:

import UIKit

extension UIView {

    /**
     Rounds the given set of corners to the specified radius

     - parameter corners: Corners to round
     - parameter radius:  Radius to round to
     */
    func round(corners: UIRectCorner, radius: CGFloat) {
        _round(corners: corners, radius: radius)
    }

    /**
     Rounds the given set of corners to the specified radius with a border

     - parameter corners:     Corners to round
     - parameter radius:      Radius to round to
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func round(corners: UIRectCorner, radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        let mask = _round(corners: corners, radius: radius)
        addBorder(mask: mask, borderColor: borderColor, borderWidth: borderWidth)
    }

    /**
     Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border

     - parameter diameter:    The view's diameter
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func fullyRound(diameter: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        layer.masksToBounds = true
        layer.cornerRadius = diameter / 2
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor;
    }

}

private extension UIView {

    @discardableResult func _round(corners: UIRectCorner, radius: CGFloat) -> CAShapeLayer {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
        return mask
    }

    func addBorder(mask: CAShapeLayer, borderColor: UIColor, borderWidth: CGFloat) {
        let borderLayer = CAShapeLayer()
        borderLayer.path = mask.path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.lineWidth = borderWidth
        borderLayer.frame = bounds
        layer.addSublayer(borderLayer)
    }

}
extension CACornerMask {

    public static var leftBottom     : CACornerMask { get { return .layerMinXMaxYCorner}}
    public static var rightBottom    : CACornerMask { get { return .layerMaxXMaxYCorner}}
    public static var leftTop        : CACornerMask { get { return .layerMaxXMinYCorner}}
    public static var rightTop       : CACornerMask { get { return .layerMinXMinYCorner}}
}

extension CALayer {

    func roundCorners(_ mask:CACornerMask,corner:CGFloat) {
        self.maskedCorners = mask
        self.cornerRadius = corner
    }
}

self.viewBack.layer.roundCorners([.leftBottom,.rightBottom], corner: 23)

Таким образом, вы можете создать такое красивое расширение:

extension UIView {

    func roundCorners(_ corners: UIRectCorner, radius: Double) {
        let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let shape = CAShapeLayer()
        shape.path = maskPath.cgPath
        layer.mask = shape
    }

}

Используйте это так:

view.roundCorners([.topRight, .bottomRight], radius: 10)

Вот значения всех углов:

  • .верхний левый
  • .в правом верхнем углу
  • .левая нижняя
  • .внизу справа

Swift 5: для скругленных углов в верхнем левом и правом верхнем углу.

      yourView.layer.cornerRadius = 12
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.layer.cornerRadius = 10.0
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner]

Лучший способ сделать это!

Версия iWasRobbed для Objective-C:

UIView + RoundCorners.h

#import <UIKit/UIKit.h>

@interface UIView (RoundCorners)

/**
 Rounds the given set of corners to the specified radius

 - parameter corners: Corners to round
 - parameter radius:  Radius to round to
 */
- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius;

/**
 Rounds the given set of corners to the specified radius with a border

 - parameter corners:     Corners to round
 - parameter radius:      Radius to round to
 - parameter borderColor: The border color
 - parameter borderWidth: The border width
 */
- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;

/**
 Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border

 - parameter diameter:    The view's diameter
 - parameter borderColor: The border color
 - parameter borderWidth: The border width
 */
- (void)fullyRoundWithDiameter:(CGFloat)diameter borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;

@end

UIView + RoundCorners.m

#import "UIView+RoundCorners.h"

@implementation UIView (RoundCorners)

- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    [self _roundCorners:corners radius:radius];
}

- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
    CAShapeLayer *mask = [self _roundCorners:corners radius:radius];
    [self addBorderWithMask:mask borderColor:borderColor borderWidth:borderWidth];
}

- (void)fullyRoundWithDiameter:(CGFloat)diameter borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
    self.layer.masksToBounds = YES;
    self.layer.cornerRadius = diameter / 2;
    self.layer.borderWidth = borderWidth;
    self.layer.borderColor = borderColor.CGColor;
}

- (CAShapeLayer *)_roundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
    CAShapeLayer *mask = [CAShapeLayer layer];
    mask.path = path.CGPath;
    self.layer.mask = mask;
    return mask;
}

- (void)addBorderWithMask:(CAShapeLayer *)mask borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
    CAShapeLayer *borderLayer = [CAShapeLayer layer];
    borderLayer.path = mask.path;
    borderLayer.fillColor = UIColor.clearColor.CGColor;
    borderLayer.strokeColor = borderColor.CGColor;
    borderLayer.lineWidth = borderWidth;
    borderLayer.frame = self.bounds;
    [self.layer addSublayer:borderLayer];
}

@end

Один простой взлом может быть следующим. Взгляните, как показано на рисунке ниже. Красный вид будет иметь закругленные углы, а желтый вид (внутри красного вида) предотвратит скругление углов.

Теперь напишите ниже код для Red View.

        self.myView.layer.cornerRadius = 15

Убедитесь, что вы не пишете код как clipsToBounds = true или masksToBounds = true.

Изображение ниже - результат

Размещение желтого вида решит, какие 2 угла не будут закругляться. Надеюсь, это легко реализовать.

Другие вопросы по тегам