UIScrollView avec UIImageView centrée, comme l'application Photos

j'aimerais avoir une vue de défilement avec une vue de contenu d'image. L'image est en fait une carte qui est beaucoup plus grande que l'écran. La carte doit être initialement au centre de la vue de défilement, comme les photos dans L'application Photos lorsque vous tournez iPhone à l'orientation paysage.

alt text

Je n'ai pas réussi à avoir la carte au centre avec le zoom correct et le défilement en même temps. À condition que l'image de la carte commence à partir du haut de l'écran (en portrait l'orientation), le code ressemble à quelque chose comme:

- (void)loadView {
    mapView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"map.jpg"]];
    CGFloat mapHeight = MAP_HEIGHT * SCREEN_WIDTH / MAP_WIDTH;
    mapView.frame = CGRectMake(0, 0, SCREEN_WIDTH, mapHeight);
    scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    scrollView.delegate = self;
    scrollView.contentSize = mapView.frame.size;
    scrollView.maximumZoomScale = MAP_WIDTH / SCREEN_WIDTH;
    scrollView.minimumZoomScale = 1;
    [scrollView addSubview:mapView];
    self.view = scrollView;
}

quand je déplace le cadre image au centre, l'image ne croît que du haut de son cadre vers le bas. J'ai essayé de jouer avec mapView transform, avec le changement dynamique du cadre d'imageView. Rien ne fonctionne pour moi jusqu'à présent.

37
demandé sur Community 2009-03-12 14:56:06

11 réponses

ce code devrait fonctionner sur la plupart des versions d'iOS (et a été testé pour fonctionner sur la version 3.1 vers le haut).

il est basé sur le code Apple WWDC mentionné dans la réponse de Jonah.

ajouter ce qui suit à votre sous-classe de UIScrollView, et remplacer tileContainerView avec la vue contenant votre image ou des tuiles:

- (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;
}
40
répondu JosephH 2010-08-13 16:42:33

voici ce que je considérerais, la solution telle qu'elle se comporte exactement comme l'application photo d'apple. J'avais été à l'aide de solutions:

-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale

pour recentrer mais je n'aime pas cette solution, car après le zoom a été fait, il avait un de rebondir ensuite rapide "sauter" dans le centre, ce qui était très sexy. Il s'avère que si vous faites à peu près la même logique mais dans cette fonction de délégué:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView

il commence à la fois centré et quand vous zoomez sur lui reste centré:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView {
CGRect innerFrame = imageView.frame;
CGRect scrollerBounds = pScrollView.bounds;

if ( ( innerFrame.size.width < scrollerBounds.size.width ) || ( innerFrame.size.height < scrollerBounds.size.height ) )
{
    CGFloat tempx = imageView.center.x - ( scrollerBounds.size.width / 2 );
    CGFloat tempy = imageView.center.y - ( scrollerBounds.size.height / 2 );
    CGPoint myScrollViewOffset = CGPointMake( tempx, tempy);

    pScrollView.contentOffset = myScrollViewOffset;

}

UIEdgeInsets anEdgeInset = { 0, 0, 0, 0};
if ( scrollerBounds.size.width > innerFrame.size.width )
{
    anEdgeInset.left = (scrollerBounds.size.width - innerFrame.size.width) / 2;
    anEdgeInset.right = -anEdgeInset.left;  // I don't know why this needs to be negative, but that's what works
}
if ( scrollerBounds.size.height > innerFrame.size.height )
{
    anEdgeInset.top = (scrollerBounds.size.height - innerFrame.size.height) / 2;
    anEdgeInset.bottom = -anEdgeInset.top;  // I don't know why this needs to be negative, but that's what works
}
pScrollView.contentInset = anEdgeInset;
}

où 'imageView' est le UIImageView vous êtes en utilisant.

23
répondu Shizam 2014-06-19 13:04:11

Apple a publié les vidéos de la session 2010 de la WWDC à tous les membres du programme des développeurs iphone. L'un des thèmes abordés est comment ils ont créé l'application photos!!! Ils construisent une application très similaire étape par étape et ont rendu tout le code disponible gratuitement.

il n'utilise pas non plus d'api privée. Je ne peux pas mettre tout le code ici à cause de l'accord de non divulgation, Mais voici un lien vers le téléchargement de code échantillon. Vous aurez probablement besoin de connexion à gagner accès.

http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645

Et voici un lien vers l'iTunes de la WWDC page:

http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj

7
répondu Jonah 2010-06-20 19:48:36

