Bir UIScrollView içeriğini içeriğine uyacak şekilde otomatik olarak nasıl boyutlandırırım?


130

UIScrollViewKaydırılan içeriğin yüksekliğine (veya genişliğine) otomatik ayarlama yapmanın bir yolu var mı ?

Gibi bir şey:

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

Yanıtlar:


306

UIScrollViewA'nın içerik boyutunu , içerdiği alt görünümlere göre güncellemek için karşılaştığım en iyi yöntem :

Objective-C

CGRect contentRect = CGRectZero;

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

hızlı

let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
    rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size

9
Bunu çalıştırıyordum, viewDidLayoutSubviewsböylece otomatik düzen bitecekti, iOS7'de iyi çalıştı, ancak iOS6'da otomatik düzeni test etmek bir nedenden dolayı işi bitirmedi, bu yüzden bazı yanlış yükseklik değerlerine sahiptim, bu yüzden viewDidAppearşimdi iyi çalışıyor .. Belki birilerinin buna ihtiyacı olacağını belirtmek için. teşekkürler
Hatem Alimam

1
İOS6 iPad ile sağ alt köşede gizemli UIImageView (7x7) vardı. Sadece (görünür ve alfa) UI bileşenlerini kabul ederek bunu filtreledim, sonra çalıştı. Bu imageView'un kesinlikle benim kodumla değil, scrollBars ile ilişkili olup olmadığını merak ediyorum.
JOM

5
Kaydırma Göstergeleri etkinleştirildiğinde lütfen dikkatli olun! her biri için bir UIImageView, SDK tarafından otomatik olarak oluşturulacaktır. Bunu hesaba katmalısınız, aksi takdirde içerik boyutunuz yanlış olacaktır. (bkz stackoverflow.com/questions/5388703/… )
AmitP

Bu çözümün benim için mükemmel çalıştığını doğrulayabilir miyim? Swift 4
Ethan Humphries

Aslında, CGRect.zero'dan birleştirmeye başlayamayız, yalnızca bazı alt görünümleri negatif koordinatlarda barındırırsanız çalışır. Birleşim alt görünüm çerçevelerini sıfırdan değil, ilk alt görünümden başlatmalısınız. Örneğin, çerçeveli (50, 50, 100, 100) bir UIView olan tek bir alt görünümünüz var. İçerik boyutunuz (50, 50, 100, 100) değil (0, 0, 150, 150) olacaktır.
Evgeny

74

UIScrollView, içeriğinin yüksekliğini otomatik olarak bilmez. Yüksekliği ve genişliği kendiniz hesaplamalısınız

Gibi bir şeyle yap

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

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

Ancak bu yalnızca görüşler birbirinin altındaysa işe yarar. Yan yana bir görünümünüz varsa, kaydırma çubuğunun içeriğini gerçekte olduğundan daha büyük ayarlamak istemiyorsanız, yalnızca birinin yüksekliğini eklemeniz gerekir.


Bunun gibi bir şeyin de işe yarayacağını düşünüyor musunuz? CGFloat scrollViewHeight = scrollView.contentSize.height; [scrollView setContentSize: (CGSizeMake (320, scrollViewHeight))]; BTW, boyutu sonra yüksekliği sormayı unutma. scrollViewHeight + = view.frame.size.height
jwerre

ScrollViewHeight öğesini yüksekliğe başlatmak, kaydırıcıyı içeriğinden daha büyük hale getirir. Bunu yaparsanız, kaydırıcı içeriğinin ilk yüksekliğinde görünen görünümlerin yüksekliğini eklemeyin. Belki bir başlangıç ​​boşluğuna veya başka bir şeye ihtiyacınız olsa da.
emenegro

21
Ya da şunu yapın:int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
Gal

@saintjab, teşekkürler, bunu resmi bir cevap olarak ekledim :)
Gal

41

Otomatik düzen kullanıyorsanız çözüm:

  • Set translatesAutoresizingMaskIntoConstraintsiçin NOkatılan tüm görünümlerde.

  • Kaydırma görünümünüzü, kaydırma görünümünün dışındaki kısıtlamalarla konumlandırın ve boyutlandırın.

  • Alt görünümleri kaydırma görünümünde düzenlemek için kısıtlamaları kullanın, kısıtlamaların kaydırma görünümünün dört kenarına da bağlı olduğundan ve boyutlarını almak için kaydırma görünümüne güvenmediğinden emin olun .

Kaynak: https://developer.apple.com/library/ios/technotes/tn2154/_index.html


