Risposte:
Il metodo migliore che abbia mai incontrato per aggiornare la dimensione del contenuto di una in UIScrollView
base alle sue visualizzazioni secondarie contenute:
Objective-C
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
veloce
let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size
UIScrollView non conosce automaticamente l'altezza del suo contenuto. Devi calcolare l'altezza e la larghezza per te stesso
Fallo con qualcosa del genere
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];
Ma questo funziona solo se le viste sono una sotto l'altra. Se hai una vista l'una accanto all'altra devi solo aggiungere l'altezza di una se non vuoi impostare il contenuto dello scroller più grande di quello che è realmente.
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
Situato translatesAutoresizingMaskIntoConstraints
a NO
su tutte le viste coinvolte.
Posiziona e ridimensiona la vista di scorrimento con vincoli esterni alla vista di scorrimento.
Utilizzare i vincoli per disporre le viste secondarie all'interno della vista di scorrimento, accertandosi che i vincoli si leghino a tutti e quattro i bordi della vista di scorrimento e non si basino sulla vista di scorrimento per ottenere le loro dimensioni.
Fonte: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
Ho aggiunto questo alla risposta di Espuz e JCC. Utilizza la posizione y delle viste secondarie e non include le barre di scorrimento. Modifica Utilizza la parte inferiore della vista secondaria inferiore visibile.
+ (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;
all'inizio e self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
alla fine del metodo?
Ecco la risposta accettata in fretta per chiunque sia troppo pigro per convertirlo :)
var contentRect = CGRectZero
for view in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
Ecco un adattamento di Swift 3 della risposta di @ leviatan:
ESTENSIONE
import UIKit
extension UIScrollView {
func resizeScrollViewContentSize() {
var contentRect = CGRect.zero
for view in self.subviews {
contentRect = contentRect.union(view.frame)
}
self.contentSize = contentRect.size
}
}
USO
scrollView.resizeScrollViewContentSize()
Molto facile da usare!
La seguente estensione sarebbe utile in 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 logica è la stessa con le risposte fornite. Tuttavia, omette le viste nascoste all'interno UIScrollView
e il calcolo viene eseguito dopo che gli indicatori di scorrimento sono stati nascosti.
Inoltre, è disponibile un parametro di funzione opzionale e puoi aggiungere un valore di offset passando il parametro alla funzione.
Ottima e migliore soluzione di @leviathan. Sto solo traducendo in rapido usando l'approccio FP (programmazione funzionale).
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), {
CGRectUnion($0, $1.frame)
}.size
self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
Puoi ottenere l'altezza del contenuto all'interno di UIScrollView calcolando quale bambino "raggiunge più lontano". Per calcolare questo devi considerare l'origine Y (inizio) e l'altezza dell'oggetto.
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))];
In questo modo gli oggetti (sottoview) non devono essere impilati direttamente uno sotto l'altro.
Ho trovato un'altra soluzione basata sulla soluzione di @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)];
Fondamentalmente, scopriamo quale elemento è più in basso nella vista e aggiunge un'imbottitura di 10px al fondo
Poiché uno scrollView può avere altri scrollView o diversi alberi subView in profondità, è preferibile eseguire in modo ricorsivo approfondito.
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)
}
}
uso
scrollView.recalculateVerticalContentSize_synchronous()
O semplicemente:
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
(Questa soluzione è stata aggiunta da me come commento in questa pagina. Dopo aver ottenuto 19 voti positivi per questo commento, ho deciso di aggiungere questa soluzione come risposta formale a beneficio della comunità!)
Per swift4 usando ridurre:
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
return $0.union($1.frame)
}).size
La dimensione dipende dal contenuto caricato al suo interno e dalle opzioni di ritaglio. Se si tratta di una visualizzazione di testo, dipende anche dall'involucro, dal numero di righe di testo, dalla dimensione del carattere e così via. Quasi impossibile per te calcolare te stesso. La buona notizia è che viene calcolata dopo il caricamento della vista e in viewWillAppear. Prima di ciò, è tutto sconosciuto e la dimensione del contenuto sarà uguale alla dimensione del frame. Tuttavia, nel metodo viewWillAppear e dopo (come viewDidAppear) le dimensioni del contenuto saranno effettive.
Avvolgendo il codice di Richy ho creato una classe UIScrollView personalizzata che automatizza completamente il ridimensionamento del contenuto!
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
Come usare:
È sufficiente importare il file .h sul controller della vista e dichiarare un'istanza SBScrollView invece di quella normale UIScrollView.
Imposta la dimensione del contenuto dinamico in questo modo.
self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
Ho anche trovato la risposta del leviatano per funzionare al meglio. Tuttavia, stava calcolando una strana altezza. Durante il ciclo tra le visualizzazioni secondarie, se la visualizzazione a scorrimento è impostata per mostrare gli indicatori di scorrimento, questi saranno nella serie di visualizzazioni secondarie. In questo caso, la soluzione è disabilitare temporaneamente gli indicatori di scorrimento prima di eseguire il looping, quindi ristabilire le impostazioni di visibilità precedenti.
-(void)adjustContentSizeToFit
è un metodo pubblico su una sottoclasse personalizzata di 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;
}
Penso che questo possa essere un modo semplice per aggiornare le dimensioni della vista del contenuto di 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
modo che l'autolayout finisse, in iOS7 ha funzionato bene, ma test su iOS6 l'autolayout per qualche motivo non ha finito il lavoro, quindi avevo alcuni valori di altezza sbagliati, quindi sono passatoviewDidAppear
ora funziona bene .. solo per sottolineare che forse qualcuno avrebbe bisogno di questo. grazie