¿Evitar que UIWebView "rebote" verticalmente?

225

¿Alguien sabe cómo evitar que un UIWebView rebote verticalmente? Quiero decir, cuando un usuario toca la pantalla de su iPhone, arrastra su dedo hacia abajo y la vista web muestra un punto en blanco sobre la página web que cargué.

He analizado las siguientes soluciones posibles, pero ninguna de ellas funcionó para mí:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/996-turn-off-scrolling-bounces-uiwebview.html

http://forums.macrumors.com/showthread.php?t=619534

¿Cómo evito que un UIScrollView rebote horizontalmente?

Brad Parks
fuente
1
Espero que hayas considerado las implicaciones de usabilidad de deshabilitar esa función. Está ahí por una razón. Dicho esto, también tengo curiosidad de cómo harías eso.
Michael Haren
De la forma en que lo tengo incrustado en mi aplicación, aparece sin parecer con algunos otros elementos de vista que tengo, y la única vez que no aparece es cuando ocurre el rebote ... ¡Sin duda, un buen punto!
Brad Parks

Respuestas:

424
for (id subview in webView.subviews)
  if ([[subview class] isSubclassOfClass: [UIScrollView class]])
    ((UIScrollView *)subview).bounces = NO;

... parece funcionar bien.

También será aceptado en la App Store.

Actualización : en iOS 5.x + hay una manera más fácil: UIWebViewtiene scrollViewpropiedad, por lo que su código puede verse así:

webView.scrollView.bounces = NO;

Lo mismo vale para WKWebView.

Mirek Rusin
fuente
Funciona. No se requieren subclases y si Apple alguna vez cambia la jerarquía UIWebView, no debería bloquearse.
leolobato
2
Parece que esta solución solo funciona en mi simulador OS 4.1, no en mi iPhone OS 3.1.3. ¿Alguien sabe si esta solución solo funciona en versiones particulares del sistema operativo?
Dan J
@MirukRusin - ¿Cómo puede garantizar que esta aplicación será aceptada en base a tres líneas de código? : P
Moshe
@Mirek - Te perdiste mi punto. ¿Cómo sabes que nada más en la aplicación lo rechazará?
Moshe
66
Esto funcionó bien para mi. Por conveniencia, agregué una categoría para UIWebViewpoder llamar a [webView setBounces:NO]donde quisiera.
zekel
46

Estaba mirando un proyecto que facilita la creación de aplicaciones web como aplicaciones instalables completas en el iPhone llamadas QuickConnect , y encontré una solución que funciona, si no quieres que tu pantalla sea desplazable, lo que en mi caso No lo hice

En la publicación de blog / proyecto mencionada anteriormente, mencionan una función de JavaScript que puede agregar para desactivar el rebote, que esencialmente se reduce a esto:

    document.ontouchmove = function(event){
        event.preventDefault();
    }

Si desea ver más acerca de cómo lo implementan, simplemente descargue QuickConnect y compruébelo ... Pero, básicamente, todo lo que hace es llamar a ese JavaScript en la carga de la página ... Intenté ponerlo en el encabezado de mi documento, y parece funcionar bien

Brad Parks
fuente
Esta respuesta es la forma correcta de detener el desplazamiento vertical del contenido dentro de un UIWebView.
Jessedc
3
Podría haber dado la respuesta completa, en primer lugar, no quiero descargar un código fuente completo de otra aplicación, y ni siquiera puedo encontrarlo. Su respuesta se basa en que otro sitio web sea el mismo que cuando respondió.
Jonathan.
Esta respuesta y la respuesta aceptada arriba parecen ser necesarias a veces. En mi caso, descubrí que si la vista web aún no se cargaba, la capacidad de recuperación estaría allí hasta que el JS anterior se cargara realmente en el sistema. Entonces, la respuesta parece ser que esto y la respuesta aceptada arriba son ambas, a veces, requeridas.
Kalle
3
como dijo Jessedc, esto detendrá el desplazamiento vertical, no solo el rebote. eso significa que si tiene div o algo que requiere desplazamiento, no se desplazará.
memical
18