10
Imho bu, her şeyin otomatik düzen ile gerçekleştiği dünyadaki gerçek çözümdür. Diğer çözümlerle ilgili olarak: Her görüntünün yüksekliğini ve bazen genişliğini hesaplarken ciddisiniz?
Fawkes

Bunu paylaştığınız için teşekkürler! Kabul edilen cevap bu olmalıdır.
SePröbläm

3
Bu, kaydırma görünümünün yüksekliğinin içerik yüksekliğine bağlı olmasını istediğinizde çalışmaz. (örneğin, belirli bir içerik miktarına kadar kaydırmak istemediğiniz ekranda ortalanmış bir açılır pencere).

Bu soruya cevap vermiyor
Igor Vasilev

Harika çözüm. Ancak, bir madde işareti daha eklerdim ... "Dikey olarak büyümesi gerekiyorsa alt yığın görünümünün yüksekliğini sınırlamayın. Kaydırma görünümünün kenarlarına sabitlemeniz yeterli."
Andrew Kirna

35

Bunu Espuz ve KİK'in cevabına ekledim. Alt görünümlerin y konumunu kullanır ve kaydırma çubuklarını içermez. Düzenle Görünen en alt alt görünümün altını kullanır.

+ (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;
}

1
Olağanüstü! Tam ihtiyacım olan şey.
Richard

2
Sınıfın bir parçası olmayan bunun gibi bir yönteme şaşırdım.
João Portela

Yöntemin self.scrollView.showsHorizontalScrollIndicator = NO; self.scrollView.showsVerticalScrollIndicator = NO;başında ve self.scrollView.showsHorizontalScrollIndicator = YES; self.scrollView.showsVerticalScrollIndicator = YES;sonunda neden yaptınız ?
João Portela

Teşekkürler richy :) Cevap için +1.
Shraddha

1
@richy haklısın kaydırma göstergeleri görünürse alt görünümlerdir, ancak mantığı göstermek / gizlemek yanlıştır. İki BOOL yerel eklemeniz gerekir: restoreHorizontal = NO, restoreVertical = NO. Yatay gösteriliyorsa, gizleyin, restoreHorizontal'ı YES olarak ayarlayın. Dikey için aynı. Sonra, for / in döngüsünden sonra if ifadesi ekleyin: if restoreHorizontal, gösterecek şekilde ayarlayın, dikey için aynı. Kodunuz her iki göstergeyi de göstermeye zorlayacak
yasadışı göçmen

13

İşte onu dönüştüremeyecek kadar tembel olan herkes için hızlı bir şekilde kabul edilen cevap :)

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

ve Swift3 için güncellendi: self.scrollView.subviews'te görüntülemek için var contentRect = CGRect.zero {contentRect = contentRect.union (view.frame)} self.scrollView.contentSize = contentRect.size
drew ..

Bunun ana ileti dizisinde çağrılması gerekiyor mu? ve didLayoutSubViews'ta?
Maksim Kniazev

8

İşte @ leviatan'ın cevabının Swift 3 uyarlaması:

UZANTI

import UIKit


extension UIScrollView {

    func resizeScrollViewContentSize() {

        var contentRect = CGRect.zero

        for view in self.subviews {

            contentRect = contentRect.union(view.frame)

        }

        self.contentSize = contentRect.size

    }

}

KULLANIM

scrollView.resizeScrollViewContentSize()

Kullanımı çok kolay!


Çok kullanışlı. Pek çok yinelemeden sonra bu çözümü buldum ve mükemmel.
Hardik Shah

Güzel! Şimdi uyguladım.
arvidurs

7

Aşağıdaki uzantı Swift'de yardımcı olacaktır .

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
    }
}

Mantık, verilen cevaplarla aynıdır. Ancak, içindeki gizli görünümleri çıkarır UIScrollViewve kaydırma göstergeleri gizlendikten sonra hesaplama gerçekleştirilir.

Ayrıca, isteğe bağlı bir işlev parametresi vardır ve parametreyi işleve ileterek bir ofset değeri ekleyebilirsiniz.


Bu çözüm çok fazla varsayım içeriyor, neden kaydırma göstergelerini içerik boyutunu belirleyen bir yöntemde gösteriyorsunuz?
Kappe

5

@Leviathan'dan harika ve en iyi çözüm. FP (fonksiyonel programlama) yaklaşımını kullanarak sadece hızlıya çevirmek.

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

Gizli görünümleri yok saymak istiyorsanız, azaltmak için geçmeden önce alt görünümleri filtreleyebilirsiniz.
Andy

Swift 3 için güncellendi:self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
John Rogers

4

