ios - constraint - ¿Cómo puedo reorganizar las vistas cuando se ejecuta automáticamente con autolayout?
swift constraints programmatically (2)
Ahora que UIStackView está disponible en iOS9, una solución más simple y garantizada sería agregar ambas vistas a una vista de pila, vincular la vista de pila a un IBOutlet y luego agregar el siguiente código al controlador de vista:
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown || orientation == UIInterfaceOrientationUnknown)
self.stackView.axis = UILayoutConstraintAxisVertical;
else
self.stackView.axis = UILayoutConstraintAxisHorizontal;
[self.stackView setNeedsLayout];
[self.stackView setNeedsDisplay];
}
y llamar a este método desde
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
Con frecuencia me encuentro con este problema donde quiero que mis vistas estén organizadas de una manera para el retrato y una manera algo diferente para el paisaje.
Para un ejemplo simplificado, considere lo siguiente:
Retrato:
Paisaje:
Observe cómo en vertical las imágenes se apilan verticalmente, pero en horizontal, ¿están una al lado de la otra?
Por supuesto, puedo calcular manualmente las posiciones y los tamaños, pero para vistas más complicadas, esto se vuelve rápidamente complejo. Además, prefiero usar Autolayout para que haya menos código específico del dispositivo (si lo hay). Pero eso parece mucho código de Autolayout para escribir. ¿Hay alguna manera de configurar las restricciones para este tipo de cosas a través del guión gráfico?
Hay dos características infrautilizadas del generador de interfaces de Xcode que podemos utilizar aquí para hacer exactamente este tipo de cosas una sincronización.
- Puede crear conexiones
NSLayoutConstraints
paraNSLayoutConstraints
. - Puede conectar "colecciones de salida", que es una matriz de objetos
IBOutlet
.
Así que con estas dos cosas en mente, lo esencial de lo que queremos hacer es crear todas nuestras restricciones de reproducción automática para una orientación, conectarlas todas en una colección de puntos de venta. Ahora, para cada una de estas restricciones, desmarque la opción "Instalado" en el generador de interfaces. Luego haga todas nuestras colecciones de outlet para otro diseño y conéctelos a otra colección de outlet. Podemos crear tantos de estos grupos de diseño como queramos.
Es importante tener en cuenta que necesitaremos una referencia a cualquier elemento de la interfaz de usuario que tenga restricciones instaladas directamente, y necesitaremos una colección de puntos de venta separada no solo para cada diseño que queramos, sino también para cada objeto de la interfaz de usuario que tenga restricciones instaladas directamente. .
Veamos el ejemplo bastante simplificado de su pregunta.
Si observa la lista de restricciones a la izquierda, puede ver que la mitad de ellas están en gris. Las restricciones en gris son las restricciones del paisaje. Creé todos estos, luego desmarque "Instalado" para cada uno de ellos:
Mientras tanto, las restricciones de aspecto normal y sin brillo son las restricciones de retrato. Para estos, los dejé "Instalados". Es completamente innecesario dejar ninguno de ellos instalado, pero se encontrará con problemas si deja varios conjuntos instalados (lo más probable es que entren en conflicto).
Asegúrese de no marcar "Eliminar en tiempo de compilación" para ninguno de estos. No queremos que se eliminen las restricciones en el momento de la compilación. Esto simplemente significa que la restricción no se ha creado en absoluto (por lo que perderemos la referencia a ella). Si dejamos esta verificación marcada mientras tengamos un IBOutlet
a la restricción, Xcode generará una advertencia:
Configuración no soportada
Conexión a la restricción de marcador de posición. Las restricciones marcadas como marcadores de posición en IB no deberían tener ninguna conexión ya que estas restricciones no se compilan en el documento y no existirán en tiempo de ejecución.
De todos modos, ahora debemos conectar las restricciones a un punto de venta para poder acceder a ellas en tiempo de ejecución.
Mantenga presionada la tecla Ctrl y haga clic y arrastre desde una de las restricciones a su archivo de código fuente, de la misma manera que conectaría cualquier otro elemento de la interfaz de usuario. En el cuadro de diálogo que aparece, seleccione Recolección de salida y un nombre descriptivo:
Ahora conecte todas las otras restricciones que coincidan con ese grupo de restricciones en la misma colección de puntos de venta:
Una vez que hemos terminado de conectar todas nuestras restricciones, es solo cuestión de eliminarlas / agregarlas en el momento adecuado.
Para un ejemplo tan simple como el escenario descrito en la pregunta, podemos anular updateViewConstraints
con algún código como este:
Rápido
class ViewController: UIViewController {
@IBOutlet var landscapeConstraints: [NSLayoutConstraint]!
@IBOutlet var portraitConstraints: [NSLayoutConstraint]!
override func updateViewConstraints() {
let isPortrait = self.view.bounds.width < self.view.bounds.height
self.view.removeConstraints(self.portraitConstraints)
self.view.removeConstraints(self.landscapeConstraints)
self.view.addConstraints(isPortrait ? self.portraitConstraints : self.landscapeConstraints)
super.updateViewConstraints()
}
}
C objetivo
@interface ViewController()
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints;
@end
@implementation ViewController
- (void)updateViewConstraints {
BOOL isPortrait = self.view.bounds.size.width < self.view.boudns.size.height;
[self.view removeConstraints:self.portraitConstraints];
[self.view removeConstraints:self.landscapeConstraints];
[self.view addConstraints:(isPortrait ? self.portraitConstraints : self.landscapeConstraints)];
[super updateViewConstraints];
}
@end
No estamos comprobando qué conjunto de restricciones tenía la vista anteriormente, así que simplemente elimine nuestros dos conjuntos de restricciones y luego agregue el conjunto apropiado que queremos usar.
Este es todo el código que necesitamos para gestionar el cambio completo de un conjunto completo de restricciones en un objeto en tiempo de ejecución. Esto permite trabajar en el generador de interfaces para configurar todas nuestras restricciones en lugar de tener que hacerlo mediante programación (lo que me parece un poco más tedioso y propenso a errores).
¿El final resulto? Reorganización de autorrotación de muy buen aspecto sin arrancarte el pelo y hacer que el código de reproducción automática se realice perfectamente: