Як додати межу лише у верхній частині UIView


Відповіді:


200

Тут я розглядаю підкласифікацію UIViewта переоцінку drawRectнадмірного рівня. Чому б не додати розширення UIViewта додати підпогляди меж?

@discardableResult
func addBorders(edges: UIRectEdge,
                color: UIColor,
                inset: CGFloat = 0.0,
                thickness: CGFloat = 1.0) -> [UIView] {

    var borders = [UIView]()

    @discardableResult
    func addBorder(formats: String...) -> UIView {
        let border = UIView(frame: .zero)
        border.backgroundColor = color
        border.translatesAutoresizingMaskIntoConstraints = false
        addSubview(border)
        addConstraints(formats.flatMap {
            NSLayoutConstraint.constraints(withVisualFormat: $0,
                                           options: [],
                                           metrics: ["inset": inset, "thickness": thickness],
                                           views: ["border": border]) })
        borders.append(border)
        return border
    }


    if edges.contains(.top) || edges.contains(.all) {
        addBorder(formats: "V:|-0-[border(==thickness)]", "H:|-inset-[border]-inset-|")
    }

    if edges.contains(.bottom) || edges.contains(.all) {
        addBorder(formats: "V:[border(==thickness)]-0-|", "H:|-inset-[border]-inset-|")
    }

    if edges.contains(.left) || edges.contains(.all) {
        addBorder(formats: "V:|-inset-[border]-inset-|", "H:|-0-[border(==thickness)]")
    }

    if edges.contains(.right) || edges.contains(.all) {
        addBorder(formats: "V:|-inset-[border]-inset-|", "H:[border(==thickness)]-0-|")
    }

    return borders
}

    // Usage:         
    view.addBorder(edges: [.all]) // All with default arguments 
    view.addBorder(edges: [.top], color: .green) // Just Top, green, default thickness
    view.addBorder(edges: [.left, .right, .bottom], color: .red, thickness: 3) // All except Top, red, thickness 3

За допомогою цього коду ви також не прив’язані до свого підкласу, ви можете застосувати його до будь-чого і всього, що має у спадок UIView- для багаторазового використання у вашому проекті та будь-яких інших. Передайте інші аргументи своїм методам, щоб визначити інші кольори та ширини. Варіантів багато.


4
Єдиним недоліком тут є те, що він не може змінити розмір.
Peter DeWeese

2
Не вдалося використати UIView та додати обмеження для автоматичного макета. Той же принцип.
Адам Уайт

1
@PeterDeWeese Це теж не є недоліком - якщо ви хочете контролювати розмір кордону, все, що вам потрібно зробити, - це щось на зразок: - (недійсного) addUpperBorderWithSize: (CGFloat) розміру, а потім замінити константу на параметр у функції . Те саме стосується інших параметрів, таких як кольори.
о.шнн

1
@AdamWaite, цей варіант автоматичного розміщення виглядає дуже добре. Дякую !
manonthemoon

2
Дякую за те, що я поділився, я складаю версію Objective-C з обмеженням автоматичного розміщення тут .
Ітачі

99

До оригінальної публікації Адама Уейта додано можливість для закруглених кутів та кілька редагувань

Важливо! Не label.layoutIfNeeded()забудьте додати " " прямо перед тим, як зателефонувати в "addborder", як це було раніше зауважено

Примітка. Я перевіряв це лише на UILabels.

extension CALayer {
    
    enum BorderSide {
        case top
        case right
        case bottom
        case left
        case notRight
        case notLeft
        case topAndBottom
        case all
    }
    
    enum Corner {
        case topLeft
        case topRight
        case bottomLeft
        case bottomRight
    }
    
