Question

Mobile Safari vous permet de changer de page en entrant une sorte de vue de pagination horizontale UIScrollView avec un contrôle de page en bas.

J'essaie de reproduire ce comportement particulier lorsqu'un UIScrollView à défilement horizontal affiche une partie du contenu de la vue suivante.

Exemple fourni par Apple: PageControl montre comment utiliser un UIScrollView pour la pagination horizontale, mais toutes les vues occupent toute la largeur de l'écran.

Comment faire en sorte que UIScrollView affiche du contenu de la vue suivante, comme le fait Safari sur mobile?

Était-ce utile?

La solution

Un UIScrollView avec la pagination activée s’arrêtera à des multiples de la largeur (ou de la hauteur) de son cadre. La première étape consiste donc à déterminer la largeur souhaitée de vos pages. Assurez-vous que la largeur de UIScrollView . Définissez ensuite la taille de votre sous-vue, quelle que soit sa taille, et définissez son centre en fonction de multiples de la largeur de UIScrollView .

Puis, puisque vous voulez voir les autres pages, bien sûr, définissez clipsToBounds sur NO comme indiqué par mhjoy. La partie astuce consiste maintenant à le faire défiler lorsque l’utilisateur commence à glisser en dehors de la plage du cadre de UIScrollView . Ma solution (alors que je devais le faire très récemment) était la suivante:

Créez une sous-classe UIView (i.e. ClipView ) qui contiendra le UIScrollView et ses sous-vues. Pour l'essentiel, il devrait avoir le cadre de ce que vous supposeriez que UIScrollView aurait dans des circonstances normales. Placez UIScrollView au centre du ClipView . Assurez-vous que le clipsToBounds de ClipView est défini sur YES si sa largeur est inférieure à celle de la vue parent. De même, ClipView nécessite une référence à UIScrollView .

La dernière étape consiste à remplacer - (UIView *) hitTest: withEvent: dans ClipView .

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  return [self pointInside:point withEvent:event] ? scrollView : nil;
}

Ceci étend essentiellement la zone tactile de UIScrollView au cadre de la vue de son parent, exactement ce dont vous avez besoin.

Une autre option consisterait à sous-classer UIScrollView et à remplacer sa méthode - (BOOL) pointInside: (CGPoint) point withEvent: (UIEvent *) , mais vous devrez toujours besoin d’une vue conteneur pour effectuer le découpage, et il peut être difficile de déterminer quand renvoyer YES uniquement en fonction du cadre de UIScrollView .

REMARQUE: Vous devriez également consulter le hitTest: withEvent: modification si vous rencontrez des problèmes d'interaction utilisateur sous-vue.

Autres conseils

La solution ClipView ci-dessus a fonctionné pour moi, mais je devais effectuer une autre implémentation - [UIView hitTest: withEvent:] . La version d'Ed Marty n'a pas permis à l'utilisateur d'interagir avec les vues de défilement verticales que j'ai à l'intérieur de la vue horizontale.

La version suivante a fonctionné pour moi:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* child = nil;
    if ((child = [super hitTest:point withEvent:event]) == self)
        return self.scrollView;     
    return child;
}

Définissez la taille du cadre de scrollView comme la taille de vos pages serait:

[self.view addSubview:scrollView];
[self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];

Vous pouvez maintenant parcourir self.view , et le contenu sur scrollView défilera.
Utilisez également scrollView.clipsToBounds = NO; pour empêcher l'écrêtage du contenu.

Je me suis retrouvé avec la coutume UIScrollView moi-même car c’était la méthode la plus simple et la plus rapide qui me semblait. Cependant, je n'ai vu aucun code exact, donc je pensais le partager. Mes besoins concernaient un UIScrollView ayant un contenu restreint. Par conséquent, UIScrollView lui-même était petit pour réaliser l'effet de pagination. Comme le poste dit, vous ne pouvez pas glisser à travers. Mais maintenant vous pouvez.

Créez une classe CustomScrollView et la sous-classe UIScrollView. Il ne reste plus qu’à ajouter ceci dans le fichier .m:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
  return (point.y >= 0 && point.y <= self.frame.size.height);
}

Ceci vous permet de faire défiler d’un côté à l’autre (horizontal). Ajustez les limites en conséquence pour définir votre zone tactile de balayage / défilement. Profitez!

