iPhone: ocultar a barra de pesquisa UITableView por padrão

85

Usei o Interface Builder para criar uma visualização de tabela, à qual adicionei a Barra de Pesquisa e o Controlador de Exibição de Pesquisa da biblioteca para adicionar a funcionalidade de pesquisa. No entanto, o IB o configurou de forma que a barra fique visível na parte superior da tela quando a visualização for exibida pela primeira vez.

Gostaria de saber como fazer com que a barra de pesquisa fique oculta por padrão, mas ainda seja rolável com a visualização da tabela (consulte o aplicativo Mail da Apple para obter um exemplo). Eu tentei chamar scrollRectToVisible:animated:em viewDidLoadpara rolar a exibição de tabela para baixo, mas sem sucesso. Qual é a maneira preferida de ocultar a barra de pesquisa por padrão?

Tim
fonte

Respostas:

116

Primeiro certifique-se de adicionar o UISearchBar ao tableHeaderView do UITableView para que seja rolado com o conteúdo da tabela e não seja fixado no topo da visualização.

A barra de pesquisa não é contada como uma linha na tableview, portanto, se você rolar a parte superior da tableview até a primeira linha, ela 'oculta' a barra de pesquisa:

[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];

ou em Swift:

yourTableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)

Certifique-se de não rolar a tableview antes que ela contenha dados ( scrollToRowAtIndexPathgerará uma exceção se o indexPath fornecido não apontar para uma linha válida (ou seja, se a tableview estiver vazia)).

Zargony
fonte
12
Na verdade, estou tendo um problema em que a visualização da tabela não rola se não houver linhas suficientes para preencher a tela - em uma tabela com uma ou duas linhas, este código não faz nada. Esse é o comportamento esperado? Posso contornar isso de alguma forma?
Tim de
18
Eu encontrei uma maneira: em vez de rolar para um caminho de índice, altere o deslocamento do conteúdo da visualização de rolagem da tabela para um CGRect em 0,0, 44,0
Tim
107
Tim, esse é o caminho a percorrer! Usei self.tableView.contentOffset = CGPointMake (0, self.searchDisplayController.searchBar.frame.size.height);
Joe D'Andrea
1
A propósito - quando eu estava inserindo este código após uma chamada para [tableView reloadData] que fez minha tabela ir de 2 linhas para linhas suficientes para permitir a rolagem normal, descobri que usar o método contentOffset causou uma exibição e ocultação da pesquisa. ao usar o método scrollToRowAtIndexPath não teve a mesma irregularidade.
Chris R
1
Esta é a única forma definitiva que funciona em QUALQUER circunstância. A configuração contentOffsetcom qualquer valor será interrompida se você estiver empregando um layout de controlador de visualização complexo. Eu não recomendo.
eonil
45

Não adicione o UISearchBar como uma subvisão do UITableView, isso não é necessário.

O UITableView tem uma propriedade tableHeaderView perfeita para isso:

- (void) viewDidLoad {
    [super viewDidLoad]; 
    self.searchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
    self.searchBar.showsCancelButton = YES;    
    self.searchBar.delegate = self;
    self.tableView.tableHeaderView = self.searchBar;     
}

Se você não quiser ver por padrão:

- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.tableView setContentOffset:CGPointMake(0, 44)];
}

Também recebo o botão Cancelar para ocultá-lo novamente .... (e remover o teclado)

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [self.tableView setContentOffset:CGPointMake(0, 44) animated:YES];
    [self.searchBar resignFirstResponder];
}
bandejapaisa
fonte
Para mim, essa foi a única solução que produziu uma implementação UISearchBar oculta que parecia estar alinhada com o HIG. As outras soluções em que ele está "preso" no topo da visualização da mesa parecem não naturais.
kurtzmarc
1
Esta solução funcionou melhor. Esta deve ser a resposta em minha opinião.
darwindeeds
Esta solução é boa (+1). No entanto, tableView não oculta a barra de pesquisa corretamente em alguns casos (no meu caso: quando o controlador de tableview é empurrado de outro controlador de visualização). Há uma pergunta sobre este problema: stackoverflow.com/questions/15222186/… Eu acho que a resposta fornecida a esta pergunta não é muito boa. @bandejapaisa, você tem alguma ideia?
Brian de
3
Isso funcionou bem para mim, embora em vez de codificar permanentemente 44 em toda parte, eu sugira usar self.tableView.tableHeaderView.frame.size.height em viewWillAppear para permitir alterações que a Apple possa fazer no futuro na altura da barra de pesquisa.
John Stephen
colocar isso viewWillAppearé uma má ideia, porque ele será executado toda vez que você navegar de volta para sua visualização. Isso significa que seu tableview irá diminuir lentamente. Bug irritante!
Sirenes de
25

