Antworten:
Die beste Methode, die ich je gefunden habe, um die Inhaltsgröße von a UIScrollView
basierend auf den enthaltenen Unteransichten zu aktualisieren:
Ziel c
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
Schnell
let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size
UIScrollView kennt die Höhe seines Inhalts nicht automatisch. Sie müssen die Höhe und Breite selbst berechnen
Mach es mit so etwas wie
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];
Dies funktioniert jedoch nur, wenn die Ansichten untereinander liegen. Wenn Sie eine Ansicht nebeneinander haben, müssen Sie nur die Höhe einer Ansicht hinzufügen, wenn Sie den Inhalt des Scrollers nicht größer einstellen möchten, als er tatsächlich ist.
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
Stellen Sie translatesAutoresizingMaskIntoConstraints
auf NO
auf allen beteiligten Ansichten.
Positionieren und skalieren Sie Ihre Bildlaufansicht mit Einschränkungen außerhalb der Bildlaufansicht.
Verwenden Sie Einschränkungen, um die Unteransichten in der Bildlaufansicht anzuordnen. Achten Sie dabei darauf, dass die Einschränkungen an alle vier Kanten der Bildlaufansicht gebunden sind und sich nicht auf die Bildlaufansicht verlassen, um ihre Größe zu ermitteln.
Quelle: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
Ich habe dies zu Espuz und JCCs Antwort hinzugefügt. Es verwendet die y-Position der Unteransichten und enthält keine Bildlaufleisten. Bearbeiten Verwendet den unteren Rand der untersten sichtbaren Unteransicht.
+ (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;
am Anfang und self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
am Ende der Methode gearbeitet?
Hier ist die akzeptierte Antwort in Kürze für alle, die zu faul sind, um sie zu konvertieren :)
var contentRect = CGRectZero
for view in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
Hier ist eine Swift 3-Adaption von @ leviatans Antwort:
ERWEITERUNG
import UIKit
extension UIScrollView {
func resizeScrollViewContentSize() {
var contentRect = CGRect.zero
for view in self.subviews {
contentRect = contentRect.union(view.frame)
}
self.contentSize = contentRect.size
}
}
VERWENDUNG
scrollView.resizeScrollViewContentSize()
Sehr einfach zu bedienen!
Die folgende Erweiterung wäre in Swift hilfreich .
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
}
}
Die Logik ist bei den gegebenen Antworten dieselbe. Es werden jedoch versteckte Ansichten darin weggelassenUIScrollView
und die Berechnung wird durchgeführt, nachdem die Bildlaufindikatoren ausgeblendet wurden.
Außerdem gibt es einen optionalen Funktionsparameter, und Sie können einen Versatzwert hinzufügen, indem Sie den Parameter an die Funktion übergeben.
Tolle und beste Lösung von @leviathan. Einfach mit dem FP-Ansatz (Functional Programming) schnell umsetzen.
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), {
CGRectUnion($0, $1.frame)
}.size
self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
Sie können die Höhe des Inhalts in UIScrollView ermitteln, indem Sie berechnen, welches Kind "weiter kommt". Um dies zu berechnen, müssen Sie Herkunft Y (Start) und Artikelhöhe berücksichtigen.
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))];
Auf diese Weise müssen Elemente (Unteransichten) nicht direkt untereinander gestapelt werden.
Ich habe eine andere Lösung gefunden, die auf der Lösung von @ emenegro basiert
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)];
Grundsätzlich ermitteln wir, welches Element in der Ansicht am weitesten unten liegt, und fügen unten eine 10-Pixel-Polsterung hinzu
Da eine scrollView andere scrollViews oder einen anderen inDepth-SubViews-Baum haben kann, ist es vorzuziehen, die Tiefe rekursiv auszuführen.
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)
}
}
Verwendung
scrollView.recalculateVerticalContentSize_synchronous()
Oder einfach:
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
(Diese Lösung wurde von mir als Kommentar auf dieser Seite hinzugefügt. Nachdem ich 19 Stimmen für diesen Kommentar erhalten habe, habe ich beschlossen, diese Lösung als formelle Antwort zum Nutzen der Community hinzuzufügen!)
Für swift4 mit reduzieren:
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
return $0.union($1.frame)
}).size
Die Größe hängt vom darin geladenen Inhalt und den Beschneidungsoptionen ab. Wenn es sich um eine Textansicht handelt, hängt dies auch vom Umbruch, der Anzahl der Textzeilen, der Schriftgröße usw. ab. Fast unmöglich für Sie, sich selbst zu berechnen. Die gute Nachricht ist, dass sie nach dem Laden der Ansicht und in viewWillAppear berechnet wird. Vorher ist alles unbekannt und die Inhaltsgröße entspricht der Frame-Größe. In der viewWillAppear-Methode und danach (z. B. viewDidAppear) ist die Inhaltsgröße jedoch die tatsächliche.
Um den Code von Richy zu verpacken Ich habe eine benutzerdefinierte UIScrollView-Klasse erstellt, die die Größenänderung von Inhalten vollständig automatisiert!
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
Verwendung:
Importieren Sie einfach die .h-Datei in Ihren View Controller und deklarieren Sie eine SBScrollView-Instanz anstelle der normalen UIScrollView-Instanz.
Stellen Sie die Größe des dynamischen Inhalts wie folgt ein.
self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
Ich fand auch Leviathans Antwort, um am besten zu funktionieren. Es wurde jedoch eine seltsame Höhe berechnet. Wenn beim Durchlaufen der Unteransichten die Bildlaufansicht so eingestellt ist, dass Bildlaufindikatoren angezeigt werden, befinden sich diese im Array der Unteransichten. In diesem Fall besteht die Lösung darin, die Bildlaufanzeigen vor dem Schleifen vorübergehend zu deaktivieren und dann ihre vorherige Sichtbarkeitseinstellung wiederherzustellen.
-(void)adjustContentSizeToFit
ist eine öffentliche Methode für eine benutzerdefinierte Unterklasse von 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;
}
Ich denke, dies kann eine gute Möglichkeit sein, die Größe der Inhaltsansicht von UIScrollView zu aktualisieren.
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
damit das Autolayout beendet wird. In iOS7 hat es gut funktioniert, aber das Testen des Autolayouts unter iOS6 hat die Arbeit aus irgendeinem Grund nicht beendet, sodass ich einige falsche Höhenwerte hatteviewDidAppear
. nur um darauf hinzuweisen, dass vielleicht jemand dies brauchen würde. danke