125

在我的应用程序中 - 有四个按钮命名如下:

  • 左上方
  • 左下方
  • 右上
  • 右下

在按钮上方有一个图像视图(或 UIView)。

现在,假设用户点击左上角的按钮。上方的图像/视图应在该特定角处四舍五入。

我在将圆角应用于 UIView 时遇到了一些困难。

现在我正在使用以下代码将圆角应用于每个视图:

    // imgVUserImg is a image view on IB.
    imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"any Url Here"];
    CALayer *l = [imgVUserImg layer];
    [l setMasksToBounds:YES];
    [l setCornerRadius:5.0];  
    [l setBorderWidth:2.0];
    [l setBorderColor:[[UIColor darkGrayColor] CGColor]];

上面的代码将圆度应用于提供的视图的每个角。相反,我只想对选定的角应用圆度,例如 - 顶部 / 顶部 + 左侧 / 底部 + 右侧等。

可能吗?如何?

4

14 回答 14

358

从 iOS 3.2 开始,您可以使用UIBezierPaths 的功能来创建一个开箱即用的圆角矩形(只有您指定的角是圆角的)。然后,您可以将其用作 a 的路径CAShapeLayer,并将其用作视图层的蒙版:

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds 
                                               byRoundingCorners:UIRectCornerTopLeft
                                                     cornerRadii:CGSizeMake(10.0, 10.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageView.bounds;
maskLayer.path = maskPath.CGPath;

// Set the newly created shape layer as the mask for the image view's layer
imageView.layer.mask = maskLayer;

就是这样 - 无需在 Core Graphics 中手动定义形状,无需在 Photoshop 中创建蒙版图像。该层甚至不需要失效。应用圆角或更改为新角就像定义一个新角UIBezierPath并将其CGPath用作蒙版层的路径一样简单。该方法的corners参数bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:是位掩码,因此可以通过 ORing 将多个角圆角。


编辑:添加阴影

如果您想为此添加阴影,则需要做更多的工作。

因为“ imageView.layer.mask = maskLayer”应用了蒙版,所以阴影通常不会出现在蒙版之外。诀窍是使用透明视图,然后将两个子层 ( CALayers) 添加到视图的层:shadowLayerroundedLayer. 两者都需要使用UIBezierPath. 图像作为 的内容添加roundedLayer

// Create a transparent view
UIView *theView = [[UIView alloc] initWithFrame:theFrame];
[theView setBackgroundColor:[UIColor clearColor]];

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:theView.bounds 
                                               byRoundingCorners:UIRectCornerTopLeft
                                                     cornerRadii:CGSizeMake(10.0f, 10.0f)];

// Create the shadow layer
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:theView.bounds];
[shadowLayer setMasksToBounds:NO];
[shadowLayer setShadowPath:maskPath.CGPath];
// ...
// Set the shadowColor, shadowOffset, shadowOpacity & shadowRadius as required
// ...

// Create the rounded layer, and mask it using the rounded mask layer
CALayer *roundedLayer = [CALayer layer];
[roundedLayer setFrame:theView.bounds];
[roundedLayer setContents:(id)theImage.CGImage];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
[maskLayer setFrame:theView.bounds];
[maskLayer setPath:maskPath.CGPath];

roundedLayer.mask = maskLayer;

// Add these two layers as sublayers to the view
[theView.layer addSublayer:shadowLayer];
[theView.layer addSublayer:roundedLayer];
于 2011-04-29T00:48:12.910 回答
45

我在如何在 iPhone 上创建圆角 UILabel?以及How is a rounded rect view with transparent on iphone 中的代码?制作此代码。

然后我意识到我回答了错误的问题(给出了一个圆形的 UILabel 而不是 UIImage)所以我用这段代码来改变它:

http://discussions.apple.com/thread.jspa?threadID=1683876

使用 View 模板制作一个 iPhone 项目。在视图控制器中,添加以下内容:

- (void)viewDidLoad
{
    CGRect rect = CGRectMake(10, 10, 200, 100);
    MyView *myView = [[MyView alloc] initWithFrame:rect];
    [self.view addSubview:myView];
    [super viewDidLoad];
}

MyView只是一个UIImageView子类:

@interface MyView : UIImageView
{
}

