web-dev-qa-db-fra.com

Centrer le contenu de UIScrollView lorsqu'il est plus petit

J'ai un UIImageView à l'intérieur d'un UIScrollView que j'utilise pour le zoom et le défilement. Si l'image/le contenu de la vue de défilement est plus grand que la vue de défilement, tout fonctionne correctement. Cependant, lorsque l'image devient plus petite que la vue par défilement, elle reste collée dans le coin supérieur gauche de la vue. Je voudrais le garder centré, comme dans l'application Photos.

Des idées ou des exemples pour garder le contenu du UIScrollView centré quand il est plus petit?

Je travaille avec iPhone 3.0.

Le code suivant fonctionne presque. L'image retourne dans le coin supérieur gauche si je la pince après avoir atteint le niveau de zoom minimum.

- (void)loadView {
    [super loadView];

    // set up main scroll view
    imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
    [imageScrollView setBackgroundColor:[UIColor blackColor]];
    [imageScrollView setDelegate:self];
    [imageScrollView setBouncesZoom:YES];
    [[self view] addSubview:imageScrollView];

    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]];
    [imageView setTag:ZOOM_VIEW_TAG];
    [imageScrollView setContentSize:[imageView frame].size];
    [imageScrollView addSubview:imageView];

    CGSize imageSize = imageView.image.size;
    [imageView release];

    CGSize maxSize = imageScrollView.frame.size;
    CGFloat widthRatio = maxSize.width / imageSize.width;
    CGFloat heightRatio = maxSize.height / imageSize.height;
    CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;

    [imageScrollView setMinimumZoomScale:initialZoom];
    [imageScrollView setZoomScale:1];

    float topInset = (maxSize.height - imageSize.height) / 2.0;
    float sideInset = (maxSize.width - imageSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}

/************************************** NOTE **************************************/
/* The following delegate method works around a known bug in zoomToRect:animated: */
/* In the next release after 3.0 this workaround will no longer be necessary      */
/**********************************************************************************/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    [scrollView setZoomScale:scale+0.01 animated:NO];
    [scrollView setZoomScale:scale animated:NO];
    // END Bug workaround

    CGSize maxSize = imageScrollView.frame.size;
    CGSize viewSize = view.frame.size;
    float topInset = (maxSize.height - viewSize.height) / 2.0;
    float sideInset = (maxSize.width - viewSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}
136
hpique

Actuellement, je sous-classe UIScrollView et redéfinis setContentOffset: pour ajuster le décalage en fonction de contentSize. Il fonctionne à la fois avec le zoom par pincement et le zoom programmatique.

@implementation HPCenteringScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
    const CGSize contentSize = self.contentSize;
    const CGSize scrollViewSize = self.bounds.size;

    if (contentSize.width < scrollViewSize.width)
    {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0;
    }

    if (contentSize.height < scrollViewSize.height)
    {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0;
    }

    [super setContentOffset:contentOffset];
}

@end

En plus d'être court et agréable, ce code produit un zoom beaucoup plus fluide que la solution @Erdemus. Vous pouvez le voir en action dans la démo RMGallery .

20
hpique

J'ai une solution très simple! Tout ce dont vous avez besoin est de mettre à jour le centre de votre sous-vue (imageview) tout en zoomant sur ScrollViewDelegate. Si l'image agrandie est plus petite que scrollview, ajustez subview.center sinon le centre est (0,0).

- (void)scrollViewDidZoom:(UIScrollView *)scrollView 
{
    UIView *subView = [scrollView.subviews objectAtIndex:0];

    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, 
                                 scrollView.contentSize.height * 0.5 + offsetY);
}
227
Erdemus

La réponse de @ EvelynCordner était celle qui fonctionnait le mieux dans mon application. Beaucoup moins de code que les autres options aussi.

Voici la version Swift si quelqu'un en a besoin:

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}
43
William T.

D'accord, je me bats de temps en temps depuis deux jours et étant finalement parvenu à une solution assez fiable (jusqu'à présent ...), j'ai pensé que je devrais la partager et sauver certaines douleurs. :) Si vous trouvez un problème avec cette solution, merci de crier!

