Parar o UIWebView de "saltar" verticalmente?

225

Alguém sabe como impedir que um UIWebView salte verticalmente? Quero dizer, quando um usuário toca na tela do iPhone, arrasta o dedo para baixo e a visualização na Web mostra um espaço em branco acima da página da Web que eu carreguei?

Analisei as seguintes soluções possíveis, mas nenhuma delas funcionou para mim:

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

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

Como faço para impedir que um UIScrollView salte horizontalmente?

Brad Parks
fonte
1
Espero que você tenha considerado as implicações de usabilidade de desativar esse recurso. Está lá por uma razão. Dito isto, também estou curioso sobre como você faria isso.
267 Michael Haren
Do jeito que eu o incorporo no meu aplicativo, ele aparece aparentemente com alguns outros elementos de exibição que tenho, e a única vez em que não aparece é quando o salto acontece ... Definitivamente, é um bom ponto!
214 Brad Brad Parks

Respostas:

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

... parece funcionar bem.

Também será aceito na App Store.

Atualização : no iOS 5.x +, há uma maneira mais fácil - UIWebViewpossui scrollViewpropriedade, portanto seu código pode ficar assim:

webView.scrollView.bounces = NO;

O mesmo vale para WKWebView.

Mirek Rusin
fonte
Funciona. Não é necessária nenhuma subclasse e, se a Apple alterar a hierarquia do UIWebView, ela não deverá travar.
Leolobato
2
Essa correção parece funcionar apenas no meu OS 4.1 Simulator, não no meu iPhone OS 3.1.3. Alguém sabe se essa correção funciona apenas em versões específicas do sistema operacional?
Dan J
@MirukRusin - Como você pode garantir que este aplicativo seja aceito com base em três linhas de código? : P
Moshe
@Mirek - Você perdeu o meu ponto. Como você sabe que nada mais no aplicativo o rejeitará?
Moshe
6
Isso funcionou bem pra mim. Por conveniência, adicionei uma categoria para UIWebViewpoder ligar para [webView setBounces:NO]qualquer lugar que quisesse.
zekel
46

Eu estava olhando para um projeto que facilita a criação de aplicativos da Web como aplicativos instaláveis ​​completos no iPhone, chamado QuickConnect , e encontrei uma solução que funciona, se você não deseja que sua tela seja rolável, o que, no meu caso, Eu não fiz.

Na postagem do projeto / blog acima mencionada, eles mencionam uma função javascript que você pode adicionar para desativar a rejeição, que basicamente se resume a isso:

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

Se você quiser saber mais sobre como eles o implementam, basta fazer o download do QuickConnect e verificá-lo .... Mas basicamente tudo o que faz é chamar esse javascript no carregamento da página ... Tentei colocá-lo no cabeçalho do meu documento, e parece funcionar bem.

Brad Parks
fonte
Esta resposta é a maneira correta de interromper a rolagem vertical do conteúdo em um UIWebView.
Jessedc
3
Você poderia ter dado a resposta completa. Primeiro, não quero fazer o download de todo o código-fonte de outro aplicativo e nem consigo encontrá-lo. Sua resposta depende de outro site ser o mesmo de quando você respondeu.
Jonathan.
Esta resposta e a resposta aceita acima parecem ser necessárias às vezes. No meu caso, descobri que, se a visualização da Web ainda não estivesse carregada, a recompensa permaneceria até que o JS acima fosse realmente carregado no sistema. Portanto, a resposta parece ser que essa e a resposta aceita acima são, às vezes, necessárias.
Kalle
3
como Jessedc disse, isso interromperá a rolagem vertical, não apenas saltando. isso significa que, se você tem div ou requer uma rolagem, ela não rolará.
Memical
18

Bem, tudo o que fiz para fazer isso é:

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 bem para mim ... Além disso, a resposta marcada para esta pergunta é uma que a Apple rejeitará se você a usar no seu aplicativo para iPhone.

