156 votes

Le contenu du centre de UIScrollView lorsque les petits

J'ai un UIImageView à l'intérieur d'un UIScrollView que j'utilise pour le zoom et le défilement. Si l'image ou le contenu de défilement de la vue, si plus grand que le défilement de la vue tout fonctionne bien. Toutefois, lorsque l'image devient plus petit que le défilement de l'affichage, il colle à l'angle supérieur gauche de défilement de la vue. Je voudrais le garder centré, à l'instar de l'app Photos.

Des idées ou des exemples sur la manière de garder le contenu de l' UIScrollView centré lorsque les petits?

Je suis en train de travailler avec l'iPhone 3.0.

Le code suivant fonctionne presque. L'image renvoie à l'angle supérieur gauche si je le pincer après avoir atteint au minimum le niveau de zoom.


- (void)loadView {
    [super loadView];

    // set up main scroll view
    imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
    [imageScrollView setBackgroundColor:[UIColor blackColor]];
    [imageScrollView setDelegate:self];
    [imageScrollView setBouncesZoom:YES];
    [[self view] addSubview:imageScrollView];

    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]];
    [imageView setTag:ZOOM_VIEW_TAG];
    [imageScrollView setContentSize:[imageView frame].size];
    [imageScrollView addSubview:imageView];

    CGSize imageSize = imageView.image.size;
    [imageView release];

    CGSize maxSize = imageScrollView.frame.size;
    CGFloat widthRatio = maxSize.width / imageSize.width;
    CGFloat heightRatio = maxSize.height / imageSize.height;
    CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;

    [imageScrollView setMinimumZoomScale:initialZoom];
    [imageScrollView setZoomScale:1];

    float topInset = (maxSize.height - imageSize.height) / 2.0;
    float sideInset = (maxSize.width - imageSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}

/************************************** NOTE **************************************/
/* The following delegate method works around a known bug in zoomToRect:animated: */
/* In the next release after 3.0 this workaround will no longer be necessary      */
/**********************************************************************************/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    [scrollView setZoomScale:scale+0.01 animated:NO];
    [scrollView setZoomScale:scale animated:NO];
    // END Bug workaround

    CGSize maxSize = imageScrollView.frame.size;
    CGSize viewSize = view.frame.size;
    float topInset = (maxSize.height - viewSize.height) / 2.0;
    float sideInset = (maxSize.width - viewSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}

236voto

Erdemus Points 1716

J'ai une solution très simple! Tous vous avez besoin est de mettre à jour le centre de votre sous-vue (imageview), tandis que le zoom dans le ScrollViewDelegate. Si l'image zoomée est plus petit que le scrollview puis ajuster la sous-vue.centre autre centre est (0,0).

- (void)scrollViewDidZoom:(UIScrollView *)scrollView 
{
    UIView *subView = [scrollView.subviews objectAtIndex:0];

    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, 
                                 scrollView.contentSize.height * 0.5 + offsetY);
}

26voto

Liam Jones Points 231

Ok, j'ai été la lutte contre cette pour les deux derniers jours et à l'extérieur et avoir finalement assez fiable (pour l'instant...) de la solution, j'ai pensé que je devrais la partager et de sauver les autres de la douleur. :) Si vous trouvez un problème avec cette solution, veuillez cri!

J'ai essentiellement passé par ce que tout le monde a: recherche de StackOverflow, les Forums des Développeurs Apple, regardé le code pour three20, ScrollingMadness, ScrollTestSuite, etc. J'ai essayé de l'élargissement de l'UIImageView image, en jouant avec le UIScrollView de l'offset et/ou encarts de la ViewController, etc. mais rien ne fonctionnait très bien (comme tout le monde l'a trouvé trop).

Après avoir dormi sur elle, j'ai essayé plusieurs alternatives angles:

  1. Dérivation de l'UIImageView de sorte qu'il modifie sa propre taille dynamiquement - cela ne fonctionne pas bien du tout.
  2. Dérivation de l'UIScrollView de sorte qu'il modifie sa propre contentOffset dynamique, c'est celle qui me semble être un gagnant pour moi.

