Moje pytanie dotyczy tytułu.
Nie wiem, jak dodać obramowanie z określonej strony, u góry lub u dołu, z dowolnej strony ...
layer.borderrysuje granicę dla całego widoku ...
Moje pytanie dotyczy tytułu.
Nie wiem, jak dodać obramowanie z określonej strony, u góry lub u dołu, z dowolnej strony ...
layer.borderrysuje granicę dla całego widoku ...
Odpowiedzi:
Rozważam tutaj podklasy UIViewi drawRectprzesadę. Dlaczego nie dodać rozszerzenia UIViewi dodać podglądy obramowań?
@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
Dzięki temu kodowi nie jesteś również powiązany ze swoją podklasą, możesz go zastosować do wszystkiego i wszystkiego, co dziedziczy UIView- wielokrotnego użytku w projekcie i wszystkich innych. Przekaż inne argumenty do swoich metod, aby zdefiniować inne kolory i szerokości. Dużo opcji.
Dodano możliwość zaokrąglania rogów do oryginalnego postu Adama Waite'a i wielu edycji
Ważne !: Nie label.layoutIfNeeded()zapomnij dodać „ ” tuż przed wywołaniem „addborder”, jak wcześniej komentowano
Uwaga: testowałem to tylko na 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)
}
}
view.layoutIfNeeded()tuż przed wywołaniem view.layer.addBorder(...). Następnie działa w Swift 3
override func viewDidLayoutSubviews()
Najlepszym sposobem dla mnie jest kategoria na UIView, ale adding viewszamiast CALayers, więc możemy take advantage of AutoresizingMasksupewnić się, że granice zmieniają się wraz z superviewem.
Cel 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];
}
Szybki 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)
}
Swift 3.0
Swift 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)
}
}
Podklasę UIViewi zaimplementuj drawRect:w swojej podklasie, np .:
Cel C
- (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);
}
Szybki 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()
}
Spowoduje to narysowanie 2-pikselowej czerwonej linii jako górnej granicy. Wszystkie inne odmiany, o których wspomniałeś, pozostawiamy jako trywialne ćwiczenie dla czytelnika.
Zalecany jest Quartz 2D Programming Guide .
UIViewpodklasa może mieć właściwość, która określa, jakie granice drawRect:rysują. Tę właściwość można zdefiniować za pomocą, NS_OPTIONSaby definiowała maskę bitową, taką jak UIViewAutoresizingmaska bitowa. Jeśli - z jakiegoś powodu - naprawdę sprzeciwiasz się podklasom, UIViewpo prostu dodaj mały podgląd podrzędny o wysokości 1-2 pikseli (lub szerokości) i nadaj mu dowolne wymiary, które chcesz symulować obramowanie.
Kod wybranej odpowiedzi na wypadek, gdyby ktoś chciał.
UWAGA: To nie działa z automatycznym układem (czyli obracaniem urządzenia w poziomie itp.).
Najpierw zdefiniuj grubość:
NSInteger borderThickness = 1;
Następnie po prostu skopiuj, użyj dowolnego lub wszystkich z nich, aby ustawić obramowanie, które chcesz ustawić.
Górna granica
UIView *topBorder = [UIView new];
topBorder.backgroundColor = [UIColor lightGrayColor];
topBorder.frame = CGRectMake(0, 0, myView.frame.size.width, borderThickness);
[myView addSubview:topBorder];
Dolna granica
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];
Lewa krawędź
UIView *leftBorder = [UIView new];
leftBorder.backgroundColor = [UIColor lightGrayColor];
leftBorder.frame = CGRectMake(0, 0, borderThickness, myView.frame.size.height);
[myView addSubview:leftBorder];
Prawa krawędź
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];
Stare pytanie, ale wciąż brakuje rozwiązania z automatycznym układaniem i dostosowaniem granic czasu wykonywania.
borders(for: [.left, .bottom], width: 2, color: .red)
Następujące rozszerzenie UIView doda obramowanie tylko na podanych krawędziach. Jeśli zmienisz krawędzie w czasie wykonywania, krawędzie zostaną odpowiednio dostosowane.
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]))
}
}
}
}
}
Wersja Swift:
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)
Edycja: Aby uzyskać zaktualizowane wersje, sprawdź moje repozytorium tutaj: https://github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/UIViewExtensions.swift
Spójrz na części addBorder
Swift 4.2 i AutoLayout
Przejrzałem proponowane rozwiązania. Wiele z nich jest opartych na ramkach To jest proste rozszerzenie, które działa z AutoLayout - użyj widoku zamiast warstwy, aby upewnić się, że możemy użyć AutoLayout - Pojedynczy widok podrzędny z 4 ograniczeniami
Użyj w następujący sposób:
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
}
}
}
Wziąłem odpowiedzi Adama Waite'a i Paulsa i połączyłem je. Dodałem również możliwość łączenia wybranych krawędzi ze sobą, więc wystarczy wywołać tylko jedną funkcję:
[self.view addBordersToEdge:(UIRectEdgeLeft|UIRectEdgeRight)
withColor:[UIColor grayColor]
andWidth:1.0];
lub tak:
[self.view addBordersToEdge:(UIRectEdgeAll)
withColor:[UIColor grayColor]
andWidth:1.0];
To, co musisz zaimplementować, to kategoria w UIView, jak sugerują inne odpowiedzi z następującą implementacją:
- (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];
}
}
// MARK: - Dodaj lewą granicę do widoku
(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];
}
// MARK: - Dodaj RightBorder do widoku
(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];
}
// MARK: - Dodaj dolną ramkę do widoku
(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];
}
Wprowadziłem kilka zmian w odpowiedzi Dana, aby móc dodać obramowanie do wielu krawędzi jednym poleceniem:
infoView.addBorder(toEdges: [.left, .bottom, .right], color: borderColor, thickness: 1)
Oto pełny kod:
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)
}
}
}
Opierając się na odpowiedzi NSBum , przyjąłem podobne podejście i stworzyłem tę prostą podklasę UIView, aby działała w Interface Builder i działała z ograniczeniami: link github
Używając CGContextFillRect zamiast CGContextStrokePath, byłem w stanie przewidzieć, że linie są całkowicie solidne i mieszczą się w granice widoku.
Oto mój wpis na blogu na ten temat: http://natrosoft.com/?p=55
- Po prostu wrzuć UIView w Interface Builder i zmień jego typ klasy na NAUIViewWithBorders.
- Następnie w widoku VC DidLoad zrób coś takiego:
/* 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];
Oto bezpośredni link do pliku .m, dzięki czemu możesz zobaczyć implementację. Jest też projekt demo. Mam nadzieję, że to komuś pomoże :)
Moja odpowiedź na podobne pytanie: https://stackoverflow.com/a/27141956/435766 Osobiście wolę podążać drogą kategorii na tym, ponieważ chcę móc go używać w dowolnej podklasie UIView.
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
}
}
}
}
Oto proste rozwiązanie. Dodaj etykietę do swojej UIView, wyczyść tekst na etykiecie i ustaw kolor tła etykiety jako kolor obramowania. Ustaw źródło (x,y)etykiety jako źródło (x,y)swojego widoku. i ustaw szerokość etykiety na szerokość swojej UIView, ustaw wysokość na 1 lub 2 (dla wysokości obramowania na górze UIView). I to powinno załatwić sprawę.
Wersja 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)
}
}
Aby ustawić odpowiednie obramowanie, należy nadpisać metodę viewDidLayoutSubviews () :
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
yourView.addBorder(toSide: UIView.ViewSide.Top, withColor: UIColor.lightGray, andThickness: 1)
Po prostu publikuję tutaj, aby pomóc komuś, kto chce dodać obramowanie. Dokonałem kilku zmian w zaakceptowanej odpowiedzi tutaj szybka etykieta tylko obramowanie . Zmieniono szerokość w przypadku UIRectEdge.Topod CGRectGetHeight(self.frame)do CGRectGetWidth(self.frame)iw przypadku UIRectEdge.Bottomod UIScreen.mainScreen().bounds.widthdo, CGRectGetWidth(self.frame)aby poprawnie uzyskać obramowanie. Korzystanie ze Swift 2.
Wreszcie rozszerzenie to:
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)
}
}
Na wypadek, gdyby ktoś kiedykolwiek potrzebował wersji 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);
}
}
Oto wersja Swift 4 odpowiedzi Paulsa
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)
}
Zainspirowany @Addisonem przepisałem rozszerzenie bez użycia jakiegokolwiek frameworka stron trzecich, ponieważ użył SnapKit i CocoaLumberjack.
Podobnie jak w podejściu @Addisons, usuwam również dodane wcześniej ramki, więc ta implementacja powinna dobrze wyglądać z widokami wielokrotnego użytku, takimi jak komórki tabeli i komórki kolekcji.
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
}
}
Jeśli buduję z poziomu storyboardu, wolę dodać UIViewza moją użyteczną UIView... Jeśli chcę utworzyć obramowanie na górze mojej UIView, po prostu zwiększam wysokość tła UIViewo szerokość mojej obramowania. To samo może być zrobione dla każdej innej strony :)
Osobiście podoba mi się podklasa view + drawRect, ale oto inny sposób na to (i działa w ten sam sposób, co zaakceptowana odpowiedź @If Pollavith):
Twoja nowa warstwa obramowania może mieć dowolne wymiary. Tak więc, podobnie jak odpowiedź @If Pollavith, tworzysz warstwę tak wysoką, jak chcesz, i tak szeroką, jak widok, który chcesz, aby graniczył. Użyj definicji ramki warstwy, aby umieścić ją tam, gdzie chcesz, a następnie dodaj ją jako warstwę podrzędną do widoku.
Dla porównania, moim własnym wymaganiem było umieszczenie obramowania po LEWEJ STRONIE widoku (proszę nie wycinać i wklejać tego kodu i oznaczać „ja tylko”, ponieważ nie powoduje to umieszczenia obramowania u góry widoku - modyfikacja poniższego kodu jest dość prosta):
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];
Myślę, że jedyną umiarkowaną korzyścią z tego i zrobienia małego UIView lub UILabel jest to, że CALayer jest podobno `` lżejszy '' i istnieje wiele interesujących poglądów (jak w opiniach) na temat przeważania drawRect w porównaniu z używaniem CALayers (jak tutaj : iOS: Korzystanie z funkcji „drawRect:” UIView w porównaniu z delagate warstwy „drawLayer: inContext:” ).
Podoba mi się kolor niebieski.
Oprócz n8tr można dodać, że istnieje możliwość ustawienia ich z storyboardu albo:
- dodaj dwie właściwości takie jak borderColoriw borderWidthpliku .h;
- wtedy możesz dodać keyPathsbezpośrednio w scenorysie, patrz link do zrzutu ekranu
Przekonwertuj odpowiedź DanShev na 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)
}
}
W przypadku platformy Xamarin w języku C # po prostu tworzę obramowanie w wierszu podczas dodawania warstwy podrzędnej
View.Layer.AddSublayer(new CALayer()
{
BackgroundColor = UIColor.Black.CGColor,
Frame = new CGRect(0, 0, View.Frame.Width, 0.5f)
});
Możesz to ustawić (zgodnie z sugestiami innych) dla dolnej, lewej i prawej krawędzi.
Możesz również sprawdzić tę kolekcję kategorii UIKit i Foundation: https://github.com/leszek-s/LSCategories
Pozwala na dodanie obramowania po jednej stronie UIView za pomocą jednej linii kodu:
[self.someView lsAddBorderOnEdge:UIRectEdgeTop color:[UIColor blueColor] width:2];
i prawidłowo obsługuje rotację widoku, podczas gdy większość zamieszczonych tutaj odpowiedzi nie obsługuje tego dobrze.
Uwaga: większość rozwiązań tutaj nie jest adaptacyjna i nie zmienia rozmiaru. Rozwiązania, które zmienią rozmiar, będą miały ogromny wpływ na czas uruchamiania, ponieważ wykorzystują dużo procesora.
Możesz skorzystać z tego rozwiązania poniżej. Działa na ścieżkach UIBezierPath, które są lżejsze niż warstwy, powodując szybki czas uruchamiania. Jest łatwy w użyciu, zobacz instrukcje poniżej.
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()
}
}
}
Do ustawiania górnej i dolnej granicy dla UIView w 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)