J'ai en gros parcouru ce que tout le monde a: chercher dans StackOverflow, le Apple pour les développeurs, examiner le code pour three20, ScrollingMadness, ScrollTestSuite, etc.). J'ai essayé d'élargir le cadre UIImageView jouer avec l'offset de UIScrollView et/ou les incrustations de ViewController, etc.

Après avoir dormi dessus, j'ai essayé plusieurs angles de vue différents:

  1. Sous-classer UIImageView pour qu'il modifie sa propre taille de manière dynamique - cela ne fonctionnait pas du tout.
  2. Sous-classer UIScrollView pour qu'il modifie son propre contenuOffset de manière dynamique - c'est celui qui semble être un gagnant pour moi.

Avec cette méthode de sous-classe UIScrollView, je redéfinis le mutateur contentOffset afin que le paramètre {0,0} ne soit pas défini lorsque l'image est plus petite que la fenêtre d'affichage. Elle définit plutôt le décalage de sorte que l'image reste centrée dans la fenêtre. Jusqu'à présent, cela semble toujours fonctionner. Je l'ai vérifié avec des images larges, hautes, minuscules et grandes et je n'ai pas le problème "fonctionne mais pincer au zoom minimum".

J'ai téléchargé un exemple de projet sur github qui utilise cette solution. Vous pouvez le trouver ici: http://github.com/nyoron/NYOBetterZoom

25
Liam Jones

Ce code devrait fonctionner sur la plupart des versions d'iOS (et a été testé pour fonctionner à partir de la version 3.1).

Il est basé sur le code WWDC Apple) du photoscoller.

Ajoutez le texte ci-dessous à votre sous-classe de UIScrollView et remplacez tileContainerView par la vue contenant votre image ou vos 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;
}
23
JosephH

Pour une solution mieux adaptée aux vues de défilement utilisant l'autolayout, utilisez des encarts de contenu de la vue de défilement plutôt que de mettre à jour les images des sous-vues de votre vue de défilement.

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
21
Evelyn Cordner

J'ai passé une journée à me battre avec ce problème et j'ai fini par implémenter le scrollViewDidEndZooming: withView: atScale: comme suit:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
    CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
    CGFloat viewWidth = view.frame.size.width;
    CGFloat viewHeight = view.frame.size.height;

    CGFloat x = 0;
    CGFloat y = 0;

    if(viewWidth < screenWidth) {
        x = screenWidth / 2;
    }
    if(viewHeight < screenHeight) {
        y = screenHeight / 2 ;
    }

    self.scrollView.contentInset = UIEdgeInsetsMake(y, x, y, x);
}

Cela garantit que, lorsque l'image est plus petite que l'écran, il y a encore suffisamment d'espace autour pour que vous puissiez la positionner exactement à l'emplacement souhaité.

