UIView 下边框?

Posted

技术标签:

【中文标题】UIView 下边框?【英文标题】:UIView bottom border? 【发布时间】:2011-12-01 18:50:09 【问题描述】:

对于UIScrollView *toScrollView(这是屏幕的宽度),我想添加一个灰色的底部边框(与 iPhone 原生消息应用程序的撰写视图的目标字段完全相同)。

为了实现这一点,我遵循Cocoa Touch: How To Change UIView's Border Color And Thickness? 并用自定义UINavigationBar 覆盖了顶部边框,并使toScrollView 的x 坐标为-1 和宽度为322,这样左右边框就关闭了屏幕。

这看起来不错,但有点像 hack,我想知道是否有更好的方法来做到这一点。

- (void)viewDidLoad 
    [super viewDidLoad];

    // Add UINavigationBar *navigationBar at top.
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
                                             initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
                                             target:self action:@selector(cancelAction)];
    UINavigationBar *navigationBar = [[UINavigationBar alloc]
                                      initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];
    navigationBar.items = [NSArray arrayWithObject:self.navigationItem];

    // Add UIScrollView *toScrollView below navigationBar.
    UIScrollView *toScrollView = [[UIScrollView alloc]
                                  initWithFrame:CGRectMake(-1.0f, 43.0f, 322.0f, 45.0f)];
    toScrollView.backgroundColor = [UIColor whiteColor];
    toScrollView.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor;
    toScrollView.layer.borderWidth = 1.0f;
    [self.view addSubview:toScrollView];
    [self.view addSubview:navigationBar]; // covers top of toScrollView

【问题讨论】:

这是一个方便的 UIView 类别,可让您在 UIView 的任何一侧创建基于层或基于视图的边框:UIView+Borders 【参考方案1】:

您可以使用CALayer,而不是像@ImreKelényi 建议的那样使用UIView

// Add a bottomBorder.
CALayer *bottomBorder = [CALayer layer];

bottomBorder.frame = CGRectMake(0.0f, 43.0f, toScrollView.frame.size.width, 1.0f);

bottomBorder.backgroundColor = [UIColor colorWithWhite:0.8f 
                                                 alpha:1.0f].CGColor;

[toScrollView.layer addSublayer:bottomBorder];

【讨论】:

别忘了#import 如果您没有 QuartzCore framework,您可能还需要将它添加到您的项目中,因为您可能会遇到编译器错误。 @Flea 现在,启用模块后,您不应再添加 QuartzCore.framework 您可以将43.0f 更改为toScrollView.frame.size.height 以确保它位于底部 使用这样的 CALayer 的大问题是它是固定的。当您的视图大小发生变化(设备旋转、自动布局等)时,您的 CALayer 将不会自动调整自身。你将不得不自己设置它。而使用 drawRect 可以自动处理更改。【参考方案2】:

这是一个更通用的 Swift 扩展,用于为任何 UIView 子类创建边框:

import UIKit

extension UIView       
  func addTopBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.backgroundColor = color.CGColor
    border.frame = CGRectMake(0, 0, self.frame.size.width, width)
    self.layer.addSublayer(border)
  

  func addRightBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.backgroundColor = color.CGColor
    border.frame = CGRectMake(self.frame.size.width - width, 0, width, self.frame.size.height)
    self.layer.addSublayer(border)
  

  func addBottomBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.backgroundColor = color.CGColor
    border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width)
    self.layer.addSublayer(border)
  

  func addLeftBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.backgroundColor = color.CGColor
    border.frame = CGRectMake(0, 0, width, self.frame.size.height)
    self.layer.addSublayer(border)
  

斯威夫特 3

extension UIView 
    func addTopBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
        self.layer.addSublayer(border)
    

    func addRightBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
        self.layer.addSublayer(border)
    

    func addBottomBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
        self.layer.addSublayer(border)
    

    func addLeftBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
        self.layer.addSublayer(border)
    

【讨论】:

您可能希望在每个函数中使用 this 删除先前添加的子层(以防它在循环中被调用) - if let subLayerArray = self.layer.sublayers for layer in subLayerArray layer. removeFromSuperlayer() 提到的宽度是多少? 对于 Swift 3.0 使用 border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) @NitinNain 小心,删除的层数比你的意思要多。例如,如果您删除 UIView.layer 的所有层...,当您显示键盘时很可能会看到崩溃 这在 Swift 5 中似乎不起作用。但我也没有看到任何错误,我检查了所有函数和属性是否存在。有什么想法吗?【参考方案3】:

在如下类别中实现:

UIButton+Border.h:

@interface UIButton (Border)

- (void)addBottomBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

- (void)addLeftBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

- (void)addRightBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

- (void)addTopBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

@end

UIButton+Border.m:

@implementation UIButton (Border)

- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    CALayer *border = [CALayer layer];
    border.backgroundColor = color.CGColor;

    border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);
    [self.layer addSublayer:border];


- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    CALayer *border = [CALayer layer];
    border.backgroundColor = color.CGColor;

    border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth);
    [self.layer addSublayer:border];


- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    CALayer *border = [CALayer layer];
    border.backgroundColor = color.CGColor;

    border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);
    [self.layer addSublayer:border];


- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    CALayer *border = [CALayer layer];
    border.backgroundColor = color.CGColor;

    border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height);
    [self.layer addSublayer:border];


@end

【讨论】:

干得好.. 但是这段代码有什么特定于 UIButton 的吗?还不如把它添加到 UIView 中没有? 不错的sn-p。我对此进行了重构并转换为 Swift。你可以找到它here。 如果按钮的主层有边框半径怎么办? 我同意@abbood 不妨将其设为UIView 上的一个类别。不过物有所值。 是的,它在 UIView 上工作得很好。相关问题:如何使用自动布局进行这项工作?显然,如果框架发生变化,您需要按原样重新绘制边框。【参考方案4】:

斯威夫特 4

如果您需要真正的自适应解决方案(适用于所有屏幕尺寸),那么就是这样:

/**
* Extends UIView with shortcut methods
*
* @author Alexander Volkov
* @version 1.0
*/
extension UIView 

    /// Adds bottom border to the view with given side margins
    ///
    /// - Parameters:
    ///   - color: the border color
    ///   - margins: the left and right margin
    ///   - borderLineSize: the size of the border
    func addBottomBorder(color: UIColor = UIColor.red, margins: CGFloat = 0, borderLineSize: CGFloat = 1) 
        let border = UIView()
        border.backgroundColor = color
        border.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(border)
        border.addConstraint(NSLayoutConstraint(item: border,
                                                attribute: .height,
                                                relatedBy: .equal,
                                                toItem: nil,
                                                attribute: .height,
                                                multiplier: 1, constant: borderLineSize))
        self.addConstraint(NSLayoutConstraint(item: border,
                                              attribute: .bottom,
                                              relatedBy: .equal,
                                              toItem: self,
                                              attribute: .bottom,
                                              multiplier: 1, constant: 0))
        self.addConstraint(NSLayoutConstraint(item: border,
                                              attribute: .leading,
                                              relatedBy: .equal,
                                              toItem: self,
                                              attribute: .leading,
                                              multiplier: 1, constant: margins))
        self.addConstraint(NSLayoutConstraint(item: border,
                                              attribute: .trailing,
                                              relatedBy: .equal,
                                              toItem: self,
                                              attribute: .trailing,
                                              multiplier: 1, constant: margins))
    

【讨论】:

@DanielBeltrami 我同意,但我不会强迫审稿人在接受新答案后检查新答案。【参考方案5】:

您可以向self.view 添加一个单独的UIView,高度为1 点,背景颜色为灰色,并将其放置在toScrollView 的正下方。

编辑:除非您有充分的理由(想要使用 CALayer 不提供的 UIView 的某些服务),否则您应该使用 CALayer 作为@MattDiPasquale suggests。 UIView 的开销更大,这在大多数情况下可能不是问题,但其他解决方案仍然更优雅。

【讨论】:

【参考方案6】:

Swift 4 的解决方案

let bottomBorder = CALayer()
        bottomBorder.frame = CGRect(x: 0.0, y: calendarView.frame.size.height-1, width: calendarView.frame.width, height: 1.0)
        bottomBorder.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
        calendarView.layer.addSublayer(bottomBorder)

背景颜色浅灰色。根据需要更改颜色。

【讨论】:

为 CALayer 添加颜色需要将其转换为 CGColor。【参考方案7】:

还有带有移除边框功能的改进代码。基于confile answer。

import UIKit

enum viewBorder: String 
    case Left = "borderLeft"
    case Right = "borderRight"
    case Top = "borderTop"
    case Bottom = "borderBottom"


extension UIView 

    func addBorder(vBorder: viewBorder, color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.CGColor
        border.name = vBorder.rawValue
        switch vBorder 
            case .Left:
                border.frame = CGRectMake(0, 0, width, self.frame.size.height)
            case .Right:
                border.frame = CGRectMake(self.frame.size.width - width, 0, width, self.frame.size.height)
            case .Top:
                border.frame = CGRectMake(0, 0, self.frame.size.width, width)
            case .Bottom:
                border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width)
        
        self.layer.addSublayer(border)
    

    func removeBorder(border: viewBorder) 
        var layerForRemove: CALayer?
        for layer in self.layer.sublayers! 
            if layer.name == border.rawValue 
                layerForRemove = layer
            
        
        if let layer = layerForRemove 
            layer.removeFromSuperlayer()
        
    


更新:Swift 3

import UIKit

enum ViewBorder: String 
    case left, right, top, bottom


extension UIView 

    func add(border: ViewBorder, color: UIColor, width: CGFloat) 
        let borderLayer = CALayer()
        borderLayer.backgroundColor = color.cgColor
        borderLayer.name = border.rawValue
        switch border 
        case .left:
            borderLayer.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
        case .right:
            borderLayer.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
        case .top:
            borderLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
        case .bottom:
            borderLayer.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
        
        self.layer.addSublayer(borderLayer)
    

    func remove(border: ViewBorder) 
        guard let sublayers = self.layer.sublayers else  return 
        var layerForRemove: CALayer?
        for layer in sublayers 
            if layer.name == border.rawValue 
                layerForRemove = layer
            
        
        if let layer = layerForRemove 
            layer.removeFromSuperlayer()
        
    


【讨论】:

不错的扩展。清理了一下删除。 func remove(border: ViewBorder) layer.sublayers? .compactMap $0 .filter $0.name == border.rawValue .forEach $0.removeFromSuperlayer() 【参考方案8】:

这些扩展方法的问题在于,当 UIView/UIButton 稍后调整它的大小时,您没有机会更改 CALayer 的大小以匹配新的大小。这会给你留下一个错误的边界。我发现子类化我的 UIButton 更好,当然你也可以子类化其他 UIView。 这是一些代码:

enum BorderedButtonSide 
    case Top, Right, Bottom, Left