我以前从未使用过图形上下文,但我设法将这段代码拼凑在一起。它缺少两个角的代码。如果你阅读了代码,你可以看到我是如何实现的(通过删除一些CGContextAddArc调用,并删除代码中的一些半径值。所有角的代码都在那里,所以以它为起点并删除创建不需要的角的部分。请注意,如果需要,您也可以制作具有 2 个或 3 个圆角的矩形。

代码并不完美,但我相信你可以稍微整理一下。

static void addRoundedRectToPath(CGContextRef context, CGRect rect, float radius, int roundedCornerPosition)
{

    // all corners rounded
    //  CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
    //  CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
    //  CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
    //                  radius, M_PI / 4, M_PI / 2, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, 
    //                          rect.origin.y + rect.size.height);
    //  CGContextAddArc(context, rect.origin.x + rect.size.width - radius, 
    //                  rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
    //  CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, 
    //                  radius, 0.0f, -M_PI / 2, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
    //  CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
    //                  -M_PI / 2, M_PI, 1);

    // top left
    if (roundedCornerPosition == 1) {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
        CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
                        radius, M_PI / 4, M_PI / 2, 1);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, 
                                rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
    }   

    // bottom left
    if (roundedCornerPosition == 2) {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, 
                                rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
        CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
                        -M_PI / 2, M_PI, 1);
    }

    // add the other corners here


    CGContextClosePath(context);
    CGContextRestoreGState(context);
}


-(UIImage *)setImage
{
    UIImage *img = [UIImage imageNamed:@"my_image.png"];
    int w = img.size.width;
    int h = img.size.height;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGContextBeginPath(context);
    CGRect rect = CGRectMake(0, 0, w, h);


    addRoundedRectToPath(context, rect, 50, 1);
    CGContextClosePath(context);
    CGContextClip(context);

    CGContextDrawImage(context, rect, img.CGImage);

    CGImageRef imageMasked = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    [img release];

    return [UIImage imageWithCGImage:imageMasked];
}

替代文字 http://nevan.net/skitch/skitched-20100224-092237.png

不要忘记,您需要在其中安装 QuartzCore 框架才能使其正常工作。

于 2010-02-23T14:59:44.513 回答
18

I have used this code in many places in my code and it works 100% correctly. You can change any corder by changed one property "byRoundingCorners:UIRectCornerBottomLeft"

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerBottomLeft cornerRadii:CGSizeMake(10.0, 10.0)];

                CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
                maskLayer.frame = view.bounds;
                maskLayer.path = maskPath.CGPath;
                view.layer.mask = maskLayer;
                [maskLayer release];
于 2012-04-20T18:33:05.063 回答
17

在 iOS 11 中,我们现在只能绕过一些角落

let view = UIView()

view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
于 2018-02-02T09:50:27.177 回答
8

使用Swift 3+语法的 CALayer 扩展

extension CALayer {

    func round(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize) -> Void {
        let bp = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: cornerRadii)
        let sl = CAShapeLayer()
        sl.frame = self.bounds
        sl.path = bp.cgPath
        self.mask = sl
    }
}

它可以像这样使用:

let layer: CALayer = yourView.layer
layer.round(roundedRect: yourView.bounds, byRoundingCorners: [.bottomLeft, .topLeft], cornerRadii: CGSize(width: 5, height: 5))
于 2016-11-22T06:52:58.257 回答
5