Zigglzworth
fonte
3
Esta é a melhor e mais simples maneira. Essa deve ser a resposta aceita! 1
PostCodeism
3
Não faça isso - meu aplicativo foi rejeitado por usar a primeira linha. Perdi muito tempo para mim. eu usei [[webView.subviews lastObject] setScrollEnabled: NO]; e a apple disse que era uma API privada e, portanto, a rejeitou; Estou prestes a reenviar agora, mas usei "userInteractionEnabled: NO", pois não preciso de nenhum link ativo no aplicativo.
Veeru
UIWebView herda de UIView. UIView possui subvisões de propriedades. O que é privado aqui? Tudo isso você pode encontrar em developer.apple.com
Valerii Pavlov
3
Eu tenho algo como 4 aplicativos na appstore usando isso. Seu aplicativo foi claramente rejeitado por outro motivo. isso não é uma API privada.
Zigglzworth 12/09
4
Isso realmente não é uma boa ideia. Você está confiando no fato de que essa UIScrollViewé a primeira subvisão UIWebViewque pode ser o caso por enquanto, mas isso pode mudar facilmente em futuras atualizações (até menores) do iOS ou em configurações específicas. Você realmente deve fazer uma foriteração sobre subviewsa realmente encontrar o UIScrollView(ele realmente não é que muito trabalho e, pelo menos, você está garantido para obter um UIScrollViewe não travar o seu programa ...
Jonathan Ellis
17

No iOS 5 SDK, você pode acessar diretamente a visualização de rolagem associada a uma visualização da Web, em vez de iterar pelas subvisões.

Portanto, para desativar o 'salto' na exibição de rolagem, você pode usar:

myWebView.scrollView.bounces = NO;

Consulte a Referência da classe UIWebView .

(No entanto, se você precisar oferecer suporte a versões do SDK anteriores à 5.0, siga as orientações de Mirek Rusin .)

jstr
fonte
12

Swift 3

webView.scrollView.bounces = false
Ego Slayer
fonte
9

Aviso. Usei setAllowsRubberBanding: no meu aplicativo e a Apple o rejeitou, afirmando que funções de API não públicas não são permitidas (citação: 3.3.1)

Raf
fonte
4

Em Swift para desativar os saltos

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

O método de Brad funcionou para mim. Se você usá-lo, você pode querer torná-lo um pouco mais seguro.

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

Se a Apple mudar alguma coisa, o retorno retornará - mas pelo menos o seu aplicativo não falhará.

Gavin Maclean
fonte
3

No iOS5, apenas se você planeja permitir que os usuários ampliem o conteúdo da visualização na web (ei: toque duas vezes), a configuração de rejeição não é suficiente. Você também deve definir as propriedades alwaysBounceHorizontal e alwaysBounceVertical como NO; caso contrário, quando reduzirem o zoom (outro toque duplo ...), o padrão será o retorno novamente.

il Malvagio Dottor Prosciutto
fonte
2

Percorri a coleção de subvisões do UIWebView e defina seus planos de fundo como [UIColor blackColor], a mesma cor do plano de fundo da página da web. A vista ainda será refletida, mas não mostrará esse fundo cinza escuro feio.

Pedro
fonte
1
Na verdade, é muito ruim, porque se a Apple mudar os elementos internos do UIWebView, esse hack poderá ser interrompido.
Bpapa
2

Parece-me que o UIWebView tem um UIScrollView. Você pode usar APIs documentadas para isso, mas o salto é definido para as duas direções, não individualmente. Isso está nos documentos da API. O UIScrollView tem uma propriedade de rejeição, então algo assim funciona (não sei se há mais de uma visualização de rolagem):

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;
    }
}
spymaster
fonte
2

Fiquei aborrecido ao descobrir que o UIWebView não é uma exibição de rolagem, então criei uma subclasse personalizada para acessar a exibição de rolagem da exibição na web. Essa subclasse contém uma visualização de rolagem para que você possa personalizar o comportamento da sua visualização na web. Os punchlines desta classe são:

@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;

}

Em seguida, ao criar uma exibição na web personalizada, você pode desativar o salto com:

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

Essa é uma ótima maneira de finalidade geral de fazer uma exibição na Web com comportamento de rolagem personalizável. Há algumas coisas a serem observadas:

  • como em qualquer visualização, você também precisará substituir - (id) initWithCoder: se você o usar no Interface Builder
  • quando você cria inicialmente uma visualização na web, seu tamanho de conteúdo é sempre o mesmo que o tamanho do quadro da visualização. Depois de rolar a web, o tamanho do conteúdo representa o tamanho do conteúdo real da web dentro da visualização. Para contornar isso, fiz algo hacky - chamando -setContentOffset: CGPointMake (0,1) animated: YES para forçar uma alteração imperceptível que definirá o tamanho de conteúdo apropriado da exibição na web.
Rolf Hendriks
fonte
2

Me deparei com isso em busca de uma resposta e, eventualmente, eu apenas tive sorte com uma resposta minha, brincando. eu fiz

[[webview scrollView] setBounces:NO];

e funcionou.

Lucas
fonte
2