Hangi çocuğun "daha ileri ulaştığını" hesaplayarak UIScrollView içindeki içeriğin yüksekliğini öğrenebilirsiniz. Bunu hesaplamak için, başlangıç ​​noktası Y (başlangıç) ve ürün yüksekliğini hesaba katmalısınız.

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))];

İşleri bu şekilde yaparak, öğelerin (alt görünümler) doğrudan birbirinin altına istiflenmesi gerekmez.


Bu tek astarı döngü içinde de kullanabilirsiniz: maxHeight = MAX (maxHeight, child.frame.origin.y + child.frame.size.height) ;)
Thomas CG de Vilhena

3

@ Emenegro çözümüne dayalı başka bir çözüm buldum

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)];

Temel olarak, görünümde hangi öğenin en aşağıda olduğunu buluruz ve alt kısma 10 piksellik bir dolgu ekleriz


3

Bir scrollView, başka scrollViews veya farklı inDepth subViews ağaçlarına sahip olabileceğinden, özyinelemeli derinlemesine çalıştırma tercih edilir. görüntü açıklamasını buraya girin

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)
    }
}

kullanım

scrollView.recalculateVerticalContentSize_synchronous()

2

Veya sadece şunu yapın:

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

(Bu çözüm benim tarafımdan bu sayfaya yorum olarak eklenmiştir. Bu yorum için 19 artı oy aldıktan sonra, bu çözümü topluluğun yararına resmi bir yanıt olarak eklemeye karar verdim!)


2

Swift4 için indirgeme kullanarak:

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

Ayrıca, en az bir alt görünümün origin == CGPoint.zero olup olmadığını veya belirli bir (örneğin en büyük) alt görünümün orijinini sıfıra atayıp atamadığını kontrol etmeye değer.
Paul B

Bu yalnızca görüşlerini CGRect () ile paylaşan kişiler için işe yarar. Kısıtlamalar kullanıyorsanız bu çalışmıyor.
linus_hologram

1

Boyut, içine yüklenen içeriğe ve kırpma seçeneklerine bağlıdır. Bu bir metin görünümü ise, o zaman kaydırmaya, kaç satır metin, yazı tipi boyutuna vb. Bağlıdır. Kendinizi hesaplamanız neredeyse imkansız. İyi haber şu ki, görünüm yüklendikten sonra ve viewWillAppear'da hesaplanıyor. Ondan önce, hepsi bilinmiyor ve içerik boyutu çerçeve boyutuyla aynı olacak. Ancak, viewWillAppear yönteminde ve sonrasında (viewDidAppear gibi) içerik boyutu gerçek olacaktır.


1

Richy'nin kodunu sarma İçeriğin tamamen yeniden boyutlandırılmasını otomatikleştiren özel bir UIScrollView sınıfı yarattım!

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

Nasıl kullanılır:
.h dosyasını görünüm denetleyicinize aktarın ve normal UIScrollView yerine bir SBScrollView örneği bildirin.


2
layoutSubviews, bu tür düzen ile ilgili kodlar için doğru yer gibi görünüyor. Ancak bir UIScrollView düzeninde, kaydırma sırasında içerik ofseti her güncellendiğinde alt görünüm çağrılır. Ve içerik boyutu genellikle kaydırırken değişmediğinden, bu oldukça israftır.
Sven

Bu doğru. Bu verimsizliği önlemenin bir yolu, [self isDragging] ve [self isDecelerating] 'i kontrol etmek ve sadece her ikisi de yanlışsa mizanpajı gerçekleştirmek olabilir.
Greg Brown

1

Dinamik içerik boyutunu şu şekilde ayarlayın.

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

0

gerçekten içeriğe bağlıdır: content.frame.height size istediğinizi verebilir mi? İçeriğin tek bir şey mi yoksa bir şeyler koleksiyonu mu olduğuna bağlıdır.


0

Ayrıca Leviathan'ın en iyi sonuç veren cevabını buldum. Ancak tuhaf bir yükseklik hesaplıyordu. Alt görünümler arasında döngü oluştururken, kaydırma görünümü kaydırma göstergelerini gösterecek şekilde ayarlanmışsa, bunlar alt görünümler dizisinde olacaktır. Bu durumda çözüm, döngü oluşturmadan önce kaydırma göstergelerini geçici olarak devre dışı bırakmak ve ardından önceki görünürlük ayarını yeniden kurmaktır.

-(void)adjustContentSizeToFit UIScrollView özel bir alt sınıfında genel bir yöntemdir.

-(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

Bunun UIScrollView içerik görüntüleme boyutunu güncellemenin düzgün bir yolu olabileceğini düşünüyorum.

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

neden tek satır kod değil ??

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

0
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)
    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.