Bueno, todo lo que hice para lograr esto es:

UIView *firstView = [webView.subviews firstObject];

if ([firstView isKindOfClass:[UIScrollView class]]) {

    UIScrollView *scroll = (UIScrollView*)firstView;
   [scroll setScrollEnabled:NO];  //to stop scrolling completely
   [scroll setBounces:NO]; //to stop bouncing 

}

Funciona bien para mí ... Además, la respuesta marcada para esta pregunta es una que Apple rechazará si la usa en su aplicación de iPhone.

Zigglzworth
fuente
3
Esta es la mejor y más simple forma. ¡Esta debería ser la respuesta aceptada! +1
PostCodeism
3
No hagas eso: mi aplicación fue rechazada por usar la primera línea. Perdí mucho tiempo para mí. utilicé [[webView.subviews lastObject] setScrollEnabled: NO]; y apple dijo que era una API privada y, por lo tanto, la rechazó; estoy a punto de volver a enviar ahora pero usé "userInteractionEnabled: NO" ya que no necesito ningún enlace activo en la aplicación.
Veeru
UIWebView hereda de UIView. UIView tiene subvistas de propiedades. ¿Qué es privado aquí? Todo esto lo puedes encontrar en developer.apple.com
Valerii Pavlov
3
Tengo algo así como 4 aplicaciones en la tienda de aplicaciones usando esto. Su aplicación fue rechazada claramente por otra razón. Esta no es una API privada.
Zigglzworth
44
Esto realmente no es una buena idea. Estás confiando en el hecho de que esta UIScrollViewes la primera subvista UIWebViewque bien podría ser el caso por ahora, pero esto podría cambiar fácilmente en futuras actualizaciones (incluso menores) para iOS o configuraciones particulares. Realmente deberías hacer una foriteración subviewspara encontrar el UIScrollView(realmente no es tanto trabajo y al menos tienes la garantía de obtener un programa UIScrollViewy no bloquearlo ...
Jonathan Ellis
17

En el SDK de iOS 5 puede acceder a la vista de desplazamiento asociada con una vista web directamente en lugar de iterar a través de sus subvistas.

Entonces, para deshabilitar el 'rebote' en la vista de desplazamiento, puede usar:

myWebView.scrollView.bounces = NO;

Ver la referencia de clase UIWebView .

(Sin embargo, si necesita admitir versiones del SDK anteriores a 5.0, debe seguir los consejos de Mirek Rusin ).

jstr
fuente
12

Swift 3

webView.scrollView.bounces = false
Ego Slayer
fuente
9

Advertencia. Usé setAllowsRubberBanding: en mi aplicación, y Apple lo rechazó, declarando que las funciones API no públicas no están permitidas (cita: 3.3.1)

Raf
fuente
4

En Swift para deshabilitar los rebotes

webViewObj.scrollView.bounces = false
Jugal K Balara
fuente
3

El método de Brad funcionó para mí. Si lo usa, es posible que desee hacerlo un poco más seguro.

id scrollView = [yourWebView.subviews objectAtIndex: 0];
if ([scrollView respondeToSelector: @selector (setAllowsRubberBanding :)])
{
    [scrollView performSelector: @selector (setAllowsRubberBanding :) withObject: NO];
}

Si Apple cambia algo, el rebote volverá, pero al menos su aplicación no se bloqueará.

Gavin Maclean
fuente
3

En iOS5 solo si planea permitir que los usuarios amplíen el contenido de la vista web (ei: doble toque), la configuración de rebote no es suficiente. También debe establecer las propiedades alwaysBounceHorizontal y alwaysBounceVertical en NO, de lo contrario, cuando se alejan (otro doble toque ...) de forma predeterminada, volverá a rebotar.

il Malvagio Dottor Prosciutto
fuente
2

Recorrí la colección de subvistas de UIWebView y configuré sus fondos en [UIColor blackColor], del mismo color que el fondo de la página web. La vista seguirá rebotando pero no mostrará ese feo fondo gris oscuro.

pedro
fuente
1
En realidad, es bastante malo, porque si Apple alguna vez cambia las partes internas de UIWebView, este truco podría romperse.
bpapa
2

Me parece que el UIWebView tiene un UIScrollView. Puede usar API documentadas para esto, pero el rebote se establece para ambas direcciones, no individualmente. Esto está en los documentos de la API. UIScrollView tiene una propiedad de rebote, por lo que algo como esto funciona (no sé si hay más de una vista de desplazamiento):

NSArray *subviews = myWebView.subviews;
NSObject *obj = nil;
int i = 0;
for (; i < subviews.count ; i++)
{
    obj = [subviews objectAtIndex:i];

    if([[obj class] isSubclassOfClass:[UIScrollView class]] == YES)
    {
        ((UIScrollView*)obj).bounces = NO;
    }
}
maestro de espionaje
fuente
2

Me molestó descubrir que UIWebView no es una vista de desplazamiento, así que hice una subclase personalizada para acceder a la vista de desplazamiento de la vista web. Este ejemplo contiene una vista de desplazamiento para que pueda personalizar el comportamiento de su vista web. Los puntos clave de esta clase son:

@class CustomWebView : UIWebview
...

- (id) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
// WebViews are subclass of NSObject and not UIScrollView and therefore don't allow customization.
// However, a UIWebView is a UIScrollViewDelegate, so it must CONTAIN a ScrollView somewhere.
// To use a web view like a scroll view, let's traverse the view hierarchy to find the scroll view inside the web view.
for (UIView* v in self.subviews){
    if ([v isKindOfClass:[UIScrollView class]]){
        _scrollView = (UIScrollView*)v; 
        break;
    }
}
return self;

}