(en supposant que votre UIScrollView contienne un UIImageView pour contenir l'image)

Il s’agit essentiellement de vérifier si la largeur/hauteur de la vue de l’image est inférieure à la largeur/hauteur de l’écran et, le cas échéant, de créer un encart égal à la moitié de la largeur/hauteur de l’écran (vous pouvez probablement l’agrandir si vous souhaitez que l’image sortir des limites de l'écran).

Notez que puisqu'il s'agit d'une méthode UIScrollViewDelegate , n'oubliez pas de l'ajouter à la déclaration de votre contrôleur de vue afin d'éviter de générer un problème de construction.

12
Vicarius

Apple a publié les vidéos de session WWDC 2010 pour tous les membres du programme pour développeurs iphone. L'un des sujets abordés est la création de l'application de photos !!! Ils construisent pas à pas une application très similaire et ont mis tout le code à disposition gratuitement.

Il n'utilise pas non plus d'api privé. Je ne peux mettre aucun code ici à cause de l'accord de non-divulgation, mais voici un lien vers l'exemple de téléchargement de code. Vous aurez probablement besoin de vous connecter pour avoir accès.

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

7
Jonah

Ok, cette solution fonctionne pour moi. J'ai une sous-classe de UIScrollView avec une référence au UIImageView qu'il affiche. Chaque fois que le UIScrollView effectue un zoom, la propriété contentSize est ajustée. C’est dans le configurateur que j’adapte UIImageView correctement et ajuste également sa position centrale.

-(void) setContentSize:(CGSize) size{
CGSize lSelfSize = self.frame.size;
CGPoint mid;
if(self.zoomScale >= self.minimumZoomScale){
    CGSize lImageSize = cachedImageView.initialSize;
    float newHeight = lImageSize.height * self.zoomScale;

    if (newHeight < lSelfSize.height ) {
        newHeight = lSelfSize.height;
    }
    size.height = newHeight;

    float newWidth = lImageSize.width * self.zoomScale;
    if (newWidth < lSelfSize.width ) {
        newWidth = lSelfSize.width;
    }
    size.width = newWidth;
    mid = CGPointMake(size.width/2, size.height/2);

}
else {
    mid = CGPointMake(lSelfSize.width/2, lSelfSize.height/2);
}

cachedImageView.center = mid;
[super  setContentSize:size];
[self printLocations];
NSLog(@"zoom %f setting size %f x %f",self.zoomScale,size.width,size.height);
}

Heure normale, je place l'image sur le UIScrollView je la redimensionne. Le UIScrollView dans le scrollview est également une classe personnalisée que j'ai créée.

-(void) resetSize{
    if (!scrollView){//scroll view is view containing imageview
        return;
    }

    CGSize lSize = scrollView.frame.size;

    CGSize lSelfSize = self.image.size; 
    float lWidth = lSize.width/lSelfSize.width;
    float lHeight = lSize.height/lSelfSize.height;

    // choose minimum scale so image width fits screen
    float factor  = (lWidth<lHeight)?lWidth:lHeight;

    initialSize.height = lSelfSize.height  * factor;
    initialSize.width = lSelfSize.width  * factor;

    [scrollView setContentSize:lSize];
    [scrollView setContentOffset:CGPointZero];
    scrollView.userInteractionEnabled = YES;
}

Avec ces deux méthodes, je peux avoir une vue qui se comporte exactement comme pour l'application Photos.

2
AHA

Je sais que certaines des réponses ci-dessus sont exactes, mais je veux juste donner ma réponse avec quelques explications, les commentaires vous permettront de comprendre pourquoi nous procédons ainsi.

Lorsque je charge le scrollView pour la première fois, j'écris le code suivant pour le centrer. Notez que nous définissons d'abord contentOffset, puis contentInset.

    scrollView.maximumZoomScale = 8
    scrollView.minimumZoomScale = 1

    // set vContent frame
    vContent.frame = CGRect(x: 0,
                            y: 0  ,
                            width: vContentWidth,
                            height: vContentWidth)
    // set scrollView.contentSize
    scrollView.contentSize = vContent.frame.size

    //on the X direction, if contentSize.width > scrollView.bounds.with, move scrollView from 0 to offsetX to make it center(using `scrollView.contentOffset`)
    // if not, don't need to set offset, but we need to set contentInset to make it center.(using `scrollView.contentInset`)
    // so does the Y direction.
    let offsetX = max((scrollView.contentSize.width - scrollView.bounds.width) * 0.5, 0)
    let offsetY = max((scrollView.contentSize.height - scrollView.bounds.height) * 0.5, 0)
    scrollView.contentOffset = CGPoint(x: offsetX, y: offsetY)

    let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)

Ensuite, lorsque je pince vContent, j'écris le code suivant pour le centrer.

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    //we just need to ensure that the content is in the center when the contentSize is less than scrollView.size.
    let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)
}
1
Changwei

