Réponses:
La meilleure méthode que j'ai jamais rencontrée pour mettre à jour la taille du contenu d'un en UIScrollView
fonction de ses sous-vues contenues:
Objectif c
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
Rapide
let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size
UIScrollView ne connaît pas automatiquement la hauteur de son contenu. Vous devez calculer vous-même la hauteur et la largeur
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, vous n'avez qu'à ajouter la hauteur d'une seule si vous ne voulez pas définir le contenu du défilement plus grand qu'il ne l'est réellement.
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
Mettre translatesAutoresizingMaskIntoConstraints
à NO
tous les points de vue en cause.
Positionnez et redimensionnez votre vue de défilement avec des contraintes externes à la vue de défilement.
Utilisez des contraintes pour disposer les sous-vues 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 comptez pas sur la vue de défilement pour obtenir leur taille.
Source: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
J'ai ajouté ceci à la réponse d'Espuz et JCC. Il utilise la position y des sous-vues et n'inclut pas les barres de défilement. Modifier Utilise le bas de la sous-vue 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;
}
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
au début et self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
à la fin de la méthode?
Voici la réponse acceptée rapidement 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
Voici une adaptation Swift 3 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 simple d'utilisation!
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 cachées à l'intérieur UIScrollView
et le calcul est effectué après que les indicateurs de défilement sont masqués.
En outre, il existe un paramètre de fonction facultatif et vous pouvez ajouter une valeur de décalage en passant le paramètre à function.
Excellente et meilleure solution de @leviathan. Il suffit de traduire rapidement en utilisant l'approche FP (programmation fonctionnelle).
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), {
CGRectUnion($0, $1.frame)
}.size
self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
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'élément.
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 faisant les choses de cette façon, les éléments (sous-vues) n'ont pas à être empilés directement les uns sous les autres.
J'ai trouvé 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 ajoutons un rembourrage de 10 pixels en bas.
Étant donné qu'un scrollView peut avoir d'autres scrollViews ou une arborescence de sous-vues inDepth différente, il est préférable d'exécuter en profondeur de manière récursive.
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)
}
}
Usage
scrollView.recalculateVerticalContentSize_synchronous()
Ou faites simplement:
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
(Cette solution a été ajoutée par moi en tant que commentaire sur cette page. Après avoir obtenu 19 votes pour ce commentaire, j'ai décidé d'ajouter cette solution comme réponse formelle au profit de la communauté!)
Pour swift4 en utilisant réduire:
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
return $0.union($1.frame)
}).size
La taille dépend du contenu chargé à l'intérieur de celui-ci et des options de détourage. S'il s'agit d'une vue de texte, cela dépend également de l'habillage, du nombre de lignes de texte, de la taille de la police, etc. Presque impossible pour vous de vous calculer. La bonne nouvelle est qu'il est calculé après le chargement de la vue et dans le viewWillAppear. Avant cela, tout est inconnu et la taille du contenu sera la même que la taille du cadre. Mais, dans la méthode viewWillAppear et après (comme viewDidAppear), la taille du contenu sera la taille réelle.
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 l'utiliser:
importez simplement le fichier .h dans votre contrôleur de vue et déclarez une instance SBScrollView au lieu de l'instance UIScrollView normale.
Définissez la taille du contenu dynamique comme ceci.
self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
J'ai également trouvé que la réponse de Léviathan fonctionnait le mieux. Cependant, il calculait une hauteur étrange. Lorsque vous parcourez les sous-vues en boucle, si la vue de défilement est définie pour afficher des indicateurs de défilement, ceux-ci seront dans le tableau des sous-vues. Dans ce cas, la solution consiste à désactiver temporairement les indicateurs de défilement avant de boucler, 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;
}
Je pense que cela peut être un bon moyen de mettre à jour la taille de la vue du contenu d'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
}
}
import UIKit
class DynamicSizeScrollView: UIScrollView {
var maxHeight: CGFloat = UIScreen.main.bounds.size.height
var maxWidth: CGFloat = UIScreen.main.bounds.size.width
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size,self.intrinsicContentSize){
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
let height = min(contentSize.height, maxHeight)
let width = min(contentSize.height, maxWidth)
return CGSize(width: width, height: height)
}
}
viewDidLayoutSubviews
J'exécutais ceci pour que la mise en page automatique se termine, dans iOS7 cela fonctionnait bien, mais tester os iOS6 la mise en page automatique pour une raison quelconque n'a pas terminé le travail, donc j'avais des valeurs de hauteur erronées, donc je suis passé àviewDidAppear
maintenant fonctionne bien. juste pour souligner que quelqu'un en aurait peut-être besoin. merci