Considero subclasificar UIViewy anular la drawRectexageración aquí. ¿Por qué no agregar una extensión UIViewy agregar subvistas de borde?
@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
Con este código no está vinculado a su subclase también, puede aplicarlo a cualquier cosa y todo lo que herede UIView, reutilizable en su proyecto y en cualquier otro. Pase otros argumentos a sus métodos para definir otros colores y anchos. Muchas opciones.
El único inconveniente aquí es que no puede cambiar el tamaño.
Peter DeWeese
2
Podría usar una UIView y agregar restricciones de diseño automático. Mismo principio
Adam Waite
1
@PeterDeWeese Esto tampoco es una desventaja: si desea controlar el tamaño del borde, todo lo que necesita hacer es algo como: - (vacío) addUpperBorderWithSize: (CGFloat) tamaño Y luego reemplace la constante con el parámetro en la función . Lo mismo ocurre con otros parámetros como los colores.
o.shnn
1
@AdamWaite, esta variante de diseño automático se ve muy bien. Gracias !
manonthemoon
2
Gracias por compartir, hago una versión de Objective-C con restricción de diseño automático aquí .
Itachi
99
Se agregó capacidad para esquinas redondeadas a la publicación original de Adam Waite y las ediciones múltiples
¡Importante !: No olvide agregar ' label.layoutIfNeeded()' justo antes de llamar a 'addborder' como se comentó anteriormente
Para que funcione, debe agregar view.layoutIfNeeded()justo antes de llamar view.layer.addBorder(...). Luego funciona en Swift 3
Adam Studenic
Funciona bien. agregue la línea en este métodooverride func viewDidLayoutSubviews()
Antony Raphel
60
La mejor manera para mí es una categoría en UIView, pero en adding viewslugar de CALayers, para que podamos take advantage of AutoresizingMasksasegurarnos de que las fronteras cambien de tamaño junto con la supervista.
Esta es, con mucho, una de las mejores soluciones para este problema. La mayoría de las otras soluciones ofrecidas NO respetan los cambios de vista (y por lo tanto, la rotación del dispositivo o las vistas divididas). Este sí.
Womble
1
¿Cómo se puede adaptar esto para soportar esquinas redondeadas?
Esto dibuja una línea roja de 2 píxeles como borde superior. Todas las otras variaciones que mencionas se dejan como un ejercicio trivial para el lector.
¿Es la única forma de hacerlo? Entonces, si necesito hacer una vista con un borde superior, y otra vista con borde inferior, y otra vista ... quiero decir siempre una vista específica con un borde o dos, tendré que crear una subclase para cada caso específico ??? No sé si es realmente agradable para crear la clase sólo por la adición de las fronteras ...
manonthemoon
1
No. Piensa en eso. Su UIViewsubclase podría tener una propiedad que determina qué bordes se drawRect:dibujan. Esta propiedad podría definirse de NS_OPTIONSmodo que defina una máscara de bits como la UIViewAutoresizingmáscara de bits. Si, por alguna razón, realmente te opones a subclasificar UIView, simplemente agrega una pequeña subvista de 1-2 píxeles de alto (o ancho) y dale las dimensiones que quieras para simular un borde.
Solución sencilla ... con una sobrecarga de UIView en comparación con CALayer desnudo
Soberman
después de leer todas las otras soluciones, estaba pensando que también agregaría una pequeña vista. ¡Todo eso funciona por una frontera! Esta solución solo requiere algunos pines para funcionar también en el diseño automático. Mucho más fácil de entender.
noobsmcgoobs
Gracias, agregué una nota arriba para que los codificadores solo la usen si su aplicación no gira.
Travis M.
15
Antigua pregunta, pero la solución de autolayout con ajustes de borde de tiempo de ejecución aún falta.
borders(for:[.left,.bottom], width:2, color:.red)
La siguiente extensión UIView agregará el borde solo en los bordes dados. Si cambia los bordes en tiempo de ejecución, los bordes se ajustarán en consecuencia.
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]))}}}}}
Tomé las respuestas de Adam Waite y Pauls y las combiné. También agregué la posibilidad de canalizar los bordes seleccionados, por lo que debe llamar solo a una función de esta manera:
Revisé las soluciones ofrecidas. Muchos se basan en marcos. Esta es una extensión simple que funciona con AutoLayout: use View en lugar de Layer para asegurarse de que podemos usar AutoLayout: Subvista única con 4 restricciones.
Partiendo de la respuesta de NSBum , tomé un enfoque similar y creé esta subclase simple de UIView para que funcione en Interface Builder y funcione con restricciones: github link
Al usar CGContextFillRect en lugar de CGContextStrokePath, pude mantener las líneas completamente sólidas y dentro de forma predecible. Los límites de la vista.
- Básicamente, simplemente coloque una UIView en Interface Builder y cambie su tipo de clase a NAUIViewWithBorders.
- Luego, en la vista de tu VC, lo hiciste hacer algo como:
/* 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];
Aquí hay un enlace directo al archivo .m para que pueda ver la implementación. También hay un proyecto de demostración. Espero que esto ayude a alguien :)
Mi respuesta a una pregunta similar: https://stackoverflow.com/a/27141956/435766
Personalmente prefiero seguir el camino de la categoría en ese caso, ya que quiero poder usarlo en cualquier subclase de UIView.
Aquí hay una solución simple. Agregue una etiqueta a su UIView, borre el texto en la etiqueta y configure el color de fondo de la etiqueta para que sea su color de borde. Establezca el origen (x,y)de su etiqueta para que sea el origen (x,y)de su vista. y establezca el ancho de la etiqueta como el ancho de su UIView, establezca la altura en 1 o 2 (para la altura de su borde en la parte superior de su UIView). Y eso debería hacer el truco.
No tiene sentido tener una etiqueta entonces, bien podría ser otra UIView
maor10
1
Esto realmente no responde a la pregunta de agregar solo el borde superior a una uiview. Esta es una solución rápida que probablemente no funcionaría para problemas más complejos.
¿Se llamará 'addSublayer' cuando el diseño obtenga actualizaciones?
porJeevan
1
Solo publique aquí para ayudar a alguien que busca agregar bordes. He realizado algunos cambios en la respuesta aceptada aquí etiqueta rápida solo borde izquierdo . Cambiado el ancho en caso UIRectEdge.Topde CGRectGetHeight(self.frame)que CGRectGetWidth(self.frame)y en el caso UIRectEdge.Bottomde UIScreen.mainScreen().bounds.widthque CGRectGetWidth(self.frame)para obtener las fronteras correctamente. Usando Swift 2.
Inspirado por @Addison, reescribí la extensión sin el uso de ningún marco de terceros, ya que usó SnapKit y CocoaLumberjack.
Al igual que en el enfoque @Addisons, también estoy eliminando los bordes agregados anteriormente, por lo que esta implementación debería funcionar bien con vistas reutilizables como celdas de tabla y celdas de colección.
fileprivate classBorderView: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)])}}}privateenumRectangularEdges{case left
case right
case top
case bottom
}}
Si estoy construyendo desde el guión gráfico, prefiero agregar un UIViewdetrás de mi útil UIView... Si quiero crear un borde en la parte superior de mi UIView, solo aumento la altura del fondo UIViewpor el ancho de mi borde ... Lo mismo puede hacerse por cualquier otro lado :)
Personalmente, me gusta la subclasificación de view + drawRect, pero aquí hay otra forma de hacerlo (y funciona de la misma manera que la respuesta aceptada por @If Pollavith):
Su nueva capa de borde se puede configurar para que tenga las dimensiones que desee. Entonces, al igual que @Si la respuesta de Pollavith, crea una capa para que sea tan alta como desee y tan ancha como la vista que desea que haya bordeado. Use la definición de marco de la capa para colocarla donde desee y luego agréguela como una subcapa a su vista.
Como referencia, mi propio requisito era poner un borde en el lado IZQUIERDO de la vista (por favor, no corte y pegue este código y des 'solo' porque no pone un borde en la parte superior de la vista - modificar el siguiente código es bastante simple):
Supongo que el único beneficio moderado sobre esto y hacer una pequeña UIView o UILabel es que el CALayer supuestamente es 'más liviano', y hay muchas vistas interesantes (como en las opiniones) sobre anular el drawRect en lugar de usar CALayers (como aquí : iOS: Uso de 'drawRect:' de UIView frente al delagate 'drawLayer: inContext:' de su capa .
Además de n8tr puede agregar que hay una disponibilidad para configurarlos desde el guión gráfico:
- agregue dos propiedades como borderColory borderWidthen el archivo .h;
- entonces podría agregar keyPathsdirectamente en el guión gráfico, ver el enlace a la captura de pantalla
Nota: la mayoría de las soluciones aquí no son adaptativas y no cambiarán de tamaño. Las soluciones que cambiarán de tamaño tendrán una enorme impacto en el tiempo de inicio ya que usan mucha CPU.
Puede usar esta solución a continuación. Funciona en UIBezierPaths, que son más ligeros que las capas, lo que provoca tiempos de inicio rápidos. Es fácil de usar, vea las instrucciones a continuación.
Respuestas:
Considero subclasificar
UIView
y anular ladrawRect
exageración aquí. ¿Por qué no agregar una extensiónUIView
y agregar subvistas de borde?Con este código no está vinculado a su subclase también, puede aplicarlo a cualquier cosa y todo lo que herede
UIView
, reutilizable en su proyecto y en cualquier otro. Pase otros argumentos a sus métodos para definir otros colores y anchos. Muchas opciones.fuente
Se agregó capacidad para esquinas redondeadas a la publicación original de Adam Waite y las ediciones múltiples
¡Importante !: No olvide agregar '
label.layoutIfNeeded()
' justo antes de llamar a 'addborder' como se comentó anteriormenteNota: solo he probado esto
UILabels
.fuente
view.layoutIfNeeded()
justo antes de llamarview.layer.addBorder(...)
. Luego funciona en Swift 3override func viewDidLayoutSubviews()
La mejor manera para mí es una categoría en UIView, pero en
adding views
lugar de CALayers, para que podamostake advantage of AutoresizingMasks
asegurarnos de que las fronteras cambien de tamaño junto con la supervista.C objetivo
Swift 5
fuente
Swift 3.0
Swift 4.1
fuente
Subclase
UIView
e implementacióndrawRect:
en su subclase, por ejemplo:C objetivo
Swift 4
Esto dibuja una línea roja de 2 píxeles como borde superior. Todas las otras variaciones que mencionas se dejan como un ejercicio trivial para el lector.
Se recomienda la guía de programación 2D Quartz .
fuente
UIView
subclase podría tener una propiedad que determina qué bordes sedrawRect:
dibujan. Esta propiedad podría definirse deNS_OPTIONS
modo que defina una máscara de bits como laUIViewAutoresizing
máscara de bits. Si, por alguna razón, realmente te opones a subclasificarUIView
, simplemente agrega una pequeña subvista de 1-2 píxeles de alto (o ancho) y dale las dimensiones que quieras para simular un borde.Código para la respuesta seleccionada, en caso de que alguien lo quiera.
NOTA: Esto no funciona con autolayout (también conocido como dispositivo giratorio a horizontal, etc.).
Primero defina un grosor:
Luego, simplemente use cualquiera o todos estos para establecer el borde que desea establecer.
Borde superior
Borde inferior
Borde izquierdo
Borde derecho
fuente
Antigua pregunta, pero la solución de autolayout con ajustes de borde de tiempo de ejecución aún falta.
La siguiente extensión UIView agregará el borde solo en los bordes dados. Si cambia los bordes en tiempo de ejecución, los bordes se ajustarán en consecuencia.
fuente
Versión rápida:
Editar: Para versiones actualizadas, consulte mi repositorio aquí: https://github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/UIViewExtensions.swift
Mira las partes addBorder
fuente
Tomé las respuestas de Adam Waite y Pauls y las combiné. También agregué la posibilidad de canalizar los bordes seleccionados, por lo que debe llamar solo a una función de esta manera:
más o menos:
Lo que necesita implementar es una categoría en UIView como se sugiere en otras respuestas con la siguiente implementación:
fuente
// MARK: - Añadir LeftBorder para ver
// MARK: - Añadir RightBorder para ver
// MARK: - Agregar borde inferior para ver
fuente
Swift 4.2 y AutoLayout
Revisé las soluciones ofrecidas. Muchos se basan en marcos. Esta es una extensión simple que funciona con AutoLayout: use View en lugar de Layer para asegurarse de que podemos usar AutoLayout: Subvista única con 4 restricciones.
Use de la siguiente manera:
fuente
Hice algunos cambios en la respuesta de Dan para poder agregar bordes a múltiples bordes con un solo comando:
Aquí está el código completo:
fuente
Partiendo de la respuesta de NSBum , tomé un enfoque similar y creé esta subclase simple de UIView para que funcione en Interface Builder y funcione con restricciones: github link
Al usar CGContextFillRect en lugar de CGContextStrokePath, pude mantener las líneas completamente sólidas y dentro de forma predecible. Los límites de la vista.
Aquí está mi publicación de blog al respecto: http://natrosoft.com/?p=55
- Básicamente, simplemente coloque una UIView en Interface Builder y cambie su tipo de clase a NAUIViewWithBorders.
- Luego, en la vista de tu VC, lo hiciste hacer algo como:
Aquí hay un enlace directo al archivo .m para que pueda ver la implementación. También hay un proyecto de demostración. Espero que esto ayude a alguien :)
fuente
Mi respuesta a una pregunta similar: https://stackoverflow.com/a/27141956/435766 Personalmente prefiero seguir el camino de la categoría en ese caso, ya que quiero poder usarlo en cualquier subclase de UIView.
fuente
fuente
Aquí hay una solución simple. Agregue una etiqueta a su
UIView
, borre el texto en la etiqueta y configure el color de fondo de la etiqueta para que sea su color de borde. Establezca el origen(x,y)
de su etiqueta para que sea el origen(x,y)
de su vista. y establezca el ancho de la etiqueta como el ancho de suUIView
, establezca la altura en 1 o 2 (para la altura de su borde en la parte superior de suUIView
). Y eso debería hacer el truco.fuente
Versión Swift 3
Para establecer el borde correspondiente, debe anular el método viewDidLayoutSubviews () :
fuente
Solo publique aquí para ayudar a alguien que busca agregar bordes. He realizado algunos cambios en la respuesta aceptada aquí etiqueta rápida solo borde izquierdo . Cambiado el ancho en caso
UIRectEdge.Top
deCGRectGetHeight(self.frame)
queCGRectGetWidth(self.frame)
y en el casoUIRectEdge.Bottom
deUIScreen.mainScreen().bounds.width
queCGRectGetWidth(self.frame)
para obtener las fronteras correctamente. Usando Swift 2.Finalmente la extensión es:
fuente
En caso de que alguien necesite la versión Xamarin:
fuente
Aquí hay una versión de Swift 4 de la respuesta de Pauls
fuente
Inspirado por @Addison, reescribí la extensión sin el uso de ningún marco de terceros, ya que usó SnapKit y CocoaLumberjack.
Al igual que en el enfoque @Addisons, también estoy eliminando los bordes agregados anteriormente, por lo que esta implementación debería funcionar bien con vistas reutilizables como celdas de tabla y celdas de colección.
fuente
Si estoy construyendo desde el guión gráfico, prefiero agregar un
UIView
detrás de mi útilUIView
... Si quiero crear un borde en la parte superior de miUIView
, solo aumento la altura del fondoUIView
por el ancho de mi borde ... Lo mismo puede hacerse por cualquier otro lado :)fuente
Personalmente, me gusta la subclasificación de view + drawRect, pero aquí hay otra forma de hacerlo (y funciona de la misma manera que la respuesta aceptada por @If Pollavith):
Su nueva capa de borde se puede configurar para que tenga las dimensiones que desee. Entonces, al igual que @Si la respuesta de Pollavith, crea una capa para que sea tan alta como desee y tan ancha como la vista que desea que haya bordeado. Use la definición de marco de la capa para colocarla donde desee y luego agréguela como una subcapa a su vista.
Como referencia, mi propio requisito era poner un borde en el lado IZQUIERDO de la vista (por favor, no corte y pegue este código y des 'solo' porque no pone un borde en la parte superior de la vista - modificar el siguiente código es bastante simple):
Supongo que el único beneficio moderado sobre esto y hacer una pequeña UIView o UILabel es que el CALayer supuestamente es 'más liviano', y hay muchas vistas interesantes (como en las opiniones) sobre anular el drawRect en lugar de usar CALayers (como aquí : iOS: Uso de 'drawRect:' de UIView frente al delagate 'drawLayer: inContext:' de su capa .
Animal451
Me gusta el color azul.
fuente
Además de n8tr puede agregar que hay una disponibilidad para configurarlos desde el guión gráfico:
- agregue dos propiedades como
borderColor
yborderWidth
en el archivo .h;- entonces podría agregar
keyPaths
directamente en el guión gráfico, ver el enlace a la captura de pantallafuente
Convierta la respuesta de DanShev a Swift 3
fuente
Para Xamarin en C #, simplemente creo el borde en línea al agregar la subcapa
Puede organizar esto (como lo sugieren otros) para los bordes inferior, izquierdo y derecho.
fuente
También puede consultar esta colección de categorías de UIKit y Foundation: https://github.com/leszek-s/LSCategories
Permite agregar bordes en un lado de UIView con una sola línea de código:
y maneja correctamente la rotación de la vista, mientras que la mayoría de las respuestas publicadas aquí no lo manejan bien.
fuente
Nota: la mayoría de las soluciones aquí no son adaptativas y no cambiarán de tamaño. Las soluciones que cambiarán de tamaño tendrán una enorme impacto en el tiempo de inicio ya que usan mucha CPU.
Puede usar esta solución a continuación. Funciona en UIBezierPaths, que son más ligeros que las capas, lo que provoca tiempos de inicio rápidos. Es fácil de usar, vea las instrucciones a continuación.
fuente
Para configurar el borde superior y el borde inferior para una vista UIView en Swift.
fuente
En Swift 4 y 3
fuente