web-dev-qa-db-fra.com

Est-il possible de désactiver les en-têtes flottants dans UITableView avec UITableViewStylePlain?

J'utilise un UITableView pour mettre en page le contenu des pages. J'utilise les en-têtes de la vue tableau pour mettre en forme certaines images, etc., et je les préférerais si elles ne flottaient pas, mais restaient statiques comme elles le sont lorsque le style est défini sur UITableViewStyleGrouped.

Autre que l’utilisation de UITableViewStyleGrouped, existe-t-il un moyen de le faire? J'aimerais éviter d'utiliser groupé car cela ajoute une marge au bas de toutes mes cellules et nécessite de désactiver la vue en arrière-plan pour chacune des cellules. Je voudrais un contrôle total de ma mise en page. Idéalement, ce serait un "UITableViewStyleBareBones", mais je n'ai pas vu cette option dans la documentation ...

Merci beaucoup,

155
Tricky

Vous devriez pouvoir simuler cela en utilisant une cellule personnalisée pour créer vos lignes d'en-tête. Celles-ci vont alors défiler comme n'importe quelle autre cellule de la vue tableau.

Il vous suffit d’ajouter de la logique dans votre cellForRowAtIndexPath pour renvoyer le bon type de cellule s’il s’agit d’une ligne d’en-tête.

Vous devrez probablement gérer vous-même vos sections, c’est-à-dire avoir tout dans une section et simuler les en-têtes. (Vous pouvez également essayer de retourner une vue masquée pour la vue d'en-tête, mais je ne sais pas si cela fonctionnera.)

26
frankodwyer

Un moyen probablement plus facile d'y parvenir:

Objectif c:

CGFloat dummyViewHeight = 40;
UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, dummyViewHeight)];
self.tableView.tableHeaderView = dummyView;
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0);

Rapide:

let dummyViewHeight = CGFloat(40)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0)

Les en-têtes de section défileront maintenant comme toute cellule normale.

312
samvermette

(Pour ceux qui sont déjà arrivés à cause d'un mauvais style de tableau) Changez le style de tableau de brut à groupé, via l'inspecteur d'attributs ou via le code:

let tableView = UITableView(frame: .zero, style: .grouped)
210
david72

[~ # ~] warning [~ # ~] : cette solution implémente une méthode d'API réservée. Cela pourrait empêcher l'application d'être approuvée par Apple pour la distribution sur l'AppStore.

J'ai décrit les méthodes privées qui transforment les en-têtes de section dans mon blog

En gros, il vous suffit de sous-classer UITableView et de retourner NO dans deux de ses méthodes:

- (BOOL)allowsHeaderViewsToFloat;
- (BOOL)allowsFooterViewsToFloat;
72
XAK

Ok, je sais qu'il est tard mais je devais le faire. J'ai passé 10 heures à chercher une solution qui fonctionne mais je n'ai pas trouvé de réponse complète. J'ai trouvé des allusions mais difficile à comprendre pour les débutants. J'ai donc dû mettre mes 2 centimes et compléter la réponse.

Comme cela a été suggéré dans quelques réponses, la seule solution efficace que j’ai pu mettre en œuvre consiste à insérer des cellules normales dans la vue tableau et à les traiter en tant qu’en-têtes de section. Toutefois, le meilleur moyen de le réaliser consiste à insérer ces cellules à rangée 0 de chaque section. De cette façon, nous pouvons gérer très facilement ces en-têtes personnalisés non flottants.

Donc, les étapes sont.

  1. Implémentez UITableView avec le style UITableViewStylePlain.

    -(void) loadView
    {
        [super loadView];
    
        UITableView *tblView =[[UITableView alloc] initWithFrame:CGRectMake(0, frame.Origin.y, frame.size.width, frame.size.height-44-61-frame.Origin.y) style:UITableViewStylePlain];
        tblView.delegate=self;
        tblView.dataSource=self;
        tblView.tag=2;
        tblView.backgroundColor=[UIColor clearColor];
        tblView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }
    
  2. Implémentez titleForHeaderInSection comme d'habitude (vous pouvez obtenir cette valeur en utilisant votre propre logique, mais je préfère utiliser des délégués standard).

    - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        NSString *headerTitle = [sectionArray objectAtIndex:section];
        return headerTitle;
    }
    
  3. Immplement numberOfSectionsInTableVoir comme d'habitude

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        int sectionCount = [sectionArray count];
        return sectionCount;
    }
    
  4. Implémentez numberOfRowsInSection comme d'habitude.

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
        int rowCount = [[cellArray objectAtIndex:section] count];
        return rowCount +1; //+1 for the extra row which we will fake for the Section Header
    }
    
  5. Renvoie 0.0f dans heightForHeaderInSection.

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    {
        return 0.0f;
    }
    
  6. NE PAS implémenter viewForHeaderInSection. Supprimez complètement la méthode au lieu de renvoyer nil.

  7. Dans heightForRowAtIndexPath. Vérifiez si (indexpath.row == 0) et renvoyez la hauteur de cellule souhaitée pour l'en-tête de section, sinon renvoyez la hauteur de la cellule.

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if(indexPath.row == 0)
        {
            return 80; //Height for the section header
        }
        else
        {
            return 70; //Height for the normal cell
        }
    }
    
  8. Maintenant, dans cellForRowAtIndexPath, vérifiez si (indexpath.row == 0) et implémentez la cellule comme vous le souhaitez pour l'en-tête de section et définissez le style de sélection sur none. SINON, implémentez la cellule comme vous le souhaitez.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SectionCell"];
            if (cell == nil)
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SectionCell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleNone; //So that the section header does not appear selected
    
                cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SectionHeaderBackground"]];
            }
    
            cell.textLabel.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:indexPath.section];
    
            return cell;
        }
        else
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    
            if (cell == nil) 
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleGray; //So that the normal cell looks selected
    
                cell.backgroundView =[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellBackground"]]autorelease];
                cell.selectedBackgroundView=[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SelectedCellBackground"]] autorelease];
            }
    
            cell.textLabel.text = [[cellArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row -1]; //row -1 to compensate for the extra header row
    
            return cell;
        }
    }
    
  9. Maintenant, implémentez willSelectRowAtIndexPath et renvoyez nil si indexpath.row == 0. Cela évitera que didSelectRowAtIndexPath ne soit jamais déclenché pour la ligne d'en-tête Section.

    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            return nil;
        }
    
        return indexPath;
    }
    
  10. Et enfin dans didSelectRowAtIndexPath, vérifiez si (indexpath.row! = 0) et continuez.

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row != 0)
        {
            int row = indexPath.row -1; //Now use 'row' in place of indexPath.row
    
            //Do what ever you want the selection to perform
        }
    }
    

