【问题标题】:Rounded Corners only on Top of a UIView仅在 UIView 顶部的圆角
【发布时间】:2012-05-06 05:03:28
【问题描述】:

您好,我正在寻找一个干净的解决方案,而不覆盖 drawRect 或类似的东西来创建一个在 视图 顶部带有圆角的 UIView。我的主要问题是如果视图正在调整大小或类似的东西,则创建可变解决方案。 有干净的解决方案吗? Apple 在第一个表项上也是这样做的。做到这一点并不难。

【问题讨论】:

  • 您是否有充分的理由不想覆盖drawRect
  • 因为这是我最不想做的事情……太恶心了,我认为有更漂亮的方法可以做到这一点。
  • drawRect 并不讨厌,而是真正的东西!
  • 对于任何寻求现代答案并支持边框的人,我在下面添加了一个非常干净的解决方案 - stackoverflow.com/a/57498969/7107094
  • 对于interface builder 的唯一解决方案请看这里:stackoverflow.com/a/58626264

标签: objective-c uiview core-graphics rounded-corners


【解决方案1】:

执行此操作的直接方法是将路径定义为您想要的形状,并用您想要用于背景的任何颜色填充它。您可以为此使用UIBezierPathCGPath。以CGPath为例,可以使用CGMoveToPoint()CGAddLineToPoint()CGAddArc()等方法构造路径。然后用CGContextFillPath() 填充它。查看Quartz 2D Programming Guide 进行完整讨论。

另一种方法是添加一个带圆角的子视图(您可以设置子视图层的cornerRadius 属性),但让子视图的一侧被父视图裁剪。

第三种方法是添加具有所需形状的背景图像。您可以使角落透明并使视图的背景透明,您将获得所需的效果。不过,这对于调整大小不太适用。

你在哪里卡住了?

【讨论】:

  • 路径是什么意思?能不能解释的详细一点?
  • 对不起,错误的答案 - 他问如何在不覆盖 drawRect 的情况下做到这一点
  • @AshleyMills OP 询问如何做到这一点“......不覆盖 drawRect 或类似的东西......”这是非常模糊的。据我们所知,覆盖-initWithCoder: 也可以称为“类似的东西”。我的回答中的第二种(现在也是第三种)方法不需要覆盖任何东西。第一个确实如此,但我认为这不会导致答案“错误”。
【解决方案2】:

您可以通过在视图层上设置mask 来做到这一点:

CAShapeLayer * maskLayer = [CAShapeLayer layer];
maskLayer.path = [UIBezierPath bezierPathWithRoundedRect: self.bounds byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: (CGSize){10.0, 10.}].CGPath;

self.layer.mask = maskLayer;

重要提示:您应该在视图的 layoutSubviews() 方法中执行此操作,因此视图已经从情节提要中调整了大小


在 Swift 中

let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: .TopLeft | .TopRight, cornerRadii: CGSize(width: 10.0, height: 10.0)).CGPath

layer.mask = maskLayer

Swift 2.x

let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: UIRectCorner.TopLeft.union(.TopRight), cornerRadii: CGSizeMake(10, 10)).CGPath
layer.mask = maskLayer

Swift 3.x

let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 10, height: 10)).cgPath
layer.mask = maskLayer

【讨论】:

  • 刚刚用这个试过了,但是背景色现在不见了:(代码是1对1你的代码…
  • 这令人费解!我创建了一个 UIView 子视图来测试它,它工作正常。
  • 有没有人遇到过这个问题?
  • @random 你的布局改变了吗?例如,如果您将代码放在UIViewControllerviewDidLoad 中并且视图通过自动布局调整大小,那么它就会发生。如果你把代码移到viewDidLayoutSubviews 就解决了。
  • 如何添加边框宽度和颜色?
【解决方案3】:

我在 Ashley 的帮助下解决了这个问题。

首先我继承了一个 UIView。为我的类创建一个自己的构造函数,名为- (id)initWithContentView:(UIView *)aView forTableView:(UITableView *)table andIndex:(NSIndexPath *)indexPath;。在这个构造函数中,我决定了我想要设置什么样的表格单元格。

然后我覆盖 l - (void)layoutSubviews 以创建 CAShapeLayer 并应用图层蒙版。

.h 文件代码

typedef enum {
    tableCellMiddle,
    tableCellTop,
    tableCellBottom,
    tableCellSingle
} tableCellPositionValue;

@interface TableCellBackgrounds : UIView
{
    tableCellPositionValue position;
}

- (id)initWithContentView:(UIView *)aView forTableView:(UITableView *)table andIndex:(NSIndexPath *)indexPath;

@end

.m 文件代码

- (id)initWithContentView:(UIView *)aView forTableView:(UITableView *)table andIndex:(NSIndexPath *)indexPath
{
    self = [super initWithFrame:aView.frame];

    [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];

    if(self)
    {
        [self setBackgroundColor:[UIColor colorWithRed:(float)230/255 green:(float)80/255 blue:(float)70/255 alpha:1]];

        if(table.style == UITableViewStyleGrouped)
        {
            int rows = [table numberOfRowsInSection:indexPath.section];


            if(indexPath.row == 0 && rows == 1)
            {
                self.layer.cornerRadius = 11;
                position = tableCellSingle;
            }
            else if (indexPath.row == 0)
                position = tableCellTop;
            else if (indexPath.row != rows - 1) 
                position = tableCellMiddle;
            else 
                position = tableCellBottom;
        }
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if(position == tableCellTop)
    {
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight cornerRadii:(CGSize){10.0, 10.0}].CGPath;
        self.layer.mask = maskLayer;
    }
    else if (position == tableCellBottom)
    {
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:(CGSize){10.0, 10.0}].CGPath;
        self.layer.mask = maskLayer;
    } 
}

