web-dev-qa-db-fra.com

iPhone: masquer la barre de recherche UITableView par défaut

J'ai utilisé Interface Builder pour créer une vue tabulaire, à laquelle j'ai ajouté la barre de recherche et le contrôleur d'affichage de la bibliothèque pour ajouter une fonctionnalité de recherche. Cependant, IB l'a configurée pour que la barre soit visible en haut de l'écran lorsque la vue est affichée pour la première fois.

J'aimerais savoir comment faire en sorte que la barre de recherche soit masquée par défaut, mais qu'elle puisse toujours défiler dans la vue sous forme de tableau (voir l'application Mail d'Apple pour un exemple). J'ai essayé d'appeler scrollRectToVisible:animated: dans viewDidLoad pour faire défiler la vue du tableau vers le bas, mais en vain. Quel est le moyen préféré pour masquer la barre de recherche par défaut?

85
Tim

Tout d’abord, assurez-vous d’ajouter UISearchBar à la tableHeaderView de la UITableView afin qu’elle défile avec le contenu de la table et ne soit pas fixée en haut de la vue.

La barre de recherche n'est pas comptée comme une ligne dans la table, donc si vous faites défiler le haut de la table jusqu'à la première ligne, elle "masque" la barre de recherche:

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

ou à Swift:

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

Veillez à ne pas faire défiler la table avant qu'elle ne contienne des données (scrollToRowAtIndexPath lève une exception si le chemin indexPath indiqué ne pointe pas sur une ligne valide (c'est-à-dire si la table est vide)).

115
Zargony

N'ajoutez pas UISearchBar en tant que sous-vue de UITableView, ce n'est pas nécessaire.

UITableView a une propriété tableHeaderView qui est parfaite pour cela:

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

Si vous ne voulez pas le voir par défaut:

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

Je reçois aussi le bouton d'annulation pour le cacher à nouveau .... (et retirer le clavier)

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [self.tableView setContentOffset:CGPointMake(0, 44) animated:YES];
    [self.searchBar resignFirstResponder];
}
45
bandejapaisa

Le contentOffset a été noté dans les commentaires de Tim et Joe D'Andrea, mais ceci est un peu développé en ajoutant une animation pour masquer la barre de recherche. J'ai remarqué qu'il est un peu inattendu que la barre de recherche disparaisse tout simplement.

Une petite mise à jour pour ceux qui veulent cibler iOS 4.0 et les versions ultérieures, essayez ceci:

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

Réponse précédente:

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

25
Andrei

Il y a beaucoup de réponses à cette solution, mais aucune n'a très bien fonctionné pour moi.

Cela fonctionne parfaitement. Aucune animation, et est faite sur -viewDidLoad

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

Remarque:

Le code suppose que vous avez la searchBar@property qui est liée à la barre de recherche . Sinon, vous pouvez utiliser self.searchDisplayController.searchBar

7
code ninja

Pour Swift 3+

J'ai fait ça:

Déclarez cette var:

    var searchController = UISearchController()

Et dans la méthode viewDidLoad()

    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)
6
Dasoga

Mon objectif était de masquer la barre de recherche ajoutée à tableHeaderView du style brut de TableView dans le storyboard lorsque le contrôleur de vue est chargé et d'afficher la barre lorsque l'utilisateur faisait défiler vers le haut UITableView. Donc, j'utilise Swift et a ajouté l'extension:

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)
            }
        }
    }
}

dans viewDidLoad () appelez simplement:

self.tableView.hideSearchBar()
5
HotJard

Le contenu doit être compensé, mais cela ne fonctionne pas dans 100% des cas.

Cela ne fonctionne pas si vous définissez estimatedRowHeight sur un nombre fixe. Je ne connais pas encore le travail. J'ai créé un projet indiquant le (s) problème (s) et un radar avec Apple.

Consultez le projet si vous voulez un exemple dans Swift sur la manière de tout configurer.

4
RyanJM

Toutes les solutions existantes ne fonctionnent pas pour moi sur iOS 8 lorsqu'il n'y a pas assez de lignes pour remplir la tableView, car iOS ajustera l'encart automatiquement dans cette situation. (Les réponses existantes sont bonnes quand il y a suffisamment de lignes)