Avec cela, vous avez terminé. Vous avez maintenant un en-tête de section parfaitement flottant et non flottant.

28
Sumit Anantwar

Dans votre interface Builder, cliquez sur votre problème Table View

problem table view

Accédez ensuite à l’Inspecteur des attributs et modifiez le style Ordinaire en Style groupé;)

easy

25

La chose intéressante à propos de UITableViewStyleGrouped est que tableView ajoute le style à cellules et non à TableView.

Le style est ajouté en tant que backgroundView aux cellules en tant que classe appelée UIGroupTableViewCellBackground, qui gère le dessin d'un arrière-plan différent en fonction de la position de la cellule dans la section.

Donc, une solution très simple consiste à utiliser UITableViewStyleGrouped, à définir clearColor sur la couleur d'arrière-plan de la table et à simplement remplacer la vue d'arrière-plan de la cellule dans cellForRow:

cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];
17
adamsiton

Changez votre style TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

Selon la Apple pour UITableView:

UITableViewStylePlain - Une vue de table simple. Tous les en-têtes de section ou les pieds de page sont affichés sous forme de séparateurs en ligne et flottants lorsque la vue tabulaire défile.

UITableViewStyleGrouped: une vue de tableau dont les sections présentent des groupes de lignes distincts. Les en-têtes et les pieds de section ne flottent pas.

12
Aks

Il existe un autre moyen délicat. L'idée principale est de doubler le nombre de sections, la première ne montre que l'en-têteView, tandis que la seconde montre les cellules réelles.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return sectionCount * 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }
    return _rowCount;
}

Il faut alors implémenter les délégués headerInSection:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        //return headerview;
    }
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        //return headerheight;
    }
    return 0;
}

Cette approche a également peu d'impact sur vos sources de données:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    int real_section = (int)indexPath.section / 2;
    //your code
}

En comparant avec d'autres approches, cette méthode est sûre sans modifier le cadre ni les contentInsets de la vue de table. J'espère que cela peut aider.

5
Lane

Le moyen le plus simple d'obtenir ce que vous voulez est de définir votre style de table comme étant UITableViewStyleGrouped, style de séparateur comme UITableViewCellSeparatorStyleNone:

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN; // return 0.01f; would work same 
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return [[UIView alloc] initWithFrame:CGRectZero];
}

N'essayez pas de retourner la vue du pied de page sous la forme nil, n'oubliez pas de définir la hauteur et la vue de l'en-tête, après avoir obtenu ce que vous souhaitiez.

Bien que cela ne résout peut-être pas votre problème, il l’a fait pour moi lorsque je voulais faire la même chose. Au lieu de définir l'en-tête, j'ai utilisé le pied de page de la section ci-dessus. Ce qui m'a sauvé, c'est que cette section était petite et statique, elle ne défilait donc jamais au-dessous du bas de la vue.

4
Torbjörn

Pour Swift 3 +