    func addBorder(side: BorderSide, thickness: CGFloat, color: CGColor, maskedCorners: CACornerMask? = nil) {
        var topWidth = frame.size.width; var bottomWidth = topWidth
        var leftHeight = frame.size.height; var rightHeight = leftHeight
        
        var topXOffset: CGFloat = 0; var bottomXOffset: CGFloat = 0
        var leftYOffset: CGFloat = 0; var rightYOffset: CGFloat = 0
        
        // Draw the corners and set side offsets
        switch maskedCorners {
        case [.layerMinXMinYCorner, .layerMaxXMinYCorner]: // Top only
            addCorner(.topLeft, thickness: thickness, color: color)
            addCorner(.topRight, thickness: thickness, color: color)
            topWidth -= cornerRadius*2
            leftHeight -= cornerRadius; rightHeight -= cornerRadius
            topXOffset = cornerRadius; leftYOffset = cornerRadius; rightYOffset = cornerRadius
            
        case [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]: // Bottom only
            addCorner(.bottomLeft, thickness: thickness, color: color)
            addCorner(.bottomRight, thickness: thickness, color: color)
            bottomWidth -= cornerRadius*2
            leftHeight -= cornerRadius; rightHeight -= cornerRadius
            bottomXOffset = cornerRadius
            
        case [.layerMinXMinYCorner, .layerMinXMaxYCorner]: // Left only
            addCorner(.topLeft, thickness: thickness, color: color)
            addCorner(.bottomLeft, thickness: thickness, color: color)
            topWidth -= cornerRadius; bottomWidth -= cornerRadius
            leftHeight -= cornerRadius*2
            leftYOffset = cornerRadius; topXOffset = cornerRadius; bottomXOffset = cornerRadius;
            
        case [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]: // Right only
            addCorner(.topRight, thickness: thickness, color: color)
            addCorner(.bottomRight, thickness: thickness, color: color)
            topWidth -= cornerRadius; bottomWidth -= cornerRadius
            rightHeight -= cornerRadius*2
            rightYOffset = cornerRadius
            
        case [.layerMaxXMinYCorner, .layerMaxXMaxYCorner,  // All
              .layerMinXMaxYCorner, .layerMinXMinYCorner]:
            addCorner(.topLeft, thickness: thickness, color: color)
            addCorner(.topRight, thickness: thickness, color: color)
            addCorner(.bottomLeft, thickness: thickness, color: color)
            addCorner(.bottomRight, thickness: thickness, color: color)
            topWidth -= cornerRadius*2; bottomWidth -= cornerRadius*2
            topXOffset = cornerRadius; bottomXOffset = cornerRadius
            leftHeight -= cornerRadius*2; rightHeight -= cornerRadius*2
            leftYOffset = cornerRadius; rightYOffset = cornerRadius
            
        default: break
        }
        
        // Draw the sides
        switch side {
        case .top:
            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)
            
        case .right:
            addLine(x: frame.size.width - thickness, y: rightYOffset, width: thickness, height: rightHeight, color: color)
            
        case .bottom:
            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)
            
        case .left:
            addLine(x: 0, y: leftYOffset, width: thickness, height: leftHeight, color: color)

        // Multiple Sides
        case .notRight:
            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)
            addLine(x: 0, y: leftYOffset, width: thickness, height: leftHeight, color: color)
            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)

        case .notLeft:
            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)
            addLine(x: frame.size.width - thickness, y: rightYOffset, width: thickness, height: rightHeight, color: color)
            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)

        case .topAndBottom:
            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)
            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)

        case .all:
            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)
            addLine(x: frame.size.width - thickness, y: rightYOffset, width: thickness, height: rightHeight, color: color)
            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)
            addLine(x: 0, y: leftYOffset, width: thickness, height: leftHeight, color: color)
        }
    }
    
    private func addLine(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, color: CGColor) {
        let border = CALayer()
        border.frame = CGRect(x: x, y: y, width: width, height: height)
        border.backgroundColor = color
        addSublayer(border)
    }
    
    private func addCorner(_ corner: Corner, thickness: CGFloat, color: CGColor) {
        // Set default to top left
        let width = frame.size.width; let height = frame.size.height
        var x = cornerRadius
        var startAngle: CGFloat = .pi; var endAngle: CGFloat = .pi*3/2
        
        switch corner {
        case .bottomLeft: startAngle = .pi/2; endAngle = .pi
            
        case .bottomRight:
            x = width - cornerRadius
            startAngle = 0; endAngle = .pi/2
            
        case .topRight:
            x = width - cornerRadius
            startAngle = .pi*3/2; endAngle = 0
            
        default: break
        }
        
        let cornerPath = UIBezierPath(arcCenter: CGPoint(x: x, y: height / 2),
                                      radius: cornerRadius - thickness,
                                      startAngle: startAngle,
                                      endAngle: endAngle,
                                      clockwise: true)

        let cornerShape = CAShapeLayer()
        cornerShape.path = cornerPath.cgPath
        cornerShape.lineWidth = thickness
        cornerShape.strokeColor = color
        cornerShape.fillColor = nil
        addSublayer(cornerShape)
    }
}

4
Цей код чомусь не працює на Swift 3, змінивши підпис CGRectMake та CGColor за Swift 3.
EmbCoder

Тут є хороша стаття medium.com/swift-programming/…, яка розповідає про те, що потрібно зробити для Swift 3.
Micah Montoya

2
Будь-яка удача реалізувати це у швидкому 3?
Marquavious Draggon

7
Щоб він працював, вам потрібно додати його view.layoutIfNeeded()безпосередньо перед викликом view.layer.addBorder(...). Потім він працює у Swift 3
Адам Студенік

це працює чудово. додайте рядок у цьому методіoverride func viewDidLayoutSubviews()
Антоній Рафель

60

Найкращий спосіб для мене - категорія на UIView, але adding viewsзамість CALayers, тому ми можемо take advantage of AutoresizingMasksпереконатися, що розмір кордонів змінюється разом із супереглядом.