J'ai effectué une autre implémentation qui peut renvoyer automatiquement le scrollview. Il n’est donc pas nécessaire d’avoir un IBOutlet qui limitera la réutilisation dans le projet.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ([self pointInside:point withEvent:event]) {
        for (id view in [self subviews]) {
            if ([view isKindOfClass:[UIScrollView class]]) {
                return view;
            }
        }
    }
    return nil;
}

J'ai une autre modification potentiellement utile pour l'implémentation de ClipView hitTest. Je n'aimais pas avoir à fournir une référence UIScrollView à ClipView. Mon implémentation ci-dessous vous permet de réutiliser la classe ClipView pour élargir la zone de test d'accès, sans avoir à lui fournir de référence.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (([self pointInside:point withEvent:event]) && (self.subviews.count >= 1))
    {
        // An extended-hit view should only have one sub-view, or make sure the
        // first subview is the one you want to expand the hit test for.
        return [self.subviews objectAtIndex:0];
    }

    return nil;
}

J'ai mis en œuvre la suggestion de vote supérieur ci-dessus, mais le UICollectionView que j'utilisais considérait que tout ce qui sortait du cadre était hors de l'écran. En conséquence, les cellules proches ne s'affichent qu'en dehors des limites lorsque l'utilisateur les fait défiler, ce qui n'est pas idéal.

J'ai fini par émuler le comportement d'un scrollview en ajoutant la méthode ci-dessous au délégué (ou UICollectionViewLayout ).

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{    
  if (velocity.x > 0) {
    proposedContentOffset.x = ceilf(self.collectionView.contentOffset.x / pageSize) * pageSize;
  }
  else {
    proposedContentOffset.x = floorf(self.collectionView.contentOffset.x / pageSize) * pageSize;
  }

  return proposedContentOffset;
}

Cela évite la délégation de l’action de glissement, ce qui était également un bonus. UIScrollViewDelegate utilise une méthode similaire appelée scrollViewWillEndDragging: withVelocity: targetContentOffset: , qui pourrait être utilisée pour appeler UITableViews et UIScrollViews.

Activez les événements tapant sur les vues enfant de la vue défilement tout en prenant en charge la technique de cette question SO. Utilise une référence à la vue de défilement (self.scrollView) pour plus de lisibilité.

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = nil;
    NSArray *childViews = [self.scrollView subviews];
    for (NSUInteger i = 0; i < childViews.count; i++) {
        CGRect childFrame = [[childViews objectAtIndex:i] frame];
        CGRect scrollFrame = self.scrollView.frame;
        CGPoint contentOffset = self.scrollView.contentOffset;
        if (childFrame.origin.x + scrollFrame.origin.x < point.x + contentOffset.x &&
            point.x + contentOffset.x < childFrame.origin.x + scrollFrame.origin.x + childFrame.size.width &&
            childFrame.origin.y + scrollFrame.origin.y < point.y + contentOffset.y &&
            point.y + contentOffset.y < childFrame.origin.y + scrollFrame.origin.y + childFrame.size.height
        ){
            hitView = [childViews objectAtIndex:i];
            return hitView;
        }
    }
    hitView = [super hitTest:point withEvent:event];
    if (hitView == self)
        return self.scrollView;
    return hitView;
}

Ajoutez ceci à votre vue enfant pour capturer l'événement tactile:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

(Ceci est une variante de la solution de user1856273. Nettoyé pour plus de lisibilité et intégrant le correctif de Bartserk. J'ai pensé modifier la réponse de user1856273 mais c'était un changement trop important à faire.)

ma version Appuyez sur le bouton situé sur le parchemin - work =)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* child = nil;
    for (int i=0; i<[[[[self subviews] objectAtIndex:0] subviews] count];i++) {

        CGRect myframe =[[[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i] frame];
        CGRect myframeS =[[[self subviews] objectAtIndex:0] frame];
        CGPoint myscroll =[[[self subviews] objectAtIndex:0] contentOffset];
        if (myframe.origin.x < point.x && point.x < myframe.origin.x+myframe.size.width &&
            myframe.origin.y+myframeS.origin.y < point.y+myscroll.y && point.y+myscroll.y < myframe.origin.y+myframeS.origin.y +myframe.size.height){
            child = [[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i];
            return child;
        }


    }

    child = [super hitTest:point withEvent:event];
    if (child == self)
        return [[self subviews] objectAtIndex:0];
    return child;
    }

mais seulement [[auto subviews] objectAtIndex: 0] doit être un défilement

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top