¿Cómo acercar / alejar un objeto de UIImage cuando el usuario pellizca la pantalla?

84

Me gustaría acercar / alejar un objeto UIImage cuando el usuario realiza la acción de pellizcar estándar en mi aplicación. Actualmente estoy usando un UIImageView para mostrar mi imagen, si ese detalle ayuda de alguna manera.

Estoy tratando de averiguar cómo hacer esto, pero no he tenido tanta suerte hasta ahora.

¿Alguna pista?

jpm
fuente

Respuestas:

85

Otra forma fácil de hacer esto es colocar su UIImageViewdentro de un UIScrollView. Como describo aquí , debe configurar el contentSize de la vista de desplazamiento para que sea el mismo que su UIImageView'stamaño. Configure la instancia de su controlador para que sea la delegada de la vista de desplazamiento e implemente los métodos viewForZoomingInScrollView:y scrollViewDidEndZooming:withView:atScale:para permitir el zoom y la panorámica de la imagen. Esto es efectivamente lo que hace la solución de Ben, solo que de una manera un poco más liviana, ya que no tiene la sobrecarga de una vista web completa.

Un problema con el que puede encontrarse es que la escala dentro de la vista de desplazamiento se presenta en forma de transformaciones aplicadas a la imagen. Esto puede provocar imágenes borrosas con factores de zoom altos. Para algo que se pueda volver a dibujar, puede seguir mis sugerencias aquí para proporcionar una pantalla más nítida después de que finalice el gesto de pellizcar. La solución de hniels podría usarse en ese momento para cambiar la escala de su imagen.

Brad Larson
fuente
2
Asegúrese de cambiar maximumZoomScale y / o minimumZoomScale :).
Chris Prince
94

Como otros describieron, la solución más fácil es poner su UIImageView en un UIScrollView. Hice esto en el archivo .xib de Interface Builder.

En viewDidLoad, establezca las siguientes variables. Configure su controlador para que sea un UIScrollViewDelegate.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.scrollView.minimumZoomScale = 0.5;
    self.scrollView.maximumZoomScale = 6.0;
    self.scrollView.contentSize = self.imageView.frame.size;
    self.scrollView.delegate = self;
}

Debe implementar el siguiente método para devolver el imageView que desea ampliar.

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}

En versiones anteriores a iOS9, es posible que también deba agregar este método de delegado vacío:

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

La Documentación de Apple hace un buen trabajo al describir cómo hacer esto:

pícaro
fuente
3
Está bien eliminar scrollViewDidEndZooming: withView: ahora, lo he probado con iOS9 Xcode7
ElonChan
1
¡Esto funciona para mi! Sin scrollViewDidEndZooming :método de implementación .
Johnny Zhang
48

La solución de Shefali para UIImageView funciona muy bien, pero necesita una pequeña modificación:

- (void)pinch:(UIPinchGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded
        || gesture.state == UIGestureRecognizerStateChanged) {
        NSLog(@"gesture.scale = %f", gesture.scale);

        CGFloat currentScale = self.frame.size.width / self.bounds.size.width;
        CGFloat newScale = currentScale * gesture.scale;

        if (newScale < MINIMUM_SCALE) {
            newScale = MINIMUM_SCALE;
        }
        if (newScale > MAXIMUM_SCALE) {
            newScale = MAXIMUM_SCALE;
        }

        CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
        self.transform = transform;
        gesture.scale = 1;
    }
}

(La solución de Shefali tenía la desventaja de que no escalaba continuamente mientras se pellizcaba. Además, al comenzar una nueva pizca, se restablecía la escala de imagen actual).

JRV
fuente
Quiero que se habilite el gesto panorámico después de que el gesto de pellizcar comience.
Gajendra K Chauhan
@Gajendra: En ese caso, creo que debería usar incrustar sus componentes en una vista de desplazamiento, que admite ambos fuera de la caja
JRV
Pero quiero funcionalidad tanto para tocar como para pellizcar. También he usado cuadrículas (capas) en Imageview. ¿Puedo saber si es posible utilizar el gesto de pellizcar y el gesto de tocar en la vista de desplazamiento?
Gajendra K Chauhan
@GajendraKChauhan: Una vista de desplazamiento admite pellizcar para hacer zoom y desplazarse fuera de la caja; solo recuerde establecer la escala de zoom mínima / máxima, pero mire también la respuesta de Brad Larson. Y si también desea tocar el gesto, puede agregarlo como un gesto.
JRV
@JRV, ¿qué delegados debo agregar para que se reconozca el self.frame.size.width?
23

El siguiente código ayuda a hacer zoom en UIImageView sin usar UIScrollView:

-(void)HandlePinch:(UIPinchGestureRecognizer*)recognizer{
    if ([recognizer state] == UIGestureRecognizerStateEnded) {
        NSLog(@"======== Scale Applied ===========");
        if ([recognizer scale]<1.0f) {
            [recognizer setScale:1.0f];
        }
        CGAffineTransform transform = CGAffineTransformMakeScale([recognizer scale],  [recognizer scale]);
        imgView.transform = transform;
    }
}
Shefali Soni
fuente
Esta es la solución suprema :). Gracias.
sabiland
19

Tenga en cuenta que NUNCA está haciendo zoom en un UIImage. SIEMPRE.

En su lugar, está acercando y alejando el viewque muestra el UIImage.