Isso funcionou para mim e muito bem (estou usando o phonegap com o webView)

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

ou

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

Tentei uma abordagem ligeiramente diferente para impedir que os objetos UIWebView rolassem e pulassem: adicionando um reconhecedor de gestos para substituir outros gestos.

Parece que o UIWebView ou sua subvisualização de rolagem usa seu próprio reconhecedor de gesto de panorâmica para detectar a rolagem do usuário. Mas, de acordo com a documentação da Apple, existe uma maneira legítima de substituir um reconhecedor de gestos por outro. O protocolo UIGestureRecognizerDelegate possui um método gestRecognizer: shouldRecognizeSimultaneamenteWithGestureRecognizer: - que permite controlar o comportamento de qualquer reconhecedor de gestos em colisão.

Então, o que eu fiz foi

no método viewDidLoad do controlador de exibição:

// 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]; 

então, o gesto substitui o método:

// 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;                                                                                                                                        
}              

Obviamente, esse método delegado pode me tornar mais complexo em um aplicativo real - podemos desativar outros reconhecedores seletivamente, analisando otherGestureRecognizer.view e tomando decisões com base em qual é a visão.

E, finalmente, por uma questão de integridade, o método que registramos como manipulador de pan:

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

pode estar vazio se tudo o que queremos é cancelar a rolagem e a rejeição das visualizações da web, ou pode conter nosso próprio código para implementar o tipo de movimentos e animações de pan que realmente queremos ...

Até agora, estou apenas experimentando todas essas coisas, e parece estar funcionando mais ou menos como eu quero. Ainda não tentei enviar aplicativos para o iStore. Mas acredito que não usei nada não documentado até agora ... Se alguém achar o contrário, informe-me.

PP-RD
fonte
1

Aqui estão duas novas soluções em potencial. Aparentemente, você pode usar o jqtouch ou o pastrykit para desativar a rolagem. No entanto, eu não tenho esses para trabalhar. Você pode ser mais competente.

desativando a rolagem vertical

cavando em pastrykit

cannyboy
fonte
0

Uma das subvisões de UIWebViewdeve ser a UIScrollView. Defina sua scrollEnabledpropriedade como NOe a exibição na Web terá a rolagem totalmente desativada.

Nota: isso é tecnicamente usando uma API privada e, portanto, seu aplicativo pode ser rejeitado ou travar em versões futuras do sistema operacional. Use @tryerespondsToSelector

rpetrich
fonte
Eu tentei isso, e ele falha quando eu tento ... Estranhamente, eu posso acessar e definir o scrollView.bounces (sem efeito), mas quando eu tento definir o scrollEnabled, ele falha ...
Brad Parks
Eu acredito que a subview é chamada UIScroller, não UIScrollView. O UIScroller é uma classe não documentada e sem suporte que compartilha muito em comum com o UIScrollView, mas você não pode depender de tudo funcionar da mesma maneira e não mudar nas versões futuras do sistema operacional.
25259 Marco Marco
Fiz isso em um aplicativo para iPad (não iPhone) executando o iOS 3.2 e consegui obter um UIScrollView do meu UIWebView. Customizei com êxito essa visão para mudar mais do que apenas seu comportamento de salto, para que eu possa testemunhar isso funcionando no iPad. Meu aplicativo não passou pela loja de aplicativos, mas acho que não haverá um problema com as APIs não documentadas aqui. A parte inteligente desta solução é que você está apenas atravessando uma hierarquia do UIView e usando um UIScrollView - ambos perfeitamente kosher.
Rolf Hendriks
0

Olhe para a bouncespropriedade de UIScrollView. Os documentos da Apple:

Se o valor da propriedade for YES(o padrão), a exibição de rolagem será devolvida quando encontrar um limite do conteúdo. Saltando visualmente indica que a rolagem atingiu uma borda do conteúdo. Se o valor for NO, a rolagem para imediatamente no limite do conteúdo sem saltar.

Verifique se você está usando o direito UIScrollView. Não sei como é a hierarquia para um UIWebView, mas a exibição de rolagem pode ser um pai, não um filho, do UIWebView.

Alex
fonte
0

Para desativar a UIWebViewrolagem, você pode usar a seguinte linha de código:

[ObjWebview setUserInteractionEnabled:FALSE];

Neste exemplo, ObjWebviewé do tipo UIWebView.

Sandip Patel - SM
fonte
0

webView.scrollView.scrollEnabled = NÃO; webView.scrollView.bounces = NÃO;

Kumaresan P
fonte