Мета C

- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
    UIView *border = [UIView new];
    border.backgroundColor = color;
    [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin];
    border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);
    [self addSubview:border];
}

- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
    UIView *border = [UIView new];
    border.backgroundColor = color;
    [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];
    border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth);
    [self addSubview:border];
}

- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
    UIView *border = [UIView new];
    border.backgroundColor = color;
    border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);
    [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin];
    [self addSubview:border];
}

- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
    UIView *border = [UIView new];
    border.backgroundColor = color;
    [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin];
    border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height);
    [self addSubview:border];
}

Швидкий 5

func addTopBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
    border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderWidth)
    addSubview(border)
}

func addBottomBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
    border.frame = CGRect(x: 0, y: frame.size.height - borderWidth, width: frame.size.width, height: borderWidth)
    addSubview(border)
}

func addLeftBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.frame = CGRect(x: 0, y: 0, width: borderWidth, height: frame.size.height)
    border.autoresizingMask = [.flexibleHeight, .flexibleRightMargin]
    addSubview(border)
}

func addRightBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]
    border.frame = CGRect(x: frame.size.width - borderWidth, y: 0, width: borderWidth, height: frame.size.height)
    addSubview(border)
}

7
Це, безумовно, одне з найкращих рішень цієї проблеми. Більшість запропонованих рішень НЕ вшановують зміни перегляду (і, таким чином, обертання пристрою або розділення переглядів). Цей робить.
Womble

1
як це можна адаптувати для підтримки закруглених кутів?
ntaj

29

Swift 3.0

Швидкий 4.1

extension CALayer {

  func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {

    let border = CALayer()

    switch edge {
    case UIRectEdge.top:
        border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness)

    case UIRectEdge.bottom:
        border.frame = CGRect(x:0, y: frame.height - thickness, width: frame.width, height:thickness)

    case UIRectEdge.left:
        border.frame = CGRect(x:0, y:0, width: thickness, height: frame.height)

    case UIRectEdge.right:
        border.frame = CGRect(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)

    default: do {}
    }

    border.backgroundColor = color.cgColor

    addSublayer(border)
 }
}

1
Це не працює, коли розмір перегляду змінюється, наприклад, зміна орієнтації.
Нестор

Якщо ви використовуєте макет на основі кадру, тоді вам доведеться реалізувати layoutSubviews і перерахувати всі кадри субперегляду (або підшару) там
trapper

22

Підклас UIViewта реалізуйте drawRect:у своєму підкласі, наприклад:

Ціль-с

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect));
    CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor] );
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

Швидкий 4

override func draw(_ rect: CGRect) {

    let cgContext = UIGraphicsGetCurrentContext()
    cgContext?.move(to: CGPoint(x: rect.minX, y: rect.minY))
    cgContext?.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
    cgContext?.setStrokeColor(UIColor.red.cgColor)
    cgContext?.setLineWidth(2.0)
    cgContext?.strokePath()
}

Це малює червону лінію в 2 пікселі як верхню межу. Усі інші варіанти, які ви згадуєте, залишаються для читача тривіальною вправою.

Рекомендується посібник з програмування кварцу 2D .


Це єдиний спосіб для цього ??? Отже, якщо мені потрібно зробити перегляд із верхньою межею, а інший - з нижньою межею та іншим видом ... Я маю на увазі завжди певний вид з однією або двома рамками, мені доведеться створити підклас для кожного конкретного випадку ??? Я не знаю, чи дійсно приємно створювати клас лише для додавання кордонів ...
manonthemoon

1
Ні. Подумайте про це. Ваш UIViewпідклас може мати властивість, яка визначає, які межі drawRect:малюється. Цю властивість можна визначити за допомогою NS_OPTIONSтого, що вона визначає біт-маску як UIViewAutoresizingбіт-маску. Якщо з будь-якої причини ви дійсно заперечуєте проти підкласифікації, UIViewтоді просто додайте невеликий (або широкий) підперегляд у 1-2 пікселі та надайте йому будь-які розміри, які ви хочете імітувати межу.
FluffulousChimp

Ей, хлопці, я створив підклас Swift, натхненний цим кодом, сподіваюся, вам сподобається: gist.github.com/asiviero/4f52ab7dea7d9252a64c
asiviero

19

Код обраної відповіді, якщо хтось цього хоче.

ПРИМІТКА. Це не працює з автоматичним розгортанням (так само, обертовим пристроєм до пейзажу тощо).

Спочатку визначте товщину:

NSInteger borderThickness = 1;

Потім просто скопіюйте, використовуйте будь-який або всі ці, щоб встановити межу, яку ви хочете встановити.

Верхня межа

UIView *topBorder = [UIView new];
topBorder.backgroundColor = [UIColor lightGrayColor];
topBorder.frame = CGRectMake(0, 0, myView.frame.size.width, borderThickness);
[myView addSubview:topBorder];