Il y a beaucoup de solutions ici, mais je risquerais de mettre ici la mienne. C'est bon pour deux raisons: cela ne gache pas l'expérience de zoom, comme le ferait la mise à jour du cadre de vue d'image en cours, et il respecte également les encarts de vue de défilement d'origine (par exemple, définis dans xib ou dans le storyboard pour une gestion élégante des barres d'outils semi-transparentes, etc.) .

Premièrement, définissez un petit assistant:

CGSize CGSizeWithAspectFit(CGSize containerSize, CGSize contentSize) {
    CGFloat containerAspect = containerSize.width / containerSize.height,
            contentAspect = contentSize.width / contentSize.height;

    CGFloat scale = containerAspect > contentAspect
                    ? containerSize.height / contentSize.height
                    : containerSize.width / contentSize.width;

    return CGSizeMake(contentSize.width * scale, contentSize.height * scale);
}

Pour conserver les encarts d'origine, champ défini:

UIEdgeInsets originalScrollViewInsets;

Et quelque part dans viewDidLoad, remplissez-le:

originalScrollViewInsets = self.scrollView.contentInset;

Pour placer UIImageView dans UIScrollView (en supposant que UIImage est lui-même dans l'oadImage var):

CGSize containerSize = self.scrollView.bounds.size;
containerSize.height -= originalScrollViewInsets.top + originalScrollViewInsets.bottom;
containerSize.width -= originalScrollViewInsets.left + originalScrollViewInsets.right;

CGSize contentSize = CGSizeWithAspectFit(containerSize, loadedImage.size);

UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRect) { CGPointZero, contentSize }];
imageView.autoresizingMask = UIViewAutoresizingNone;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = loadedImage;

[self.scrollView addSubview:imageView];
self.scrollView.contentSize = contentSize;

[self centerImageViewInScrollView];

scrollViewDidZoom: à partir de UIScrollViewDelegate pour cette vue de défilement:

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    if (scrollView == self.scrollView) {
        [self centerImageViewInScrollView];
    }
}

Et enfin, en se centrant:

- (void)centerImageViewInScrollView {
    CGFloat excessiveWidth = MAX(0.0, self.scrollView.bounds.size.width - self.scrollView.contentSize.width),
            excessiveHeight = MAX(0.0, self.scrollView.bounds.size.height - self.scrollView.contentSize.height),
            insetX = excessiveWidth / 2.0,
            insetY = excessiveHeight / 2.0;

    self.scrollView.contentInset = UIEdgeInsetsMake(
            MAX(insetY, originalScrollViewInsets.top),
            MAX(insetX, originalScrollViewInsets.left),
            MAX(insetY, originalScrollViewInsets.bottom),
            MAX(insetX, originalScrollViewInsets.right)
    );
}

Je n’ai pas encore testé le changement d’orientation (c’est-à-dire la réaction appropriée pour redimensionner UIScrollView lui-même), mais la solution à ce problème devrait être relativement facile.

1
jazzcat

Pour ce faire, j'ai ajouté une vue supplémentaire à la hiérarchie:

UIScrollView -> UIView -> UIImageView

Donnez à votre UIView le même rapport d'aspect que votre UIScrollView et centrez votre UIImageView dans celui-ci.

1
hatfinch

Vous constaterez que la solution publiée par Erdemus fonctionne, mais… Il y a des cas où la méthode scrollViewDidZoom n'est pas invoquée et votre image est collée dans le coin supérieur gauche. Une solution simple consiste à appeler explicitement la méthode lorsque vous affichez initialement une image, comme ceci:

[self scrollViewDidZoom: scrollView];

Dans de nombreux cas, vous pouvez invoquer cette méthode à deux reprises, mais il s'agit d'une solution plus propre que certaines des autres réponses à cette rubrique.

1
russes

Pour que l’animation se déroule bien, définissez

self.scrollview.bouncesZoom = NO;

et utiliser cette fonction (trouver le centre en utilisant la méthode de cette réponse )

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
    [UIView animateWithDuration:0.2 animations:^{
        float offsetX = MAX((scrollView.bounds.size.width-scrollView.contentSize.width)/2, 0);
        float offsetY = MAX((scrollView.bounds.size.height-scrollView.contentSize.height)/2, 0);
        self.imageCoverView.center = CGPointMake(scrollView.contentSize.width*0.5+offsetX, scrollView.contentSize.height*0.5+offsetY);
    }];
}