class BorderedButton : UIButton 

    private var borderTop: CALayer?
    private var borderTopWidth: CGFloat?
    private var borderRight: CALayer?
    private var borderRightWidth: CGFloat?
    private var borderBottom: CALayer?
    private var borderBottomWidth: CGFloat?
    private var borderLeft: CALayer?
    private var borderLeftWidth: CGFloat?


    func setBorder(side: BorderedButtonSide, _ color: UIColor, _ width: CGFloat) 

        let border = CALayer()
        border.backgroundColor = color.CGColor

        switch side 
        case .Top:
            border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: width)
            borderTop?.removeFromSuperlayer()
            borderTop = border
            borderTopWidth = width
        case .Right:
            border.frame = CGRect(x: frame.size.width - width, y: 0, width: width, height: frame.size.height)
            borderRight?.removeFromSuperlayer()
            borderRight = border
            borderRightWidth = width
        case .Bottom:
            border.frame = CGRect(x: 0, y: frame.size.height - width, width: frame.size.width, height: width)
            borderBottom?.removeFromSuperlayer()
            borderBottom = border
            borderBottomWidth = width
        case .Left:
            border.frame = CGRect(x: 0, y: 0, width: width, height: frame.size.height)
            borderLeft?.removeFromSuperlayer()
            borderLeft = border
            borderLeftWidth = width
        

        layer.addSublayer(border)
    

    override func layoutSubviews() 
        super.layoutSubviews()
        borderTop?.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderTopWidth!)
        borderRight?.frame = CGRect(x: frame.size.width - borderRightWidth!, y: 0, width: borderRightWidth!, height: frame.size.height)
        borderBottom?.frame = CGRect(x: 0, y: frame.size.height - borderBottomWidth!, width: frame.size.width, height: borderBottomWidth!)
        borderLeft?.frame = CGRect(x: 0, y: 0, width: borderLeftWidth!, height: frame.size.height)
    


【讨论】:

不错的解决方案!谢谢分享。【参考方案9】:

或者,最性能友好的方法是重载drawRect,就像这样:

@interface TPActionSheetButton : UIButton

@property (assign) BOOL drawsTopLine;
@property (assign) BOOL drawsBottomLine;
@property (assign) BOOL drawsRightLine;
@property (assign) BOOL drawsLeftLine;
@property (strong, nonatomic) UIColor * lineColor;

@end

@implementation TPActionSheetButton

- (void) drawRect:(CGRect)rect

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(ctx, 0.5f * [[UIScreen mainScreen] scale]);
    CGFloat red, green, blue, alpha;
    [self.lineColor getRed:&red green:&green blue:&blue alpha:&alpha];
    CGContextSetRGBStrokeColor(ctx, red, green, blue, alpha);

    if(self.drawsTopLine) 
        CGContextBeginPath(ctx);
        CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMinY(rect));
        CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMinY(rect));
        CGContextStrokePath(ctx);
    
    if(self.drawsBottomLine) 
        CGContextBeginPath(ctx);
        CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect));
        CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
        CGContextStrokePath(ctx);
    
    if(self.drawsLeftLine) 
        CGContextBeginPath(ctx);
        CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMinY(rect));
        CGContextAddLineToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect));
        CGContextStrokePath(ctx);
    
    if(self.drawsRightLine) 
        CGContextBeginPath(ctx);
        CGContextMoveToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMinY(rect));
        CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
        CGContextStrokePath(ctx);
    

    [super drawRect:rect];


@end

【讨论】:

覆盖drawRect: 不一定对性能友好。我很确定使用层具有更好的性能。 @ThomasW 但是当边界响应设备旋转而改变时会发生什么?还是拆分视图?或者自动布局有什么作用? @Womble 您需要更新图层以覆盖setFrame:setBounds:layoutSubviews 方法。 我推荐 layoutSublayersOfLayer: 而不是 setFrame: 或其他任何人【参考方案10】:

如果您使用约束(因此没有框架大小),那么您可以添加具有所需约束的边框视图

// MARK: - Add a border to one side of a view

public enum BorderSide 
    case top, bottom, left, right


extension UIView 
    public func addBorder(side: BorderSide, color: UIColor, width: CGFloat) 
        let border = UIView()
        border.translatesAutoresizingMaskIntoConstraints = false
        border.backgroundColor = color
        self.addSubview(border)

        let topConstraint = topAnchor.constraint(equalTo: border.topAnchor)
        let rightConstraint = trailingAnchor.constraint(equalTo: border.trailingAnchor)
        let bottomConstraint = bottomAnchor.constraint(equalTo: border.bottomAnchor)
        let leftConstraint = leadingAnchor.constraint(equalTo: border.leadingAnchor)
        let heightConstraint = border.heightAnchor.constraint(equalToConstant: width)
        let widthConstraint = border.widthAnchor.constraint(equalToConstant: width)


        switch side 
        case .top:
            NSLayoutConstraint.activate([leftConstraint, topConstraint, rightConstraint, heightConstraint])
        case .right:
            NSLayoutConstraint.activate([topConstraint, rightConstraint, bottomConstraint, widthConstraint])
        case .bottom:
            NSLayoutConstraint.activate([rightConstraint, bottomConstraint, leftConstraint, heightConstraint])
        case .left:
            NSLayoutConstraint.activate([bottomConstraint, leftConstraint, topConstraint, widthConstraint])
        
    

然后设置如下

myButton.addBorder(side: .left, color: UIColor.lightGray, width: 1)

(灵感来自this answer)

【讨论】:

喜欢这个扩展。谢谢一百万!【参考方案11】:

Swift 3 版本的 Confile 答案:

import UIKit

extension UIView 
    func addTopBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
        self.layer.addSublayer(border)
    

    func addRightBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
        self.layer.addSublayer(border)
    

    func addBottomBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
        self.layer.addSublayer(border)
    

    func addLeftBorderWithColor(color: UIColor, width: CGFloat) 
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
        self.layer.addSublayer(border)
    

使用自动布局时的用法:

class CustomView: UIView 

    override func awakeFromNib() 
        super.awakeFromNib()
    

    override func layoutSubviews() 
        addBottomBorderWithColor(color: UIColor.white, width: 1)
    

【讨论】:

在 iPad 和 iPhone plus 设备上,这条线没有填满整个屏幕。这个@spogebob92 有解决办法吗 猜测是自动布局已经完成,所以在添加边框之前,可能调用 layoutIfNeeded()【参考方案12】:

斯威夫特

创建 UIView 扩展

private var bottomLineColorAssociatedKey : UIColor = .black
private var topLineColorAssociatedKey : UIColor = .black
private var rightLineColorAssociatedKey : UIColor = .black
private var leftLineColorAssociatedKey : UIColor = .black
extension UIView 

@IBInspectable var bottomLineColor: UIColor 
    get 
        if let color = objc_getAssociatedObject(self, &bottomLineColorAssociatedKey) as? UIColor 
            return color
         else 
            return .black
        
     set 
        objc_setAssociatedObject(self, &bottomLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
    

@IBInspectable var bottomLineWidth: CGFloat 
    get 
        return self.bottomLineWidth
    
    set 
        DispatchQueue.main.async 
            self.addBottomBorderWithColor(color: self.bottomLineColor, width: newValue)
        
    

@IBInspectable var topLineColor: UIColor 
    get 
        if let color = objc_getAssociatedObject(self, &topLineColorAssociatedKey) as? UIColor 
            return color
         else 
            return .black
        
     set 
        objc_setAssociatedObject(self, &topLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
    

@IBInspectable var topLineWidth: CGFloat 
    get 
        return self.topLineWidth
    
    set 
        DispatchQueue.main.async 
            self.addTopBorderWithColor(color: self.topLineColor, width: newValue)
        
    

@IBInspectable var rightLineColor: UIColor 
    get 
        if let color = objc_getAssociatedObject(self, &rightLineColorAssociatedKey) as? UIColor 
            return color
         else 
            return .black
        
     set 
        objc_setAssociatedObject(self, &rightLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
    

@IBInspectable var rightLineWidth: CGFloat 
    get 
        return self.rightLineWidth
    
    set 
        DispatchQueue.main.async 
            self.addRightBorderWithColor(color: self.rightLineColor, width: newValue)
        
    

@IBInspectable var leftLineColor: UIColor 
    get 
        if let color = objc_getAssociatedObject(self, &leftLineColorAssociatedKey) as? UIColor 
            return color
         else 
            return .black
        
     set 
        objc_setAssociatedObject(self, &leftLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
    

@IBInspectable var leftLineWidth: CGFloat 
    get 
        return self.leftLineWidth
    
    set 
        DispatchQueue.main.async 
            self.addLeftBorderWithColor(color: self.leftLineColor, width: newValue)
        
    

func addTopBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.name = "topBorderLayer"
    removePreviouslyAddedLayer(name: border.name ?? "")
    border.backgroundColor = color.cgColor
    border.frame = CGRect(x: 0, y : 0,width: self.frame.size.width, height: width)
    self.layer.addSublayer(border)
    self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 1111) )


func addRightBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.name = "rightBorderLayer"
    removePreviouslyAddedLayer(name: border.name ?? "")
    border.backgroundColor = color.cgColor
    border.frame = CGRect(x: self.frame.size.width - width, y: 0, width : width, height :self.frame.size.height)
    self.layer.addSublayer(border)
     self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 2222) )


func addBottomBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.name = "bottomBorderLayer"
    removePreviouslyAddedLayer(name: border.name ?? "")
    border.backgroundColor = color.cgColor
    border.frame = CGRect(x: 0, y: self.frame.size.height - width,width : self.frame.size.width,height: width)
    self.layer.addSublayer(border)
    self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 3333) )

func addLeftBorderWithColor(color: UIColor, width: CGFloat) 
    let border = CALayer()
    border.name = "leftBorderLayer"
    removePreviouslyAddedLayer(name: border.name ?? "")
    border.backgroundColor = color.cgColor
    border.frame = CGRect(x:0, y:0,width : width, height : self.frame.size.height)
    self.layer.addSublayer(border)
    self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 4444) )

override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 

    if let objectView = object as? UIView,
        objectView === self,
        keyPath == #keyPath(UIView.bounds) 
        switch context 
        case UnsafeMutableRawPointer(bitPattern: 1111):
            for border in self.layer.sublayers ?? [] 
                if border.name == "topBorderLayer" 
                    border.frame = CGRect(x: 0, y : 0,width: self.frame.size.width, height: border.frame.height)
                
            
        case UnsafeMutableRawPointer(bitPattern: 2222):
            for border in self.layer.sublayers ?? [] 
                if border.name == "rightBorderLayer" 
                     border.frame = CGRect(x: self.frame.size.width - border.frame.width, y: 0, width : border.frame.width, height :self.frame.size.height)
                
            
        case UnsafeMutableRawPointer(bitPattern: 3333):
            for border in self.layer.sublayers ?? [] 
                if border.name == "bottomBorderLayer" 
                    border.frame = CGRect(x: 0, y: self.frame.size.height - border.frame.height,width : self.frame.size.width,height: border.frame.height)
                
            
        case UnsafeMutableRawPointer(bitPattern: 4444):
            for border in self.layer.sublayers ?? [] 
                if border.name == "leftBorderLayer" 
                   border.frame = CGRect(x:0, y:0,width : border.frame.width, height : self.frame.size.height)
                
            
        default:
            break
        
    

func removePreviouslyAddedLayer(name : String) 
    if self.layer.sublayers?.count ?? 0 > 0 
        self.layer.sublayers?.forEach 
            if $0.name == name 
                $0.removeFromSuperlayer()
            
        
    
   

目标 C

创建UIView的类别类

UIView+Border.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@interface UIView (Border) 