Нижня межа

UIView *bottomBorder = [UIView new];
bottomBorder.backgroundColor = [UIColor lightGrayColor];
bottomBorder.frame = CGRectMake(0, myView.frame.size.height - borderThickness, myView.frame.size.width, borderThickness);
[myView addSubview:bottomBorder];

Ліва межа

UIView *leftBorder = [UIView new];
leftBorder.backgroundColor = [UIColor lightGrayColor];
leftBorder.frame = CGRectMake(0, 0, borderThickness, myView.frame.size.height);
[myView addSubview:leftBorder];

Правий кордон

UIView *rightBorder = [UIView new];
rightBorder.backgroundColor = [UIColor lightGrayColor];
rightBorder.frame = CGRectMake(myView.frame.size.width - borderThickness, 0, borderThickness, myView.frame.size.height);
[myView addSubview:rightBorder];

Шляхом прямого вирішення ... з накладними накладками UIView порівняно з голою CALayer
Soberman

прочитавши всі інші рішення, я думав, що просто додам крихітний вигляд. Все, що працює за кордон! Для цього рішення потрібно також декілька шпильок для роботи в автоматичному макеті. Шлях легше обійти голову.
noobsmcgoobs

Дякую, я додав примітку вище, щоб кодери використовуватимуть лише те, що їх додаток не обертається.
Травіс М.

15

Старе питання, але автоматичне розв’язання з коригуванням меж виконання ще не вистачає.

borders(for: [.left, .bottom], width: 2, color: .red)

Наступне розширення UIView додасть межу лише для заданих ребер. Якщо ви будете змінювати краї під час виконання, рамки будуть відповідно коригуватися.

extension UIView {
    func borders(for edges:[UIRectEdge], width:CGFloat = 1, color: UIColor = .black) {

        if edges.contains(.all) {
            layer.borderWidth = width
            layer.borderColor = color.cgColor
        } else {
            let allSpecificBorders:[UIRectEdge] = [.top, .bottom, .left, .right]

            for edge in allSpecificBorders {
                if let v = viewWithTag(Int(edge.rawValue)) {
                    v.removeFromSuperview()
                }

                if edges.contains(edge) {
                    let v = UIView()
                    v.tag = Int(edge.rawValue)
                    v.backgroundColor = color
                    v.translatesAutoresizingMaskIntoConstraints = false
                    addSubview(v)

                    var horizontalVisualFormat = "H:"
                    var verticalVisualFormat = "V:"

                    switch edge {
                    case UIRectEdge.bottom:
                        horizontalVisualFormat += "|-(0)-[v]-(0)-|"
                        verticalVisualFormat += "[v(\(width))]-(0)-|"
                    case UIRectEdge.top:
                        horizontalVisualFormat += "|-(0)-[v]-(0)-|"
                        verticalVisualFormat += "|-(0)-[v(\(width))]"
                    case UIRectEdge.left:
                        horizontalVisualFormat += "|-(0)-[v(\(width))]"
                        verticalVisualFormat += "|-(0)-[v]-(0)-|"
                    case UIRectEdge.right:
                        horizontalVisualFormat += "[v(\(width))]-(0)-|"
                        verticalVisualFormat += "|-(0)-[v]-(0)-|"
                    default:
                        break
                    }

                    self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: horizontalVisualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["v": v]))
                    self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: verticalVisualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["v": v]))
                }
            }
        }
    }
}

13

Швидка версія:

var myView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
myView.backgroundColor = UIColor.yellowColor() 

var border = CALayer()
border.backgroundColor = UIColor.lightGrayColor()
border.frame = CGRect(x: 0, y: 0, width: myView.frame.width, height: 0.5)

myView.layer.addSublayer(border)

Редагувати: Для оновлених версій дивіться мою репо тут: https://github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/UIViewExtensions.swift

Подивіться на частини addBorder


5

Swift 4.2 та AutoLayout

Я пройшов запропоновані рішення. Багато хто базується на кадрах Це просте розширення, яке працює з AutoLayout - використовуйте View замість шару, щоб переконатися, що ми можемо використовувати AutoLayout - Single subview з 4 обмеженнями

Використовуйте наступним чином:

self.addBorder(.bottom, color: .lightGray, thickness: 0.5)


extension UIView {
    func addBorder(_ edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
        let subview = UIView()
        subview.translatesAutoresizingMaskIntoConstraints = false
        subview.backgroundColor = color
        self.addSubview(subview)
        switch edge {
        case .top, .bottom:
            subview.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
            subview.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
            subview.heightAnchor.constraint(equalToConstant: thickness).isActive = true
            if edge == .top {
                subview.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            } else {
                subview.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            }
        case .left, .right:
            subview.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            subview.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            subview.widthAnchor.constraint(equalToConstant: thickness).isActive = true
            if edge == .left {
                subview.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
            } else {
                subview.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
            }
        default:
            break
        }
    }
}

