Faça o UIScrollView rolar para o topo

Respostas:

316

ATUALIZAÇÃO PARA iOS 7

[self.scrollView setContentOffset:
    CGPointMake(0, -self.scrollView.contentInset.top) animated:YES];

ORIGINAL

[self.scrollView setContentOffset:CGPointZero animated:YES];

ou se você deseja preservar a posição de rolagem horizontal e redefinir a posição vertical:

[self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, 0)
    animated:YES];
Rob Mayoff
fonte
50
Se você estiver fazendo isso no iOS 7, talvez seja necessário levar em consideração o UIScrollView contentInset, infelizmente. [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top) animated:YES];faz o trabalho para mim
runmad
11
Consegui rolar para o topo, logo abaixo da barra de navegação e status. [scrollView setContentOffset:CGPointMake(0, -scrollView.contentInset.top) animated:YES];Funciona no iOS 6 e 7
Amozoss
Isso não funcionou para mim, provavelmente porque estou fazendo algo louco com uma visão invertida no kit de sprite. Mas se alguém é tão louco como eu - e seu scrollview é na parte inferior por padrão - o que irá rolar para o topo:[scrollView setContentOffset:CGPointMake(0, scrollView.contentSize.height-scrollView.frame.size.height) animated:YES];
GilesDMiddleton
Comentário tardio: você deve ajustar o conteúdo inserido independentemente da versão do sistema operacional. O iOS 7 o usa para permitir o conteúdo de rolagem abaixo da barra de status, mas ele sempre foi exposto como uma propriedade, portanto sempre foi potencialmente usado por alguém.
Tommy
2
Para o iOS 11 ou superior, a resposta de Jakub Truhlář abaixo, com o ajustadoContentInset, foi útil, pelo menos no meu caso.
tnev
61

Aqui está uma extensão Swift que facilita:

extension UIScrollView {
    func scrollToTop() {
        let desiredOffset = CGPoint(x: 0, y: -contentInset.top)
        setContentOffset(desiredOffset, animated: true)
   }
}

Uso:

myScrollView.scrollToTop()
Andrew Schreiber
fonte
40

iOS 11 e superior

tente trabalhar com o novo adjustedContentInset.

Por exemplo (role para o topo):

var offset = CGPoint(
    x: -scrollView.contentInset.left, 
    y: -scrollView.contentInset.top)

if #available(iOS 11.0, *) {
    offset = CGPoint(
        x: -scrollView.adjustedContentInset.left, 
        y: -scrollView.adjustedContentInset.top)    
}

scrollView.setContentOffset(offset, animated: true)

Mesmo se você usar prefersLargeTitles, safe areaetc.

Jakub Truhlář
fonte
40

Para Swift 4

scrollView.setContentOffset(.zero, animated: true)
Rajasekhar Pasupuleti
fonte
23

Usar setContentOffset:animated:

[scrollView setContentOffset:CGPointZero animated:YES];
Bryan Chen
fonte
17

Resposta para Swift 2.0 / 3.0 / 4.0 e iOS 7 ou superior:

let desiredOffset = CGPoint(x: 0, y: -self.scrollView.contentInset.top)
self.scrollView.setContentOffset(desiredOffset, animated: true)
worthbak
fonte
8

No iOS7, tive problemas para obter uma visualização de rolagem específica para ir ao topo, que funcionava no iOS6, e usei isso para definir a visualização de rolagem para o topo.

[self.myScroller scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
buzinar
fonte
Eu tenho o mesmo problema no iOS7. Alguém sabe como habilitar isso para um UITableView?
DZenBot
1
É engraçado como isso funciona quando animado é NÃO, mas não quando animado é SIM. Se sim, então ele rola quase ao topdevgm16
Matt Becker
4

Versão Swift 3.0.1 da resposta de rob mayoff :

self.scrollView.setContentOffset(
CGPoint(x: 0,y: -self.scrollView.contentInset.top),
animated: true)
Roni Leshes
fonte
2

Para replicar completamente o comportamento scrollToTop da barra de status, não apenas precisamos definir o contentOffset, mas também queremos garantir que os scrollIndicators sejam exibidos. Caso contrário, o usuário poderá se perder rapidamente.

O único método público para fazer isso é flashScrollIndicators. Infelizmente, chamá-lo uma vez após definir o contentOffset não tem efeito porque é redefinido imediatamente. Descobri que funciona sempre que o flash é disparado scrollViewDidScroll:.

// define arbitrary tag number in a global constants or in the .pch file
#define SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG 19291

- (void)scrollContentToTop {
    [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top) animated:YES];

    self.scrollView.tag = SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.scrollView.tag = 0;
    });
}

No seu UIScrollViewDelegate (ou UITable / UICollectionViewDelegate) implemente isto:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.tag == SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG) {
        [scrollView flashScrollIndicators];
    }
}

