web-dev-qa-db-fra.com

Comment personnaliser la taille d'un UIScrollView pour l'adapter à son contenu

Est-il possible de faire un UIScrollView ajustement automatique à la hauteur (ou à la largeur) du contenu défilant?

Quelque chose comme:

[scrollView setContentSize:(CGSizeMake(320, content.height))];
123
jwerre

La meilleure méthode que j'ai jamais rencontrée pour mettre à jour la taille du contenu d'un UIScrollView en fonction de ses sous-vues contenues:

Objective-C

CGRect contentRect = CGRectZero;

for (UIView *view in self.scrollView.subviews) {
    contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;

rapide

var contentRect = CGRect.zero

for view in mainScrollView.subviews {
   contentRect = contentRect.union(view.frame)
}
mainScrollView.contentSize = contentRect.size
296
leviathan

UIScrollView ne connaît pas automatiquement la hauteur de son contenu. Vous devez calculer la hauteur et la largeur pour vous-même

Faites-le avec quelque chose comme

CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
   scrollViewHeight += view.frame.size.height;
}

[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];

Mais cela ne fonctionne que si les vues sont l'une en dessous de l'autre. Si vous avez une vue côte à côte, il vous suffit d’en ajouter la hauteur si vous ne souhaitez pas que le contenu du défilement soit plus grand qu’il ne l’est réellement.

75
emenegro

Solution si vous utilisez la mise en page automatique:

  • Définissez translatesAutoresizingMaskIntoConstraints sur NO sur toutes les vues impliquées.

  • Positionnez et redimensionnez votre vue de défilement avec des contraintes externes à la vue de défilement.

  • Utilisez des contraintes pour organiser les vues secondaires dans la vue de défilement, , en vous assurant que les contraintes sont liées aux quatre bords de la vue de défilement et ne reposent pas sur la faites défiler la vue pour obtenir leur taille.

Source: https://developer.Apple.com/library/ios/technotes/tn2154/_index.html

38
Adam Waite

J'ai ajouté ceci à la réponse d'Espuz et de JCC. Il utilise la position y des sous-vues et n'inclut pas les barres de défilement. Edit Utilise le bas de la vue secondaire la plus basse visible.

+ (CGFloat) bottomOfLowestContent:(UIView*) view
{
    CGFloat lowestPoint = 0.0;

    BOOL restoreHorizontal = NO;
    BOOL restoreVertical = NO;

    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if ([(UIScrollView*)view showsHorizontalScrollIndicator])
        {
            restoreHorizontal = YES;
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:NO];
        }
        if ([(UIScrollView*)view showsVerticalScrollIndicator])
        {
            restoreVertical = YES;
            [(UIScrollView*)view setShowsVerticalScrollIndicator:NO];
        }
    }
    for (UIView *subView in view.subviews)
    {
        if (!subView.hidden)
        {
            CGFloat maxY = CGRectGetMaxY(subView.frame);
            if (maxY > lowestPoint)
            {
                lowestPoint = maxY;
            }
        }
    }
    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if (restoreHorizontal)
        {
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:YES];
        }
        if (restoreVertical)
        {
            [(UIScrollView*)view setShowsVerticalScrollIndicator:YES];
        }
    }

    return lowestPoint;
}
35
richy

Voici la réponse acceptée dans Swift pour quiconque est trop paresseux pour le convertir :)

var contentRect = CGRectZero
for view in self.scrollView.subviews {
    contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
13
Josh

Voici une Swift 3 adaptation de la réponse de @ leviatan:

EXTENSION

import UIKit


extension UIScrollView {

    func resizeScrollViewContentSize() {

        var contentRect = CGRect.zero

        for view in self.subviews {

            contentRect = contentRect.union(view.frame)

        }

        self.contentSize = contentRect.size

    }

}

USAGE

scrollView.resizeScrollViewContentSize()

Très facile à utiliser!

8
fredericdnd

L'extension suivante serait utile dans Swift.

extension UIScrollView{
    func setContentViewSize(offset:CGFloat = 0.0) {
        // dont show scroll indicators
        showsHorizontalScrollIndicator = false
        showsVerticalScrollIndicator = false

        var maxHeight : CGFloat = 0
        for view in subviews {
            if view.isHidden {
                continue
            }
            let newHeight = view.frame.Origin.y + view.frame.height
            if newHeight > maxHeight {
                maxHeight = newHeight
            }
        }
        // set content size
        contentSize = CGSize(width: contentSize.width, height: maxHeight + offset)
        // show scroll indicators
        showsHorizontalScrollIndicator = true
        showsVerticalScrollIndicator = true
    }
}

La logique est la même avec les réponses données. Cependant, il omet les vues masquées dans UIScrollView et le calcul est effectué après que les indicateurs de défilement ont été masqués.

En outre, il existe un paramètre de fonction facultatif et vous pouvez ajouter une valeur de décalage en transmettant le paramètre à fonction.

6
gokhanakkurt

La meilleure solution de @leviathan. Il suffit de traduire en Swift en utilisant l’approche FP (programmation fonctionnelle)).

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), { 
  CGRectUnion($0, $1.frame) 
}.size
5
othierry42

Vous pouvez obtenir la hauteur du contenu dans UIScrollView en calculant quel enfant "atteint plus loin". Pour calculer cela, vous devez prendre en compte l'origine Y (début) et la hauteur de l'article.

float maxHeight = 0;
for (UIView *child in scrollView.subviews) {
    float childHeight = child.frame.Origin.y + child.frame.size.height;
    //if child spans more than current maxHeight then make it a new maxHeight
    if (childHeight > maxHeight)
        maxHeight = childHeight;
}
//set content size
[scrollView setContentSize:(CGSizeMake(320, maxHeight))];

