web-dev-qa-db-fra.com

IOS: ajouter une vue d'image dans une vue de défilement pour avoir un zoom

Je veux définir un UIImageView avec un UIImage et mettre cette imageview dans un UIScrollView pour obtenir un zoom de cette image; et je veux que ces UIImageView et UIScrollView tiennent dans le rect au centre de la vue ... est-ce possible?

29
CrazyDev
  1. Configurez votre contrôleur de vue en tant que <UIScrollViewDelegate>
  2. Dessinez votre UIScrollView de la taille souhaitée pour le rectangle au centre de la vue. Réglez le zoom maximum dans l'inspecteur sur quelque chose de plus grand que 1. Comme 4 ou 10.
  3. Cliquez avec le bouton droit sur la vue de défilement et connectez le délégué à votre contrôleur de vue.
  4. Dessinez votre UIImageView dans le UIScrollView et configurez-le avec l'image que vous voulez. Donnez-lui la même taille que le UIScrollView.
  5. Ctrl + faites glisser le formulaire que vous UIImageView vers le .h de votre contrôleur View pour créer un IBOutlet pour le UIImageView, appelez-le quelque chose d'intelligent comme imageView.
  6. Ajoutez ce code:

    -(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
    {
        return self.imageView;
    }
    
  7. Exécutez l'application et pincez et faites un panoramique jusqu'au contenu de votre cœur.

90
Justin Paulson

Téléchargez les fichiers this et this . Vous en aurez besoin pour gérer les touches.

Ajoutez à votre vue le délégué scrollView <UIScrollViewDelegate> et déclarer les points de vente:

 @property (nonatomic, retain) IBOutlet UIScrollView *imageScrollView;
 @property (nonatomic, retain) UIImageView *imageView;

Importez le fichier téléchargé dans l'écran et faites:

#import "TapDetectingImageView.h"

#define ZOOM_STEP 2.0
@interface myView (UtilityMethods)
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center;
@end


@implementation myView
@synthesize imageScrollView, imageView;


- (void)viewDidLoad
{

    [super viewDidLoad];

    //Setting up the scrollView    
    imageScrollView.bouncesZoom = YES;
    imageScrollView.delegate = self;
    imageScrollView.clipsToBounds = YES;

    //Setting up the imageView
    imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"myImage.png"]];
    imageView.userInteractionEnabled = YES;
    imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin);

    //Adding the imageView to the scrollView as subView
    [imageScrollView addSubview:imageView];
    imageScrollView.contentSize = CGSizeMake(imageView.bounds.size.width, imageView.bounds.size.height);
    imageScrollView.decelerationRate = UIScrollViewDecelerationRateFast;

    //UITapGestureRecognizer set up
    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
    UITapGestureRecognizer *twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTap:)];

    [doubleTap setNumberOfTapsRequired:2];
    [twoFingerTap setNumberOfTouchesRequired:2];

    //Adding gesture recognizer
    [imageView addGestureRecognizer:doubleTap];
    [imageView addGestureRecognizer:twoFingerTap];

    [singleTap release];
    [doubleTap release];
    [twoFingerTap release];

    // calculate minimum scale to perfectly fit image width, and begin at that scale
    float minimumScale = 1.0;//This is the minimum scale, set it to whatever you want. 1.0 = default
    imageScrollView.maximumZoomScale = 4.0;
    imageScrollView.minimumZoomScale = minimumScale;
    imageScrollView.zoomScale = minimumScale;
    [imageScrollView setContentMode:UIViewContentModeScaleAspectFit];
    [imageView sizeToFit];
    [imageScrollView setContentSize:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];



}

- (void)scrollViewDidZoom:(UIScrollView *)aScrollView {
    CGFloat offsetX = (imageScrollView.bounds.size.width > imageScrollView.contentSize.width)? 
    (imageScrollView.bounds.size.width - imageScrollView.contentSize.width) * 0.5 : 0.0;
    CGFloat offsetY = (imageScrollView.bounds.size.height > imageScrollView.contentSize.height)? 
    (imageScrollView.bounds.size.height - imageScrollView.contentSize.height) * 0.5 : 0.0;
    imageView.center = CGPointMake(imageScrollView.contentSize.width * 0.5 + offsetX, 
                                   imageScrollView.contentSize.height * 0.5 + offsetY);
}

- (void)viewDidUnload {
    self.imageScrollView = nil;
    self.imageView = nil;
}



#pragma mark UIScrollViewDelegate methods

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

#pragma mark TapDetectingImageViewDelegate methods

- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {
    // zoom in
    float newScale = [imageScrollView zoomScale] * ZOOM_STEP;

    if (newScale > self.imageScrollView.maximumZoomScale){
        newScale = self.imageScrollView.minimumZoomScale;
        CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];

        [imageScrollView zoomToRect:zoomRect animated:YES];

    }
    else{

        newScale = self.imageScrollView.maximumZoomScale;
        CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];

        [imageScrollView zoomToRect:zoomRect animated:YES];
    }
}


- (void)handleTwoFingerTap:(UIGestureRecognizer *)gestureRecognizer {
    // two-finger tap zooms out
    float newScale = [imageScrollView zoomScale] / ZOOM_STEP;
    CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
    [imageScrollView zoomToRect:zoomRect animated:YES];
}

#pragma mark Utility methods

- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {

    CGRect zoomRect;

    // the zoom rect is in the content view's coordinates. 
    //    At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
    //    As the zoom scale decreases, so more content is visible, the size of the rect grows.
    zoomRect.size.height = [imageScrollView frame].size.height / scale;
    zoomRect.size.width  = [imageScrollView frame].size.width  / scale;

    // choose an Origin so as to get the right center.
    zoomRect.Origin.x    = center.x - (zoomRect.size.width  / 2.0);
    zoomRect.Origin.y    = center.y - (zoomRect.size.height / 2.0);

    return zoomRect;
}