@property (nonatomic) IBInspectable UIColor *topLineColor;
@property (nonatomic) IBInspectable CGFloat topLineWidth;
@property (nonatomic) IBInspectable UIColor *bottomLineColor;
@property (nonatomic) IBInspectable CGFloat bottomLineWidth;
@property (nonatomic) IBInspectable UIColor *rightLineColor;
@property (nonatomic) IBInspectable CGFloat rightLineWidth;
@property (nonatomic) IBInspectable UIColor *leftLineColor;
@property (nonatomic) IBInspectable CGFloat leftLineWidth;

- (void)addBottomBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

- (void)addLeftBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

- (void)addRightBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

- (void)addTopBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;

@end

UIView+Border.m

static void *topBorderContext = &topBorderContext;
static void *bottomBorderContext = &bottomBorderContext;
static void *leftBorderContext = &leftBorderContext;
static void *rightBorderContext = &rightBorderContext;
static char bottomLineColorKey,topLineColorKey,rightLineColorKey,leftLineColorKey;
@implementation UIView(Utility)
@dynamic borderColor,borderWidth,cornerRadius,bottomLineWidth,topLineWidth,rightLineWidth,leftLineWidth;

-(void)setBorderColor:(UIColor *)borderColor
    [self.layer setBorderColor:borderColor.CGColor];


-(void)setBorderWidth:(CGFloat)borderWidth
    [self.layer setBorderWidth:borderWidth];


-(void)setCornerRadius:(CGFloat)cornerRadius
    [self.layer setCornerRadius:cornerRadius];

// for Bottom Line
- (UIColor *)bottomLineColor 
    return objc_getAssociatedObject(self, &bottomLineColorKey);

- (void)setBottomLineColor:(UIColor *)bottomLineColor 
    objc_setAssociatedObject(self, &bottomLineColorKey,
                             bottomLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

-(void)setBottomLineWidth:(CGFloat)bottomLineWidth 
    [self addBottomBorderWithColor:[self bottomLineColor] andWidth:bottomLineWidth];

// for top Line
- (UIColor *)topLineColor 
    return objc_getAssociatedObject(self, &topLineColorKey);

- (void)setTopLineColor:(UIColor *)topLineColor 
    objc_setAssociatedObject(self, &topLineColorKey,
                             topLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (void)setTopLineWidth:(CGFloat)topLineWidth
    [self addTopBorderWithColor:[self topLineColor] andWidth:topLineWidth];

// for right Line
- (UIColor *)rightLineColor 
    return objc_getAssociatedObject(self, &rightLineColorKey);

-(void)setRightLineColor:(UIColor *)rightLineColor 
    objc_setAssociatedObject(self, &rightLineColorKey,
                             rightLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

-(void)setRightLineWidth:(CGFloat)rightLineWidth
    [self addRightBorderWithColor:[self rightLineColor] andWidth:rightLineWidth];

// for left Line
-(UIColor *)leftLineColor 
    return objc_getAssociatedObject(self, &leftLineColorKey);

-(void)setLeftLineColor:(UIColor *)leftLineColor
    objc_setAssociatedObject(self, &leftLineColorKey,
                             leftLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

-(void)setLeftLineWidth:(CGFloat)leftLineWidth
    [self addLeftBorderWithColor:[self leftLineColor] andWidth:leftLineWidth];


- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    dispatch_async(dispatch_get_main_queue(), ^
        CALayer *border = [CALayer layer];
        border.name = @"topBorderLayer";
        [self removePreviouslyAddedLayer:border.name];
        border.backgroundColor = color.CGColor;
        border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);
        [self.layer addSublayer:border];
        [self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:topBorderContext];
    );


- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    dispatch_async(dispatch_get_main_queue(), ^
        CALayer *border = [CALayer layer];
        border.name = @"bottomBorderLayer";
        [self removePreviouslyAddedLayer:border.name];
        border.backgroundColor = color.CGColor;
        border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth);
        [self.layer addSublayer:border];
        [self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:bottomBorderContext];
    );


- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    dispatch_async(dispatch_get_main_queue(), ^
        CALayer *border = [CALayer layer];
        border.name = @"leftBorderLayer";
        [self removePreviouslyAddedLayer:border.name];
        border.backgroundColor = color.CGColor;
        border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);
        [self.layer addSublayer:border];
        [self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:leftBorderContext];
    );


- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth 
    dispatch_async(dispatch_get_main_queue(), ^
        CALayer *border = [CALayer layer];
        border.name = @"rightBorderLayer";
        [self removePreviouslyAddedLayer:border.name];
        border.backgroundColor = color.CGColor;
        border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height);
        [self.layer addSublayer:border];
        [self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:rightBorderContext];
    );

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

    if (context == topBorderContext) 
        for (CALayer *border in self.layer.sublayers) 
            if ([border.name isEqualToString:@"topBorderLayer"]) 
                [border setFrame:CGRectMake(0, 0, self.frame.size.width, border.frame.size.height)];
            
        
     else if (context == bottomBorderContext) 
        for (CALayer *border in self.layer.sublayers) 
            if ([border.name isEqualToString:@"bottomBorderLayer"]) 
                [border setFrame:CGRectMake(0, self.frame.size.height - border.frame.size.height, self.frame.size.width, border.frame.size.height)];
            
        
     else if (context == leftBorderContext) 
        for (CALayer *border in self.layer.sublayers) 
            if ([border.name isEqualToString:@"leftBorderLayer"]) 
                [border setFrame:CGRectMake(0, 0, border.frame.size.width, self.frame.size.height)];
            
        
     else if (context == rightBorderContext) 
        for (CALayer *border in self.layer.sublayers) 
            if ([border.name isEqualToString:@"rightBorderLayer"]) 
                [border setFrame:CGRectMake(self.frame.size.width - border.frame.size.width, 0, border.frame.size.width, self.frame.size.height)];
            
        
     else 
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    