Luego, cuando crea una vista web personalizada, puede deshabilitar el rebote con:

customWebView.scrollView.bounces = NO; //(or customWebView.scrollView.alwaysBounceVertically = NO)

Esta es una excelente forma de uso general para crear una vista web con un comportamiento de desplazamiento personalizable. Solo hay algunas cosas a tener en cuenta:

  • Al igual que con cualquier vista, también deberá anular - (id) initWithCoder: si lo usa en Interface Builder
  • Cuando crea inicialmente una vista web, su tamaño de contenido es siempre el mismo que el del marco de la vista. Después de desplazarse por la web, el tamaño del contenido representa el tamaño del contenido web real dentro de la vista. Para evitar esto, hice algo extravagante: llamar a -setContentOffset: CGPointMake (0,1) animated: YES para forzar un cambio imperceptible que establecerá el tamaño de contenido adecuado de la vista web.
Rolf Hendriks
fuente
2

Encontré esto en busca de una respuesta y finalmente tuve la suerte de obtener una respuesta propia al jugar. yo hice

[[webview scrollView] setBounces:NO];

Y funcionó.

Luke
fuente
2

Esto funcionó para mí, y maravillosamente también (estoy usando phonegap con webView)

[[webView.webView scrollView] setScrollEnabled:NO];

o

[[webView scrollView] setScrollEnabled:NO];
Jason G
fuente
1

Intenté un enfoque ligeramente diferente para evitar que los objetos UIWebView se desplazaran y rebotaran: agregando un reconocedor de gestos para anular otros gestos.

Parece que UIWebView o su subvista de desplazamiento utiliza su propio reconocedor de gestos panorámicos para detectar el desplazamiento del usuario. Pero de acuerdo con la documentación de Apple, existe una forma legítima de anular un reconocedor de gestos con otro. El protocolo UIGestureRecognizerDelegate tiene un método GestorReconocidor: shouldRecognizeSim simultáneamentelywithGestureRecognizer: - que permite controlar el comportamiento de cualquier reconocedor de gestos en colisión.

Entonces, lo que hice fue

en el método viewDidLoad del controlador de vista:

// Install a pan gesture recognizer                                                                                        // We ignore all the touches except the first and try to prevent other pan gestures                                                     
// by registering this object as the recognizer's delegate                                                                                        
UIPanGestureRecognizer *recognizer;                                                                                                               
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanFrom:)];                                                   
recognizer.delegate = self;                                                                                                                       
recognizer.maximumNumberOfTouches = 1;                                                                                                            
[self.view addGestureRecognizer:recognizer];                                                                                                          
self.panGestureFixer = recognizer;                                                                                                                  
[recognizer release]; 