O atraso da ocultação é um pouco menor em comparação com o comportamento scrollToTop da barra de status, mas ainda parece bom.

Observe que estou abusando da tag view para comunicar o estado "isScrollingToTop" porque preciso disso nos controladores de exibição. Se você estiver usando tags para outra coisa, poderá substituí-lo por um iVar ou uma propriedade.

Ortwin Gentz
fonte
Eu não testei, mas ele deve ser lançado no próximo ciclo de execução se você apenas usar DISPATCH_TIME_NOW. Obrigado por fazer este trabalho, ainda não tenho certeza de que preciso, mas é bom saber.
Dan Rosenstark
Faz um tempo e não me lembro exatamente, mas acredito que isso não funcionou. Adiar para o próximo runloop é geralmente a primeira coisa que tento nesses casos.
Ortwin Gentz
2

Acho que tenho uma resposta que deve ser totalmente compatível com o iOS 11 e com versões anteriores (para rolagem vertical)

Isso leva em consideração o novo ajustadoContentInset e também considera o deslocamento adicional necessário quando prefereLargeTitles está ativado na navegação, que parece exigir um deslocamento adicional de 52px sobre o valor padrão.

Isso foi um pouco complicado, pois o ajustadoContentInset muda dependendo do estado titleBar (título grande vs título pequeno), então eu precisei verificar e ver qual era a altura do titleBar e não aplicar o deslocamento de 52px se ele já estiver no estado grande. Não foi possível encontrar outro método para verificar o estado da barra de navegação. Se alguém tiver uma opção melhor do que ver se a altura é> 44,0, eu gostaria de ouvi-la

func scrollToTop(_ scrollView: UIScrollView, animated: Bool = true) {
    if #available(iOS 11.0, *) {
        let expandedBar = (navigationController?.navigationBar.frame.height ?? 64.0 > 44.0)
        let largeTitles = (navigationController?.navigationBar.prefersLargeTitles) ?? false
        let offset: CGFloat = (largeTitles && !expandedBar) ? 52: 0
        scrollView.setContentOffset(CGPoint(x: 0, y: -(scrollView.adjustedContentInset.top + offset)), animated: animated)
    } else {
        scrollView.setContentOffset(CGPoint(x: 0, y: -scrollView.contentInset.top), animated: animated)
    }
}

Inspirado na solução de Jakub

user2898617
fonte
2

É muito comum quando a barra de navegação se sobrepõe à pequena parte do conteúdo do scrollView e parece que o conteúdo não começa da parte superior. Para corrigi-lo, fiz duas coisas:

  • Inspetor de tamanho - Exibição de rolagem - Inserções de conteúdo -> Alterar de automático para nunca.
  • Inspetor de tamanho - restrições - "Alinhar parte superior a" (restrições de alinhamento superior) - Segundo item -> Alterar de Superview.Top para Safe Area.Top e o valor (campo constante) definido como 0

Inserções de conteúdo - nunca Alinhar ScrolView.Top a Safe Area.Top

Vitya Shurapov
fonte
1

Vá até o topo para UITableViewController, UICollectionViewControllerou qualquer UIViewControllerterUIScrollView

extension UIViewController {

  func scrollToTop(animated: Bool) {
    if let tv = self as? UITableViewController {
        tv.tableView.setContentOffset(CGPoint.zero, animated: animated)
    } else if let cv = self as? UICollectionViewController{
        cv.collectionView?.setContentOffset(CGPoint.zero, animated: animated)
    } else {
        for v in view.subviews {
            if let sv = v as? UIScrollView {
                sv.setContentOffset(CGPoint.zero, animated: animated)
            }
        }
    }
  }
}
Shoaib
fonte
0

No SWIFT 5, defina o deslocamento do conteúdo como zero.

scrollView.setContentOffset(CGPoint.zero, animated: true)
Abdul Karim Khan
fonte
-4

Eu tentei de todas as maneiras. Mas nada funcionou para mim. Finalmente eu fiz assim.

Eu adicionei self.view .addSubview(self.scroll)linha de código no viewDidLoad. Após iniciar a configuração do quadro para a visualização de rolagem e adicionar componentes à visualização de rolagem.

Funcionou para mim.

Certifique-se de adicionar uma self.view .addSubview(self.scroll)linha no começo. então você pode adicionar elementos da interface do usuário.

Narasimha Nallamsetty
fonte
Por favor, apague isso. Embora certamente fizesse sentido no momento, seus problemas de hierarquia de subvisões não são relevantes para esta questão.
Dan Rosenstark
1
@Cristik se a visualização de rolagem não estiver na hierarquia da visualização ou o computador estiver desligado, é claro que nada disso funcionará. Mas o OP pediu algo muito estreito
Dan Rosenstark