Aumento de la resistencia de bandas de goma UIScrollView

Tengo un escenario que estoy intentando implementar en mi aplicación donde me gustaría hacer que un UIScrollView comporte más 'rígido' una vez que comienzas a forzarla a desplazarte más allá de sus límites de contenido normales.

Lo que quiero decir con esto es cuando estás en la parte superior o inferior de una vista de desplazamiento, si presionas y sigues arrastrando, normalmente puedes get la vista de desplazamiento para seguir desplazándose más allá de sus límites, pero gradualmente aumenta la resistencia , hasta que generalmente se detiene a medio path en el medio de los límites de la vista. Cuando levantas el dedo, vuelve a los límites de la región de desplazamiento.

Lo que estoy tratando de lograr es que me gustaría hacer que el efecto de arrastrar fuera de los límites sea mucho más pesado, así que en lugar de que el usuario arrastre la vista de desplazamiento y se 'aclare' a mitad de path a través del los límites de la vista de desplazamiento, se detienen por completo en torno a un 20% más o less de sus límites de desplazamiento.

He estado experimentando con anular el contenido de la vista de contentOffset en el método scrollViewDidScroll: delegate, pero eso no parece funcionar desde que volví a establecer el contenido de la configuration, parece arruinar más llamadas de delegado del mismo método.

Mi siguiente idea fue monitorear el UIPanGestureRecognizer asociado con scrollview e intentar y determinar los events off basados ​​en el apagado de contenido UIScrollView adecuados. Dicho eso, pensé que tal vez comenzaría a ponerse del lado hackeado, así que pensé que pediría aquí cualquier otra solución que no hubiera considerado antes de intentar algo que potencialmente podría ser desorderado.

¡Gracias!