entonces, el método de anulación de gestos:

// Control gestures precedence                                                                                                                            
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer                                                                                        
        shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer                                                  
{                                                                                                                                                         
        // Prevent all panning gestures (which do nothing but scroll webViews, something we want to disable in                                          
        // the most painless way)                                                                                                                         
        if ([otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])                                                                        
        {
            // Just disable every other pan gesture recognizer right away                                                                             
            otherGestureRecognizer.enabled = FALSE;
        }                                                                                                                                                  
        return NO;                                                                                                                                        
}              

Por supuesto, este método delegado puede ser más complejo en una aplicación real: podemos deshabilitar otros reconocedores de forma selectiva, analizar otherGestureRecognizer.view y tomar una decisión en función de qué vista es.

Y, finalmente, en aras de la exhaustividad, el método que registramos como manejador de pan:

- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer 
{ 
    // do nothing as of yet
}

puede estar vacío si todo lo que queremos es cancelar el desplazamiento y el rebote de las vistas web, o puede contener nuestro propio código para implementar el tipo de movimientos panorámicos y animaciones que realmente queremos ...

Hasta ahora solo estoy experimentando con todo esto, y parece estar funcionando más o menos como lo quiero. Sin embargo, todavía no he intentado enviar ninguna aplicación a iStore. Pero creo que no he usado nada indocumentado hasta ahora ... Si alguien encuentra lo contrario, infórmeme.

PP-RD
fuente
0

Una de las subvistas de UIWebViewdebería ser a UIScrollView. Establezca su scrollEnabledpropiedad en NOy la vista web tendrá el desplazamiento deshabilitado por completo.

Nota: técnicamente se trata de una API privada y, por lo tanto, su aplicación podría ser rechazada o bloquearse en futuras versiones del sistema operativo. Uso @tryyrespondsToSelector

rpetrich
fuente
Probé esto, y falla cuando lo intento ... Extrañamente, puedo acceder y configurar scrollView.bounces (sin efecto), pero cuando intento configurar scrollEnabled, falla ...
Brad Parks
Creo que la subvista se llama UIScroller, no UIScrollView. UIScroller es una clase indocumentada y no compatible que comparte mucho en común con UIScrollView, pero no puede depender de que todo funcione de la misma manera y no cambie en futuras versiones del sistema operativo.
Marco
Lo hice en una aplicación de iPad (no iPhone) con iOS 3.2, y pude obtener un UIScrollView de mi UIWebView. Personalicé con éxito esta vista para cambiar más que solo su comportamiento de rebote para poder testificar que esto funciona en iPad. Mi aplicación no pasó por la tienda de aplicaciones, pero no creo que haya un problema con las API indocumentadas aquí. La parte inteligente de esta solución es que solo está atravesando una jerarquía UIView y utilizando un UIScrollView, los cuales son perfectamente kosher.
Rolf Hendriks
0

Mira en la bouncespropiedad de UIScrollView. Quoth los documentos de Apple:

Si el valor de la propiedad es YES(el valor predeterminado), la vista de desplazamiento rebota cuando encuentra un límite del contenido. Rebotar visualmente indica que el desplazamiento ha alcanzado un borde del contenido. Si el valor es NO, el desplazamiento se detiene inmediatamente en el límite de contenido sin rebotar.

Asegúrate de estar usando lo correcto UIScrollView. No estoy seguro de cómo se ve la jerarquía para un UIWebView, pero la vista de desplazamiento podría ser un padre, no un hijo, del UIWebView.

Alex
fuente
0

Para deshabilitar el UIWebViewdesplazamiento, puede usar la siguiente línea de código:

[ObjWebview setUserInteractionEnabled:FALSE];

En este ejemplo, ObjWebviewes de tipo UIWebView.

Sandip Patel - SM
fuente
0

webView.scrollView.scrollEnabled = NO; webView.scrollView.bounces = NO;

Kumaresan P
fuente