- (void)removePreviouslyAddedLayer:(NSString *)name 
    if (self.layer.sublayers.count > 0) 
        for (CALayer *layer in self.layer.sublayers) 
            if ([layer.name isEqualToString:name]) 
                [layer removeFromSuperlayer];
            
        
    


@end

用法:- 从情节提要中选择任何控件,然后显示属性检查器(右侧)您将看到下图示例。(注意:边框仅在运行时出现。)

现在您可以设置边框颜色和宽度的任意一侧。

【讨论】:

它的工作.. 谢谢.. 但如果嵌入 UINavigationBar,它计算坐标错误..【参考方案13】:

Swift 4

基于:https://***.com/a/32821607/9980800

UIView+边框

extension UIView 

  enum ViewBorder: String 
      case left, right, top, bottom
  

  func add(Border border: ViewBorder, withColor color: UIColor = UIColor.lightGray, andWidth width: CGFloat = 1.0) 

    let borderView = UIView()
    borderView.backgroundColor = color
    borderView.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(borderView)
    NSLayoutConstraint.activate(getConstrainsFor(forView: borderView, WithBorderType: border, andWidth: width))
  

  private func getConstrainsFor(forView borderView: UIView, WithBorderType border: ViewBorder, andWidth width: CGFloat) -> [NSLayoutConstraint] 

    let height = borderView.heightAnchor.constraint(equalToConstant: width)
    let widthAnchor = borderView.widthAnchor.constraint(equalToConstant: width)
    let leading = borderView.leadingAnchor.constraint(equalTo: self.leadingAnchor)
    let trailing = borderView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
    let top = borderView.topAnchor.constraint(equalTo: self.topAnchor)
    let bottom = borderView.bottomAnchor.constraint(equalTo: self.bottomAnchor)

    switch border 

    case .bottom:
        return [bottom, leading, trailing, height]

    case .top:
        return [top, leading, trailing, height]

    case .left:
        return [top, bottom, leading, widthAnchor]

    case .right:
        return [top, bottom, trailing, widthAnchor]
    

用法:-

class ViewController: UIViewController 

@IBOutlet weak var sampleView: UIView!
override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    sampleView.add(Border: .bottom)


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.


【讨论】:

【参考方案14】:

斯威夫特 4

基于https://***.com/a/32513578/5391914

import UIKit
enum ViewBorder: String 
    case Left = "borderLeft"
    case Right = "borderRight"
    case Top = "borderTop"
    case Bottom = "borderBottom"


extension UIView 
    
    func addBorder(vBorders: [ViewBorder], color: UIColor, width: CGFloat) 
        vBorders.forEach  vBorder in
            let border = CALayer()
            border.backgroundColor = color.cgColor
            border.name = vBorder.rawValue
            switch vBorder 
            case .Left:
                border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
            case .Right:
                border.frame = CGRect(x:self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
            case .Top:
                border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
            case .Bottom:
                border.frame = CGRect(x: 0, y: self.frame.size.height - width , width: self.frame.size.width, height: width)
            
            self.layer.addSublayer(border)
        
    

【讨论】:

【参考方案15】:

我写了一个通用方法,可以在任何UIView 的任意一侧添加边框。您可以为每一边定义厚度、颜色、边距和zOrder

/*
 view: the view to draw border around
 thickness: thickness of the border on the given side
 color: color of the border on the given side
 margin: space between the border's outer edge and the view's frame edge on the given side.
 zOrder: defines the order to add the borders to the view.  The borders will be added by zOrder from lowest to highest, thus making the highest priority border visible when two borders overlap at the corners.
*/

    +(void) drawBorderAroundUIView:(UIView *) view thicknessLeft:(CGFloat) thicknessLeft colorLeft:(UIColor *)colorLeft marginLeft:(CGFloat) marginLeft zOrderLeft:(int) zOrderLeft thicknessRight:(CGFloat) thicknessRight colorRight:(UIColor *)colorRight marginRight:(CGFloat) marginRight zOrderRight:(int) zOrderRight thicknessTop:(CGFloat) thicknessTop colorTop:(UIColor *)colorTop marginTop:(CGFloat) marginTop zOrderTop:(int) zOrderTop thicknessBottom:(CGFloat) thicknessBottom colorBottom:(UIColor *)colorBottom marginBottom:(CGFloat) marginBottom zOrderBottom:(int) zOrderBottom
    //make margins be the outside edge and make positive margin represent a smaller rectangle
    marginBottom = -1 * marginBottom - thicknessBottom;
    marginTop = -1 * marginTop - thicknessTop;
    marginLeft = -1 * marginLeft - thicknessLeft;
    marginRight = -1 * marginRight - thicknessRight;

    //get reference points for corners
    CGPoint upperLeftCorner = CGPointZero;
    CGPoint lowerLeftCorner = CGPointMake(upperLeftCorner.x, upperLeftCorner.y + view.frame.size.height);
    CGPoint upperRightCorner = CGPointMake(upperLeftCorner.x + view.frame.size.width, upperLeftCorner.y);

    //left
    CALayer *leftBorder = [CALayer layer];
    leftBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, upperLeftCorner.y - thicknessTop - marginTop, thicknessLeft, view.frame.size.height + marginTop + marginBottom + thicknessBottom + thicknessTop);
    leftBorder.backgroundColor = colorLeft.CGColor;

    //right
    CALayer *rightBorder = [CALayer layer];
    rightBorder.frame = CGRectMake(upperRightCorner.x + marginRight, upperRightCorner.y - thicknessTop - marginTop, thicknessRight, view.frame.size.height + marginTop + marginBottom + thicknessBottom + thicknessTop);
    rightBorder.backgroundColor = colorRight.CGColor;

    //top
    CALayer *topBorder = [CALayer layer];
    topBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, upperLeftCorner.y - thicknessTop - marginTop, view.frame.size.width + marginLeft + marginRight + thicknessLeft + thicknessRight, thicknessTop);
    topBorder.backgroundColor = colorTop.CGColor;

    //bottom
    CALayer *bottomBorder = [CALayer layer];
    bottomBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, lowerLeftCorner.y + marginBottom, view.frame.size.width + marginLeft + marginRight + thicknessLeft + thicknessRight, thicknessBottom);
    bottomBorder.backgroundColor = colorBottom.CGColor;

    //define dictionary keys to be used for adding borders in order of zOrder
    NSString *borderDK = @"border";
    NSString *zOrderDK = @"zOrder";

    //storing borders in dictionaries in preparation to add them in order of zOrder
    NSDictionary *leftBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:leftBorder, borderDK, [NSNumber numberWithInt:zOrderLeft], zOrderDK, nil];
    NSDictionary *rightBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:rightBorder, borderDK, [NSNumber numberWithInt:zOrderRight], zOrderDK, nil];
    NSDictionary *topBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:topBorder, borderDK, [NSNumber numberWithInt:zOrderTop], zOrderDK, nil];
    NSDictionary *bottomBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:bottomBorder, borderDK, [NSNumber numberWithInt:zOrderBottom], zOrderDK, nil];

    NSMutableArray *borders = [NSMutableArray arrayWithObjects:leftBorderDictionary, rightBorderDictionary, topBorderDictionary, bottomBorderDictionary, nil];

    //add borders in order of zOrder (lowest -> highest).  Thus the highest zOrder will be added last so it will be on top.
    while (borders.count)
    
        //look for the next lowest zOrder border to add
        NSDictionary *nextBorderToLayDown = [borders objectAtIndex:0];
        for (int indexOfBorder = 0; indexOfBorder < borders.count; indexOfBorder++)
        
            NSDictionary *borderAtIndex = [borders objectAtIndex:indexOfBorder];
            if ([[borderAtIndex objectForKey:zOrderDK] intValue] < [[nextBorderToLayDown objectForKey:zOrderDK] intValue])
            
                nextBorderToLayDown = borderAtIndex;
            
        
        //add the border to the view
        [view.layer addSublayer:[nextBorderToLayDown objectForKey:borderDK]];
        [borders removeObject:nextBorderToLayDown];
    