O contentOffset foi observado nos comentários de Tim e Joe D'Andrea, mas é um pouco expandido com a adição de uma animação para ocultar a barra de pesquisa. Percebi que é um pouco inesperado para a barra de pesquisa simplesmente desaparecer.

Uma pequena atualização para aqueles que desejam direcionar o iOS 4.0 e superior, tente isto:

[UIView animateWithDuration:0.4 animations:^{
    self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);
 } completion:nil];

Resposta anterior:

[UIView beginAnimations:@"hidesearchbar" context:nil];
[UIView setAnimationDuration:0.4];
[UIView setAnimationBeginsFromCurrentState:YES];

self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);

[UIView commitAnimations];

Andrei
fonte
9
Descobri que a animação é a chave para essa solução. Tentar definir o deslocamento de conteúdo sem a animação parecerá não fazer nada. Em vez de usar os métodos de animação (ou blocos) neste exemplo, no entanto, acho que simplesmente chamar [self.tableView setContentOffset: CGPointMake (0,44) animated: TRUE] funciona bem o suficiente.
quickthyme
Parece não funcionar sem a animação. @quickthyme fez a mesma observação.
Thilo
5
Tive sucesso com isso sem precisar das animações se você implementá-las em em viewDidAppear:vez de viewDidLoad.
wbyoung
8

Existem muitas respostas para esta solução, porém nenhuma funcionou muito bem para mim.

Isso funciona perfeitamente. Sem animação e é feito em-viewDidLoad

 - (void)viewDidLoad {
     [super viewDidLoad];
     self.tableView.contentOffset = CGPointMake(0,  self.searchBar.frame.size.height - self.tableView.contentOffset.y);
 }

Nota:

O código presume que você tenha o searchBar @propertyque está vinculado à barra de pesquisa. Se não, você pode usar self.searchDisplayController.searchBarem vez

Matej
fonte
este é o único que funcionou para mim! , mas ele apenas descompacta o viewdidload ... então a primeira vez é escondido, mas depois não é mais escondido enquanto o aplicativo está rodando. E eu tentei colocá-lo em viewwillappear e viewdidappear, mas funciona errado. Obrigado!
fguespe
@fguespe isso é estranho .. O viewDidLoad deve ser chamado toda vez que a visão é instanciada. Nunca me aconteceu que o UISearchBar ficou visível depois de voltar de outra vista.
Matej
estou usando um aplicativo com guias. Talvez aquilo?
fguespe
@fguespe Vejo que esse pode ser o problema, hmm. Infelizmente, não tenho certeza de como você faria para resolver isso se -viewWillAppearnão funcionar. Ele é chamado quando o controlador de guia muda para a visualização?
Matej
viewwillappear e viewdidappear são chamados, mas eu tentei com ambos e o modo de exibição é como um deslocamento incorreto. Não retorna o resultado esperado. Quer dizer, algo faz, mas faz mal.
fguespe
7

Para Swift 3+

Eu fiz isso:

Declare isto var:

    var searchController = UISearchController()

E no viewDidLoad()método

    searchController = UISearchController(searchResultsController: nil)
    searchController.searchResultsUpdater = self
    searchController.hidesNavigationBarDuringPresentation = false
    searchController.dimsBackgroundDuringPresentation = true
    searchController.searchBar.placeholder = NSLocalizedString("Search", comment: "")
    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar

    tableView.contentOffset = CGPoint(x: 0, y: searchController.searchBar.frame.size.height)
Dasoga
fonte
5

Meu objetivo era ocultar a barra de pesquisa adicionada a tableHeaderView do estilo de TableView simples no storyboard quando o controlador de visualização é carregado e mostrar a barra quando o usuário rola para baixo na parte superior de UITableView. Então, estou usando o Swift e adicionou uma extensão:

extension UITableView {
    func hideSearchBar() {
        if let bar = self.tableHeaderView as? UISearchBar {
            let height = CGRectGetHeight(bar.frame)
            let offset = self.contentOffset.y
            if offset < height {
                self.contentOffset = CGPointMake(0, height)
            }
        }
    }
}

em viewDidLoad () basta chamar:

self.tableView.hideSearchBar()
HotJard
fonte
5

Todas as soluções existentes não funcionam para mim no iOS 8 quando não há linhas suficientes para preencher o tableView, já que o iOS ajustará o inset automaticamente nesta situação. (No entanto, as respostas existentes são boas quando há linhas suficientes)