Cela crée un effet de rebond mais n'implique pas de mouvements brusques au préalable.

1
ldanilek

Vous pouvez regarder la propriété contentSize de UIScrollView (en utilisant l'observation clé-valeur ou similaire), et ajuster automatiquement le contentInset chaque fois que contentSize devient inférieur à la taille de la vue de défilement.

1
Tim

Voici un moyen élégant de centrer le contenu de UISCrollView.

Ajoutez un observateur au contentSize de votre UIScrollView. Cette méthode sera donc appelée à chaque changement de contenu ...

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

Maintenant sur votre méthode d'observateur:

- (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]. Remplacez par votre vue de contenu UIView.

    • Changez également imageGallery en pointant vers la taille de votre fenêtre.

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

NOTE: La seule manière dont ce contenu ne fonctionne pas très bien est avec la fonctionnalité de zoom standard du UIScrollView.

Juste la réponse approuvée dans Swift, mais sans sous-classement à l'aide du délégué

func centerScrollViewContents(scrollView: UIScrollView) {
    let contentSize = scrollView.contentSize
    let scrollViewSize = scrollView.frame.size;
    var contentOffset = scrollView.contentOffset;

    if (contentSize.width < scrollViewSize.width) {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0
    }

    if (contentSize.height < scrollViewSize.height) {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0
    }

    scrollView.setContentOffset(contentOffset, animated: false)
}

// UIScrollViewDelegate    
func scrollViewDidZoom(scrollView: UIScrollView) {
    centerScrollViewContents(scrollView)
}
1
LightMan

Ceci est ma solution à ce problème qui fonctionne très bien pour tout type de vue dans un scrollview.

-(void)scrollViewDidZoom:(__unused UIScrollView *)scrollView 
    {
    CGFloat top;
    CGFloat left;
    CGFloat bottom;
    CGFloat right;

    if (_scrollView.contentSize.width < scrollView.bounds.size.width) {
        DDLogInfo(@"contentSize %@",NSStringFromCGSize(_scrollView.contentSize));

        CGFloat width = (_scrollView.bounds.size.width-_scrollView.contentSize.width)/2.0;

        left = width;
        right = width;


    }else {
        left = kInset;
        right = kInset;
    }

    if (_scrollView.contentSize.height < scrollView.bounds.size.height) {

        CGFloat height = (_scrollView.bounds.size.height-_scrollView.contentSize.height)/2.0;

        top = height;
        bottom = height;

    }else {
        top = kInset;
        right = kInset;
    }

    _scrollView.contentInset = UIEdgeInsetsMake(top, left, bottom, right);



  if ([self.tiledScrollViewDelegate respondsToSelector:@selector(tiledScrollViewDidZoom:)])
  {
        [self.tiledScrollViewDelegate tiledScrollViewDidZoom:self];
  }
}
1
beat843796

L'exemple de Photo Scroller d'Apple fait exactement ce que vous recherchez. Mettez ceci dans votre sous-classe UIScrollView et changez _zoomView pour qu'il soit votre UIImageView.

-(void)layoutSubviews{
  [super layoutSubviews];
  // center the zoom view as it becomes smaller than the size of the screen
  CGSize boundsSize = self.bounds.size;
  CGRect frameToCenter = self.imageView.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;
  }
  self.imageView.frame = frameToCenter; 
}

exemple de code de Photo Scroller d’Apple

1
Korey Hinton

Si votre image intérieure a une largeur spécifique initiale (par exemple 300) et que vous souhaitez simplement centrer sa largeur niquement sur un zoom inférieur à sa largeur initiale, cela pourrait également vous aider.

 func scrollViewDidZoom(scrollView: UIScrollView){
    if imageView.frame.size.width < 300{
        imageView.center.x = self.view.frame.width/2
    }
  }
1
dejix

Bien que la question soit un peu ancienne, le problème existe toujours. Je l'ai résolu en Xcode 7 en rendant la contrainte d'espace vertical de l'élément le plus élevé (dans ce cas, le topLabel) en superViews (le scrollView) en haut d'un IBOutlet, puis recalculant sa constante chaque fois que le contenu change en fonction de la hauteur des sous-vues du scrollView (topLabel et bottomLabel).

class MyViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var topLabel: UILabel!
    @IBOutlet weak var bottomLabel: UILabel!
    @IBOutlet weak var toTopConstraint: NSLayoutConstraint!

    override func viewDidLayoutSubviews() {
        let heightOfScrollViewContents = (topLabel.frame.Origin.y + topLabel.frame.size.height - bottomLabel.frame.Origin.y)
        // In my case abs() delivers the perfect result, but you could also check if the heightOfScrollViewContents is greater than 0.
        toTopConstraint.constant = abs((scrollView.frame.height - heightOfScrollViewContents) / 2)
    }

    func refreshContents() {
        // Set the label's text …

        self.view.layoutIfNeeded()
    }
}
0
Nick Podratz

J'ai eu exactement le même problème. Voici comment j'ai résolu

Ce code doit être appelé à la suite de scrollView:DidScroll:

CGFloat imageHeight = self.imageView.frame.size.width * self.imageView.image.size.height / self.imageView.image.size.width;
BOOL imageSmallerThanContent = (imageHeight < self.scrollview.frame.size.height) ? YES : NO;
CGFloat topOffset = (self.imageView.frame.size.height - imageHeight) / 2;

// If image is not large enough setup content offset in a way that image is centered and not vertically scrollable
if (imageSmallerThanContent) {
     topOffset = topOffset - ((self.scrollview.frame.size.height - imageHeight)/2);
}

self.scrollview.contentInset = UIEdgeInsetsMake(topOffset * -1, 0, topOffset * -1, 0);
0
aryaxt

Voici la façon dont je fais ce travail actuellement. C'est mieux mais toujours pas parfait. Essayez de régler:

 myScrollView.bouncesZoom = YES; 

pour résoudre le problème avec la vue ne centrant pas quand à minZoomScale.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGSize screenSize = [[self view] bounds].size;//[[UIScreen mainScreen] bounds].size;//
CGSize photoSize = [yourImage size];
CGFloat topInset = (screenSize.height - photoSize.height * [myScrollView zoomScale]) / 2.0;
CGFloat sideInset = (screenSize.width - photoSize.width * [myScrollView zoomScale]) / 2.0;

if (topInset < 0.0)
{ topInset = 0.0; }
if (sideInset < 0.0)
{ sideInset = 0.0; } 
[myScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
ApplicationDelegate *appDelegate = (ApplicationDelegate *)[[UIApplication sharedApplication] delegate];

CGFloat scrollViewHeight; //Used later to calculate the height of the scrollView
if (appDelegate.navigationController.navigationBar.hidden == YES) //If the NavBar is Hidden, set scrollViewHeight to 480
{ scrollViewHeight = 480; }
if (appDelegate.navigationController.navigationBar.hidden == NO) //If the NavBar not Hidden, set scrollViewHeight to 360
{ scrollViewHeight = 368; }

imageView.frame = CGRectMake(0, 0, CGImageGetWidth(yourImage)* [myScrollView zoomScale], CGImageGetHeight(yourImage)* [myScrollView zoomScale]);

[imageView setContentMode:UIViewContentModeCenter];
}

De même, je fais ce qui suit pour empêcher l’image de coller au côté après le zoom arrière.

- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
myScrollView.frame = CGRectMake(0, 0, 320, 420);
 //put the correct parameters for your scroll view width and height above
}
0
Jonah

D'accord, je pense avoir trouvé une très bonne solution à ce problème. L'astuce consiste à réajuster constamment le imageView's Cadre. Je trouve que cela fonctionne beaucoup mieux que d’ajuster constamment les contentInsets ou contentOffSets. J'ai dû ajouter un peu de code supplémentaire pour prendre en charge les images de portrait et de paysage.

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];
}
0
Jonah

Il suffit de désactiver la pagination pour que tout fonctionne correctement:

scrollview.pagingEnabled = NO;
0
Nagaraj