Terminé!

Fondamentalement, ce code fait pour ajouter le imageView comme sous-vue du imageScrollView.

Ensuite, il ajoute les méthodes de classe TapDetecting à scrollView, afin de reconnaître le nombre de taps - le pincement de l'utilisateur et l'ajout de fonctionnalités de zoom.

Vous pouvez définir le minimumScale de l'image, si vous laissez 1.0 l'image doit être affichée telle quelle (si vous la réglez un peu plus bas, elle est mise à l'échelle), et maximumZoomScale, je vous suggère de la laisser à 4, ça va!

Vous pouvez maintenant charger des images par programme à partir de là.

La dernière chose que vous devez faire est d'insérer un UIScrollView dans votre fichier xib et de le lier à imageScrollView. Vous aurez l'image au centre parfait, vous pouvez appuyer deux fois dessus pour zoomer, pincer pour zoomer lors de la configuration du code.

29
Phillip

Avec Swift 4 et iOS 11, vous pouvez utiliser l'une des deux solutions suivantes afin de résoudre votre problème.


#1. Utilisation d'encarts

ViewController.Swift

import UIKit

final class ViewController: UIViewController {

    private let scrollView = ImageScrollView(image: UIImage(named: "image")!)

    override func viewDidLoad() {
        view.backgroundColor = .black
        view.addSubview(scrollView)

        scrollView.frame = view.frame
        scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }

}

ImageScrollView.Swift

import UIKit

final class ImageScrollView: UIScrollView {

    private let imageView = UIImageView()
    override var frame: CGRect {
        didSet {
            if frame.size != oldValue.size { setZoomScale() }
        }
    }

    required init(image: UIImage) {
        super.init(frame: .zero)

        imageView.image = image
        imageView.sizeToFit()
        addSubview(imageView)
        contentSize = imageView.bounds.size

        contentInsetAdjustmentBehavior = .never // Adjust content according to safe area if necessary
        showsVerticalScrollIndicator = false
        showsHorizontalScrollIndicator = false
        alwaysBounceHorizontal = true
        alwaysBounceVertical = true
        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - Helper methods

    func setZoomScale() {
        let widthScale = frame.size.width / imageView.bounds.width
        let heightScale = frame.size.height / imageView.bounds.height
        let minScale = min(widthScale, heightScale)
        minimumZoomScale = minScale
        zoomScale = minScale
    }

}
extension ImageScrollView: UIScrollViewDelegate {

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        let imageViewSize = imageView.frame.size
        let scrollViewSize = scrollView.bounds.size
        let verticalInset = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0
        let horizontalInset = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0
        scrollView.contentInset = UIEdgeInsets(top: verticalInset, left: horizontalInset, bottom: verticalInset, right: horizontalInset)
    }

}

# 2. Utilisation de la mise en page automatique

ViewController.Swift

import UIKit

final class ViewController: UIViewController {

    private let scrollView = ImageScrollView(image: UIImage(named: "image")!)

    override func viewDidLoad() {
        view.backgroundColor = .black
        view.addSubview(scrollView)

        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    }

    override func viewDidLayoutSubviews() {        
        scrollView.setZoomScale()
    }

}

ImageScrollView.Swift

import UIKit

final class ImageScrollView: UIScrollView {

    private let imageView = UIImageView()
    private var imageViewBottomConstraint = NSLayoutConstraint()
    private var imageViewLeadingConstraint = NSLayoutConstraint()
    private var imageViewTopConstraint = NSLayoutConstraint()
    private var imageViewTrailingConstraint = NSLayoutConstraint()

    required init(image: UIImage) {
        super.init(frame: .zero)

        imageView.image = image
        imageView.sizeToFit()
        addSubview(imageView)

        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: leadingAnchor)
        imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: trailingAnchor)
        imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: topAnchor)
        imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: bottomAnchor)
        NSLayoutConstraint.activate([imageViewLeadingConstraint, imageViewTrailingConstraint, imageViewTopConstraint, imageViewBottomConstraint])

        contentInsetAdjustmentBehavior = .never // Adjust content according to safe area if necessary
        showsVerticalScrollIndicator = false
        showsHorizontalScrollIndicator = false
        alwaysBounceHorizontal = true
        alwaysBounceVertical = true
        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - Helper methods

    func setZoomScale() {
        let widthScale = frame.size.width / imageView.bounds.width
        let heightScale = frame.size.height / imageView.bounds.height
        let minScale = min(widthScale, heightScale)
        minimumZoomScale = minScale
        zoomScale = minScale
    }

}
extension ImageScrollView: UIScrollViewDelegate {

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        let yOffset = max(0, (bounds.size.height - imageView.frame.height) / 2)
        imageViewTopConstraint.constant = yOffset
        imageViewBottomConstraint.constant = yOffset

        let xOffset = max(0, (bounds.size.width - imageView.frame.width) / 2)
        imageViewLeadingConstraint.constant = xOffset
        imageViewTrailingConstraint.constant = xOffset

        layoutIfNeeded()
    }

}

Sources:

5
Imanou Petit

J'ai écrit un exemple d'application qui prend également en charge AutoLayout et Storyboards pour illustrer ce comportement. J'espère que cela fera gagner du temps à tout le monde en essayant de comprendre cela: http://rexstjohn.com/facebook-like-ios-photo-modal-gallery-swipe-gestures/ .

3
Delete