【问题标题】:Create a rectangle with just two rounded corners in swift?快速创建一个只有两个圆角的矩形?
【发布时间】:2015-06-19 13:21:24
【问题描述】:

我需要快速创建一个只有两个圆角的矩形(Objective C 代码也可以)。

目前我的代码正在使用

创建两个矩形
CGPathCreateWithRoundedRect(CGRectMake(0, 0, 30, 60), 5, 5, nil);

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

并合并它们(有两个直角和两个圆角),但我对代码不满意,我很确定应该有更好的方法来做到这一点。

我是 iOS 和图形开发和 swift 的新手。

【问题讨论】:

标签: ios swift core-graphics


【解决方案1】:

更新:请参阅下面的 this answer 了解 Swift 4 / iOS 11,这要容易得多


这是一个快速的 Swift 3 扩展,您可以使用它来进行舍入和可选边框。

注意:如果您使用自动布局,您可能需要在视图受到约束后在 viewDidLayoutSubviewslayoutSubviews 等视图生命周期回调之一中调用它。

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)
    }
    
}

【讨论】:

  • 我在 UITextField 上使用它,但它改变了文本字段的宽度。为什么它会改变宽度(使用 Autolayout 设置)。
  • 更新:只需要在viewDidLayoutSubviews中调用扩展
  • @Onichan 我添加了一个fullyRound 方法,该方法也适用于自动布局视图,因为如果从viewDidLoad 代码调用框架则不会设置
  • @DaveG 适用于任何 UIView 子类(如 UIButton、UILabel、UITextField 等)
  • 小心addBorder(mask:borderColor:borderWidth:) 方法,因为它总是添加一个新层。如果在空的 UIView 中使用 round(corners:radius:borderColor:borderWidth:) 方法调用 viewDidLayoutSubviewslayoutSubviews 5 次...该视图将有 5 个子层!
【解决方案2】:

Swift 4+、iOS 11+

如果您已经有一个名为myViewUIView 被引用为IBOutlet,请尝试在ViewDidLoad() 或正在加载它的任何位置添加以下两行:

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

您可以将数组[] 更改为MinXMinYMaxXMaxY 的任意组合以选择所需的角。上面的示例将底部的两个角倒圆。

这只是另一种方法,根据您的设计可能会更简单一些。

【讨论】:

  • 大警告,此解决方案仅适用于 iOS 11+
  • 对于前两个角,您可以使用 view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
  • 非常感谢。虽然像 layerMinXMinYCorner 这样的命名在技术上是正确的,但我想知道为什么 Swift 有时必须那么丑
【解决方案3】:

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];

【讨论】:

  • 我可以用你的回答解决我的问题。但只是好奇,因为我试图使用 UIBezierPath.addArcWithCenter 自己绘制圆角,并且开始和结束角度与developer.apple.com/library/ios/documentation/UIKit/Reference/… 上记录的所有角度都不匹配
  • 你问的是使用起始角和结束角吗?
  • #define RADIANS(degrees) ((degrees) / (180.0 / M_PI))
  • 并用作 - double startAngle = RADIANS(45);双 endAngle = RADIANS(135);
  • 不适用于底部。即 bottomLeft & bottomRight
【解决方案4】:

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)

【讨论】:

  • 如何为这个视图设置边框或阴影?
  • @VadlapalliMasthan 与您通常的方式相同。只需确保在圆角并应用阴影和边框之前设置视图的框架
【解决方案5】:

在 Sanjay 的出色答案的基础上,我为 Swift 2.3 编写了一个快速的 CALayer 扩展,以防您需要多次执行这种“只绕一些角落”的事情。

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)

【讨论】:

  • (好一个 - 像往常一样,Swift 有许多小的变化,例如常量的大写等)
  • 对我来说,有时当我将顶角变圆时图像不会显示,所以我需要在之前的行中添加 myView.layoutIfNeeded()。
  • hi @satoukum - 在现代 Xcode 中处理该问题的正确方法如下面的回答所示,干杯
【解决方案6】:

仅限 iOS 11+ |你可以查看iOS使用统计here

说明

由于CACornerMask rawValue 是UInt,你知道CACornerMask rawValue 是每个CACornerMask.Element 的总和rawValue

更具体地说:

  • 左上角 (layerMinXMinYCorner) = 1
  • TopRight (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])

【讨论】:

    【解决方案7】:

    这是您在 Swift 2.0

    中所做的事情
    var maskPath = UIBezierPath(roundedRect: anyView.bounds,
            byRoundingCorners: [.BottomLeft, .BottomRight],
            cornerRadii: CGSize(width: 10.0, height: 10.0))
    

    【讨论】:

    • 不适用于底部。即 bottomLeft & bottomRight
    【解决方案8】:

    2021 年最新...

    请注意,自从很久以前提出这个问题以来,语法/系统发生了很大变化!

    import UIKit
    @IBDesignable
    class RoundedEnds: UIView {
        
        override func layoutSubviews() {
            super.layoutSubviews()
            setup()
        }
        
        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
        }
    }
    

    对于某些角落,只需将path 代码行更改为:

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

    【讨论】:

      【解决方案9】:

      斯威夫特 4:

      let maskPath = UIBezierPath(
                  roundedRect: view.bounds,
                  byRoundingCorners: [.allCorners],
                  cornerRadii: CGSize(width: 10.0, height: 10.0)
              )
      
      let shape = CAShapeLayer()
      shape.path = maskPath.cgPath
      
      view.layer.mask = shape
      

      【讨论】:

      • 如果我只想要四个角中的三个呢? [.bottomLeft, .bottomRight, .topRight] 不工作。
      【解决方案10】:

      更新了 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)
          }
      
      }
      

      【讨论】:

        【解决方案11】:
        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)
        

        【讨论】:

        • 嗨,Mohammad Akbari,欢迎您。请考虑添加解释并正确格式化代码。
        【解决方案12】:

        总之,你可以像这样创建漂亮的扩展:

        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)
        

        这是所有角的值:

        • .topLeft
        • .topRight
        • .bottomLeft
        • .bottomRight

        【讨论】:

          【解决方案13】:
          view.layer.cornerRadius = 10.0
          view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner]
          

          最好的方法!

          【讨论】:

            【解决方案14】:

            Swift 5:用于左上角和右上角的圆角。

            yourView.layer.cornerRadius = 12
            yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
            

            【讨论】:

              【解决方案15】:

              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
              

              【讨论】:

                【解决方案16】:

                一个简单的hack可能如下。采取如下图示例中的视图。 Red View 将具有圆角,Yellow View(在 Red View 内)将防止圆角

                现在为 Red View 编写以下代码。

                        self.myView.layer.cornerRadius = 15
                

                确保不要将任何代码编写为 clipsToBounds = truema​​sksToBounds = true

                下图是结果

                黄色视图的位置将决定哪 2 个角不会被圆角。希望这很容易实现。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-08-10
                  • 2016-04-14
                  • 1970-01-01
                  • 2015-05-06
                  • 1970-01-01
                  • 2017-03-14
                  • 1970-01-01
                  相关资源
                  最近更新 更多