Depois de perder 6 horas com esse problema, finalmente consegui essa solução.

Resumindo, você precisa inserir células vazias em tableView se não houver células suficientes, então o tamanho do conteúdo de tableView é grande o suficiente para que o iOS não ajuste o inset para você.

Aqui está como eu fiz no Swift:

1.) declarar uma variável minimumCellNumcomo uma propriedade de classe

var minimumCellNum: Int?

2.) calcular minimumCellNume definir tableView.contentOffsetemviewWillAppear

let screenHeight = Int(UIScreen.mainScreen().bounds.height)

// 101 = Height of Status Bar(20) + Height of Navigation Bar(44) + Height of Tab Bar(49)
// you may need to subtract the height of other custom views from the screenHeight. For example, the height of your section headers.
self.minimumCellNum = (screenHeight - 103 - heightOfOtherCustomView) / heightOfYourCell
self.tableView.contentOffset = CGPointMake(0, 44)

3.) em tableView(tableView: UITableView, numberOfRowsInSection section: Int))

let numOfYourRows = YOUR LOGIC
if numOfYourRows > minimumCellNum {
    return numOfYourRows
} else {
    return minimumCellNum!
}

4.) Registre uma célula vazia, cujo selectionatributo seja None, no storyboard e emtableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)

if indexPath.row < numOfYourRows {
    return YOUR CUSTOM CELL
} else {
    let cell = tableView.dequeueReusableCellWithIdentifier("EmptyCell", forIndexPath: indexPath) as! UITableViewCell
    return cell
}

5.) em tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

if tableView == self.tableView {
    if numOfYourRows < (indexPath.row + 1) {
        return
    }
    YOUR LOGIC OF SELECTING A CELL
}

Esta não é uma solução perfeita, mas é a única solução alternativa que realmente funciona para mim no iOS 8. Gostaria de saber se existe uma solução mais limpa.

Brian
fonte
Eu tive que fazer isso para iOS 11. Definir contentOffset sozinho funcionou para iOS 9/10 para 0 ou mais células, e também funcionou no iOS 11 para 8+ células. Qualquer coisa menos exigia essa solução. Existe uma razão para isso?
Tony
Acho que o primeiro parágrafo deste post explica o porquê.
Brian
Eu entendi o primeiro parágrafo, mas ainda era estranho que ele só estivesse "quebrado" no iOS 11 para mim. Eu esperava um comportamento semelhante para iOS 9, 10 e 11 quando o número de células é o mesmo (menos do que o suficiente). Obrigado novamente e desculpe por trazer um post antigo!
Tony
4

Nenhuma das opções acima funcionou para mim, apenas no caso de alguém ter o mesmo problema.

Eu searchBarconfigurei como tableHeaderViewem uma tabela agrupada no iOS7. Em viewDidLoadque tenho tableView.contentOffset = CGPointMake(0, 44);de manter searchBarinicialmente "escondido". Quando o usuário rola para baixo, searchBaré exibido

Objetivo: ocultar a barra de pesquisa ao tocar no botão Cancelar

Dentro searchBarCancelButtonClicked(searchBar: UISearchBar!)

