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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15184133/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:32:29  来源:igfitidea点击:

Add a border outside of a UIView (instead of inside)

iosuiviewborder

提问by remykits

If a add a border of a view using code in a view like

如果在视图中使用代码添加视图的边框,例如

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

the border is added inside the view like the following: enter image description here

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

the right view is the original view, as you can see, the black area of bordered view is less than the original one. but what I want to get is a border outside of original view, like this:enter image description here. the black area is equal to original one, how can I implement it?

右视图是原始视图,如您所见,边框视图的黑色区域小于原始视图。但我希望得到的是原始视图的境外,像这样:在此处输入图片说明。黑色区域等于原始区域,我该如何实现?

回答by Elliott James Perry

Unfortunately, there isn't simply a little property you can set to align the border to the outside. It draws aligned to the inside because the UIViews default drawing operations draw within its bounds.

不幸的是,您不能简单地设置一些属性来将边框与外部对齐。它绘制到内部对齐,因为 UIViews 默认绘制操作在其边界内绘制。

The simplest solution that comes to mind would be to expand the UIView by the size of the border width when applying the border:

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

CGFloat borderWidth = 2.0f;

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

回答by Hugues Duvillier

Ok, there already is an accepted answer but I think there is a better way to do it, you just have to had a new layer a bit larger than your view and do not mask it to the bounds of the view's layer (which actually is the default behaviour). Here is the sample code :

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

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;

Of course this is if you want your border to be 1 unity large, if you want more you adapt the borderWidthand the frame of the layer accordingly. This is better than using a second view a bit larger as a CALayeris lighter than a UIViewand you don't have do modify the frame of myView, which is good for instance if myViewis aUIImageView

当然,这是如果您希望边框为 1 个统一大,如果您想要更多,则相应地调整borderWidth图层的框架。这比使用第二个视图要好一些,因为 aCALayer比 a 轻,UIView并且您不必修改 的框架myView,例如如果myView是 aUIImageView

N.B :For me the result was not perfect on simulator (the layer was not exactly at the right position so the layer was thicker on one side sometimes) but was exactly what is asked for on real device.

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

EDIT

编辑

Actually the problem I talk about in the N.Bwas just because I had reduced the screen of the simulator, on normal size there is absolutely no issue

其实我在NB讲的问题只是因为我把模拟器的屏幕缩小了,在正常大小上绝对没有问题

Hope it helps

希望能帮助到你

回答by Peter Kreinz

With the above accepted best answer i made experiences with such not niceresults and unsightly edges:

通过上述公认的最佳答案,我获得了如此糟糕的结果和难看的边缘的体验:

border without bezier path

没有贝塞尔路径的边框

So i will share my UIView Swiftextension with you, that uses a UIBezierPath instead as border outline – without unsightly edges (inspired by @Fattie):

所以我将与你分享我的 UIView Swift扩展,它使用 UIBezierPath 作为边框轮廓——没有难看的边缘(灵感来自@Fattie):

border with bezier path

带贝塞尔曲线的边界

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

}

Example:

例子:

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

回答by picciano

For a Swift implementation, you can add this as a UIView extension.

对于 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() { 
[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;
.name == Constants.ExternalBorderName }.forEach() {
[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;
.removeFromSuperlayer() } } func removeExternalBorder(externalBorder: CALayer) { guard externalBorder.name == Constants.ExternalBorderName else { return } externalBorder.removeFromSuperlayer() } }

回答by Lithu T.V

Well there is no direct method to do it You can consider some workarounds.

那么没有直接的方法可以做到这一点您可以考虑一些解决方法。

  1. Change and increase the frame and add bordercolor as you did
  2. Add a view behind the current view with the larger size so that it appears as border.Can be worked as a custom class of view
  3. If you dont need a definite border (clearcut border) then you can depend on shadow for the purpose

    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;
    
  1. 像您一样更改和增加框架并添加边框颜色
  2. 在当前视图后面添加一个更大尺寸的视图,使其显示为边框。可以作为自定义视图类工作
  3. 如果您不需要明确的边框(清晰边框),那么您可以依靠阴影来达到目的

    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
    
        }
    

回答by Hossam Ghareeb

Increase the width and height of view's frame with border width before adding the border:

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

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

回答by Maksim Kniazev

I liked solution of @piccianoIf you want exploding circle instead of square replace addExternalBorderfunction with:

我喜欢@picciano 的解决方案 如果你想要爆炸圆而不是正方形,请将addExternalBorder函数替换为:

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() { ##代码##.name == Constants.externalBorderName }.forEach() {
            ##代码##.removeFromSuperlayer()
        }
    }

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

回答by NGI

There is actually a very simple solution. Just set them both like this:

其实有一个非常简单的解决方案。只需像这样设置它们:

##代码##

回答by JonesJr876

How I placed a border around my UI view (main - SubscriptionAd) in Storyboard is to place it inside another UI view (background - BackgroundAd). The Background UIView has a background colour that matches the border colour i want, and the Main UIView has constraints value 2 from each side.

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

I will link the background view to my ViewController and then turn the border on and off by changing the background colour.

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

Image Of Nested UIView with Top View 2px constraints all around, making it smaller than larger

带有顶视图 2px 约束的嵌套 UIView 的图像,使其小于大于

回答by Pacyjent

Swift 5

斯威夫特 5

##代码##