Stuarts 对特定角进行圆角的示例效果很好。如果你想像左上角和右上角这样的多个角,这是怎么做的

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageview
                                               byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight
                                                     cornerRadii:CGSizeMake(10.0, 10.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageview.bounds;
maskLayer.path = maskPath.CGPath;

// Set the newly created shape layer as the mask for the image view's layer
imageview.layer.mask = maskLayer; 
于 2013-11-11T12:43:45.500 回答
5

感谢分享。在这里我想分享一下swift 2.0上的解决方案,以供进一步参考这个问题。(符合 UIRectCorner 的协议)

let mp = UIBezierPath(roundedRect: cell.bounds, byRoundingCorners: [.bottomLeft, .TopLeft], cornerRadii: CGSize(width: 10, height: 10))
let ml = CAShapeLayer()
ml.frame = self.bounds
ml.path = mp.CGPath
self.layer.mask = ml
于 2015-10-12T00:57:37.127 回答
4

有一个更简单、更快捷的答案,可能会根据您的需要起作用,并且也适用于阴影。您可以将 superlayer 上的 maskToBounds 设置为 true,并偏移子层,使它们的 2 个角在 superlayer 边界之外,从而有效地将两侧的圆角切割掉。

当然,这仅在您希望在同一侧仅具有 2 个圆角并且当您从一侧切掉几个像素时图层的内容看起来相同时才有效。非常适合仅在顶部四舍五入的条形图。

于 2012-09-04T11:34:20.443 回答
3

请参阅此相关问题。您必须将自己的矩形绘制到CGPath带有一些圆角的矩形上,然后添加CGPath到您的矩形中CGContext,然后使用CGContextClip.

您还可以将带有 alpha 值的圆角矩形绘制到图像上,然后使用该图像创建一个新层,您将其设置为层的mask属性(请参阅 Apple 的文档)。

于 2010-02-18T10:00:16.017 回答
2

仅对某些角进行四舍五入对于自动调整大小或自动布局不会起到很好的作用。

因此,另一种选择是使用常规cornerRadius并隐藏您不想要的角在另一个视图下或在其超级视图边界之外,确保将其设置为剪辑其内容。

于 2015-05-21T12:38:23.260 回答
2

晚了五年,但我认为目前人们这样做的方式并不是 100% 正确的。很多人都遇到过使用 UIBezierPath + CAShapeLayer 方法会干扰 Auto-layout 的问题,尤其是在 Storyboard 上设置时。没有答案,所以我决定添加我自己的。

drawRect(rect: CGRect)有一个非常简单的方法可以绕过它:在函数中绘制圆角。

例如,如果我想要 UIView 的顶部圆角,我会将 UIView 子类化,然后在适当的地方使用该子类。

import UIKit

class TopRoundedView: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        var maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: UIRectCorner.TopLeft | UIRectCorner.TopRight, cornerRadii: CGSizeMake(5.0, 5.0))

        var maskLayer = CAShapeLayer()
        maskLayer.frame = self.bounds
        maskLayer.path = maskPath.CGPath

        self.layer.mask = maskLayer
    }
}

这是解决问题的最佳方法,根本不需要任何时间来适应。

于 2015-09-20T09:30:11.960 回答
1

为了添加到答案添加UIView中,我在 Swift 中创建了一个简单的、可重用的。根据您的用例,您可能想要进行修改(避免在每个布局上创建对象等),但我想使其尽可能简单。UIImageView如果您不喜欢子类化,该扩展允许您更轻松地将其应用于其他视图(例如)。

extension UIView {

    func roundCorners(_ roundedCorners: UIRectCorner, toRadius radius: CGFloat) {
        roundCorners(roundedCorners, toRadii: CGSize(width: radius, height: radius))
    }

    func roundCorners(_ roundedCorners: UIRectCorner, toRadii cornerRadii: CGSize) {
        let maskBezierPath = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: roundedCorners,
            cornerRadii: cornerRadii)
        let maskShapeLayer = CAShapeLayer()
        maskShapeLayer.frame = bounds
        maskShapeLayer.path = maskBezierPath.cgPath
        layer.mask = maskShapeLayer
    }
}

class RoundedCornerView: UIView {

    var roundedCorners: UIRectCorner = UIRectCorner.allCorners
    var roundedCornerRadii: CGSize = CGSize(width: 10.0, height: 10.0)

    override func layoutSubviews() {
        super.layoutSubviews()
        roundCorners(roundedCorners, toRadii: roundedCornerRadii)
    }
}

以下是您将其应用于 a 的方法UIViewController

class MyViewController: UIViewController {

    private var _view: RoundedCornerView {
        return view as! RoundedCornerView
    }

    override func loadView() {
        view = RoundedCornerView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _view.roundedCorners = [.topLeft, .topRight]
        _view.roundedCornerRadii = CGSize(width: 10.0, height: 10.0)
    }
}
于 2017-03-27T14:17:56.770 回答
0

总结斯图尔特的答案,你可以有如下圆角方法:

@implementation UIView (RoundCorners)

- (void)applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];

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

    self.layer.mask = maskLayer;
}

@end

因此,要应用圆角,您只需执行以下操作:

[self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];
于 2015-06-18T06:36:43.930 回答
0

我建议定义一个图层的蒙版。掩码本身应该是CAShapeLayer具有专用路径的对象。您可以使用下一个 UIView 扩展(Swift 4.2):

extension UIView {
    func round(corners: UIRectCorner, with radius: CGFloat) {
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: corners,
            cornerRadii: CGSize(width: radius, height: radius)
        ).cgPath
        layer.mask = maskLayer
   }
}
于 2018-10-31T16:10:56.633 回答