je soupçonne que vous avez besoin de mettre le UIScrollViewcontentOffset.

2
répondu Rog 2014-06-19 13:04:19

j'aimerais que ce soit aussi simple. J'ai fait quelques recherches sur le net et j'ai découvert que ce n'est pas seulement mon problème, mais que beaucoup de gens se débattent avec le même problème non seulement sur iPhone, mais aussi sur Apple bureau cacao. Voir les liens suivants:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html

La solution décrite est basée sur la propriété UIViewContentModeScaleAspectFit de l'image, mais malheureusement il ne fonctionne pas très bien .L'image est centrée et se développe correctement, mais la zone rebondissante semble être beaucoup plus grande que l'image.

ce type n'a pas eu la réponse non plus:

http://discussions.apple.com/thread.jspa?messageID=8322675

et enfin, le même problème sur le cacao de bureau D'Apple:

http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView

je suppose que la solution fonctionne, mais il est basé sur le NSClipView, qui n'est pas sur iPhone...

Quelqu'un a une solution qui fonctionne sur iPhone?

1
répondu Martin Ludvik 2009-03-13 07:29:54

Cela a fonctionné pour moi:

- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
CGFloat tempx = view.center.x-160;
CGFloat tempy = view.center.y-160;
myScrollViewOffset = CGPointMake(tempx,tempy);

}

UIScrollView.

puis plus tard j'ai mis le contenuoffset sur celui capturé ici.

1
répondu Puppet 2014-06-19 13:05:44

Note: cette méthode fonctionne en quelque sorte. si l'image est plus petite que imageView, elle défile partiellement de l'écran. Pas une grosse affaire, mais pas aussi beau que l'application photos.

tout d'abord, il est important de comprendre que nous avons affaire à 2 vues, l'imageview avec l'image dedans, et la scrollview avec l'imageview dedans. Donc, tout d'abord définir l'imageview à la taille de l'écran:

 [myImageView setFrame:self.view.frame];

puis, centrez votre image dans le imageview:

 myImageView.contentMode = UIViewContentModeCenter;

voici tout mon code:

- (void)viewDidLoad {
AppDelegate *appDelegate = (pAppDelegate *)[[UIApplication sharedApplication] delegate];
 [super viewDidLoad];
 NSString *Path = [[NSBundle mainBundle] bundlePath];
 NSString *ImagePath = [Path stringByAppendingPathComponent:(@"data: %@", appDelegate.MainImageName)];
 UIImage *tempImg = [[UIImage alloc] initWithContentsOfFile:ImagePath];
 [imgView setImage:tempImg];

 myScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
 [myScrollView addSubview:myImageView];

//Set ScrollView Appearance
 [myScrollView setBackgroundColor:[UIColor blackColor]];
 myScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;

//Set Scrolling Prefs
 myScrollView.bounces = YES;
 myScrollView.delegate = self;
 myScrollView.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
 [myScrollView setCanCancelContentTouches:NO];
 [myScrollView setScrollEnabled:YES];


//Set Zooming Prefs
 myScrollView.maximumZoomScale = 3.0;
 myScrollView.minimumZoomScale = CGImageGetWidth(tempImg.CGImage)/320;
 myScrollView.zoomScale = 1.01; //Added the .01 to enable scrolling immediately upon view load.
 myScrollView.bouncesZoom = YES;


 [myImageView setFrame:self.view.frame];//rect];// .frame.size.height = imageHeight;
 myImageView.contentMode = UIViewContentModeCenter;
 self.view = myScrollView;
 [tempImg release];
 }
0
répondu Jonah 2009-09-19 20:48:41

OK, je pense que j'ai trouvé une assez bonne solution à ce problème. Le truc est de réajuster constamment le cadre d'imageView. Je trouve que cela fonctionne beaucoup mieux que de constamment ajuster les contenutinsets ou contentOffSets. J'ai dû ajouter un peu de code supplémentaire pour accommoder les images de portrait et de paysage.

Voici le code:

- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {

CGSize screenSize = [[self view] bounds].size;

if (myScrollView.zoomScale <= initialZoom +0.01) //This resolves a problem with the code not working correctly when zooming all the way out.
{
    imageView.frame = [[self view] bounds];
    [myScrollView setZoomScale:myScrollView.zoomScale +0.01];
}

if (myScrollView.zoomScale > initialZoom)
{
    if (CGImageGetWidth(temporaryImage.CGImage) > CGImageGetHeight(temporaryImage.CGImage)) //If the image is wider than tall, do the following...
    {
            if (screenSize.height >= CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is greater than the zoomed height of the image do the following...
            {
                    imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), 368);
            }
            if (screenSize.height < CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is less than the zoomed height of the image do the following...
            {
                    imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]);
            }
    }
    if (CGImageGetWidth(temporaryImage.CGImage) < CGImageGetHeight(temporaryImage.CGImage)) //If the image is taller than wide, do the following...
    {
            CGFloat portraitHeight;
            if (CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale] < 368)
            { portraitHeight = 368;}
            else {portraitHeight = CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale];}

            if (screenSize.width >= CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is greater than the zoomed width of the image do the following...
            {
                    imageView.frame = CGRectMake(0, 0, 320, portraitHeight);
            }
            if (screenSize.width < CGImageGetWidth (temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is less than the zoomed width of the image do the following...
            {
                    imageView.frame = CGRectMake(0, 0, CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale], portraitHeight);
            }
    }
    [myScrollView setZoomScale:myScrollView.zoomScale -0.01];
}
0
répondu Jonah 2009-10-04 02:16:27

une façon élégante de centrer le contenu de UISCrollView est-ce.

Ajouter un observateur à l' contentSize de votre UIScrollView, donc cette méthode sera appelée à chaque fois que le contenu change...

[myScrollView addObserver:delegate 
               forKeyPath:@"contentSize"
                  options:(NSKeyValueObservingOptionNew) 
                  context:NULL];

Maintenant sur votre observateur de la méthode:

- (void)observeValueForKeyPath:(NSString *)keyPath   ofObject:(id)object   change:(NSDictionary *)change   context:(void *)context { 

    // Correct Object Class.
    UIScrollView *pointer = object;

    // Calculate Center.
    CGFloat topCorrect = ([pointer bounds].size.height - [pointer viewWithTag:100].bounds.size.height * [pointer zoomScale])  / 2.0 ;
            topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );

    topCorrect = topCorrect - (  pointer.frame.origin.y - imageGallery.frame.origin.y );

    // Apply Correct Center.
    pointer.center = CGPointMake(pointer.center.x,
                                 pointer.center.y + topCorrect ); }
  • vous devriez changer le [pointer viewWithTag:100]. Remplacer par votre l'affichage du contenu UIView.

    • modifier imageGallery pointant vers votre fenêtre taille.

ceci corrigera le centre du contenu chaque fois que sa taille change.

NOTE: la seule façon dont ce contenu ne fonctionne pas très bien est avec la fonctionnalité de zoom standard du UIScrollView.

0
répondu SEQOY Development Team 2009-11-03 17:14:45

en Monotouch ça a marché pour moi.

this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false);
0
répondu ozan.aksu 2012-08-08 14:45:59

avec cette classe, vous devez lui passer l'imageView avec laquelle il travaille. J'ai été tenté de faire l'auto détecter, mais c'est plus rapide et vous voulez que tout le vitesse vous pouvez obtenir dans le layoutSubviews méthode.

Note: comme tel, cela nécessite que L'AutoLayout ne soit pas activé pour le scrollView.

//
//  CentringScrollView.swift
//  Cerebral Gardens
//
//  Created by Dave Wood
//  Copyright © 2016 Cerebral Gardens Inc. All rights reserved.
//

import UIKit

class CentringScrollView: UIScrollView {

    var imageView: UIImageView?

    override func layoutSubviews() {
        super.layoutSubviews()

        guard let superview = superview else { return }
        guard let imageView = imageView else { return }
        guard let image = imageView.image else { return }

        var frameToCentre = imageView.frame

        let imageWidth = image.size.width
        let imageHeight = image.size.height

        let widthRatio = superview.bounds.size.width / imageWidth
        let heightRatio = superview.bounds.size.height / imageHeight

        let minRatio = min(widthRatio, heightRatio, 1.0)

        let effectiveImageWidth = minRatio * imageWidth * zoomScale
        let effectiveImageHeight = minRatio * imageHeight * zoomScale

        contentSize = CGSize(width: max(effectiveImageWidth, bounds.size.width), height: max(effectiveImageHeight, bounds.size.height))

        frameToCentre.origin.x = (contentSize.width - frameToCentre.size.width) / 2
        frameToCentre.origin.y = (contentSize.height - frameToCentre.size.height) / 2

        imageView.frame = frameToCentre
    }
}
0
répondu Dave Wood 2016-05-05 09:56:02