web-dev-qa-db-fra.com

UIScrollView horizontal dans une UITableViewCell

J'essaie de créer exactement le même effet que dans l'application AppStore pour afficher les captures d'écran: à l'intérieur d'un UITableView, j'ai des sous-classes personnalisées de UITableViewCell. L'un d'eux est conçu pour afficher des aperçus de certains produits, disons 4 images. Je veux qu'ils montrent la même manière que l'AppStore présente des captures d'écran d'une application: à l'intérieur d'un UIScrollView horizontal avec son UIPageControl attaché.

J'ajoute donc mon UIScrollView et mon UIPageControl à mon UITableViewCell, juste comme ça:

@implementation phVolumePreviewCell
- (id)init {
    if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kVolumePreviewCellIdentifier]) {
        [self setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
        [self setSelectionStyle:UITableViewCellSeparatorStyleNone];

        previews = [[NSMutableArray alloc] initWithCapacity:4];
        for (int i = 0; i < 4; i++) {
            [previews addObject:@"dummy"];
        }

        scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
        [scrollView setPagingEnabled:YES];
        [scrollView setBackgroundColor:[UIColor grayColor]];
        [scrollView setIndicatorStyle:UIScrollViewIndicatorStyleBlack];
        [scrollView setShowsHorizontalScrollIndicator:YES];
        [scrollView setBounces:YES];
        [scrollView setScrollEnabled:YES];
        [scrollView setDelegate:self];
        [self.contentView addSubview:scrollView];
        [scrollView release];

        pageControl = [[UIPageControl alloc] initWithFrame:CGRectZero];
        [pageControl setNumberOfPages:[previews count]];
        [pageControl setBackgroundColor:[UIColor grayColor]];
        [self.contentView addSubview:pageControl];
        [pageControl release];
    }
    return self;
}

(Remarque: je remplis les "aperçus" avec des données factices ici, juste pour avoir un [nombre d'aperçus] qui fonctionne; je veux afficher l'indicateur de défilement de scrollView à des fins de test uniquement, je les cacherai plus tard).

Mon problème est que je peux faire défiler l'ensemble de UITableView contenant ce phVolumePreviewCell (sous-classe de UITableViewCell), mais je ne peux pas faire défiler UIScrollView. Comment puis-je atteindre cet objectif?

Les seules informations que j'ai trouvées sont là: http://theexciter.com/articles/touches-and-uiscrollview-inside-a-uitableview.html Mais il parle d'anciens SDK (à savoir 2.2) et je suis sûr qu'il existe une approche plus "récente" pour ce faire.

Quelques précisions:

  • Je peux faire défiler UIScrollView avec deux doigts. Ce n'est pas ce que je veux, je veux qu'il puisse défiler avec un seul doigt.
  • Lorsque je fais défiler avec deux doigts, la TableView intercepte toujours les touches et fait défiler un peu vers le haut ou vers le bas. J'aimerais que le TableView reste à sa position lorsque je touche le ScrollView.

Je crois que je dois trouver un moyen d'intercepter les contacts sur ma TableView, et si le contact se trouve sur la phVolumePreviewCell (sous-classe UITableViewCell personnalisée), la transmettre à la cellule au lieu de la gérer sur la TableView.

Pour mémoire, ma TableView est créée par programmation dans une sous-classe UITableViewController:

@interface phVolumeController : UITableViewController <UITableViewDelegate> {
    LocalLibrary *lib;
        NSString *volumeID;
        LLVolume *volume;
}

- (id)initWithLibrary:(LocalLibrary *)newLib andVolumeID:(NSString *)newVolumeID;
@end

@implementation phVolumeController

#pragma mark -
#pragma mark Initialization

- (id)initWithLibrary:(LocalLibrary *)newLib andVolumeID:(NSString *)newVolumeID {
    if (self = [super initWithStyle:UITableViewStylePlain]) {
        lib          = [newLib retain];
        volumeID     = newVolumeID;
        volume       = [(LLVolume *)[LLVolume alloc] initFromLibrary:lib forID:volumeID];
        [[self navigationItem] setTitle:[volume title]];
    }
    return self;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kVolumePreviewCellIdentifier];
    if (cell == nil) {
        cell = [[[phVolumePreviewCell alloc] init] autorelease];
    }

    [(phVolumePreviewCell *)cell setVolume:volume];
    return (UITableViewCell *)cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return kVolumePreviewCellHeight;
}

Merci pour toute aide!

36
Cyrille

J'ai trouvé une solution de contournement.

Recréer à partir de zéro une UITableView très simple (8 lignes) et insérer un UIScrollView dans la deuxième ligne seulement, fonctionne parfaitement. Les vues de défilement imbriquées (TableView et ScrollView) défilent indépendamment comme prévu. Tout cela a été fait dans un projet de test à fichier unique, dans l'AppDelegate.

Alors ... d'abord, j'ai pensé "cela pourrait être un problème de délégation", alors j'ai dit au scrollView que son délégué était le TableView, pas ma sous-classe TableViewCell personnalisée. En vain.

Ensuite, au lieu de recourir à une sous-classe TableViewCell personnalisée et propre qui ajoute simplement ScrollView et PageControl, j'ai eu recours à la création d'un TableViewCell ordinaire dans la méthode cellForRowAtIndexPath: de TableView. Ensuite, je crée un UIScrollView juste après l'initialisation du TableViewCell, puis je l'ajoute au TableViewCell, et shazam ... ça marche!

Avant:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kVolumePreviewCellIdentifier];
if (cell == nil) {
    cell = [[[phVolumePreviewCell alloc] init] autorelease];
}

[(phVolumePreviewCell *)cell setVolume:volume];
// That does the job of creating the cell's scrollView and pageControl

return (UITableViewCell *)cell

Après:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kvcPreviewRowIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kvcPreviewRowIdentifier] autorelease];

    previewScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, kvcPreviewScrollViewHeight)];
    [previewScrollView setContentSize:CGSizeMake(1000, kvcPreviewScrollViewHeight)];
    [[cell contentView] addSubview:previewScrollView];

    previewPageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, kvcPreviewScrollViewHeight, tableView.frame.size.width, kvcPreviewPageControlHeight)];
    [previewPageControl setNumberOfPages:4];
    [[cell contentView] addSubview:previewPageControl];
}

return (UITableViewCell *)cell;

Comment diable vient que lorsque je crée mon scrollView à partir de la sous-classe TVCell, il ne joue pas Nice; mais quand je crée le scrollView à partir du TableView juste après avoir créé un TVCell "classique", ça marche?

J'aurais peut-être trouvé une solution, mais je suis toujours perplexe par la raison.

21
Cyrille