4

Я взяв відповіді Адама Вайта і Поласа і поєднав їх. Я також додав можливість з'єднати вибрані краї разом, тому вам потрібно викликати лише одну функцію так:

[self.view addBordersToEdge:(UIRectEdgeLeft|UIRectEdgeRight)
                  withColor:[UIColor grayColor]
                   andWidth:1.0];

або так:

[self.view addBordersToEdge:(UIRectEdgeAll)
                  withColor:[UIColor grayColor]
                   andWidth:1.0];

Що вам потрібно застосувати, це категорія на UIView, як це пропонується в інших відповідях із наступною реалізацією:

- (void)addBordersToEdge:(UIRectEdge)edge withColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
    if (edge & UIRectEdgeTop) {
        UIView *border = [UIView new];
        border.backgroundColor = color;
        [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin];
        border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);
        [self addSubview:border];
    }

    if (edge & UIRectEdgeLeft) {
        UIView *border = [UIView new];
        border.backgroundColor = color;
        border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);
        [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin];
        [self addSubview:border];
    }

    if (edge & UIRectEdgeBottom) {
        UIView *border = [UIView new];
        border.backgroundColor = color;
        [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];
        border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth);
        [self addSubview:border];
    }

    if (edge & UIRectEdgeRight) {
        UIView *border = [UIView new];
        border.backgroundColor = color;
        [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin];
        border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height);
        [self addSubview:border];
    }
}

4

// ПОЗНАЧЕННЯ: - Додати ліву рамку для перегляду

(void)prefix_addLeftBorder:(UIView *) viewName
{
    CALayer *leftBorder = [CALayer layer];
    leftBorder.backgroundColor = [UIColor colorWithRed:221/255.0f green:221/255.0f blue:221/255.0f alpha:1.0f].CGColor;
    leftBorder.frame = CGRectMake(0,0,1.0,viewName.frame.size.height);
    [viewName.layer addSublayer:leftBorder];
}

// ПОЗНАЧЕННЯ: - Додати праву рамку для перегляду

(void)prefix_addRightBorder:(UIView *) viewName
{
    CALayer *rightBorder = [CALayer layer];
    rightBorder.backgroundColor = [UIColor colorWithRed:221/255.0f green:221/255.0f blue:221/255.0f alpha:1.0f].CGColor;
    rightBorder.frame = CGRectMake(viewName.frame.size.width - 1.0,0,1.0,viewName.frame.size.height);
    [viewName.layer addSublayer:rightBorder];
}

// ОЗНАКА: - Додайте нижню межу для перегляду

(void)prefix_addbottomBorder:(UIView *) viewName
{
    CALayer *bottomBorder = [CALayer layer];
    bottomBorder.backgroundColor = [UIColor colorWithRed:221/255.0f green:221/255.0f blue:221/255.0f alpha:1.0f].CGColor;
    bottomBorder.frame = CGRectMake(0,viewName.frame.size.height - 1.0,viewName.frame.size.width,1.0);
    [viewName.layer addSublayer:bottomBorder];
}

3

Я вніс деякі зміни у відповідь Дена, щоб я міг додати межі до декількох країв однією командою:

infoView.addBorder(toEdges: [.left, .bottom, .right], color: borderColor, thickness: 1)

Ось повний код:

extension UIView {
    func addBorder(toEdges edges: UIRectEdge, color: UIColor, thickness: CGFloat) {

        func addBorder(toEdge edges: UIRectEdge, color: UIColor, thickness: CGFloat) {
            let border = CALayer()
            border.backgroundColor = color.cgColor

            switch edges {
            case .top:
                border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness)
            case .bottom:
                border.frame = CGRect(x: 0, y: frame.height - thickness, width: frame.width, height: thickness)
            case .left:
                border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.height)
            case .right:
                border.frame = CGRect(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)
            default:
                break
            }

            layer.addSublayer(border)
        }

        if edges.contains(.top) || edges.contains(.all) {
            addBorder(toEdge: .top, color: color, thickness: thickness)
        }

        if edges.contains(.bottom) || edges.contains(.all) {
            addBorder(toEdge: .bottom, color: color, thickness: thickness)
        }

        if edges.contains(.left) || edges.contains(.all) {
            addBorder(toEdge: .left, color: color, thickness: thickness)
        }

        if edges.contains(.right) || edges.contains(.all) {
            addBorder(toEdge: .right, color: color, thickness: thickness)
        }
    }
}

2

Будівництво геть NSBum в відповідь, я прийняв подібний підхід і створив цю просту UIView підклас так , що він працює в Interface Builder і працює з обмеженнями: GitHub посилання
Використовуючи CGContextFillRect замість CGContextStrokePath, я був в стані передбачувано тримати лінії цільно і в межі погляду.

