Respostas:
O melhor método que eu já encontrei para atualizar o tamanho do conteúdo de um com UIScrollView
base em suas subvisões contidas:
Objetivo-C
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
Rápido
let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size
O UIScrollView não conhece a altura do seu conteúdo automaticamente. Você deve calcular a altura e a largura para si mesmo
Faça isso com algo como
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];
Mas isso só funciona se as visualizações estiverem uma abaixo da outra. Se você tiver uma visualização próxima, precisará adicionar a altura de uma se não quiser definir o conteúdo do scroller maior do que realmente é.
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
Defina translatesAutoresizingMaskIntoConstraints
como NO
em todas as visualizações envolvidas.
Posicione e dimensione sua exibição de rolagem com restrições externas à exibição de rolagem.
Use restrições para organizar as subvisões na visualização de rolagem, certificando-se de que as restrições estejam vinculadas às quatro arestas da visualização de rolagem e não se baseiem na visualização de rolagem para obter seu tamanho.
Fonte: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
Eu adicionei isso à resposta de Espuz e JCC. Ele usa a posição y das subvisões e não inclui as barras de rolagem. Editar Usa a parte inferior da subvisualização mais baixa visível.
+ (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;
no começo e self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
no final do método?
Aqui está a resposta aceita rapidamente para quem é muito preguiçoso para convertê-lo :)
var contentRect = CGRectZero
for view in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
Aqui está uma adaptação do Swift 3 da resposta de @ leviatan:
EXTENSÃO
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()
Muito fácil de usar!
A extensão a seguir seria útil no 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
}
}
A lógica é a mesma com as respostas dadas. No entanto, omite as visualizações ocultas UIScrollView
e o cálculo é realizado após os indicadores de rolagem serem ocultos.
Além disso, há um parâmetro de função opcional e você pode adicionar um valor de deslocamento passando o parâmetro para a função.
Excelente e melhor solução de @leviathan. Apenas traduzindo para rápido usando a abordagem FP (programação funcional).
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), {
CGRectUnion($0, $1.frame)
}.size
self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
Você pode obter a altura do conteúdo no UIScrollView calculando qual filho "alcança mais longe". Para calcular isso, é necessário levar em consideração a origem Y (início) e a altura do item.
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))];
Fazendo assim, os itens (subvisões) não precisam ser empilhados diretamente um sob o outro.
Eu vim com outra solução baseada na solução da @ Montenegro
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)];
Basicamente, descobrimos qual elemento está mais abaixo na visualização e adicionamos um preenchimento de 10px na parte inferior
Como um scrollView pode ter outros scrollViews ou uma árvore inDepth subViews diferente, é preferível executar em profundidade recursivamente.
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()
Ou apenas faça:
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
(Esta solução foi adicionada por mim como um comentário nesta página. Depois de receber 19 votos positivos para esse comentário, decidi adicionar essa solução como uma resposta formal para o benefício da comunidade!)
Para swift4 usando reduzir:
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
return $0.union($1.frame)
}).size
O tamanho depende do conteúdo carregado dentro dele e das opções de recorte. Se for uma visualização de texto, também depende da disposição, quantas linhas de texto, tamanho da fonte e assim por diante. Quase impossível para você se calcular. A boa notícia é que é calculada após o carregamento da visualização e no viewWillAppear. Antes disso, é tudo desconhecido e o tamanho do conteúdo será igual ao tamanho do quadro. Mas, no método viewWillAppear e depois (como o viewDidAppear), o tamanho do conteúdo será o real.
Envolvendo o código de Richy, criei uma classe UIScrollView personalizada que automatiza o redimensionamento completo do conteúdo!
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
Como usar:
Simplesmente importe o arquivo .h para o seu controlador de exibição e declare uma instância do SBScrollView em vez da instância normal do UIScrollView.
Defina o tamanho do conteúdo dinâmico como este.
self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
Também achei a resposta do leviatã para funcionar melhor. No entanto, estava calculando uma altura estranha. Ao percorrer as subvisões, se a visualização de rolagem estiver configurada para mostrar indicadores de rolagem, eles estarão na matriz de subvisões. Nesse caso, a solução é desativar temporariamente os indicadores de rolagem antes de fazer o loop e, em seguida, restabelecer sua configuração de visibilidade anterior.
-(void)adjustContentSizeToFit
é um método público em uma subclasse personalizada do 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;
}
Eu acho que essa pode ser uma maneira interessante de atualizar o tamanho da visualização de conteúdo do 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
para que o autolayout terminasse, no iOS7 funcionou bem, mas testar o iOS6 por algum motivo não terminou o trabalho, então eu tinha alguns valores de altura errados, então mudei paraviewDidAppear
agora funciona bem .. apenas para apontar que talvez alguém precise disso. obrigado