Estoy pinchando en el siguiente código en Swift. Esto es para desplazamiento horizontal y puede adaptarse fácilmente a uno vertical. Las soluciones difieren según si la pagination está habilitada o no. Ambos se dan a continuación.

 class ScrollViewDelegate : NSObject, UIScrollViewDelegate { let maxOffset: CGFloat // offset of the rightmost content (scrollview contentSize.width - frame.width) var prevOffset: CGFloat = 0 // previous offset (after adjusting the value) var totalDistance: CGFloat = 0 // total distance it would have moved (had we not restricted) let networkinguctionFactor: CGFloat = 0.2 // percent of total distance it will be allowed to move (under restriction) let scaleFactor: CGFloat = UIScreen.mainScreen().scale // pixels per point, for smooth translation in respective devices init(maxOffset: CGFloat) { self.maxOffset = maxOffset // scrollView.contentSize.width - scrollView.frame.size.width } func scrollViewDidScroll(scrollView: UIScrollView) { let flipped = scrollView.contentOffset.x >= maxOffset // dealing with left edge or right edge rubber band let currentOffset = flipped ? maxOffset - scrollView.contentOffset.x : scrollView.contentOffset.x // for right edge, flip the values as if screen is folded in half towards the left if(currentOffset <= 0) // if dragging/moving beyond the edge { if(currentOffset <= prevOffset) // if dragging/moving beyond previous offset { totalDistance += currentOffset - prevOffset // add the "proposed delta" move to total distance prevOffset = round(scaleFactor * totalDistance * networkinguctionFactor) / scaleFactor // set the prevOffset to fraction of total distance scrollView.contentOffset.x = flipped ? maxOffset - prevOffset : prevOffset // set the target offset, after negating any flipping } else // if dragging/moving is reversed, though still beyond the edge { totalDistance = currentOffset / networkinguctionFactor // set totalDistance from offset (reverse of prevOffset calculation above) prevOffset = currentOffset // set prevOffset } } else // if dragging/moving inside the edge { totalDistance = 0 // reset the values prevOffset = 0 } } } 

Cuando se habilita la pagination, el rebote al punto de reposo no parece funcionar como se esperaba. En lugar de detenerse en el límite de la página, la banda de goma está sobrepasando y deteniéndose en un desplazamiento que no sea de página. Esto sucede si el tirón del borde es con un movimiento rápido de manera que continúa moviéndose en esa dirección incluso después de levantar el dedo, antes de invertir la dirección y volver al punto de reposo. Parece que funciona bien si simplemente pausa y lo deja o incluso lo vuelve a mover hacia el punto de reposo. Para abordar esto, en el código siguiente trato de identificar la posibilidad de sobrepasar y detenerlo forzadamente mientras regresa e intenta cruzar el límite de página esperado.

 class PageScrollViewDelegate : NSObject, UIScrollViewDelegate { let maxOffset: CGFloat // offset of the rightmost content (scrollview contentSize.width - frame.width) var prevOffset: CGFloat = 0 // previous offset (after adjusting the value) var totalDistance: CGFloat = 0 // total distance it would have moved (had we not restricted) let networkinguctionFactor: CGFloat = 0.2 // percent of total distance it will be allowed to move (under restriction) let scaleFactor: CGFloat = UIScreen.mainScreen().scale // pixels per point, for smooth translation in respective devices var draggingOver: Bool = false // finger dragging is over or not var overshoot: Bool = false // is there a chance for page to overshoot page boundary while falling back init(maxOffset: CGFloat) { self.maxOffset = maxOffset // scrollView.contentSize.width - scrollView.frame.size.width } func scrollViewWillBeginDragging(scrollView: UIScrollView) { draggingOver = false // reset the flags overshoot = false } func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { draggingOver = true // finger dragging is over } func scrollViewDidScroll(scrollView: UIScrollView) { let flipped = scrollView.contentOffset.x >= 0.5 * maxOffset // dealing with left edge or right edge rubber band let currentOffset = flipped ? maxOffset - scrollView.contentOffset.x : scrollView.contentOffset.x // for right edge, flip the values as if screen is folded in half towards the left if(currentOffset <= 0) // if dragging/moving beyond the edge { if(currentOffset <= prevOffset) // if dragging/moving beyond previous offset { overshoot = draggingOver // is content moving farther away even after dragging is over (caused by fast flick, which can cause overshooting page boundary while falling back) totalDistance += currentOffset - prevOffset // add the "proposed delta" move to total distance prevOffset = round(scaleFactor * totalDistance * networkinguctionFactor) / scaleFactor // set the prevOffset to fraction of total distance scrollView.contentOffset.x = flipped ? maxOffset - prevOffset : prevOffset // set the target offset, after negating any flipping } else // if dragging/moving is reversed, though still beyond the edge { totalDistance = currentOffset / networkinguctionFactor // set totalDistance from offset (reverse of prevOffset calculation above) prevOffset = currentOffset // set prevOffset } } else // if dragging/moving inside the edge { if(overshoot) // if this movement is a result of overshooting { scrollView.setContentOffset(CGPointMake(flipped ? maxOffset : 0, scrollView.contentOffset.y), animated: false) // bring it to resting point and stop further scrolling (this is a patch to control overshooting) } totalDistance = 0 // reset the values prevOffset = 0 } } } 

Esto no va a ser fácil.

Si esto es solo una vista de desplazamiento única, podría sugerirte usar UIKit Dynamics con algunos impulsos, datos adjuntos y comportamientos de resorte para get exactamente el efecto que deseas.

Si está buscando hacer esto para cada vista de tabla en su aplicación, creo que ver el reconocimiento de gestos panorámicos es un enfoque lo suficientemente razonable. Justo en lo alto de mi cabeza observaría el estado del gesto, cuando termine, capturaría la velocidad vertical de la vista, UIScrollView método UIScrollView para calcular su position de parada y luego animarlo desde su position actual hasta su position de descanso con un animation de spring Tendrá que calcular la duración usted mismo usando la velocidad final de la sartén y la distancia restante + la cantidad de sobreimpulso.

He realizado un proyecto GitHub que maneja este caso.

Vista de desplazamiento de rebote

Puedes controlar la resistencia del desplazamiento a través de él.

Este código detendrá el desplazamiento en el margen que desee

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat margin = scrollView.frame.size.height/4.0; if (scrollView.contentOffset.y < -margin) { CGPoint offset = scrollView.contentOffset; offset.y = -margin; scrollView.contentOffset = offset; } else if (scrollView.contentOffset.y > (scrollView.contentSize.height-scrollView.frame.size.height) + margin) { CGPoint offset = scrollView.contentOffset; offset.y = (scrollView.contentSize.height-scrollView.frame.size.height) + margin; scrollView.contentOffset = offset; } } 

Espero eso ayude