¿Cómo puedo obtener el ancho y alto actual de una vista cuando uso restricciones de autolayout?

No estoy hablando de la propiedad frame, porque de eso solo se puede obtener el tamaño de la vista en el xib. Estoy hablando de cuándo se cambia el tamaño de la vista debido a sus limitaciones (tal vez después de una rotación o en respuesta a un evento). ¿Hay alguna forma de obtener su ancho y altura actuales?

Traté de iterar a través de sus restricciones buscando restricciones de ancho y alto, pero eso no es muy limpio y falla cuando hay restricciones intrínsecas (ya que no puedo diferenciar entre las dos). Además, eso solo funciona si tienen restricciones de ancho y alto, lo que no ocurre si dependen de otras restricciones para cambiar el tamaño.

¿Por qué es esto tan difícil para mí? ¡ARG!

La respuesta es [view layoutIfNeeded] .

Este es el por qué:

Aún obtienes el ancho y alto actual de la vista al inspeccionar view.bounds.size.width y view.bounds.size.height (o el marco, que es equivalente a menos que estés jugando con view.transform ).

Si lo que desea es el ancho y alto que implican sus restricciones existentes, la respuesta no es inspeccionar las restricciones manualmente, ya que eso requeriría que vuelva a implementar toda la lógica de resolución de restricciones del sistema de diseño automático. En cambio, lo que debe hacer es pedirle al diseño automático que actualice ese diseño , para que resuelva las restricciones y actualice el valor de view.bounds con la solución correcta, y luego inspeccione view.bounds.

¿Cómo se solicita el diseño automático para actualizar el diseño? Llame a [view setNeedsLayout] si desea que el diseño automático actualice el diseño en el próximo giro del ciclo de ejecución.

Sin embargo, si desea que actualice el diseño inmediatamente, para que pueda acceder inmediatamente al nuevo valor de límites más tarde dentro de su función actual, o en otro punto antes del giro del ciclo de ejecución, debe llamar a [view setNeedsLayout] y [view layoutIfNeeded] .

Hiciste una segunda pregunta: “¿cómo puedo cambiar una restricción de altura / ancho si no tengo una referencia directamente?”.

Si crea la restricción en IB, la mejor solución es crear un IBOutlet en su controlador de vista o su vista, de modo que tenga una referencia directa a él. Si creó la restricción en el código, debe mantener una referencia en una propiedad débil interna en el momento en que la creó. Si alguien más creó la restricción, entonces necesita encontrarla examinando la propiedad view.constraints en la vista, y posiblemente toda la jerarquía de vista, y la lógica de implementación que encuentra la crucial NSLayoutConstraint. Esta es probablemente la forma incorrecta de hacerlo, ya que también requiere que usted determine qué restricción particular determinó el tamaño de los límites, cuando no se garantiza que sea una respuesta simple a esa pregunta. El valor de los límites finales podría ser la solución a un sistema muy complicado de restricciones múltiples, con prioridades múltiples, etc., de modo que ninguna restricción única es la “causa” del valor final.

Tuve un problema similar en el que necesitaba agregar un borde superior e inferior a un UITableView que cambia de tamaño en función de su configuración de restricciones en UIStoryboard . Pude acceder a las restricciones actualizadas con - (void)viewDidLayoutSubviews . Esto es útil para que no necesite subclasificar una vista y anular su método de diseño.

 /*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/ - (void)addBorders { CALayer *topBorder = [CALayer layer]; topBorder.frame = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f); topBorder.backgroundColor = [UIColor redColor].CGColor; CALayer *bottomBorder = [CALayer layer]; bottomBorder.frame = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f); bottomBorder.backgroundColor = [UIColor redColor].CGColor; [self.view.layer addSublayer:topBorder]; [self.view.layer addSublayer:bottomBorder]; } /*** GET AUTORESIZED FRAME DIMENSIONS ***/ - (void)viewDidLayoutSubviews{ [self addBorders]; } 

Sin llamar al método desde el método viewDidLayoutSubview , solo el borde superior se dibuja correctamente, ya que el borde inferior está fuera de la pantalla.

Para aquellos que aún pueden enfrentar tales problemas, especialmente con TableviewCell.

Simplemente anule el método:

 -(void)layoutSubviews { //your code here like drawing a shadow } 

En el caso de UITableViewCell o UICollectionViewCell, cree una subclase de la celda y anule el mismo método:

 -(void)layoutSubviews { //your code here like drawing a shadow } 

El marco sigue siendo válido. Al final, la vista usa su propiedad de marco para distribuirse. Calcula ese marco basado en todas las restricciones. Las restricciones solo se usan para el diseño inicial (y en cualquier momento se llama a layoutSubviews en una vista como después de una rotación). Después de eso, la información de posición está en la propiedad del marco. ¿O estás viendo de otra manera?

Utilice -(void)viewWillAppear:(BOOL)animated y llame [self.view layoutIfNeeded]; – Funciona lo he intentado.

porque si usa -(void)viewDidLayoutSubviews funcionará definitivamente, pero este método se -(void)viewDidLayoutSubviews cada vez que su UI exige actualizaciones / cambios. Lo cual será difícil de manejar. La tecla progtwigble utiliza una variable bool para evitar ese tipo de llamadas. mejor uso viewWillAppear . Recuerde que también se llamará a viewWillAppear si la vista se vuelve a cargar (sin reasignación).