Isso não funcionou: [yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; tableView estava rolando muito para cima, resultando na linha 0 ficando atrás da barra de ferramentas.

Isso não funcionou: tableView.ContentOffset = CGPointMake(0, 44) - nada acontece

Isso não funcionou: tableView.ContentOffset = CGPointMake(0, searchBar.frame.size.height) - nada acontece

Isso não funcionou: tableView.setContentOffset(CGPointMake(0, 44), animated: true); novamente, tableView estava rolando muito para cima, resultando na linha 0 ficando atrás da barra de ferramentas.

Isso funcionou parcialmente: tableView.setContentOffset(CGPointMake(0, 0) mas titleForHeaderInSectionestava indo para trás da barra de ferramentas

ISSO FUNCIONOU: tableView.setContentOffset(CGPointMake(0, -20)

Não parece lógico, mas apenas -20 o moveu para a posição correta

grep
fonte
Você realmente não deve codificar seus pontos, por que não usa CGRectGetHeight(searchBar.bounds)?
Zorayr,
Eu acho que você deve estar fazendo isso em viewDidLoad () / init (), onde nenhum quadro foi definido para você obter a altura da barra de pesquisa. Talvez você deva tentar com viewDidLayoutSubviews ()
Kaushil Ruparelia
Se o tableView não terminou de carregar, nenhuma das ContentOffsetsoluções funcionará
Maor
4

O deslocamento de conteúdo é o caminho a percorrer, mas não funciona 100% do tempo.

Não funciona se estiver configurando estimatedRowHeightpara um número fixo. Eu não sei como funciona ainda. Eu criei um projeto mostrando o (s) problema (s) e criei um radar com a Apple.

Confira o projeto se quiser um exemplo rápido de como configurar tudo.

RyanJM
fonte
1
Obrigado! Era isso que eu procurava. Você precisa subir.
user2387149
3

Observe que a resposta aceita travará se não houver dados no tableview. E adicioná-lo ao viewDidLoad pode não funcionar em certas circunstâncias.

[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; // crashes if no rows/data

Portanto, em vez disso, adicione esta linha de código:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [yourTableView setContentOffset:CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height)];
}
user3246173
fonte
2

Honestamente, nenhuma das respostas realmente resolveu o problema (para mim). Se a visualização da tabela não tiver linhas OU a altura das linhas for diferente, essas respostas não serão suficientes.

A única coisa que funciona:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.tableView.contentOffset = (CGPoint){.y =  self.tableView.contentOffset.y + self.searchController.searchBar.frame.size.height};
}
p0lAris
fonte
Boa solução para o problema de deslocamento. Funciona para mim ao usar um UINavigationController.
Andreas Kraft
1
A solução não funciona quando o número de linhas não cobre a tela inteira - você saberia uma solução alternativa para isso?
Zorayr
Não tenho certeza se entendi isso completamente. Isso funciona para mim quando tenho apenas algumas linhas na tabela. No entanto eu uso o seguinte: self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];.
p0lAris
2

O scrollToRowAtIndexPathmétodo causa um travamento se não houver nenhuma linha na tabela.

UITableView é apenas uma visualização de rolagem, portanto, você pode simplesmente alterar o deslocamento do conteúdo.

Isso verifica se você tem algo como seu tableHeaderView e, supondo que você role para ocultá-lo

    if let searchHeight = tableView.tableHeaderView?.frame.height {
        tableView.setContentOffset(CGPoint.init(x: 0, y:searchHeight ), animated: false)
    }
Vorlon confuso
fonte
1

Acho que o aplicativo "Notes" não consegue pesquisar itens quando tableView está em branco. Então eu acho que é só usar-(void)addSubview:(UIView *)view para adicionar uma tabela em branco no início. Quando você adiciona um item à sua matriz de fonte de dados, ele executa outro trecho de código para carregar o tableView.

Então, minha solução é:

if([self.arrayList count] > 0)
{
     [self.tableView reloadData];     //use jdandrea's code to scroll view when cell==nil in cellForRowAtIndexPath
     self.tableView.scrollEnabled = YES;
}
else
{
     tableBlank = [[UITableView alloc]initWithFrame:CGRectMake(0,0,320,416) style:UITableViewStylePlain] autorelease];
     tableBlank.delegate = self;
     tableBlank.dataSource = self;
     [self.view addSubview:tableBlank];
}

Espero que isto ajude :-)

Chilly Zhong
fonte
1

Altere os limites do tableView, isso pode ser feito internamente viewDidLoade você não precisa se preocupar se a tabela está vazia ou não (para evitar a exceção).

CGRect newBounds = self.tableView.bounds;
newBounds.origin.y = newBounds.origin.y + self.searchBar.bounds.size.height;
self.tableView.bounds = newBounds;

Assim que o usuário rolar a tabela para baixo, a barra de pesquisa aparecerá e se comportará normalmente

Homem leve
fonte
1

As outras respostas a essa pergunta não funcionaram para mim, tiveram um comportamento estranho quando tableView tinha 0 linhas ou bagunçaram a posição de rolagem ao ir e voltar entre os itens de linha pai e aninhados. Isso funcionou para mim (o objetivo era ocultar o UISearchBar durante o carregamento):

public override func viewWillAppear(animated: Bool) {        
    if self.tableView.contentOffset.y == 0 {
        self.tableView.contentOffset = CGPoint(x: 0.0, y: self.searchBar.frame.size.height) 
    }
}

Explicação
Sempre tableViewque aparecerá, este código verifica se o UISearchBar está visível (ou seja, a yposição do contentOffset, também conhecido como posição de rolagem, é 0). Se estiver, ele simplesmente rola para baixo na altura da barra de pesquisa. Se a barra de pesquisa não estiver visível (pense em uma lista maior de itens tableView), ela não tentará rolar o tableView, pois isso bagunçaria o posicionamento quando você retornar de um item de linha aninhado.

