CALayer:如何在一个边框上添加边框

65

我可以通过以下方式向CALayer添加边框:

[webView.layer setBorderColor: [[UIColor colorWithRed:0.6 green:0.7 blue:0.2 alpha:1] CGColor]];
[webView.layer setBorderWidth: 2.75];   

但是有没有可能只在一侧添加边框?我只需要底部的边框。或者我能用其他属性来实现这个效果,比如frame、bounds、mask等吗?

图片描述

谢谢你的帮助!


@Control-V

        UIWebView *webView = [[UIWebView alloc] init];
        CALayer *webViewLayer = webView.layer;

        // now you can do a lot of stuff like borders:
        [webViewLayer setBorderColor: [[UIColor greenColor] CGColor]];
        [webViewLayer setBorderWidth: 2.75];    

请查看CALayer文档: https://developer.apple.com/documentation/quartzcore/calayer

并且在这里查看: http://iosdevelopertips.com/cocoa/add-rounded-corners-and-border-to-uiwebview.html


我仍然在想,你是如何找到UIWebView的层属性的。我在苹果官方文档中努力查找,但没有找到。 - Praveen-K
@Control-V:我为你编辑了我的问题。 - Manni
9
你正在劫持webview的层?那是一个非常糟糕的想法,请不要这样做。除非在极为罕见的情况下,你应该始终将UIWebView视为不可检查和完全不透明的对象。不要尝试搞乱像它的层这样的东西(因为它的层不是CALayer,而是CATiledLayer,例如),因为UIWebView对配置的方式非常挑剔。 - Dave DeLong
7个回答

132

我用以下方法创建了右边框:

leftScrollView.clipsToBounds = YES;

CALayer *rightBorder = [CALayer layer];
rightBorder.borderColor = [UIColor darkGrayColor].CGColor;
rightBorder.borderWidth = 1;
rightBorder.frame = CGRectMake(-1, -1, CGRectGetWidth(leftScrollView.frame), CGRectGetHeight(leftScrollView.frame)+2);

[leftScrollView.layer addSublayer:rightBorder];

2
谢谢您的回复,我很快会查看您的代码! - Manni
2
如果您尝试在不剪裁子视图的视图上使用此功能,则它将无法正常工作。您可以在IB的“属性检查器”选项卡中切换此选项(称为“剪裁子视图”),或通过代码设置属性view.clipsToBounds = YES; - FreeAsInBeer
3
我认为框架代码应该是leftBorder.frame = CGRectMake(0, 0, 1, leftScrollView.frame.size.height); - Krzysztof Romanowski
1
不,这只在右侧绘制边框,而不是其他任何边缘。但是,您可以调整偏移量以在多个侧面上获得边框。这是一种巧妙的方法,可以节省绘制多个层用于多个边缘的时间。需要更多的解释才能清楚地说明实际发生了什么,但这是一个好的解决方案。 - SeanR
如果您需要边框宽度大于1,请考虑使用rightBorder.frame = CGRectMake(CGRectGetWidth(button.frame) - 1, 0, 1, CGRectGetHeight(button.frame)); - Ramiro
显示剩余4条评论

18

最简单的方法是添加一个子层,以绘制选择性边框,但在选择此解决方案时需要考虑一些事情,最大的问题是确保边框子层始终在顶部,并且当您更改图层的框架时边框会发生变化。

我实现了一个开源解决方案,可以解决这些问题,并允许您像这样声明选择性边框:

myView.borderDirection = AUIFlexibleBordersDirectionRight | AUIFlexibleBordersDirectionTop;

你可以获取代码,并在这里阅读更多相关信息。


1
非常酷的代码。 简单易用,完美地实现了我的目的。 非常感谢!! - Maverick1st
这个答案比上面的“正确”答案更好,因为它支持对视图进行自动布局。 - Itachi

11

我的Swift解决方案。它涉及四个不同的函数,都是UIView的扩展。每个函数都添加了不同的边框。

extension UIView {
@discardableResult func addRightBorder(color: UIColor, width: CGFloat) -> UIView {
    let layer = CALayer()
    layer.borderColor = color.cgColor
    layer.borderWidth = width
    layer.frame = CGRect(x: self.frame.size.width-width, y: 0, width: width, height: self.frame.size.height)
    self.layer.addSublayer(layer)
    return self
    }
@discardableResult func addLeftBorder(color: UIColor, width: CGFloat) -> UIView {
    let layer = CALayer()
    layer.borderColor = color.cgColor
    layer.borderWidth = width
    layer.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
    self.layer.addSublayer(layer)
    return self
    }
@discardableResult func addTopBorder(color: UIColor, width: CGFloat) -> UIView {
    let layer = CALayer()
    layer.borderColor = color.cgColor
    layer.borderWidth = width
    layer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
    self.layer.addSublayer(layer)
    return self
    }
@discardableResult func addBottomBorder(color: UIColor, width: CGFloat) -> UIView {
    let layer = CALayer()
    layer.borderColor = color.cgColor
    layer.borderWidth = width
    layer.frame = CGRect(x: 0, y: self.frame.size.height-width, width: self.frame.size.width, height: width)
    self.layer.addSublayer(layer)
    return self
    }
}