Utilisez simplement UITableViewStyleGrouped et définissez la hauteur du pied de page sur zéro de la manière suivante:

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return .leastNormalMagnitude
}
3
Tamás Sengel

En réfléchissant à la façon d’aborder ce problème, je me suis souvenu d’un détail très important concernant UITableViewStyleGrouped. La manière dont UITableView applique le style groupé (les bordures arrondies autour des cellules) consiste à ajouter un backgroundView personnalisé à UITableViewCells et non à UITableView. A chaque cellule est ajouté un backgroundView en fonction de sa position dans la section (les rangées supérieures correspondent à la partie supérieure de la bordure de la section, celles du milieu à la bordure latérale et celle du bas à la partie inférieure). Donc, si nous voulons juste un style simple et que nous n’avons pas de backgroundView personnalisé pour nos cellules (ce qui est le cas dans 90% des cas), il suffit d’utiliser UITableViewStyleGrouped et de supprimer l’arrière-plan personnalisé. . Cela peut être fait en suivant ces deux étapes:

Changez notre style de tableView en UITableViewStyleGrouped. Ajoutez la ligne suivante à cellForRow, juste avant de renvoyer la cellule:

cell.backgroundView = [[[[UIView alloc]] initWithFrame: cell.bounds] autorelease];

Et c'est tout. Le style tableView deviendra exactement comme UITableViewStylePlain, à l'exception des en-têtes flottants.

J'espère que cela t'aides!

3
James Laurenstin

Vous pouvez ajouter une section (avec zéro ligne) ci-dessus, puis définir le sectionFooterView ci-dessus comme headerView de la section actuelle, footerView ne flotte pas. J'espère que cela vous aidera.

1
Lee Lam

Vous pourriez peut-être utiliser scrollViewDidScroll sur la tableView et changer le contentInset en fonction de l'en-tête visible actuel.

Cela semble fonctionner pour mon cas d'utilisation!

extension ViewController : UIScrollViewDelegate{

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard   let tableView = scrollView as? UITableView,
            let visible = tableView.indexPathsForVisibleRows,
            let first = visible.first else {
            return
        }

        let headerHeight = tableView.rectForHeader(inSection: first.section).size.height
        let offset =  max(min(0, -tableView.contentOffset.y), -headerHeight)
        self.tableView.contentInset = UIEdgeInsetsMake(offset, 0, -offset, 0)
   }
}
1
Martin

Une autre façon de le faire est de créer une section vide juste avant celle sur laquelle vous voulez l'en-tête et de placer votre en-tête sur cette section. Comme la section est vide, l'en-tête défilera immédiatement.

1
devguydavid

Une variante de la solution de @ samvermette:

/// Allows for disabling scrolling headers in plain-styled tableviews
extension UITableView {

    static let shouldScrollSectionHeadersDummyViewHeight = CGFloat(40)

    var shouldScrollSectionHeaders: Bool {
        set {
            if newValue {
                tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: bounds.size.width, height: UITableView.shouldScrollSectionHeadersDummyViewHeight))
                contentInset = UIEdgeInsets(top: -UITableView.shouldScrollSectionHeadersDummyViewHeight, left: 0, bottom: 0, right: 0)
            } else {
                tableHeaderView = nil
                contentInset = .zero
            }
        }

        get {
            return tableHeaderView != nil && contentInset.top == UITableView.shouldScrollSectionHeadersDummyViewHeight
        }
    }

}
0
raf

Vérifiez la réponse sur la manière d'implémenter les en-têtes avec StoryBoard: Vues d'en-tête de table dans StoryBoards

Notez également que si vous ne mettez pas en œuvre

viewForHeaderInSection:(NSInteger)section

il ne va pas flotter ce qui est exactement ce que vous voulez.

0
Raphael Oliveira

Ignorer XAK. N'explorez aucune méthode privée si vous souhaitez que votre application ait la chance d'être acceptée par Apple.

Ceci est plus facile si vous utilisez Interface Builder. Vous ajouteriez un UIView en haut de la vue (où les images iraient), puis ajoutez votre table en dessous. IB doit le dimensionner en conséquence; c'est-à-dire que le haut de la table touche le bas de la vue UIV que vous venez d'ajouter et que sa hauteur recouvre le reste de l'écran.

L'idée est que si UIView ne fait pas réellement partie de la vue table, il ne défilera pas avec la vue table. c'est-à-dire ignorer l'en-tête tableview.

Si vous n'utilisez pas le générateur d'interface, c'est un peu plus compliqué, car vous devez corriger le positionnement et la hauteur de la vue de table.

0
imnk

Pour supprimer complètement les sections d'en-tête de section flottante, procédez comme suit:

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return [[UIView alloc] init];
}

return nil ne fonctionne pas.

Pour désactiver les en-têtes de section flottants mais toujours affichés, vous pouvez fournir une vue personnalisée avec ses propres comportements.

0
djskinner