Observação lateral
Eu recomendo colocar este código em um método separado para garantir a responsabilidade única e dar a você a capacidade de reutilização para chamá-lo a qualquer momento em seu código.

kbpontius
fonte
1

Se a sua tabela sempre terá pelo menos uma linha, basta rolar até a primeira linha da tabela e a barra de pesquisa será ocultada automaticamente.

let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)

self.tableView.selectRowAtIndexPath (firstIndexPath, animated: false, scrollPosition: .Top)

Se você colocar o código acima em viewDidLoad , ele gerará um erro porque a tableView ainda não foi carregada, então você deve colocá-la em viewDidAppear porque a esta altura a tableView já foi carregada.

Se você colocá-lo em viewDidAppear , toda vez que você abrir o tableView, ele rolará para o topo.

Talvez você não queira este comportamento se o tableView permanecer aberto, como quando é um UITabBar View Controller ou quando você faz uma segue e depois volta. Se você deseja apenas rolar para o topo da carga inicial, pode criar uma variável para verificar se é uma carga inicial, de forma que role para o topo apenas uma vez.

Defina primeiro uma variável chamada isInitialLoad na classe do controlador de visualização e defina-a igual a "true":

var isInitialLoad = true

Em seguida, verifique se isInitialLoad é verdadeiro em viewDidAppear e, se for verdadeiro, role para o topo e defina a variável isInitialLoad como falsa:

if isInitialLoad {
            let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)
            self.tableView.selectRowAtIndexPath(firstIndexPath, animated: false, scrollPosition: .Top)

            isInitialLoad = false
        }
drdrdrdr
fonte
1

O código abaixo está funcionando para mim

self.tableView.contentOffset = CGPointMake(0.0, 44.0);
Ashu
fonte
1

Tentei definir o deslocamento de conteúdo e scrollToIndexpath, mas nada funcionou de forma satisfatória. Removi a configuração de tableHeaderView como searchBar de viewDidLoad e a defini no delegado scrollview como abaixo

override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    if scrollView.contentOffset.y < 0 && tableView.tableHeaderView == nil {
        tableView.tableHeaderView = searchController.searchBar
        tableView.setContentOffset(CGPointMake(0, scrollView.contentOffset.y + searchController.searchBar.frame.size.height), animated: false)
    }
}

Isso funcionou perfeitamente. A configuração de deslocamento de conteúdo é para propósitos de rolagem suave

Abdur Rahman
fonte
0

Não se esqueça de levar a barra de status e a barra de navegação em consideração. Meu controlador de visualização de tabela está embutido em um controlador de navegação, em viewDidAppear:

tableView.contentOffset = CGPointMake(0, -20) // -20 = 44 (search bar height) - 20 (status bar height) - 44 (navigation bar height)

trabalhou para mim.

fujianjin6471
fonte
0

Para Swift:

Basta fazer com que o conteúdo do tableview seja deslocado, que deve ser como a altura da barra de pesquisa.

self.tableView.contentOffset = CGPointMake(0, self.searchController.searchBar.frame.size.height)
Stakahop
fonte
0

Swift 3

funciona com a mesa vazia também!

No meu ambiente, eu carrego células personalizadas por arquivo xib e o rowHeight é definido automaticamente.

    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        self.tableView.contentOffset = CGPoint(x: 0, y: self.tableView.contentOffset.y + self.searchController.searchBar.bounds.height)
}
John
fonte
0

Eu tive um problema semelhante. E a única maneira que encontrei de resolver esse problema foi usar o observador de valor-chave na propriedade UITableView contentOffset.

Depois de alguma depuração, percebi que o problema aparece apenas se o tamanho do conteúdo do table view for menor do que o tableview. Eu inicio KVO quando em viewWillAppear. E despache a parada KVO 0,3 segundos após o aparecimento da vista. Cada vez que contentOffset torna-se 0,0, o KVO reage e define contentOffset na altura da barra de pesquisa. Eu paro o KVO assíncrono após 0,3 segundos porque o layout de exibição irá redefinir o contentOffset mesmo após a exibição de exibição.

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}

-(void)viewDidAppear:(BOOL)animated{
   __weak typeof (self) weakSelf = self;
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [weakSelf.tableView removeObserver:self forKeyPath:@"contentOffset"];});
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentOffset"] && self.tableView.contentOffset.y == 0.0) {
       self.tableView.contentOffset = CGPointMake(0, CGRectGetHeight(self.tableView.tableHeaderView.frame));
    }
}

-(void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
Slobodans
fonte