Ось мій пост у цьому блозі: http://natrosoft.com/?p=55

- В основному просто занесіть UIView в Interface Builder і змініть тип свого класу на NAUIViewWithBorders.
- Тоді у вашому представленні VCDidLoad зробіть щось на кшталт:

/* For a top border only ———————————————- */
self.myBorderView.borderColorTop = [UIColor redColor];
self.myBorderView..borderWidthsAll = 1.0f;

/* For borders with different colors and widths ————————— */
self.myBorderView.borderWidths = UIEdgeInsetsMake(2.0, 4.0, 6.0, 8.0);
self.myBorderView.borderColorTop = [UIColor blueColor];
self.myBorderView.borderColorRight = [UIColor redColor];
self.myBorderView.borderColorBottom = [UIColor greenColor];
self.myBorderView.borderColorLeft = [UIColor darkGrayColor];

Ось пряме посилання на файл .m, щоб ви могли бачити реалізацію. Також є демонстраційний проект. Сподіваюся, це комусь допоможе :)


2

Моя відповідь на аналогічне запитання: https://stackoverflow.com/a/27141956/435766 Я особисто вважаю за краще спуститися по дорозі категорій на тому, оскільки я хочу мати змогу використовувати його на будь-якому підкласі UIView.


2
extension UIView {

    func addBorder(edge: UIRectEdge, color: UIColor, borderWidth: CGFloat) {

        let seperator = UIView()
        self.addSubview(seperator)
        seperator.translatesAutoresizingMaskIntoConstraints = false

        seperator.backgroundColor = color

        if edge == .top || edge == .bottom
        {
            seperator.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
            seperator.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
            seperator.heightAnchor.constraint(equalToConstant: borderWidth).isActive = true

            if edge == .top
            {
                seperator.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            }
            else
            {
                seperator.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            }
        }
        else if edge == .left || edge == .right
        {
            seperator.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            seperator.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            seperator.widthAnchor.constraint(equalToConstant: borderWidth).isActive = true

            if edge == .left
            {
                seperator.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
            }
            else
            {
                seperator.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
            }
        }
    }

}

2

Ось просте рішення. Додайте ярлик до свого UIView, очистіть текст на етикетці і встановіть колір тла мітки як ваш колір облямівки. Встановіть походження (x,y)етикетки як джерело (x,y)вашого перегляду. і встановіть ширину мітки як ширину вашої UIView, встановіть висоту 1 або 2 (для висоти вашої межі у верхній частині вашої UIView). І це повинно зробити трюк.


28
Тоді немає сенсу мати етикетку, може бути просто іншим UIView
maor10

1
Це насправді не відповідає на питання про додавання лише верхньої межі до uiview. Це швидке виправлення, яке, ймовірно, не спрацювало б для складніших проблем.
simon_smiley


2

Версія Swift 3

extension UIView {
    enum ViewSide {
        case Top, Bottom, Left, Right
    }

    func addBorder(toSide side: ViewSide, withColor color: UIColor, andThickness thickness: CGFloat) {

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

        switch side {
        case .Top:
            border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: thickness)
        case .Bottom:
            border.frame = CGRect(x: 0, y: frame.size.height - thickness, width: frame.size.width, height: thickness)
        case .Left:
            border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.size.height)
        case .Right:
            border.frame = CGRect(x: frame.size.width - thickness, y: 0, width: thickness, height: frame.size.height)
        }

        layer.addSublayer(border)
    }
}

Для того щоб встановити відповідну межу, вам слід перекрити метод viewDidLayoutSubviews () :

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    yourView.addBorder(toSide: UIView.ViewSide.Top, withColor: UIColor.lightGray, andThickness: 1)

Чи буде він називати "addSublayer", коли макет отримуватиме оновлення?
byJeevan

1

Просто розміщуйте тут, щоб допомогти тому, хто шукає додавання меж. Я вніс декілька змін у прийняту відповідь, тут швидко напишіть лише межу . Змінено ширина в разі UIRectEdge.Topз CGRectGetHeight(self.frame)до CGRectGetWidth(self.frame)і в разі UIRectEdge.Bottomз UIScreen.mainScreen().bounds.widthдо CGRectGetWidth(self.frame)отримати кордону правильно. Використання Swift 2.

Нарешті розширення:

extension CALayer {

func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {

    let border = CALayer();

    switch edge {
    case UIRectEdge.Top:
        border.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), thickness); 
        break
    case UIRectEdge.Bottom:
        border.frame = CGRectMake(0, CGRectGetHeight(self.frame) - thickness, CGRectGetWidth(self.frame), thickness)
        break
    case UIRectEdge.Left:
        border.frame = CGRectMake(0, 0, thickness, CGRectGetHeight(self.frame))
        break
    case UIRectEdge.Right:
        border.frame = CGRectMake(CGRectGetWidth(self.frame) - thickness, 0, thickness, CGRectGetHeight(self.frame))
        break
    default:
        break
    }

    border.backgroundColor = color.CGColor;

    self.addSublayer(border)
}

}