En este caso particular, puede optar por crear un UIViewdibujo personalizado con un dibujo personalizado para mostrar la imagen, uno UIImageViewque muestre la imagen para usted, o UIWebViewque necesitará algo de HTML adicional para respaldarlo.

En todos los casos, deberá implementar touchesBegan, touchesMovedy similares para determinar qué está tratando de hacer el usuario (zoom, panorámica, etc.).

agosto
fuente
+1 para una aclaración, nunca lo pensé de esa manera, salud, amigo
Elmo
@August, ¿me puede proporcionar algún ejemplo que acerque y gire la imagen en touchesBegan, touchesMoved?
Mitesh Dobareeya
7

Aquí hay una solución que he usado antes que no requiere que use UIWebView.

- (UIImage *)scaleAndRotateImage(UIImage *)image
{
    int kMaxResolution = 320; // Or whatever

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);


    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = bounds.size.width / ratio;
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = bounds.size.height * ratio;
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient) {

        case UIImageOrientationUp: //EXIF = 1
            transform = CGAffineTransformIdentity;
            break;

        case UIImageOrientationUpMirrored: //EXIF = 2
            transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            break;

        case UIImageOrientationDown: //EXIF = 3
            transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationDownMirrored: //EXIF = 4
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
            transform = CGAffineTransformScale(transform, 1.0, -1.0);
            break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationLeft: //EXIF = 6
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationRightMirrored: //EXIF = 7
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeScale(-1.0, 1.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        case UIImageOrientationRight: //EXIF = 8
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

    }

    UIGraphicsBeginImageContext(bounds.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
        CGContextScaleCTM(context, -scaleRatio, scaleRatio);
        CGContextTranslateCTM(context, -height, 0);
    }
    else {
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextTranslateCTM(context, 0, -height);
    }

    CGContextConcatCTM(context, transform);

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}

El artículo se puede encontrar en Soporte de Apple en: http://discussions.apple.com/message.jspa?messageID=7276709#7276709

Niels Hansen
fuente
-1 para una página de soporte alojada en jsp. agita el puño en la sintaxis del punto
esharp
¿Me pueden indicar cómo llamar a este método (al mover o tocar el mouse)?
Bhumesh Purohit
5

Las respuestas de Shafali y JRV se ampliaron para incluir la panorámica y pellizcar para hacer zoom:

#define MINIMUM_SCALE 0.5
#define MAXIMUM_SCALE 6.0
@property CGPoint translation;


- (void)pan:(UIPanGestureRecognizer *)gesture {
    static CGPoint currentTranslation;
    static CGFloat currentScale = 0;
    if (gesture.state == UIGestureRecognizerStateBegan) {
        currentTranslation = _translation;
        currentScale = self.view.frame.size.width / self.view.bounds.size.width;
    }
    if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateChanged) {

        CGPoint translation = [gesture translationInView:self.view];

        _translation.x = translation.x + currentTranslation.x;
        _translation.y = translation.y + currentTranslation.y;
        CGAffineTransform transform1 = CGAffineTransformMakeTranslation(_translation.x , _translation.y);
        CGAffineTransform transform2 = CGAffineTransformMakeScale(currentScale, currentScale);
        CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
        self.view.transform = transform;
    }
}


- (void)pinch:(UIPinchGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateChanged) {
//        NSLog(@"gesture.scale = %f", gesture.scale);

        CGFloat currentScale = self.view.frame.size.width / self.view.bounds.size.width;
        CGFloat newScale = currentScale * gesture.scale;

        if (newScale < MINIMUM_SCALE) {
            newScale = MINIMUM_SCALE;
        }
        if (newScale > MAXIMUM_SCALE) {
            newScale = MAXIMUM_SCALE;
        }

        CGAffineTransform transform1 = CGAffineTransformMakeTranslation(_translation.x, _translation.y);
        CGAffineTransform transform2 = CGAffineTransformMakeScale(newScale, newScale);
        CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
        self.view.transform = transform;
        gesture.scale = 1;
    }
}
Paul King
fuente
evite mencionar la respuesta de otros, ya que pueden eliminar su respuesta en cualquier momento.
Enamul Hassan
3

La forma más sencilla de hacer esto, si todo lo que desea es hacer zoom, es colocar su imagen dentro de un UIWebView(escriba una pequeña cantidad de código de envoltura html, haga referencia a su imagen y básicamente ya está). La forma más completa de hacer esto es usar touchesBegan, touchesMovedy touchesEndedrealizar un seguimiento de los dedos del usuario y ajustar la propiedad de transformación de la vista de manera adecuada.

Ben Gottlieb
fuente
¿Puedes sugerirme algún ejemplo relacionado con tu respuesta? ¿Cómo puedo hacer zoom y rotar la imagen usando los métodos touchesBegan, touchesMoved? Enlace de mi pregunta => stackoverflow.com/questions/36833841/…
Mitesh Dobareeya
0

Tenga en cuenta que no desea acercar / alejar UIImage. En su lugar, intente acercar / alejar la vista que contiene el controlador de vista UIImage.

He creado una solución para este problema. Eche un vistazo a mi código:

@IBAction func scaleImage(sender: UIPinchGestureRecognizer) {
        self.view.transform = CGAffineTransformScale(self.view.transform, sender.scale, sender.scale)
        sender.scale = 1
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        view.backgroundColor = UIColor.blackColor()
    }

NB: No olvide conectar PinchGestureRecognizer.

G Azam
fuente