Я считаю, что создание подклассов UIViewи переопределение является drawRectизлишним. Почему бы не добавить расширение UIViewи не добавить подпредставления границ?
С помощью этого кода вы тоже не привязаны к своему подклассу, вы можете применить его ко всему и всему, что унаследовано от UIView- многоразового использования в вашем проекте и любых других. Передайте другие аргументы вашим методам, чтобы определить другие цвета и ширину. Вариантов много.
Единственным недостатком здесь является то, что он не может изменять размер.
Peter DeWeese
2
Можно использовать UIView и добавить ограничения автоматического макета. Тот же принцип.
Адам Уэйт
1
@PeterDeWeese Это тоже не недостаток - если вы хотите контролировать размер границы, все, что вам нужно сделать, это что-то вроде: - (void) addUpperBorderWithSize: (CGFloat) size А затем заменить константу параметром в функции . То же самое и с другими параметрами, такими как цвета.
o.shnn 07
1
@AdamWaite, этот вариант автоматического макета выглядит очень хорошо. Спасибо !
manonthemoon
2
Спасибо за обмен, я сделать версию Objective-C с автоматической верстки ограничений здесь .
Итачи,
100
Добавлена возможность закругления углов оригинального сообщения Адама Уэйта и несколько правок.
Важно! Не 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 offsetsswitch 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 sidesswitch 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 Sidescase .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/2switch corner {
case .bottomLeft: startAngle = .pi/2; endAngle = .pi
case .bottomRight:
x = width - cornerRadius
startAngle = 0; endAngle = .pi/2case .topRight:
x = width - cornerRadius
startAngle = .pi*3/2; endAngle = 0default: 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()прямо перед звонком view.layer.addBorder(...). Тогда это работает в Swift 3
Адам
работает нормально. добавьте строчку в этот методoverride func viewDidLayoutSubviews()
Энтони Рафел
63
Лучший способ для меня - это категория в UIView, но adding viewsвместо CALayers, поэтому мы можем take advantage of AutoresizingMasksубедиться, что границы изменяются вместе с супервизором.
На сегодняшний день это одно из лучших решений этой проблемы. Большинство других предлагаемых решений НЕ учитывают изменения представления (и, следовательно, поворот устройства или разделение представлений). Этот делает.
Womble
1
как это можно адаптировать для поддержки закругленных углов?
Это рисует красную линию размером 2 пикселя в качестве верхней границы. Все остальные варианты, которые вы упомянули, оставлены в качестве тривиального упражнения для читателя.
Это единственный способ ??? Итак, если мне нужно создать представление с верхней границей и другое представление с нижней границей и другое представление ... Я имею в виду всегда конкретное представление с одной или двумя рамками, мне придется создавать подкласс для каждого конкретного случая ??? Я не знаю, действительно ли приятно создавать класс только для добавления границ ...
manonthemoon
1
Нет. Подумай об этом. У вашего UIViewподкласса может быть свойство, определяющее, какие границы drawRect:рисуются. Это свойство можно определить NS_OPTIONSтак, чтобы оно определяло битовую маску, как и UIViewAutoresizingбитовая маска. Если - по какой-то причине - вы действительно категорически возражаете против UIViewсоздания подклассов, просто добавьте небольшое подсмотрение высотой 1-2 пикселя (или шириной) и задайте ему любые размеры, которые вы хотите имитировать для границы.
Прямое решение ... с накладными расходами UIView по сравнению с голым CALayer
Соберман
после прочтения всех других решений я подумал, что просто добавлю крошечный вид. Все это работает за границу! Это решение также требует нескольких выводов для работы в автоматическом макете. Намного легче осмыслить.
noobsmcgoobs
Спасибо, я добавил примечание выше, чтобы кодировщики могли использовать его только в том случае, если их приложение не вращается.
Travis M.
16
Старый вопрос, но решение autolayout с настройками границ времени выполнения все еще отсутствует.
Следующее расширение 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 {
caseUIRectEdge.bottom:
horizontalVisualFormat += "|-(0)-[v]-(0)-|"
verticalVisualFormat += "[v(\(width))]-(0)-|"caseUIRectEdge.top:
horizontalVisualFormat += "|-(0)-[v]-(0)-|"
verticalVisualFormat += "|-(0)-[v(\(width))]"caseUIRectEdge.left:
horizontalVisualFormat += "|-(0)-[v(\(width))]"
verticalVisualFormat += "|-(0)-[v]-(0)-|"caseUIRectEdge.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]))
}
}
}
}
}
Я просмотрел предложенные решения. Многие из них основаны на фреймах. Это простое расширение, которое работает с AutoLayout - используйте View вместо Layer, чтобы убедиться, что мы можем использовать AutoLayout - Single subview с 4 ограничениями
Я взял ответы Адама Уэйта и Полса и объединил их. Я также добавил возможность соединить выбранные ребра вместе, поэтому вам нужно вызвать только одну функцию, например:
Основываясь на ответе NSBum , я применил аналогичный подход и создал этот простой подкласс UIView, чтобы он работал в Interface Builder и работал с ограничениями: ссылка github.
Используя CGContextFillRect вместо CGContextStrokePath, я смог предсказуемо сохранить линии полностью сплошными и внутри границы представления.
- Просто вставьте UIView в Интерфейсный Разработчик и измените его тип класса на NAUIViewWithBorders.
- Затем в viewDidLoad вашего ВК сделайте что-нибудь вроде:
/* 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, чтобы вы могли увидеть реализацию. Также есть демонстрационный проект. Надеюсь, это кому-то поможет :)
Мой ответ на аналогичный вопрос: https://stackoverflow.com/a/27141956/435766
Я лично предпочитаю идти по пути категорий на этом, так как я хочу иметь возможность использовать его в любом подклассе UIView.
Вот простое решение. Добавьте метку на свой UIView, очистите текст на этикетке и установите цвет фона метки в качестве цвета границы. Установите исходную точку (x,y)вашей метки как исходную (x,y)точку вашего представления. и установите ширину метки равной ширине вашего UIView, установите высоту равной 1 или 2 (для высоты вашей границы вверху UIView). И это должно помочь.
Тогда нет смысла иметь лейбл, с таким же успехом может быть просто еще один UIView
maor10
1
На самом деле это не отвечает на вопрос о добавлении только верхней границы в uiview. Это быстрое решение, которое, вероятно, не сработает для более сложных проблем.
Будет ли он вызывать addSublayer при обновлении макета?
byJeevan
1
Просто разместите здесь сообщение, чтобы помочь тем, кто хочет добавить границы. Я внес несколько изменений в принятый ответ здесь, оставив только границу быстрой метки . Изменена ширина UIRectEdge.Topот CGRectGetHeight(self.frame)до CGRectGetWidth(self.frame)и UIRectEdge.Bottomот UIScreen.mainScreen().bounds.widthдоCGRectGetWidth(self.frame) для правильного получения границ. Использование Swift 2.
Вдохновленный @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 edgesfor view in subviews {
if view is BorderView {
view.removeFromSuperview()
}
}
// Add new edgesif 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 = falseswitch 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
}
}
Если я строю из раскадровки, я предпочитаю добавлять UIViewпозади мои полезные UIView... Если я хочу создать границу сверху UIView, я просто увеличиваю высоту фона UIViewна ширину моей границы .. То же самое можно делать для любой другой стороны :)
Лично мне нравится подкласс view + drawRect, но вот еще один способ сделать это (и он вроде работает в том же направлении, что и принятый ответ @If Pollavith):
Ваш новый пограничный слой может иметь любые размеры, которые вам нравятся. Итак, как и в случае с ответом @If Pollavith, вы создаете слой такой высоты, какой вы хотите, и такой же ширины, как вид, который вы хотите ограничить. Используйте определение кадра слоя, чтобы разместить его там, где вы хотите, а затем добавьте его в качестве подслоя к вашему виду.
Для справки, мое собственное требование состояло в том, чтобы поставить границу на ЛЕВОЙ стороне представления (пожалуйста, не вырезайте и не вставляйте этот код, и это означает, что он не помещает границу в верхней части представления. - изменить приведенный ниже код достаточно просто):
В дополнение к n8tr можно добавить, что есть возможность установить их из раскадровки:
- добавить два свойства, например, borderColorи borderWidthв файл .h;
- тогда вы можете добавить keyPathsпрямо в раскадровку, см. ссылку на скриншот
Примечание: большинство решений здесь не адаптивны и не изменяют размер. Решения, которые изменят размер, окажут огромное влияние на время запуска, поскольку они используют много ресурсов процессора.
Вы можете использовать это решение ниже. Он работает с UIBezierPaths, которые легче слоев, что приводит к быстрому запуску. Его легко использовать, см. Инструкции ниже.
Ответы:
Я считаю, что создание подклассов
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
- многоразового использования в вашем проекте и любых других. Передайте другие аргументы вашим методам, чтобы определить другие цвета и ширину. Вариантов много.источник
Добавлена возможность закругления углов оригинального сообщения Адама Уэйта и несколько правок.
Важно! Не
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) } }
источник
view.layoutIfNeeded()
прямо перед звонкомview.layer.addBorder(...)
. Тогда это работает в Swift 3override func viewDidLayoutSubviews()
Лучший способ для меня - это категория в 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]; }
Swift 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) } }
источник
Подкласс
UIView
и реализоватьdrawRect:
в своем подклассе, например:Цель-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); }
Swift 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 пикселя в качестве верхней границы. Все остальные варианты, которые вы упомянули, оставлены в качестве тривиального упражнения для читателя.
Рекомендуется Quartz 2D Programming Guide .
источник
UIView
подкласса может быть свойство, определяющее, какие границыdrawRect:
рисуются. Это свойство можно определитьNS_OPTIONS
так, чтобы оно определяло битовую маску, как иUIViewAutoresizing
битовая маска. Если - по какой-то причине - вы действительно категорически возражаете противUIView
создания подклассов, просто добавьте небольшое подсмотрение высотой 1-2 пикселя (или шириной) и задайте ему любые размеры, которые вы хотите имитировать для границы.Код для выбранного ответа, если он кому-то нужен.
ПРИМЕЧАНИЕ. Это не работает с автоматическим раскладыванием (иначе говоря, поворот устройства в альбомную ориентацию и т. Д.).
Сначала определите толщину:
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];
источник
Старый вопрос, но решение autolayout с настройками границ времени выполнения все еще отсутствует.
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])) } } } } }
источник
Быстрая версия:
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
источник
Swift 4.2 и AutoLayout
Я просмотрел предложенные решения. Многие из них основаны на фреймах. Это простое расширение, которое работает с AutoLayout - используйте View вместо Layer, чтобы убедиться, что мы можем использовать 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 } } }
источник
Я взял ответы Адама Уэйта и Полса и объединил их. Я также добавил возможность соединить выбранные ребра вместе, поэтому вам нужно вызвать только одну функцию, например:
[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]; } }
источник
// MARK: - Добавить левую границу для просмотра
(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]; }
источник
Основываясь на ответе NSBum , я применил аналогичный подход и создал этот простой подкласс UIView, чтобы он работал в Interface Builder и работал с ограничениями: ссылка github.
Используя CGContextFillRect вместо CGContextStrokePath, я смог предсказуемо сохранить линии полностью сплошными и внутри границы представления.
Вот моя запись в блоге об этом: http://natrosoft.com/?p=55
- Просто вставьте UIView в Интерфейсный Разработчик и измените его тип класса на NAUIViewWithBorders.
- Затем в viewDidLoad вашего ВК сделайте что-нибудь вроде:
/* 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, чтобы вы могли увидеть реализацию. Также есть демонстрационный проект. Надеюсь, это кому-то поможет :)
источник
Я внес некоторые изменения в ответ Дэна, чтобы добавить границы к нескольким краям с помощью одной команды:
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) } } }
источник
Мой ответ на аналогичный вопрос: https://stackoverflow.com/a/27141956/435766 Я лично предпочитаю идти по пути категорий на этом, так как я хочу иметь возможность использовать его в любом подклассе UIView.
источник
На случай, если кому-то когда-нибудь понадобится версия 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); } }
источник
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 } } } }
источник
Вот простое решение. Добавьте метку на свой
UIView
, очистите текст на этикетке и установите цвет фона метки в качестве цвета границы. Установите исходную точку(x,y)
вашей метки как исходную(x,y)
точку вашего представления. и установите ширину метки равной ширине вашегоUIView
, установите высоту равной 1 или 2 (для высоты вашей границы вверхуUIView
). И это должно помочь.источник
Версия 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)
источник
Просто разместите здесь сообщение, чтобы помочь тем, кто хочет добавить границы. Я внес несколько изменений в принятый ответ здесь, оставив только границу быстрой метки . Изменена ширина
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) } }
источник
Для Xamarin в C # я просто создаю встроенную границу при добавлении подслоя.
View.Layer.AddSublayer(new CALayer() { BackgroundColor = UIColor.Black.CGColor, Frame = new CGRect(0, 0, View.Frame.Width, 0.5f) });
Вы можете расположить это (как предлагают другие) для нижней, левой и правой границ.
источник
Вот версия 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) }
источник
Вдохновленный @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 } }
источник
Если я строю из раскадровки, я предпочитаю добавлять
UIView
позади мои полезныеUIView
... Если я хочу создать границу сверхуUIView
, я просто увеличиваю высоту фонаUIView
на ширину моей границы .. То же самое можно делать для любой другой стороны :)источник
Лично мне нравится подкласс view + drawRect, но вот еще один способ сделать это (и он вроде работает в том же направлении, что и принятый ответ @If Pollavith):
Ваш новый пограничный слой может иметь любые размеры, которые вам нравятся. Итак, как и в случае с ответом @If 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: Использование "drawRect:" UIView по сравнению с делагатированием его слоя "drawLayer: inContext:" ).
Животные451
Мне нравится синий цвет.
источник
В дополнение к n8tr можно добавить, что есть возможность установить их из раскадровки:
- добавить два свойства, например,
borderColor
иborderWidth
в файл .h;- тогда вы можете добавить
keyPaths
прямо в раскадровку, см. ссылку на скриншотисточник
Преобразование ответа 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) } }
источник
Вы также можете проверить эту коллекцию категорий UIKit и Foundation: https://github.com/leszek-s/LSCategories
Это позволяет добавить границу с одной стороны UIView с помощью одной строки кода:
[self.someView lsAddBorderOnEdge:UIRectEdgeTop color:[UIColor blueColor] width:2];
и он правильно обрабатывает вращение вида, в то время как большинство ответов, опубликованных здесь, не справляются с этим.
источник
Примечание: большинство решений здесь не адаптивны и не изменяют размер. Решения, которые изменят размер, окажут огромное влияние на время запуска, поскольку они используют много ресурсов процессора.
Вы можете использовать это решение ниже. Он работает с 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 в 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)
источник
В 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)
источник