Avec ce sous-classement UIScrollView méthode, je suis en substituant l'contentOffset mutateur donc ce n'est pas la mise en {0,0} lorsque l'image est mise à l'échelle plus petite que la fenêtre d'affichage - à la place, c'est le réglage de l'offset, tels que l'image sera conservée centré dans la fenêtre d'affichage. Jusqu'à présent, on a toujours l'impression de travailler. Je l'ai vérifié avec de largeur, de hauteur, de petites et de grandes images et qui n'ont pas de "travaille, mais pincement au minimum de zoom, il tombe".

J'ai téléchargé un exemple de projet de github qui utilise cette solution, vous pouvez le trouver ici: http://github.com/nyoron/NYOBetterZoom

23voto

JosephH Points 21074

Ce code devrait fonctionner sur la plupart des versions de l'iOS (et a été testé pour fonctionner sur les 3.1 vers le haut).

Il est basé sur l'Apple de la WWDC code de la photoscoller.

Ajouter le ci-dessous pour votre sous-classe de UIScrollView, et remplacer tileContainerView avec la vue contenant votre image ou de la dalle:

- (void)layoutSubviews {
    [super layoutSubviews];

    // center the image as it becomes smaller than the size of the screen
    CGSize boundsSize = self.bounds.size;
    CGRect frameToCenter = tileContainerView.frame;

    // center horizontally
    if (frameToCenter.size.width < boundsSize.width)
        frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
    else
        frameToCenter.origin.x = 0;

    // center vertically
    if (frameToCenter.size.height < boundsSize.height)
        frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
    else
        frameToCenter.origin.y = 0;

    tileContainerView.frame = frameToCenter;
}

21voto

hpique Points 23090

Actuellement, je suis sous-classement UIScrollView et primordial, setContentOffset: pour régler le décalage basé sur contentSize. Il fonctionne à la fois avec pincement et programatic zoom.

@implementation HPCenteringScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
    const CGSize contentSize = self.contentSize;
    const CGSize scrollViewSize = self.bounds.size;

    if (contentSize.width < scrollViewSize.width)
    {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0;
    }

    if (contentSize.height < scrollViewSize.height)
    {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0;
    }

    [super setContentOffset:contentOffset];
}

@end

En plus d'être court et doux, ce code est plus lisse de zoom que @Erdemus solution. Vous pouvez le voir en action dans la RMGallery démo.

12voto

Vicarius Points 124

J'ai passé une journée de combat avec ce problème, et a terminé la mise en œuvre de la scrollViewDidEndZooming:withView:atScale: comme suit:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
    CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
    CGFloat viewWidth = view.frame.size.width;
    CGFloat viewHeight = view.frame.size.height;

    CGFloat x = 0;
    CGFloat y = 0;

    if(viewWidth < screenWidth) {
        x = screenWidth / 2;
    }
    if(viewHeight < screenHeight) {
        y = screenHeight / 2 ;
    }

    self.scrollView.contentInset = UIEdgeInsetsMake(y, x, y, x);
}

Cela garantit que lorsque l'image est plus petite que l'écran, il ya encore suffisamment d'espace autour d'elle de sorte que vous pouvez le positionner à l'endroit exact que vous voulez.

(en supposant que votre UIScrollView contient une UIImageView à contenir l'image)

Essentiellement, ce qu'il fait est de vérifier que votre affichage de l'image de la largeur / hauteur est plus petite que la largeur de l'écran et la hauteur, et si oui, de créer un encart de la moitié de la largeur de l'écran / hauteur (vous pourriez probablement faire cela plus si vous voulez l'image pour sortir de l'écran de limites).

Notez que puisque c'est un UIScrollViewDelegate méthode, n'oubliez pas de l'ajouter à votre point de vue du contrôleur de la déclaration, afin d'éviter une accumulation de problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X