1

У випадку, якщо комусь колись знадобиться версія Xamarin:

public static class UIUtils
{
    public static void AddBorder(this CALayer cALayer, UIRectEdge edge, UIColor color, float thickness)
    {

        var border = new CALayer();
        switch (edge) 
        {
            case UIRectEdge.Top:
                border.Frame = new CGRect(0, 0, cALayer.Frame.Width, height: thickness);
                break;
            case UIRectEdge.Bottom:
                border.Frame = new CGRect(0, cALayer.Frame.Height - thickness, width: cALayer.Frame.Width, height: thickness);
                break;
            case UIRectEdge.Left:
                border.Frame = new CGRect(0, 0, width: thickness, height: cALayer.Frame.Height);
                break;
            case UIRectEdge.Right:
                border.Frame = new CGRect(cALayer.Frame.Width - thickness, y: 0, width: thickness, height: cALayer.Frame.Height);
                break;
            default: break;
        }
        border.BackgroundColor = color.CGColor;
        cALayer.AddSublayer(border);
    }
}

1

Ось Swift 4 версія відповіді Паулса

func addTopBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
    border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: thickness)
    addSubview(border)
}

func addBottomBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
    border.frame = CGRect(x: 0, y: frame.size.height - thickness, width: frame.size.width, height: thickness)
    addSubview(border)
}

func addLeftBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleHeight, .flexibleRightMargin]
    border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.size.height)
    addSubview(border)
}

func addRightBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]
    border.frame = CGRect(x: frame.size.width - thickness, y: 0, width: thickness, height: frame.size.height)
    addSubview(border)
}

1

Натхненний @Addison, я переписав розширення без використання будь-яких сторонніх фреймворків, оскільки він використовував SnapKit та CocoaLumberjack.

Як і в підході @Addisons, я також видаляю раніше додані межі, тому ця реалізація повинна чудово реагувати на представлення повторного використання як осередки таблиці, так і комірки колекції.

fileprivate class BorderView: UIView {} // dummy class to help us differentiate among border views and other views
                                        // to enabling us to remove existing borders and place new ones

extension UIView {

    func setBorders(toEdges edges: [UIRectEdge], withColor color: UIColor, inset: CGFloat = 0, thickness: CGFloat) {
        // Remove existing edges
        for view in subviews {
            if view is BorderView {
                view.removeFromSuperview()
            }
        }
        // Add new edges
        if edges.contains(.all) {
            addSidedBorder(toEdge: [.left,.right, .top, .bottom], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.left) {
            addSidedBorder(toEdge: [.left], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.right) {
            addSidedBorder(toEdge: [.right], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.top) {
            addSidedBorder(toEdge: [.top], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.bottom) {
            addSidedBorder(toEdge: [.bottom], withColor: color, inset: inset, thickness: thickness)
        }
    }

    private func addSidedBorder(toEdge edges: [RectangularEdges], withColor color: UIColor, inset: CGFloat = 0, thickness: CGFloat) {
        for edge in edges {
            let border = BorderView(frame: .zero)
            border.backgroundColor = color
            addSubview(border)
            border.translatesAutoresizingMaskIntoConstraints = false
            switch edge {
            case .left:
                NSLayoutConstraint.activate([
                border.leftAnchor.constraint(equalTo: self.leftAnchor, constant: inset),
                    border.topAnchor.constraint(equalTo: self.topAnchor, constant: inset),
                    border.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -inset),
                    NSLayoutConstraint(item: border, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: thickness) ])
            case .right:
                NSLayoutConstraint.activate([
                    border.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -inset),
                    border.topAnchor.constraint(equalTo: self.topAnchor, constant: inset),
                    border.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -inset),
                    NSLayoutConstraint(item: border, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: thickness) ])
            case .top:
                NSLayoutConstraint.activate([
                    border.leftAnchor.constraint(equalTo: self.leftAnchor, constant: inset),
                    border.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -inset),
                    border.topAnchor.constraint(equalTo: self.topAnchor, constant: inset),
                    NSLayoutConstraint(item: border, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: thickness) ])
            case .bottom:
                NSLayoutConstraint.activate([
                    border.leftAnchor.constraint(equalTo: self.leftAnchor, constant: inset),
                    border.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -inset),
                    border.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -inset),
                    NSLayoutConstraint(item: border, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: thickness) ])
            }
        }
    }

    private enum RectangularEdges {
        case left
        case right
        case top
        case bottom
    }
}

1

