UIScrollView con UIImageView centrado, como la aplicación Fotos

votos
37

Me gustaría tener vista de desplazamiento con una vista de contenido de imagen. La imagen es en realidad un mapa que es mucho más grande que la pantalla. El mapa debe estar inicialmente en el centro de la vista de desplazamiento, como las fotos en la aplicación Fotos cuando se convierte el iPhone en orientación horizontal.

texto

No logré tener el mapa en el centro con el zoom correcto y el desplazamiento al mismo tiempo. Siempre que la imagen del mapa comience desde la parte superior de la pantalla (en orientación vertical), el código se ve más o menos así:

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

Cuando muevo el marco de la imagen al centro, la imagen crece solo desde la parte superior de su marco hacia abajo. Intenté jugar con la transformación mapView, con un marco dinámicamente cambiante de imageView. Nada funciona para mí hasta ahora.

Publicado el 12/03/2009 a las 12:56
fuente por usuario
En otros idiomas...                            


11 respuestas

votos
40

Este código debería funcionar en la mayoría de las versiones de IOS (y ha sido probado para trabajar en 3.1 arriba).

Se basa en el código de Apple WWDC mencionado en la respuesta de Jonás.

Añadir el siguiente a su subclase de UIScrollView, y reemplazar tileContainerView con la vista que contiene la imagen o azulejos:

- (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;
}
Respondida el 13/08/2010 a las 17:42
fuente por usuario

votos
23

Esto es lo que yo consideraría, la solución como en ella se comporta exactamente como aplicación de fotos de manzana. Yo había estado utilizando soluciones que utilizan:

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

para volver a centrar, pero no me gustaba esa solución porque después del zoom se llevó a cabo, sería entonces rebotar rápido 'salto' en el centro, que era muy poco atractiva. Resulta que si lo hace casi exactamente la misma lógica pero en esta función delegada:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView

lo tanto comienza centrada y cuando se aleja se mantiene centrada:

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

Donde 'imageView' es el UIImageViewque está utilizando.

Respondida el 03/02/2010 a las 03:51
fuente por usuario

votos
7

Apple ha publicado los vídeos de las sesiones de la WWDC 2010 a todos los miembros del programa de desarrolladores de iPhone. Uno de los temas tratados es cómo crearon la aplicación de fotos !!! Construyen un paso aplicación muy similar a paso y han hecho todo el código disponible de forma gratuita.

No utiliza la API privada tampoco. No puedo poner cualquier parte del código aquí por el acuerdo de confidencialidad, pero aquí hay un enlace a la descarga de código de ejemplo. Es probable que tenga que iniciar sesión para obtener acceso.

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

Y, aquí hay un enlace a la página de 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

Respondida el 20/06/2010 a las 20:48
fuente por usuario

votos
2

Sospecho que necesitas configurar los UIScrollView's contentOffset.

Respondida el 12/03/2009 a las 20:16
fuente por usuario

votos
1

Esto funcionó para mí:

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

}

donde 160 es la mitad del ancho / alto de su UIScrollView.

Luego más tarde configuré el contentoffset al capturado aquí.

Respondida el 31/03/2009 a las 09:15
fuente por usuario

votos
1

Ojalá fuera así de simple. Hice algunas investigaciones en la red y descubrí que no es solo mi problema, pero muchas personas están luchando con el mismo problema no solo en el iPhone, sino también en el escritorio Cocoa de Apple. Ver los siguientes enlaces:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
La solución descrita se basa en la propiedad UIViewContentModeScaleAspectFit de la imagen, pero desafortunadamente no funciona muy bien. La imagen es centrado y crece correctamente, pero el área de rebote parece ser mucho más grande que la imagen.

Este tipo tampoco obtuvo la respuesta:
http://discussions.apple.com/thread.jspa?messageID=8322675

Y finalmente, el mismo problema en el escritorio de Apple Cocoa:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
Supongo que la solución funciona, pero está basada en NSClipView, que no está en iPhone ...

¿Alguien tiene alguna solución para iPhone?

Respondida el 13/03/2009 a las 08:29
fuente por usuario

votos
0

Aquí hay una solución alternativa, similar a la respuesta de @ JosephH, pero éste tiene en cuenta las dimensiones reales de la imagen. Así que cuando las sartenes de usuario / zoom, nunca tienen más espacio en blanco en la pantalla de lo necesario. Este es un problema común, por ejemplo, al mostrar una imagen de paisaje en una pantalla vertical. No va a haber espacio en blanco por encima y por debajo de la imagen cuando la imagen completa está en la pantalla (Aspecto Fit). Entonces, cuando el zoom, las otras soluciones consideran que los espacios en blanco como parte de la imagen, porque es en el imageView. Se le hará una panorámica de la mayor parte de la imagen fuera de la pantalla, dejando sólo el espacio en blanco visible. Esto se ve mal para el usuario.

Con esta clase, usted tiene que pasar el imageView que está trabajando. Estaba tentado a tener que detectar automáticamente, pero esto es más rápido y desea que toda la velocidad que puede obtener en el layoutSubviewsmétodo.

Nota: Como es, esto requiere que diseño automático no está habilitado para el 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
    }
}
Respondida el 05/05/2016 a las 06:56
fuente por usuario

votos
0

En MonoTouch que trabajó para mí.

this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false);
Respondida el 08/08/2012 a las 15:45
fuente por usuario

votos
0

Una manera elegante de centrar el contenido de UISCrollViewesto es esto.

Agregue un observador al contenido Tamaño de su UIScrollView, por lo que se llamará a este método cada vez que cambie el contenido ...

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

Ahora en tu método de observación:

- (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 ); }
  • Deberías cambiar el [pointer viewWithTag:100]. Reemplazar por su vista de contenido UIView.

    • También cambie imageGalleryseñalando el tamaño de su ventana.

Esto corregirá el centro del contenido cada vez que cambie su tamaño.

NOTA: La única forma en que este contenido no funciona muy bien es con la funcionalidad de zoom estándar del UIScrollView.

Respondida el 03/11/2009 a las 18:14
fuente por usuario

votos
0

De acuerdo, creo que he encontrado una solución bastante buena para este problema. El truco es reajustar constantemente el marco de imageView. Encuentro que esto funciona mucho mejor que ajustar constantemente contentInsets o contentOffSets. Tuve que agregar un poco de código adicional para acomodar imágenes de retrato y paisaje.

Aquí está el código:

- (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];
}
Respondida el 04/10/2009 a las 03:16
fuente por usuario

votos
0

Nota: este método funciona de alguna manera. si la imagen es más pequeña que la vista de imagen, se desplazará parcialmente fuera de la pantalla. No es un gran problema, pero tampoco es tan agradable como la aplicación de fotos.

En primer lugar, es importante comprender que estamos tratando con 2 vistas, la vista de la imagen con la imagen y la vista de desplazamiento con la vista de la imagen que contiene. Entonces, primero configure la vista de la imagen para el tamaño de la pantalla:

 [myImageView setFrame:self.view.frame];

Luego, centra tu imagen en la vista de imagen:

 myImageView.contentMode = UIViewContentModeCenter;

Aquí está mi código completo:

- (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];
 }
Respondida el 08/09/2009 a las 03:14
fuente por usuario

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