Czy istnieje sposób na UIScrollView
automatyczne dostosowanie wysokości (lub szerokości) przewijanej treści?
Coś jak:
[scrollView setContentSize:(CGSizeMake(320, content.height))];
Odpowiedzi:
Najlepsza metoda, z jaką kiedykolwiek się spotkałem, aby zaktualizować rozmiar treści na UIScrollView
podstawie zawartych w nim podglądów podrzędnych:
Cel C
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
Szybki
let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size
UIScrollView nie zna automatycznie wysokości swojej zawartości. Musisz sam obliczyć wysokość i szerokość
Zrób to z czymś w rodzaju
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];
Ale to działa tylko wtedy, gdy widoki są jeden pod drugim. Jeśli masz widok obok siebie, wystarczy dodać wysokość jednego, jeśli nie chcesz ustawiać zawartości przewijacza na większą niż jest w rzeczywistości.
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
Ustaw translatesAutoresizingMaskIntoConstraints
się NO
na wszystkich widokach zaangażowanych.
Ustaw i zmień rozmiar widoku przewijania z ograniczeniami zewnętrznymi w stosunku do widoku przewijania.
Użyj ograniczeń, aby rozłożyć podglądy w widoku przewijania, upewniając się, że ograniczenia wiążą się ze wszystkimi czterema krawędziami widoku przewijania i nie polegają na widoku przewijania, aby uzyskać ich rozmiar.
Źródło: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
Dodałem to do odpowiedzi Espuza i JCC. Używa pozycji y widoków podrzędnych i nie obejmuje pasków przewijania. Edytuj Używa dolnej części najniższego widocznego widoku podrzędnego.
+ (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;
na początku i self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
na końcu metody?
Oto szybko akceptowana odpowiedź dla każdego, kto jest zbyt leniwy, aby ją przekonwertować :)
var contentRect = CGRectZero
for view in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
Oto adaptacja odpowiedzi @ leviatan w języku Swift 3:
ROZBUDOWA
import UIKit
extension UIScrollView {
func resizeScrollViewContentSize() {
var contentRect = CGRect.zero
for view in self.subviews {
contentRect = contentRect.union(view.frame)
}
self.contentSize = contentRect.size
}
}
STOSOWANIE
scrollView.resizeScrollViewContentSize()
Bardzo łatwy w użyciu!
Poniższe rozszerzenie byłoby pomocne w 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
}
}
Logika jest taka sama z podanymi odpowiedziami. Jednak pomija ukryte widoki, UIScrollView
a obliczenia są wykonywane po ustawieniu wskaźników przewijania jako ukrytych.
Istnieje również opcjonalny parametr funkcji i możesz dodać wartość przesunięcia, przekazując parametr do funkcji.
Świetne i najlepsze rozwiązanie od @leviathan. Po prostu przekłada się na szybkość przy użyciu podejścia FP (programowanie funkcjonalne).
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), {
CGRectUnion($0, $1.frame)
}.size
self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
Możesz uzyskać wysokość zawartości wewnątrz UIScrollView, obliczając, które dziecko „sięga dalej”. Aby to obliczyć, należy wziąć pod uwagę początek Y (początek) i wysokość przedmiotu.
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))];
Robiąc to w ten sposób, elementy (podglądy) nie muszą być układane bezpośrednio jeden pod drugim.
Wymyśliłem inne rozwiązanie oparte na rozwiązaniu @ 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)];
Zasadniczo ustalamy, który element jest najbardziej wysunięty w dół w widoku i dodajemy wypełnienie 10 pikseli na dole
Ponieważ scrollView może mieć inne scrollViews lub inne drzewo subViews inDepth, preferowane jest rekurencyjne uruchamianie dogłębne.
Szybki 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)
}
}
Stosowanie
scrollView.recalculateVerticalContentSize_synchronous()
Lub po prostu zrób:
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
(To rozwiązanie zostało dodane przeze mnie jako komentarz na tej stronie. Po uzyskaniu 19 głosów pozytywnych na ten komentarz, zdecydowałem się dodać to rozwiązanie jako formalną odpowiedź dla dobra społeczności!)
Dla swift4 przy użyciu redukuj:
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
return $0.union($1.frame)
}).size
Rozmiar zależy od zawartości załadowanej do niego i opcji przycinania. Jeśli jest to widok tekstu, zależy to również od zawijania, liczby wierszy tekstu, rozmiaru czcionki itd. I tak dalej. Prawie niemożliwe do obliczenia siebie. Dobrą wiadomością jest to, że jest obliczany po załadowaniu widoku i w pliku viewWillAppear. Wcześniej wszystko jest nieznane, a rozmiar zawartości będzie taki sam jak rozmiar ramki. Ale w metodzie viewWillAppear i po (na przykład viewDidAppear) rozmiar zawartości będzie rzeczywisty.
Zawijanie kodu Richy'ego Stworzyłem niestandardową klasę UIScrollView, która całkowicie automatyzuje zmianę rozmiaru treści!
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
Jak używać:
Po prostu zaimportuj plik .h do kontrolera widoku i zadeklaruj instancję SBScrollView zamiast zwykłej instancji UIScrollView.
Uważam również, że odpowiedź Lewiatana działa najlepiej. Jednak obliczał dziwną wysokość. Jeśli w pętli podglądów podrzędnych ustawiono wyświetlanie wskaźników przewijania, będą one znajdować się w tablicy widoków podrzędnych. W takim przypadku rozwiązaniem jest tymczasowe wyłączenie wskaźników przewijania przed zapętleniem, a następnie przywrócenie ich poprzedniego ustawienia widoczności.
-(void)adjustContentSizeToFit
jest metodą publiczną w niestandardowej podklasie 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;
}
Myślę, że może to być zgrabny sposób aktualizowania rozmiaru widoku zawartości 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
}
}
Ustaw dynamiczny rozmiar treści w ten sposób.
self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
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
więc automatyczne układanie się zakończyło, w iOS7 działało dobrze, ale testowanie systemu operacyjnego iOS6 z jakiegoś powodu nie zakończyło pracy, więc miałem złe wartości wysokości, więc przełączyłem się naviewDidAppear
teraz działa dobrze. żeby tylko wskazać, że może ktoś by tego potrzebował. dzięki