【讨论】:

  • 谢谢您的好心先生!我需要将视图的底部弯曲,并且您的代码可以完美运行。即使我有一个 tableView,我也不必执行 initWithContentView。只有 else if(position==tableCellBottom) 里面的 3 行对我有用。
【解决方案4】:

刚试过Swift 3.0Xcode 8.0

记住viewDidLayoutSubviews()layoutSubViews中的按钮设置为 @rob 描述了 here

当你想改变你的按钮背景时,你只需要调用:

yourButton.backgroundColor = UIColor.someColour

来源:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    yourButton.layer.masksToBounds = true
    yourButton.roundCorners(corners: [.topLeft,.topRight], radius: 5)
}

extension UIButton
{
    func roundCorners(corners:UIRectCorner, radius: CGFloat)
    {
        let maskLayer = CAShapeLayer()
        maskLayer.path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)).cgPath
        self.layer.mask = maskLayer
    }
}
  • 结果如下:

默认状态:

选中状态:

希望有帮助!!

【讨论】:

  • 那么你可以在你的 roundCorners 函数中添加 self.layer.masksToBounds = true ,不是吗?
【解决方案5】:

使用 swift 3.0,以下内容对我有用

let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 10, height: 10)).cgPath
(imageView.)layer.mask = maskLayer

重要提示:确保它位于“layoutSubviews”而不是“awakeFromNib”(如果您使用 TableViewCell)或类似的 UIView 中,或者只有左上角是圆角的!

【讨论】:

    【解决方案6】:
     CAShapeLayer * maskLayer = [CAShapeLayer layer];
        maskLayer.path = [UIBezierPath bezierPathWithRoundedRect: registerbtn.bounds byRoundingCorners: UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii: (CGSize){9.0, 12.0}].CGPath;
    
        registerbtn.layer.mask = maskLayer;
    

    这只会做一个圆角

    【讨论】:

      【解决方案7】:

      一个圆角的 UIView 扩展(Swift 4):

      extension UIView {
      
          /// Round UIView selected corners
          ///
          /// - Parameters:
          ///   - corners: selected corners to round
          ///   - radius: round amount
          func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
              let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
              let mask = CAShapeLayer()
              mask.path = path.cgPath
              self.layer.mask = mask
          }
      }
      

      示例:

      ratingView.roundCorners([.topLeft, .topRight, .bottomRight], radius: 6)
      

      【讨论】:

        【解决方案8】:

        对于 iOS11 及更高版本,您可以使用视图的图层属性:

        @property CACornerMask maskedCorners
        

        这定义了使用 cornerRadius 属性时四个角中的哪个角接收掩蔽。默认为所有四个角。 (苹果文档)

        【讨论】:

        【解决方案9】:

        现代简单的解决方案

        iOS 11+

        现在我们在视图层上拥有了maskedCorners 属性,它让生活变得更加轻松。

        只需设置所需的拐角半径并指定应遮蔽的拐角。最好的部分是这与边框很好 - 图层边框将跟随图层的边缘,无论它是否是圆形的!在 Playground 中尝试以下代码(记得按command+option+return 打开实时视图,以便查看它的样子)

        import UIKit
        import PlaygroundSupport
        
        let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 160))
        wrapperView.backgroundColor = .lightGray
        
        let roundedCornerView = UIView(frame: CGRect(x: 50, y: 50, width: 300, height: 60))
        roundedCornerView.backgroundColor = .white
        
        wrapperView.addSubview(roundedCornerView)
        
        roundedCornerView.layer.cornerRadius = 10
        roundedCornerView.layer.borderColor = UIColor.red.cgColor
        roundedCornerView.layer.borderWidth = 1
        
        
        // this is the key part - try out different corner combinations to achieve what you need
        roundedCornerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        
        
        PlaygroundPage.current.liveView = wrapperView
        

        它是这样的:

        【讨论】:

          【解决方案10】:

          在 Objective-C 中它看起来像:

          [oCollectionViewCell.layer setMasksToBounds:YES];
          [oCollectionViewCell.layer setCornerRadius:5.0];
          [oCollectionViewCell.layer setMaskedCorners:kCALayerMinXMinYCorner|kCALayerMaxXMinYCorner];
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-02-21
            • 2023-02-03
            • 2011-11-09
            • 1970-01-01
            • 2019-09-21
            • 2012-01-27
            • 2012-02-14
            相关资源
            最近更新 更多