Après avoir perdu environ 6 heures sur cette question, j'ai finalement eu cette solution.

En bref, vous devez insérer des cellules vides dans la tableView s'il n'y a pas assez de cellules. La taille du contenu de la tableView est donc suffisamment grande pour qu'iOS ne modifie pas l'encart à votre place.

Voici comment je l'ai fait à Swift:

1.) déclarer une variable minimumCellNum en tant que propriété de classe

var minimumCellNum: Int?

2.) calculer minimumCellNum et définir tableView.contentOffset dans viewWillAppear

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.) en tableView(tableView: UITableView, numberOfRowsInSection section: Int))

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

4.) Enregistrez une cellule vide, dont l'attribut selection est None, sur le storyboard et dans tableView(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.) en tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

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

Ce n'est pas une solution parfaite, mais c'est la seule solution de contournement qui fonctionne vraiment pour moi sur iOS 8. J'aimerais savoir s'il existe une solution plus rationnelle.

4
Brian

Aucun de ce qui précède n'a fonctionné pour moi, donc juste au cas où quelqu'un aurait le même problème.

J'ai searchBar défini comme tableHeaderView sur une table groupée sur iOS7. Dans viewDidLoad j'ai tableView.contentOffset = CGPointMake(0, 44); pour garder searchBar initialement "masqué". Lorsque l'utilisateur fait défiler searchBar est affiché

Objectif: masquer la barre de recherche du bouton d'annulation, appuyez sur

Dans searchBarCancelButtonClicked(searchBar: UISearchBar!)

Cela n'a pas fonctionné: [yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; tableView faisait trop défiler vers le haut, entraînant la ligne 0 derrière la barre d'outils.

Cela n'a pas fonctionné: tableView.ContentOffset = CGPointMake(0, 44) - rien ne se passe

Cela n'a pas fonctionné: tableView.ContentOffset = CGPointMake(0, searchBar.frame.size.height) - rien ne se passe

Cela n'a pas fonctionné: tableView.setContentOffset(CGPointMake(0, 44), animated: true); Encore une fois, tableView défilait trop, ce qui a pour conséquence que la ligne 0 est placée derrière la barre d'outils.

Cela a fonctionné partiellement: tableView.setContentOffset(CGPointMake(0, 0) mais titleForHeaderInSection allait derrière toolBar 

CECI A TRAVAILLÉ: tableView.setContentOffset(CGPointMake(0, -20)

Ne semble pas logique mais seulement -20 l'a déplacé vers le haut pour corriger sa position

4
grep

Notez que la réponse acceptée plantera s'il n'y a pas de données dans la table. Et l'ajouter à viewDidLoad peut ne pas fonctionner dans certaines circonstances.

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

Par conséquent, ajoutez plutôt cette ligne de code:

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

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

Honnêtement, aucune des réponses n'a vraiment résolu le problème (pour moi). Si votre affichage sous forme de tableau ne contient pas de lignes OR, la hauteur des lignes est différente, ces réponses ne suffisent pas.

La seule chose qui fonctionne:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.tableView.contentOffset = (CGPoint){.y =  self.tableView.contentOffset.y + self.searchController.searchBar.frame.size.height};
}
2
p0lAris

Les autres réponses à cette question ne fonctionnaient pas du tout pour moi, avaient un comportement étrange lorsque la table affichait 0 ligne, ou altéraient la position de défilement lors du va-et-vient entre les éléments de campagne parent et imbriqué. Cela a fonctionné pour moi (l'objectif étant de masquer la barre UISearchBar lors du chargement):

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) 
    }
}

Explication
À chaque fois que la tableView apparaîtra, ce code vérifie si le UISearchBar est visible (ce qui signifie que la position y de la contentOffset, ou position de défilement, est 0). Si tel est le cas, il fait simplement défiler la hauteur de la barre de recherche. Si la barre de recherche n'est pas visible (imaginez une liste plus longue d'éléments dans tableView), le système ne tentera pas de faire défiler la tableView, car cela gênerait le positionnement lorsque vous revenez d'un élément de ligne imbriqué.

Note latérale
Je recommanderais de mettre ce code dans une méthode distincte pour garantir une responsabilité unique et vous permettre de le réutiliser à tout moment dans votre code.

