ios - scrolling - Cómo hacer que UIScrollView haga zoom en una sola dirección cuando usa el diseño automático
uiscrollview swift programmatically (4)
Estoy intentando hacer que UIScrollView solo permita hacer zoom en la dirección horizontal. La vista de desplazamiento está configurada con un enfoque de autodiseño puro. El enfoque habitual es el sugerido en varias respuestas de desbordamiento de pila (por ejemplo, esta ) y otros recursos. Lo he usado con éxito en aplicaciones antes de que existiera el autolayout. El enfoque es el siguiente: en la vista de contenido de la vista de desplazamiento, setTransform()
y setTransform()
la transformación pasada en escala en la dirección x:
override var transform: CGAffineTransform {
get { return super.transform }
set {
var t = newValue
t.d = 1.0
super.transform = t
}
}
También reinicié el desplazamiento de contenido de la vista de desplazamiento para que no se desplace verticalmente durante el zoom:
func scrollViewDidZoom(scrollView: UIScrollView) {
scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
}
Esto funciona muy bien cuando no se utiliza el diseño automático:
Sin embargo, cuando se usa el diseño automático, el desplazamiento del contenido termina mal durante el acercamiento. Mientras que la vista de contenido solo se escala en la dirección horizontal, se mueve verticalmente:
He puesto un proyecto de ejemplo en GitHub (utilizado para hacer los videos en esta pregunta). Contiene dos guiones gráficos, Main.storyboard y NoAutolayout.storyboard, que son idénticos, excepto que Main.storyboard tiene el autolayout encendido mientras NoAutolayout no. Cambie entre ellos cambiando la configuración del proyecto de la interfaz principal y puede ver el comportamiento en ambos casos.
Realmente prefiero no desactivar el diseño automático ya que resuelve una serie de problemas con la implementación de mi IU de una manera mucho más agradable de lo que se requiere con el diseño manual. ¿Hay alguna manera de mantener el desplazamiento del contenido vertical correcto (es decir, cero) durante el acercamiento con autolayout?
EDITAR: He agregado un tercer guión gráfico, AutolayoutVariableColumns.storyboard, al proyecto de muestra. Esto agrega un campo de texto para cambiar el número de columnas en GridView, lo que cambia su tamaño de contenido intrínseco. Esto muestra más de cerca el comportamiento que necesito en la aplicación real que provocó esta pregunta.
No estoy seguro si esto satisface su requisito de 100% de autodesplazamiento, pero esta es una solución, donde UIScrollView
se configura con autolayout y gridView
agregado como una subvista.
Su ViewController.swift
debería verse así:
// Add an outlet for the scrollView in interface builder.
@IBOutlet weak var scrollView: UIScrollView!
// Remove the outlet and view for gridView.
//@IBOutlet var gridView: GridView!
// Create the gridView here:
var gridView: GridView!
// Setup some views
override func viewDidLoad() {
super.viewDidLoad()
// The width for the gridView is set to 1000 here, change if needed.
gridView = GridView(frame: CGRect(x: 0, y: 0, width: 1000, height: self.view.bounds.height))
scrollView.addSubview(gridView)
scrollView.contentSize = CGSize(width: gridView.frame.width, height: scrollView.frame.height)
gridView.contentMode = .ScaleAspectFill
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return gridView
}
func scrollViewDidZoom(scrollView: UIScrollView) {
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0)
scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
}
Creo que lo descubrí. Debe aplicar una traducción en la transformación para compensar el centrado que UIScrollView intenta hacer al hacer zoom.
Agregue esto a su GridView:
var unzoomedViewHeight: CGFloat?
override func layoutSubviews() {
super.layoutSubviews()
unzoomedViewHeight = frame.size.height
}
override var transform: CGAffineTransform {
get { return super.transform }
set {
if let unzoomedViewHeight = unzoomedViewHeight {
var t = newValue
t.d = 1.0
t.ty = (1.0 - t.a) * unzoomedViewHeight/2
super.transform = t
}
}
}
Para calcular la transformación, necesitamos saber la altura sin escala de la vista. Aquí, acabo de tomar el tamaño del fotograma durante layoutSubviews () y supongo que contiene la altura sin unzona. Probablemente haya algunos casos extremos en los que eso no es correcto; podría ser un lugar mejor en el ciclo de actualización de vista para calcular la altura, pero esta es la idea básica.
Intenta establecer translatesAutoresizingMaskIntoConstraints
func scrollViewDidZoom(scrollView: UIScrollView) {
gridView.translatesAutoresizingMaskIntoConstraints = true
}
En el proyecto de muestra, funciona. Si esto no es suficiente, intente crear nuevas restricciones programáticamente en scrollViewDidEndZooming:
podría ayudar.
Además, si esto no ayuda, actualice el proyecto de muestra para que podamos reproducir el problema con la variable intrinsicContentSize()
Este artículo de Ole Begemann me ayudó mucho
Cómo aprendí a dejar de preocuparme y a amar el diseño automático de Cocoa
Video de la WWDC 2015
Misterios del diseño automático, parte 1
Misterios del diseño automático, parte 2
Y afortunadamente, hay una bandera para eso. Se llama translatesAutoResizingMask IntoConstraints [sin espacio].
Es un poco complicado, pero hace más de lo que dice. Hace que las vistas se comporten de la manera que lo hicieron en el sistema Legacy Layout pero en un mundo de Diseño automático.
Aunque la solución de @ Anna funciona, UIScrollView proporciona una forma de trabajar con el diseño automático . Pero debido a que las vistas de desplazamiento funcionan de manera diferente a otras vistas, las restricciones también se interpretan de manera diferente:
- Las restricciones entre los bordes / márgenes de la vista de desplazamiento y su contenido se conectan al área de contenido de la vista de desplazamiento.
- Las restricciones entre la altura , el ancho o los centros se unen al marco de la vista de desplazamiento.
- Las restricciones entre la vista de desplazamiento y las vistas fuera de la vista de desplazamiento funcionan como una vista normal.
Por lo tanto, cuando agrega una subvista a la vista de desplazamiento anclada a sus bordes / márgenes, esa subvista se convierte en el área de contenido o la vista de contenido de la vista de desplazamiento.
Apple sugiere el siguiente enfoque al trabajar con vistas de desplazamiento :
- Agregue la vista de desplazamiento a la escena.
- Dibuje restricciones para definir el tamaño y la posición de la vista de desplazamiento, como es normal.
- Agregue una vista a la vista de desplazamiento. Establezca la etiqueta específica de Xcode de la vista en la Vista de contenido.
- Fije los bordes superior, inferior, inicial y posterior de la vista de contenido en los bordes correspondientes de la vista de desplazamiento. La vista de contenido ahora define el área de contenido de la vista de desplazamiento.
- (...)
- (...)
- Coloque el contenido de la vista de desplazamiento dentro de la vista de contenido. Use restricciones para posicionar el contenido dentro de la vista de contenido de forma normal.
En su caso, GridView debe estar dentro de la vista de contenido :
Puede mantener las restricciones de vista de cuadrícula que ha estado usando pero, ahora, adjuntas a la vista de contenido. Y para el zoom horizontal solamente, mantenga el código exactamente como está. Su alteración de transform
maneja muy bien.