【讨论】:

【参考方案16】:

您不必为每个边框添加图层,只需使用贝塞尔路径绘制一次即可。

CGRect rect = self.bounds;

CGPoint destPoint[4] = CGPointZero,
    (CGPoint)0, rect.size.height,
    (CGPoint)rect.size.width, rect.size.height,
    (CGPoint)rect.size.width, 0;

BOOL position[4] = _top, _left, _bottom, _right;

UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:destPoint[3]];

for (int i = 0; i < 4; ++i) 
    if (position[i]) 
        [path addLineToPoint:destPoint[i]];
     else 
        [path moveToPoint:destPoint[i]];
    


CAShapeLayer *borderLayer = [CAShapeLayer new];
borderLayer.frame = self.bounds;
borderLayer.path  = path.CGPath;
borderLayer.lineWidth   = _borderWidth ?: 1 / [UIScreen mainScreen].scale;
borderLayer.strokeColor = _borderColor.CGColor;
borderLayer.fillColor   = [UIColor clearColor].CGColor;

[self.layer addSublayer:borderLayer];

【讨论】:

【参考方案17】:

斯威夫特 4/3

您可以在下面使用此解决方案。它适用于比层更轻的 UIBezierPaths,从而导致快速启动时间。它易于使用,请参阅下面的说明。

class ResizeBorderView: UIView 
    var color = UIColor.white
    var lineWidth: CGFloat = 1
    var edges = [UIRectEdge]()
        didSet 
            setNeedsDisplay()
        
    
    override func draw(_ rect: CGRect) 
        if edges.contains(.top) || edges.contains(.all)
            let path = UIBezierPath()
            path.lineWidth = lineWidth
            color.setStroke()
            UIColor.blue.setFill()
            path.move(to: CGPoint(x: 0, y: 0 + lineWidth / 2))
            path.addLine(to: CGPoint(x: self.bounds.width, y: 0 + lineWidth / 2))
            path.stroke()
        
        if edges.contains(.bottom) || edges.contains(.all)
            let path = UIBezierPath()
            path.lineWidth = lineWidth
            color.setStroke()
            UIColor.blue.setFill()
            path.move(to: CGPoint(x: 0, y: self.bounds.height - lineWidth / 2))
            path.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - lineWidth / 2))
            path.stroke()
        
        if edges.contains(.left) || edges.contains(.all)
            let path = UIBezierPath()
            path.lineWidth = lineWidth
            color.setStroke()
            UIColor.blue.setFill()
            path.move(to: CGPoint(x: 0 + lineWidth / 2, y: 0))
            path.addLine(to: CGPoint(x: 0 + lineWidth / 2, y: self.bounds.height))
            path.stroke()
        
        if edges.contains(.right) || edges.contains(.all)
            let path = UIBezierPath()
            path.lineWidth = lineWidth
            color.setStroke()
            UIColor.blue.setFill()
            path.move(to: CGPoint(x: self.bounds.width - lineWidth / 2, y: 0))
            path.addLine(to: CGPoint(x: self.bounds.width - lineWidth / 2, y: self.bounds.height))
            path.stroke()
        
    

    将 UIView 的类设置为 ResizeBorderView 在 viewDidAppear 方法中使用 yourview.color 和 yourview.lineWidth 设置颜色和线宽 设置边缘,例如:yourview.edges = [.right, .left] ([.all]) for all 享受快速启动和调整边框大小

【讨论】:

【参考方案18】:
extension UIView  

 func addBottomLine(color: UIColor, height: CGFloat)  

   let bottomView = UIView(frame: CGRect(x: 0, y: self.frame.height - 1, width: self.frame.width, height: height))
    bottomView.translatesAutoresizingMaskIntoConstraints = false
    bottomView.autoresizingMask = .flexibleWidth
    bottomView.backgroundColor = color
    self.addSubview(bottomView)
 


【讨论】:

虽然这段代码可以回答这个问题,但最好包含一些上下文,解释它是如何工作的以及何时使用它。从长远来看,纯代码的答案没有用处。 很高兴收到您的来信,我会回答您的问题 -> 1- 首先创建名为您想要的文件,然后从 UIView 创建扩展名,然后添加一个方法,首先是 CGFloat,其次是UIColor 在该方法中添加上面的代码,并将第一行中的 1 更改为高度:CGFloat params 并设置 bottomView.backgroundColor = THE_COLOR_PARAMS【参考方案19】:

最完整的答案。 https://github.com/oney/UIView-Border

let rectangle = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 60))
rectangle.backgroundColor = UIColor.grayColor()
view.addSubview(rectangle)
rectangle.borderTop = Border(size: 3, color: UIColor.orangeColor(), offset: UIEdgeInsets(top: 0, left: -10, bottom: 0, right: -5))
rectangle.borderBottom = Border(size: 6, color: UIColor.redColor(), offset: UIEdgeInsets(top: 0, left: 10, bottom: 10, right: 0))
rectangle.borderLeft = Border(size: 2, color: UIColor.blueColor(), offset: UIEdgeInsets(top: 10, left: -10, bottom: 0, right: 0))
rectangle.borderRight = Border(size: 2, color: UIColor.greenColor(), offset: UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 0))

【讨论】:

【参考方案20】:

带有边框宽度和颜色的 Swift 4 扩展。 效果很好!

@IBDesignable
final class SideBorders: UIView 
@IBInspectable var topColor: UIColor = UIColor.clear
@IBInspectable var topWidth: CGFloat = 0

@IBInspectable var rightColor: UIColor = UIColor.clear
@IBInspectable var rightWidth: CGFloat = 0

@IBInspectable var bottomColor: UIColor = UIColor.clear
@IBInspectable var bottomWidth: CGFloat = 0

@IBInspectable var leftColor: UIColor = UIColor.clear
@IBInspectable var leftWidth: CGFloat = 0

override func draw(_ rect: CGRect) 
    let topBorder = CALayer()
    topBorder.backgroundColor = topColor.cgColor
    topBorder.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: topWidth)
    self.layer.addSublayer(topBorder)

    let rightBorder = CALayer()
    rightBorder.backgroundColor = rightColor.cgColor
    rightBorder.frame = CGRect(x: self.frame.size.width - rightWidth, y: 0, width: rightWidth, height: self.frame.size.height)
    self.layer.addSublayer(rightBorder)

    let bottomBorder = CALayer()
    bottomBorder.backgroundColor = bottomColor.cgColor
    bottomBorder.frame = CGRect(x: 0, y: self.frame.size.height - bottomWidth, width: self.frame.size.width, height: bottomWidth)
    self.layer.addSublayer(bottomBorder)

    let leftBorder = CALayer()
    leftBorder.backgroundColor = leftColor.cgColor
    leftBorder.frame = CGRect(x: 0, y: self.frame.size.height - leftWidth, width: self.frame.size.width, height: leftWidth)
    self.layer.addSublayer(leftBorder)

【讨论】:

【参考方案21】:

斯威夫特 5.1。与两个扩展一起使用,方法返回 CALayer,因此您可以重复使用它来更新帧。

enum Border: Int 
    case top = 0
    case bottom
    case right
    case left


extension UIView 
    func addBorder(for side: Border, withColor color: UIColor, borderWidth: CGFloat) -> CALayer 
       let borderLayer = CALayer()
       borderLayer.backgroundColor = color.cgColor

       let xOrigin: CGFloat = (side == .right ? frame.width - borderWidth : 0)
       let yOrigin: CGFloat = (side == .bottom ? frame.height - borderWidth : 0)

       let width: CGFloat = (side == .right || side == .left) ? borderWidth : frame.width
       let height: CGFloat = (side == .top || side == .bottom) ? borderWidth : frame.height

       borderLayer.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
       layer.addSublayer(borderLayer)
       return borderLayer
    


extension CALayer 
    func updateBorderLayer(for side: Border, withViewFrame viewFrame: CGRect) 
        let xOrigin: CGFloat = (side == .right ? viewFrame.width - frame.width : 0)
        let yOrigin: CGFloat = (side == .bottom ? viewFrame.height - frame.height : 0)

        let width: CGFloat = (side == .right || side == .left) ? frame.width : viewFrame.width
        let height: CGFloat = (side == .top || side == .bottom) ? frame.height : viewFrame.height

        frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
    

【讨论】:

如何在addBorder中使用UpdateBorderLayer? 您需要存储一个从 addBorder 方法返回的 CALayer。然后当你需要更新时(例如在 viewDidLayoutSubviews 中)你调用这个方法。否则使用约束【参考方案22】:
    extension UIView

enum side:String 
    case top
    case bottom
    case left
    case right

func addBorder(side:side,color:UIColor,width:CGFloat) 
    let border = CALayer()
    border.backgroundColor = color.cgColor
    switch side 
    case .top:
        border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width)
    case .bottom:
        border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
    case .left:
        border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
    case .right:
        border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
    
    self.layer.addSublayer(border)

【讨论】:

以上是关于UIView 下边框?的主要内容,如果未能解决你的问题,请参考以下文章

如何在运行时设置 UIView 的边框颜色 [重复]

UIView 发出与边框形状匹配的环

使 UIView 渐变的边框颜色

UIView画虚线边框

UIView边框覆盖子视图?

如何从 UIView 的某些部分删除边框?