Якщо я будую всередині аркуша розкадри, я вважаю UIViewза краще додати позаду свого корисного UIView... Якщо я хочу створити рамку на верхній частині моєї UIView, я просто збільшую висоту фону UIViewна мою ширину кордону. Те саме можна робити для будь-якої іншої сторони :)


0

Особисто мені подобається підкласифікація подання + drawRect, але ось лише інший спосіб зробити це (і це працює так само, як і прийнята відповідь від @If Pollavith):

Ваш новий рамковий шар може бути налаштований так, щоб він мав будь-які розміри. Отже, як і у відповіді @I Pollavith, ви створюєте шар таким же високим, як ви хочете, щоб він був, і таким же широким, як вигляд, який ви хочете межувати. Використовуйте визначення кадру шару, щоб розмістити його там, де ви хочете, а потім додайте його в якості підшару для перегляду.

Для довідки, моя власна вимога полягала в тому, щоб поставити рамку на лівій стороні подання (будь ласка, не вирізайте і вставити цей код, і не "просто мені", оскільки це не ставить межу у верхній частині подання - змінити код нижче досить просто):

    CALayer *leftBorder = [CALayer layer];
leftBorder.borderColor = [UIColor colorWithRed:0.0 green:91.0/255.0 blue:141.0/255.0 alpha:1.0].CGColor;
leftBorder.borderWidth = 1;
leftBorder.frame = CGRectMake(0, 0, 1.0, CGRectGetHeight(self.myTargetView.frame));
[self.myTargetView.layer addSublayer:leftBorder];

Я здогадуюсь, що єдина поміркована вигода від цього та створення невеликого UIView або UILabel полягає в тому, що CALayer нібито має легшу вагу, і є багато цікавих поглядів (як на думку) щодо перенапрямлення drawRect порівняно з використанням CALayers (наприклад, тут : iOS: Використання UIView 'drawRect:' порівняно з делегатом його рівня 'drawLayer: inContext:' ).

Тварина451

Мені подобається колір синій.


0

На додаток до n8tr можна додати, що є можливість встановити їх із розкадрівки:
- додати два властивості, як borderColorі borderWidthу .h файл;
- тоді ви можете додати keyPathsпрямо в раскадровку, див. посилання на скріншот


0

Перетворіть відповідь DanShev у Swift 3

extension CALayer {

func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {

    let border = CALayer()

    switch edge {
    case .top:
        border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
        break
    case .bottom:
        border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
        break
    case .left:
        border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
        break
    case .right:
        border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
        break
    default:
        break
    }

    border.backgroundColor = color.cgColor;

    self.addSublayer(border)
}
}

0

Для Xamarin в C # я просто створюю рамку в рядку при додаванні підшару

  View.Layer.AddSublayer(new CALayer()
    {
        BackgroundColor = UIColor.Black.CGColor,
        Frame = new CGRect(0, 0, View.Frame.Width, 0.5f)
    });

Ви можете домовитись про це (як пропонують інші) для нижньої, лівої та правої меж.


0

Ви також можете перевірити цю колекцію категорій UIKit та Foundation: https://github.com/leszek-s/LSCa категории

Це дозволяє додавати межу на одній стороні UIView одним рядком коду:

[self.someView lsAddBorderOnEdge:UIRectEdgeTop color:[UIColor blueColor] width:2];

і він належним чином обробляє обертання огляду, хоча більшість відповідей, розміщених тут, не справляються з цим.


0

Примітка: більшість рішень тут не адаптуються та не змінять розмір. Рішення, які дозволять змінити розмір, матимуть величезний вплив на час запуску, оскільки вони використовують багато процесора.

Ви можете використовувати це рішення внизу. Він працює на 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()
        }
    }
}
  1. Встановіть клас вашого UIView на ResizeBorderView
  2. Встановіть колір та ширину рядків, використовуючи yourview.color та yourview.lineWidth у своєму методі viewDidAppear
  3. Встановіть ребра, наприклад: yourview.edges = [.right, .left] ([.all]) для всіх
  4. Насолоджуйтесь швидким початком і зміною кордонів

0

Налаштування верхньої межі та нижньої межі для UIView у Swift.

let topBorder = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 1))
topBorder.backgroundColor = UIColor.black
myView.addSubview(topBorder)

let bottomBorder = UIView(frame: CGRect(x: 0, y: myView.frame.size.height - 1, width: 10, height: 1))
bottomBorder.backgroundColor = UIColor.black
myView.addSubview(bottomBorder)

0

У Swift 4 і 3

let borderThickness = 2
let topBorder = UIView()
topBorder.backgroundColor = UIColor.red
topBorder.frame = CGRect(x: 0, y: 0, width: 
                  Int(yourViewFromOutlet.frame.size.width), height: 
                  borderThickness)
yourViewFromOutlet.addSubview(topBorder)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.