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

voix
37

Je voudrais avoir défilante avec vue sur le contenu de l'image. L'image est en réalité la carte qui est beaucoup plus grand que l'écran. La carte devrait être d'abord au centre de la vue de défilement, comme des photos dans l'application de photos lorsque vous tournez iPhone à l'orientation paysage.

alt

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

- (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;
}

Lorsque je déplace le cadre de l'image au centre, l'image ne pousse que du haut de son cadre vers le bas. J'ai essayé de jouer avec Mapview transformer, changer dynamiquement cadre avec de la imageView. Rien ne fonctionne pour moi jusqu'à présent.

Créé 12/03/2009 à 12:56
source utilisateur
Dans d'autres langues...                            


11 réponses

voix
40

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

Il est basé sur le code d'Apple WWDC mentionné dans la réponse de Jonas.

Ajouter le ci-dessous à votre sous-classe de UIScrollView et remplacer tileContainerView avec la vue contenant votre image ou de 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;
}
Créé 13/08/2010 à 17:42
source utilisateur

voix
23

Voici ce que je considère, comme la solution en elle se comporte exactement comme l'application photo de pomme. J'avais été en utilisant des solutions utilisées:

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

à recentrera mais ne me plaisait pas cette solution car après le zoom a été fait, ce serait alors rebondir rapidement « sauter » dans le centre qui était très non sexy. Si vous tourne à peu près faire exactement la même logique, mais dans cette fonction de délégué:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView

il commence à la fois centrée et lorsque vous effectuez un zoom arrière , il 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 UIImageViewque vous utilisez.

Créé 03/02/2010 à 03:51
source utilisateur

voix
7

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

Il n'utilise pas api privé que ce soit. Je ne peux pas mettre tout le code ici à cause de l'accord de non-divulgation, mais voici un lien vers le code exemple téléchargement. Vous aurez probablement besoin de se connecter pour y accéder.

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

Et, voici un lien vers la page iTunes WWDC:

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

Créé 20/06/2010 à 20:48
source utilisateur

voix
2

Je pense que vous devez définir le UIScrollView« s contentOffset.

Créé 12/03/2009 à 20:16
source utilisateur

voix
1

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);

}

où 160 est la moitié de la largeur / hauteur de votre UIScrollView.

Puis plus tard, je mis le contentoffset à celui capturé ici.

Créé 31/03/2009 à 09:15
source utilisateur

voix
1

Je souhaiterais que ce soit aussi simple que cela. Je l'ai fait quelques recherches sur le net et a constaté que ce n'est pas seulement mon problème, mais beaucoup de gens sont aux prises avec le même problème non seulement sur iPhone, mais sur le cacao de bureau d'Apple ainsi. 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 UIViewContentModeScaleAspectFit propriété 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 rebondissant semble être beaucoup plus grand que l'image.

Ce gars n'a pas obtenu la réponse soit:
http://discussions.apple.com/thread.jspa?messageID=8322675

Et enfin, le même problème sur le bureau d'Apple Cocoa:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
Je suppose que la solution fonctionne, mais il est basé sur le NSClipView, ce qui est sur l' iPhone ...

Tout le monde a une solution de travail sur l'iPhone?

Créé 13/03/2009 à 08:29
source utilisateur

voix
0

Voici une solution alternative, similaire à @ la réponse de JosephH, mais celui-ci prend en compte les dimensions réelles de l'image. De sorte que lorsque les casseroles utilisateur / zooms, vous n'avez plus des espaces à l'écran que nécessaire. Ceci est un problème commun, par exemple en montrant une image de paysage sur un écran de portrait. Il va être des espaces dessus et en dessous de l'image lorsque l'image entière est à l'écran (Aspect Fit). Ensuite, lorsque vous zoomez, les autres solutions considèrent que les espaces dans le cadre de l'image, car il est dans le imageView. Ils vous laisseront un panoramique la majorité de l'image hors de l'écran, ne laissant que les espaces blancs visibles. Cela semble mauvais pour l'utilisateur.

Avec cette classe, vous avez besoin de passer le imageView il travaille avec. Je suis tenté de le faire détecter automatiquement, mais cela est plus rapide et que vous voulez toute la vitesse que vous pouvez obtenir dans la layoutSubviewsméthode.

Remarque: Comme, cela exige que AutoLayout n'est 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
    }
}
Créé 05/05/2016 à 09:56
source utilisateur

voix
0

En MonoTouch qui a fonctionné pour moi.

this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false);
Créé 08/08/2012 à 15:45
source utilisateur

voix
0

Une façon élégante de centrer le contenu UISCrollViewest la suivante.

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

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

Maintenant sur votre méthode d'observation:

- (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 point de vue du contenu UIView.

    • Également modifier imageGallerypointant vers votre taille de la fenêtre.

Cela corrigera le centre du contenu à chaque fois que son de changement de taille.

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

Créé 03/11/2009 à 18:14
source utilisateur

voix
0

D'accord, je pense que je l'ai trouvé une assez bonne solution à ce problème. L'astuce consiste à se réadapter en permanence le cadre du imageView. Je trouve cela fonctionne beaucoup mieux que d'ajuster constamment les contentInsets ou contentOffSets. Je devais ajouter un peu de code supplémentaire pour accueillir à la fois portrait et paysage images.

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];
}
Créé 04/10/2009 à 03:16
source utilisateur

voix
0

Remarque: ce type de méthode d'œuvres. si l'image est plus petite que la imageView, il défilera partiellement hors de l'écran. Pas un gros problème, mais pas aussi bien que l'application de photos.

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

 [myImageView setFrame:self.view.frame];

Ensuite, centrez votre image dans le imageview:

 myImageView.contentMode = UIViewContentModeCenter;

Voici mon code entier:

- (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];
 }
Créé 08/09/2009 à 03:14
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more