Comment faire un zoom avant / arrière sur un objet UIImage lorsque l'utilisateur pince l'écran?

84

Je souhaite effectuer un zoom avant / arrière sur un objet UIImage lorsque l'utilisateur exécute l'action de pincement standard sur mon application. J'utilise actuellement une UIImageView pour afficher mon image, si ce détail aide de quelque manière que ce soit.

J'essaie de comprendre comment faire cela, mais pas de chance pour l'instant.

Des indices?

jpm
la source

Réponses:

85

Un autre moyen simple de le faire est de placer votre UIImageViewfichier dans un fichier UIScrollView. Comme je le décris ici , vous devez définir le contentSize de la vue de défilement pour qu'il soit identique à votre UIImageView'staille. Définissez votre instance de contrôleur comme délégué de la vue de défilement et implémentez les méthodes viewForZoomingInScrollView:et scrollViewDidEndZooming:withView:atScale:pour permettre le zoom par pincement et le panoramique de l'image. C'est effectivement ce que fait la solution de Ben, mais de manière légèrement plus légère, car vous n'avez pas la surcharge d'une vue Web complète.

Un problème que vous pouvez rencontrer est que la mise à l'échelle dans la vue de défilement se présente sous la forme de transformations appliquées à l'image. Cela peut entraîner un flou à des facteurs de zoom élevés. Pour quelque chose qui peut être redessiné, vous pouvez suivre mes suggestions ici pour fournir un affichage plus net une fois le geste de pincement terminé. La solution de hniels pourrait être utilisée à ce stade pour redimensionner votre image.

Brad Larson
la source
2
Assurez-vous de modifier maximumZoomScale et / ou minimumZoomScale :).
Chris Prince
94

Comme d'autres l'ont décrit, la solution la plus simple consiste à placer votre UIImageView dans un UIScrollView. Je l'ai fait dans le fichier .xib d'Interface Builder.

Dans viewDidLoad, définissez les variables suivantes. Définissez votre contrôleur comme 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;
}

Vous devez implémenter la méthode suivante pour renvoyer l'imageView que vous souhaitez agrandir.

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

Dans les versions antérieures à iOS9, vous devrez peut-être également ajouter cette méthode de délégué vide:

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

La documentation Apple décrit bien comment procéder:

coquin
la source
3
Vous pouvez supprimer scrollViewDidEndZooming: withView: maintenant, j'ai testé avec iOS9 Xcode7
ElonChan
1
Ça marche pour moi! Sans scrollViewDidEndZooming :méthode de mise en œuvre .
Johnny Zhang
48

La solution de Shefali pour UIImageView fonctionne très bien, mais elle nécessite une petite modification:

- (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 solution de Shefali avait l'inconvénient de ne pas être mise à l'échelle en continu pendant le pincement. De plus, lors du démarrage d'un nouveau pincement, l'échelle d'image actuelle était réinitialisée.)

JRV
la source
Je veux que le mouvement de panoramique soit activé après le début du mouvement de pincement, comment faire?
Gajendra K Chauhan
@Gajendra: Dans ce cas, je pense que vous devriez utiliser incorporer vos composants dans une vue de défilement, qui prend en charge les deux
prêts à l'emploi
Mais je veux des fonctionnalités pour les gestes de pression et de pincement. J'ai également utilisé la grille (couches) sur Imageview. Puis-je savoir, est-il possible d'utiliser le geste de pincement et le geste de toucher en mode défilement.?
Gajendra K Chauhan
@GajendraKChauhan: Une vue de défilement prend en charge le pincement pour zoomer et le panoramique hors de la boîte - n'oubliez pas de définir l'échelle de zoom minimum / maximum, mais regardez également la réponse de Brad Larsons. Et si vous voulez également un geste de pression, vous pouvez l'ajouter en tant que geste.
JRV
@JRV, quels délégués dois-je ajouter pour que le self.frame.size.width soit reconnu?
23

Le code ci-dessous permet de zoomer UIImageView sans utiliser 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
la source
C'est la solution suprême :). Merci.
sabiland
19

Gardez à l'esprit que vous ne zoomez JAMAIS sur un fichier UIImage. DÉJÀ.

Au lieu de cela, vous effectuez un zoom avant et arrière sur le viewqui affiche le UIImage.

Dans ce cas particulier, vous devez choisir de créer un UIViewdessin personnalisé avec un dessin personnalisé pour afficher l'image, un UIImageViewqui affiche l'image pour vous, ou un UIWebViewqui aura besoin de HTML supplémentaire pour le sauvegarder.

Dans tous les cas, vous devrez implémenter touchesBegan, touchesMovedetc. pour déterminer ce que l'utilisateur essaie de faire (zoom, panoramique, etc.).

août
la source
+1 pour clarification, je n'y ai jamais pensé de cette façon, bravo mec
Elmo
@Août, pouvez-vous me donner un exemple de zoom et de rotation de l'image sur les touchesBegan, touchesMoved?
Mitesh Dobareeya
7

Voici une solution que j'ai déjà utilisée et qui ne vous oblige pas à utiliser 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;
}

Cet article est disponible sur l'assistance Apple à l' adresse : http://discussions.apple.com/message.jspa?messageID=7276709#7276709

Niels Hansen
la source
-1 pour une page de support hébergée par jsp. secoue le poing à la syntaxe des points
esharp
Pouvez-vous s'il vous plaît me fournir comment appeler cette méthode (sur déplacement de la souris ou toucher)?
Bhumesh Purohit
5

Les réponses de Shafali et JRV ont été étendues pour inclure le panoramique et le pincement pour zoomer:

#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
la source
veuillez éviter de mentionner la réponse des autres, car ils peuvent supprimer leur réponse à tout moment.
Enamul Hassan
3

Le moyen le plus simple de le faire, si tout ce que vous voulez est un zoom par pincement, est de placer votre image dans un UIWebView(écrivez une petite quantité de code html, référencez votre image et vous avez pratiquement terminé). Plus complcated façon de le faire est d'utiliser touchesBegan, touchesMovedet touchesEndedde garder une trace des doigts de l'utilisateur, et d' ajuster votre vue de transformer la propriété appropriée.

Ben Gottlieb
la source
pouvez-vous me suggérer un exemple lié à votre réponse. Comment puis-je zoomer et faire pivoter l'image à l'aide de touchesBegan, méthodes touchesMoved. Ma question lien => stackoverflow.com/questions/36833841/…
Mitesh Dobareeya
0

Gardez à l'esprit que vous ne souhaitez pas effectuer un zoom avant / arrière sur UIImage. Essayez plutôt de zoomer / dézoomer la vue qui contient le contrôleur de vue UIImage.

J'ai fait une solution à ce problème. Jetez un œil à mon code:

@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: N'oubliez pas de brancher le PinchGestureRecognizer.

G Azam
la source