En procédant ainsi, les éléments (sous-vues) ne doivent pas être empilés directement les uns sous les autres.

4
paxx

Je suis venu avec une autre solution basée sur la solution de @ emenegro

NSInteger maxY = 0;
for (UIView* subview in scrollView.subviews)
{
    if (CGRectGetMaxY(subview.frame) > maxY)
    {
        maxY = CGRectGetMaxY(subview.frame);
    }
}
maxY += 10;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, maxY)];

Fondamentalement, nous déterminons quel élément est le plus bas dans la vue et ajoute un remplissage de 10px au bas

3
Chris

Etant donné qu'une scrollView peut avoir d'autres scrollViews ou différentes arborescences de sous-vues inDepth, il est préférable d'effectuer une analyse en profondeur de manière récursive. enter image description here

Swift 2

extension UIScrollView {
    //it will block the mainThread
    func recalculateVerticalContentSize_synchronous () {
        let unionCalculatedTotalRect = recursiveUnionInDepthFor(self)
        self.contentSize = CGRectMake(0, 0, self.frame.width, unionCalculatedTotalRect.height).size;
    }

    private func recursiveUnionInDepthFor (view: UIView) -> CGRect {
        var totalRect = CGRectZero
        //calculate recursevly for every subView
        for subView in view.subviews {
            totalRect =  CGRectUnion(totalRect, recursiveUnionInDepthFor(subView))
        }
        //return the totalCalculated for all in depth subViews.
        return CGRectUnion(totalRect, view.frame)
    }
}

Utilisation

scrollView.recalculateVerticalContentSize_synchronous()
3
iluvatar_GR

Ou juste faire:

int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];

(Cette solution a été ajoutée par moi dans mes commentaires sur cette page. Après 19 votes positifs pour ce commentaire, j'ai décidé d'ajouter cette solution à titre de réponse officielle au profit de la communauté!)

2
Gal

Enveloppant le code de Richy, j'ai créé une classe UIScrollView personnalisée qui automatise complètement le redimensionnement du contenu!

SBScrollView.h

@interface SBScrollView : UIScrollView
@end

SBScrollView.m:

@implementation SBScrollView
- (void) layoutSubviews
{
    CGFloat scrollViewHeight = 0.0f;
    self.showsHorizontalScrollIndicator = NO;
    self.showsVerticalScrollIndicator = NO;
    for (UIView* view in self.subviews)
    {
        if (!view.hidden)
        {
            CGFloat y = view.frame.Origin.y;
            CGFloat h = view.frame.size.height;
            if (y + h > scrollViewHeight)
            {
                scrollViewHeight = h + y;
            }
        }
    }
    self.showsHorizontalScrollIndicator = YES;
    self.showsVerticalScrollIndicator = YES;
    [self setContentSize:(CGSizeMake(self.frame.size.width, scrollViewHeight))];
}
@end

Comment utiliser:
Importez simplement le fichier .h dans votre contrôleur de vue et déclarez une instance de SBScrollView au lieu de celle d’UIScrollView normale.

1
AmitP

Pour Swift4 using, réduisez:

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
   return $0.union($1.frame)
}).size
1
mm282

Définissez la taille du contenu dynamique comme ceci.

 self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
1
Monika Patel

La taille dépend du contenu chargé à l'intérieur et des options de découpage. Si c'est une vue texte, alors cela dépend aussi du retour à la ligne, du nombre de lignes de texte, de la taille de la police, etc. Presque impossible pour vous de calculer vous-même. La bonne nouvelle est qu’elle est calculée après le chargement de la vue et dans l’apparitionVue. Avant cela, tout est inconnu et la taille du contenu sera identique à celle du cadre. Mais, dans la méthode viewWillAppear et après (tel que viewDidAppear), la taille du contenu sera la taille réelle.

1
PapaSmurf

pourquoi pas une seule ligne de code?

_yourScrollView.contentSize = CGSizeMake(0, _lastView.frame.Origin.y + _lastView.frame.size.height);
0
Ali

Je pense que cela peut être un moyen judicieux de mettre à jour la taille d'affichage du contenu de UIScrollView.

extension UIScrollView {
    func updateContentViewSize() {
        var newHeight: CGFloat = 0
        for view in subviews {
            let ref = view.frame.Origin.y + view.frame.height
            if ref > newHeight {
                newHeight = ref
            }
        }
        let oldSize = contentSize
        let newSize = CGSize(width: oldSize.width, height: newHeight + 20)
        contentSize = newSize
    }
}
0
Furqan Khan

J'ai aussi trouvé que la réponse de Léviathan fonctionnait le mieux. Cependant, il calculait une hauteur étrange. Lorsque vous parcourez les sous-vues, si l'affichage de défilement est configuré pour afficher des indicateurs de défilement, ceux-ci figureront dans le tableau des sous-vues. Dans ce cas, la solution consiste à désactiver temporairement les indicateurs de défilement avant la mise en boucle, puis à rétablir leur paramètre de visibilité précédent.

-(void)adjustContentSizeToFit est une méthode publique sur une sous-classe personnalisée de UIScrollView.

-(void)awakeFromNib {    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self adjustContentSizeToFit];
    });
}

-(void)adjustContentSizeToFit {

    BOOL showsVerticalScrollIndicator = self.showsVerticalScrollIndicator;
    BOOL showsHorizontalScrollIndicator = self.showsHorizontalScrollIndicator;

    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;

    CGRect contentRect = CGRectZero;
    for (UIView *view in self.subviews) {
        contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.contentSize = contentRect.size;

    self.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
    self.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
}
0
clayzar

cela dépend vraiment du contenu: content.frame.height peut vous donner ce que vous voulez? Cela dépend si le contenu est une seule chose ou une collection de choses.

0
Andiih