Restricción de diseño automático en CALayer IOS
autolayout (7)
De acuerdo con this respuesta, layoutSubviews
no se llama en todos los casos necesarios. He encontrado este método de delegado más efectivo:
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self != nil) {
[self.layer addSublayer:self.mySublayer];
}
return self;
}
- (void)layoutSublayersOfLayer:(CALayer*)layer
{
self.mySublayer.frame = self.bounds;
}
Hola, estoy desarrollando una aplicación para iPhone en la que intenté establecer un borde lateral para editar el texto. Hice esto de la siguiente manera:
int borderWidth = 1;
CALayer *leftBorder = [CALayer layer];
leftBorder.borderColor = [UIColor whiteColor].CGColor;
leftBorder.borderWidth = borderWidth;
leftBorder.frame = CGRectMake(0, textField.frame.size.height - borderWidth, textField
.frame.size.width, borderWidth);
[textField.layer addSublayer:leftBorder];
Pongo algunas restricciones en mi texto de edición en IB para que cuando gire mi dispositivo ajuste el ancho del campo de texto de acuerdo con eso. Mi problema es que ajusta el ancho del texto de edición y no el ancho de CALayer que configuré para mi texto de edición. Así que creo que también tengo que poner algunas restricciones para mi artículo CALayer. Pero no sé cómo hacerlo. ¿Alguien sabe de esto? Necesitas ayuda. Gracias.
Echa un vistazo a esta solución. Use KVO, para la ruta "YourView.bounds" como se indica a continuación.
self.addObserver(self, forKeyPath: "YourView.bounds", options: .New, context: nil)
Luego manéjalo como se indica a continuación.
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if (keyPath == "YourView.bounds") {
YourLayer.frame = YourView.bounds
return
}
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
Para más información, consulte el siguiente enlace.
Implementé el método layoutSubviews de mi vista personalizada; Dentro de este método, simplemente actualizo el marco de cada subcapa para que coincida con los límites actuales de la capa de mi subvista.
-(void)layoutSubviews{
sublayer1.frame = self.layer.bounds;
}
Mi solución cuando creo con borde discontinuo
class DashedBorderView: UIView {
let dashedBorder = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
//custom initialization
dashedBorder.strokeColor = UIColor.red.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.frame = self.bounds
dashedBorder.fillColor = nil
dashedBorder.cornerRadius = 2
dashedBorder.path = UIBezierPath(rect: self.bounds).cgPath
self.layer.addSublayer(dashedBorder)
}
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
dashedBorder.path = UIBezierPath(rect: self.bounds).cgPath
dashedBorder.frame = self.bounds
}
}
Todo el negocio de autoresizing es específico de vista. las capas no autorizan
Lo que tienes que hacer, en código, es cambiar el tamaño de la capa tú mismo
p.ej
en un viewController que harías
- (void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews]; //if you want superclass''s behaviour...
// resize your layers based on the view''s new frame
self.editViewBorderLayer.frame = self.editView.bounds;
}
o en un UIView personalizado que podrías usar
- (void)layoutSubviews {
[super layoutSubviews]; //if you want superclass''s behaviour... (and lay outing of children)
// resize your layers based on the view''s new frame
layer.frame = self.bounds;
}
Tuve un problema similar: tuve que establecer el marco de un "CALayer" cuando utilizaba el diseño automático con vistas (en código, no en IB ).
En mi caso, tenía una jerarquía ligeramente complicada, teniendo un controlador de vista dentro de un controlador de vista. Terminé con esta pregunta de SO y examiné el enfoque del uso de viewDidLayoutSubviews
. Eso no funcionó. En caso de que su situación sea similar a la mía, esto es lo que encontré ...
Visión general
Quería establecer el marco para el CAGradientLayer
de una UIView
que estaba posicionando como una subvista dentro de un UIViewController
usando restricciones de diseño automático.
Llame a la subvista gradientView
y al controlador de vista child_viewController
.
child_viewController
era un controlador de vista que configuré como un tipo de componente reutilizable. Por lo tanto, la view
de child_viewController
se estaba componiendo en un controlador de vista principal: llame a ese parent_viewController
.
Cuando se llamó a viewDidLayoutSubviews
de child_viewController
, el frame
de gradientView
aún no estaba establecido.
(En este punto, recomendaría rociar algunas declaraciones de NSLog
para tener una idea de la secuencia de creación de vistas en su jerarquía, etc.)
Así que seguí adelante para intentar usar viewDidAppear
. Sin embargo, debido a la naturaleza anidada de child_viewController
, encontré que viewDidAppear
no estaba siendo llamado.
(Vea esta pregunta SO: viewWillAppear, viewDidAppear no se llama, no se dispara ).
Mi solucion actual
He agregado viewDidAppear
en parent_viewController
y desde allí llamo viewDidAppear
en child_viewController
.
Para la carga inicial, necesito viewDidAppear
ya que no es hasta que se llama en child_viewController
que todas las subvistas tienen sus cuadros establecidos. Entonces puedo establecer el marco para CAGradientLayer
...
He dicho que esta es mi solución actual porque no estoy particularmente contento con ella.
Después de configurar inicialmente el marco de CAGradientLayer
, ese marco puede CAGradientLayer
ser válido si el diseño cambia, por ejemplo, girando el dispositivo.
Para manejar esto, estoy usando viewDidLayoutSubviews
en child_viewController
: para mantener el marco de CAGradientLayer
dentro de gradientView
, correcto.
Funciona pero no se siente bien. (¿Hay alguna manera mejor?)
Swift 3.x KVO Solution (actualización de la respuesta de @ arango_86)
Añadir observador
self.addObserver(
self,
forKeyPath: "<YOUR_VIEW>.bounds",
options: .new,
context: nil
)
Observar valores
override open func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?
) {
if (keyPath == "<YOUR_VIEW.bounds") {
updateDashedShapeLayerFrame()
return
}
super.observeValue(
forKeyPath: keyPath,
of: object,
change: change,
context: context
)
}