在UIView外部添加边框(而不是在内部)


82

如果使用类似视图的代码在视图中添加边框

self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = 2.0f;

将边框添加到视图内部,如下所示: 在此处输入图片说明

如您所见,右边的视图是原始视图,带边框的视图的黑色区域小于原始视图。但我想得到的是原始视图之外的边框,如下所示:在此处输入图片说明。黑色区域等于原始区域,我该如何实施?

Answers:


100

不幸的是,您不仅可以设置将边框与外部对齐的小属性。它以与内部对齐的方式进行绘制,因为UIViews默认绘制操作在其范围内进行绘制。

想到的最简单的解决方案是在应用边框时通过边框宽度的大小扩展UIView:

CGFloat borderWidth = 2.0f;

self.frame = CGRectInset(self.frame, -borderWidth, -borderWidth);
self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = borderWidth;

我已经在uitableviewcell的视图上使用过它。但在网上,self.frame = CGRectInset(self.frame,-borderWidth,-borderWidth); 如果我们继续滚动表视图,则视图的宽度和高度会不断增加。.因此,我最终将其删除。
Neela 2015年

此方法会不断增加视图高度,因此我删除了此代码,并使用以下代码self.view.layer.borderColor = [[UIColor colorWithRed:209.0f / 255.0f绿色:33.0f / 255.0f蓝色:8.0f / 255.0f alpha: 1.0f] CGColor]; self.view.layer.borderWidth = 1.0f;
Rizwan Shah 2015年

4
在Swift中尝试了此解决方案,不幸的是,该方法无法正常工作,边框一直在UIImageView中绘图
theDC 2016年

如果此代码位于多次调用的方法内,则它将不断增加大小,因为self.frame会自行重复。为了防止这种情况,声明一个属性来存储self.frame并将其设置在viewDidload中。例如_originalSize = self.frame; 其中originalSize是CGRect属性。然后将代码更改为self.frame = CGRectInset(_originalSize,-borderWidth,-borderWidth);
GeneCode

当在ViewDidLoad中实现此功能时,视图会自动调整为屏幕大小(即,视图调整为原始大小,因此边框变为可见)。如果在ViewDidAppear中实现,则不会在原始视图之外完全创建边框。关于如何实现它,以便在原始视图之外创建边框并且视图不调整大小的任何想法?
JeffB6688

23

好的,已经有一个可以接受的答案,但是我认为有一种更好的方法,您只需要拥有一个比视图大一点的新层,并且不必将其蒙版到视图层的边界即可(实际上是默认行为)。这是示例代码:

CALayer * externalBorder = [CALayer layer];
externalBorder.frame = CGRectMake(-1, -1, myView.frame.size.width+2, myView.frame.size.height+2);
externalBorder.borderColor = [UIColor blackColor].CGColor;
externalBorder.borderWidth = 1.0;

[myView.layer addSublayer:externalBorder];
myView.layer.masksToBounds = NO;

当然,这是如果您希望边框为1个单位大,如果您希望更大,则可以相应地调整borderWidth图层的和。这比使用第二个视图更好,因为第二个视图CALayer要比a小一些,UIView并且您不必修改的框架myView,例如,如果myViewUIImageView

注意:对我来说,结果在模拟器上并不完美(该层的位置不正确,因此该层的一侧有时更厚),但这恰恰是实际设备上所要求的。

编辑

实际上,我在笔记本电脑中谈论的问题仅仅是因为我缩小了模拟器的屏幕,在正常尺寸下绝对没有问题

希望能帮助到你


2
myView.layer.masksToBounds = NO; 当myView具有CornerRadius时,这会造成问题。
umakanta

.masksToBounds =无//这也将溢出如果图片的缩放模式是aspectFill
iOS的铁匠

22

通过以上公认的最佳答案,我获得了不好的结果和难看的优势:

没有贝塞尔曲线的边界

因此,我将与您分享我的UIView Swift扩展,它使用UIBezierPath代替边框轮廓-没有难看的边缘(受@Fattie启发):

贝塞尔曲线边界

//  UIView+BezierPathBorder.swift

import UIKit

extension UIView {

    fileprivate var bezierPathIdentifier:String { return "bezierPathBorderLayer" }

    fileprivate var bezierPathBorder:CAShapeLayer? {
        return (self.layer.sublayers?.filter({ (layer) -> Bool in
            return layer.name == self.bezierPathIdentifier && (layer as? CAShapeLayer) != nil
        }) as? [CAShapeLayer])?.first
    }

    func bezierPathBorder(_ color:UIColor = .white, width:CGFloat = 1) {

        var border = self.bezierPathBorder
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius:self.layer.cornerRadius)
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask

        if (border == nil) {
            border = CAShapeLayer()
            border!.name = self.bezierPathIdentifier
            self.layer.addSublayer(border!)
        }

        border!.frame = self.bounds
        let pathUsingCorrectInsetIfAny =
            UIBezierPath(roundedRect: border!.bounds, cornerRadius:self.layer.cornerRadius)

        border!.path = pathUsingCorrectInsetIfAny.cgPath
        border!.fillColor = UIColor.clear.cgColor
        border!.strokeColor = color.cgColor
        border!.lineWidth = width * 2
    }

    func removeBezierPathBorder() {
        self.layer.mask = nil
        self.bezierPathBorder?.removeFromSuperlayer()
    }

}

例:

let view = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 100))
view.layer.cornerRadius = view.frame.width / 2
view.backgroundColor = .red

//add white 2 pixel border outline
view.bezierPathBorder(.white, width: 2)

//remove border outline (optional)
view.removeBezierPathBorder()

就我而言,它应该放在里面:)
Peter Kreinz '18

好的思维方式,但您的边界仍在“内部”,因此需要固定以待概述:)
Serj Rubens

15

对于Swift实施,您可以将其添加为UIView扩展。

extension UIView {

    struct Constants {
        static let ExternalBorderName = "externalBorder"
    }

    func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.whiteColor()) -> CALayer {
        let externalBorder = CALayer()
        externalBorder.frame = CGRectMake(-borderWidth, -borderWidth, frame.size.width + 2 * borderWidth, frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.CGColor
        externalBorder.borderWidth = borderWidth
        externalBorder.name = Constants.ExternalBorderName

        layer.insertSublayer(externalBorder, atIndex: 0)
        layer.masksToBounds = false

        return externalBorder
    }

    func removeExternalBorders() {
        layer.sublayers?.filter() { $0.name == Constants.ExternalBorderName }.forEach() {
            $0.removeFromSuperlayer()
        }
    }

    func removeExternalBorder(externalBorder: CALayer) {
        guard externalBorder.name == Constants.ExternalBorderName else { return }
        externalBorder.removeFromSuperlayer()
    }

}

优秀!谢谢!在Swift 4中有一些更改,现在看起来有些不同,但是Xcode解释了如何修改代码。在方法“ removeExternalBorder”中必须为“ guard externalBorder.name ==”。
DmitryKanunnikoff

13

那么没有直接的方法可以执行此操作。您可以考虑一些解决方法。

  1. 像您一样更改和增加框架并添加bordercolor
  2. 在当前视图后面添加一个较大的视图,使其以边框显示。可以用作自定义视图类
  3. 如果您不需要确定的边界(明晰边界),则可以依靠阴影来达到目的

    [view1 setBackgroundColor:[UIColor blackColor]];
    UIColor *color = [UIColor yellowColor];
    view1.layer.shadowColor = [color CGColor];
    view1.layer.shadowRadius = 10.0f;
    view1.layer.shadowOpacity = 1;
    view1.layer.shadowOffset = CGSizeZero;
    view1.layer.masksToBounds = NO;
    

2

在添加边框之前,使用边框宽度增加视图框架的宽度和高度:

float borderWidth = 2.0f
CGRect frame = self.frame;
frame.width += borderWidth;
frame.height += borderWidth;
 self.layer.borderColor = [UIColor yellowColor].CGColor;
 self.layer.borderWidth = 2.0f;

2

实际上有一个非常简单的解决方案。只需将它们设置如下:

view.layer.borderWidth = 5

view.layer.borderColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor

view.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.25).cgColor

1

我喜欢@picciano的解决方案 如果要爆炸圆形而不是正方形,请使用以下命令替换addExternalBorder函数:

func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) {
        let externalBorder = CALayer()
        externalBorder.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + 2 * borderWidth, height: frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.cgColor
        externalBorder.borderWidth = borderWidth
        externalBorder.cornerRadius = (frame.size.width + 2 * borderWidth) / 2
        externalBorder.name = Constants.ExternalBorderName
        layer.insertSublayer(externalBorder, at: 0)
        layer.masksToBounds = false

    }

0

我喜欢@picciano和@Maksim Kniazev的解决方案。我们还可以使用以下方法创建环形边框:

func addExternalAnnularBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) {
    let externalBorder = CALayer()
    externalBorder.frame = CGRect(x: -borderWidth*2, y: -borderWidth*2, width: frame.size.width + 4 * borderWidth, height: frame.size.height + 4 * borderWidth)
    externalBorder.borderColor = borderColor.cgColor
    externalBorder.borderWidth = borderWidth
    externalBorder.cornerRadius = (frame.size.width + 4 * borderWidth) / 2
    externalBorder.name = Constants.ExternalBorderName
    layer.insertSublayer(externalBorder, at: 0)
    layer.masksToBounds = false
}

0

如何在Storyboard中的UI视图(主要-SubscriptionAd)周围放置边框,是将其放置在另一个UI视图(背景-BackgroundAd)中。Background UIView的背景颜色与我想要的边框颜色匹配,并且Main UIView的每一侧约束值均为2。

我将背景视图链接到我的ViewController,然后通过更改背景颜色来打开和关闭边框。

具有顶部视图2px约束的嵌套UIView的图像,使其比较大的要小


0

迅捷5

extension UIView {
    fileprivate struct Constants {
        static let externalBorderName = "externalBorder"
    }

    func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) -> CALayer {
        let externalBorder = CALayer()
        externalBorder.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + 2 * borderWidth, height: frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.cgColor
        externalBorder.borderWidth = borderWidth
        externalBorder.name = Constants.ExternalBorderName

        layer.insertSublayer(externalBorder, at: 0)
        layer.masksToBounds = false

        return externalBorder
    }

    func removeExternalBorders() {
        layer.sublayers?.filter() { $0.name == Constants.externalBorderName }.forEach() {
            $0.removeFromSuperlayer()
        }
    }

    func removeExternalBorder(externalBorder: CALayer) {
        guard externalBorder.name == Constants.externalBorderName else { return }
        externalBorder.removeFromSuperlayer()
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.