1
kbpontius

Le code ci-dessous fonctionne pour moi

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

Si votre table aura toujours au moins une ligne, faites défiler jusqu'à la première ligne du tableau et la barre de recherche sera automatiquement masquée.

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

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

Si vous placez le code ci-dessus sur viewDidLoad , une erreur se produira, car la tableView n'est pas encore chargée. Vous devez donc le mettre dans viewDidAppear car, à ce stade, la tableView est déjà chargée. 

Si vous le mettez sur viewDidAppear , chaque fois que vous ouvrez le tableau, il défilera vers le haut. 

Peut-être que vous ne souhaitez pas ce comportement si la table reste ouverte, comme s'il s'agissait d'un contrôleur de vue UITabBar ou lorsque vous effectuez une séquence puis revenez. Si vous souhaitez simplement qu’il défile vers le haut lors du chargement initial, vous pouvez créer une variable pour vérifier s’il s’agit d’un chargement initial, de sorte qu’il ne défile qu’une seule fois vers le haut. 

Définissez d'abord une variable appelée isInitialLoad dans la classe du contrôleur de vue et définissez-la égale à "true":

var isInitialLoad = true

Puis vérifiez si isInitialLoad est vrai sur viewDidAppear et si c'est vrai, faites défiler vers le haut et définissez la variable isInitialLoad sur false:

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

            isInitialLoad = false
        }
1
drdrdrdr

La méthode scrollToRowAtIndexPath provoque un blocage s'il n'y a aucune ligne dans la table.

UITableView est juste un affichage par défilement, vous pouvez donc simplement changer le décalage du contenu.

Cela vérifie que vous avez une barre de recherche comme tableHeaderView et suppose que vous faites défiler pour le cacher

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

Je trouve que l'application "Notes" est incapable de rechercher des éléments lorsque la table est vide. Je pense donc que nous utilisons simplement -(void)addSubview:(UIView *)view pour ajouter un tableau vide au début. Lorsque vous ajoutez un élément à son tableau de source de données, il lance un autre code pour charger tableView.

Donc ma solution est:

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

J'espère que cela t'aides :-)

1
Chilly Zhong

Changez les limites de la table, cela peut être fait dans viewDidLoad et vous n'avez pas à vous soucier de savoir si la table est vide ou pas (pour éviter l'exception).

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

Une fois que l'utilisateur a fait défiler le tableau, la barre de recherche apparaît et se comporte normalement. 

1
LightMan

J'ai eu le même problème. Et le seul moyen que j’ai trouvé pour résoudre ce problème était d’utiliser l’observateur de valeur clé sur la propriété UITableView contentOffset.

Après un certain débogage, je me suis rendu compte que le problème apparaissait juste si la taille du contenu de la vue table était plus petite que tableview. Je lance KVO lorsque viewWillAppear. Et l'envoi stop KVO 0,3 seconde après l'affichage apparaissent. À chaque fois que contentOffset devient 0.0, KVO réagit et définit contentOffset sur la hauteur de la barre de recherche . J'arrête KVO de manière asynchrone au bout de 0,3 seconde, car la présentation de la vue réinitialisera contentOffset même après son affichage. 

- (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"];
}
0
slobodans

Pour Swift:

Il suffit de créer un décalage de contenu de table, qui devrait être comme la hauteur de la barre de recherche.

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

Swift 3

fonctionne aussi avec une table vide!

Dans mon environnement, je charge des cellules personnalisées par fichier xib et le paramètre rowHeight est défini automatiquement.

    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)
}
0
John

J'ai essayé de régler le contenu et scrollToIndexpath mais rien n'a fonctionné de manière satisfaisante. J'ai supprimé le paramètre tableHeaderView en tant que searchBar de viewDidLoad et je l'ai défini dans le délégué scrollview comme indiqué ci-dessous.

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)
    }
}

Cela a fonctionné comme un charme.Le paramètre de décalage du contenu est pour le défilement régulier

0
Abdur Rahman

N'oubliez pas de prendre en compte la barre d'état et la barre de navigation . Mon contrôleur de vue de table est intégré à un contrôleur de navigation, dans viewDidAppear:

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

a travaillé pour moi.

0
fujianjin6471