你可能想把 self.layer.addSublayer(layer) 改为 self.layer.insertSublayer(layer, at: UInt32(self.layer.sublayers?.count ?? 0))。 - johndpope
我的还是有右边距,但我不想要这个。 - Jeff Bootsholz
完美,被采纳的答案让我遇到了一些问题,而这个答案却没有。 - John Montgomery

7

边框属性总是在您的视图的4个边上添加边框。您可以编写自己的绘制方法,在视图底部绘制边框。

但是,您为什么不只需添加一个视图到您的UIWebView上方,使其看起来像一个边框呢?


6
这是Swift语言的等效代码。
leftScrollView.clipsToBounds = true
let rightBorder: CALayer = CALayer()
rightBorder.borderColor = UIColor.darkGrayColor().CGColor
rightBorder.borderWidth = 1
rightBorder.frame = CGRectMake(-1, -1, CGRectGetWidth(leftScrollView.frame), CGRectGetHeight(leftScrollView.frame)+2)
leftScrollView.layer.addSublayer(rightBorder)

刚试了一下,结果出现了一个边框,但宽度与我添加它的按钮大小不匹配,边框几乎是两倍长。 - Dave G
已点赞,但你现在可以用属性替换那些C风格的getter,并使用CGRect的init方法。 - Doug Mead

2
还有一种方法可以实现这个效果。 CAShapeLayer 有一个名为“strokeStart”和“strokeEnd”的属性。考虑到描边从0开始,并在到达原点时结束,完成路径,您可以设置开始和结束值以在一侧绘制边框。

开始描边路径的相对位置。可动画化。此属性的值必须在0.0到1.0范围内。此属性的默认值为0.0。结合strokeEnd属性,此属性定义要描边的路径子区域。在此属性中的值表示沿路径开始描边的相对点,而strokeEnd属性则定义了结束点。值为0.0表示路径的开始,而值为1.0表示路径的结束。路径长度之间的值被解释为线性。

例如:

let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.CGPath
mask.strokeColor = UIColor.redColor().CGColor
mask.strokeStart = 0.45
mask.strokeEnd = 0.92
self.layer.mask = mask

1
为什么要费力地计算那些奇怪的数字,当你已经在使用形状图层呢?只需计算线条的路径,将其提供给形状图层即可,无需使用图层蒙版。 - Chris Birch

1
以下内容适用于Swift 4.2,用于在现有的UIView上应用边框。
extension UIView {
    enum Side {
        case top
        case bottom
        case left
        case right
    }

    func addBorder(to side: Side, color: UIColor, borderWidth: CGFloat) {
        let subLayer = CALayer()
        subLayer.borderColor = color.cgColor
        subLayer.borderWidth = borderWidth
        let origin = findOrigin(side: side, borderWidth: borderWidth)
        let size = findSize(side: side, borderWidth: borderWidth)
        subLayer.frame = CGRect(origin: origin, size: size)
        layer.addSublayer(subLayer)
    }

    private func findOrigin(side: Side, borderWidth: CGFloat) -> CGPoint {
        switch side {
        case .right:
            return CGPoint(x: frame.maxX - borderWidth, y: 0)
        case .bottom:
            return CGPoint(x: 0, y: frame.maxY - borderWidth)
        default:
            return .zero
        }
    }

    private func findSize(side: Side, borderWidth: CGFloat) -> CGSize {
        switch side {
        case .left, .right:
            return CGSize(width: borderWidth, height: frame.size.height)
        case .top, .bottom:
            return CGSize(width: frame.size.width, height: borderWidth)
        }
    }
}

你可以用以下代码调用它:

myView.addBorder(to: .left, color: .black, borderWidth: 2.0)

发生了什么:

  • 用户提供了一个枚举中定义的Side,以及一个colorborderWidth
  • 创建了一个新的子层,并赋予一个borderColorborderWidth
  • 计算原点,由侧面决定
  • 计算大小,也由侧面决定
  • 最后,给该层一个框架,然后将其作为子层添加

您的代码在将视图应用圆角时无效。边框在边缘处被剪切。 - Birju

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接