webentwicklung-frage-antwort-db.com.de

Auto-Layout UIScrollView mit Unteransichten mit dynamischen Höhen

Ich habe Probleme mit UIScrollView mit automatischen Layouteinschränkungen ..__ Ich habe die folgende Ansichtshierarchie mit durch IB festgelegten Einschränkungen:

- ScrollView (leading, trailing, bottom and top spaces to superview)
-- ContainerView (leading, trailing, bottom and top spaces to superview)
--- ViewA (full width, top of superview)
--- ViewB (full width, below ViewA)
--- Button (full width, below ViewB)

ViewA und ViewB haben anfängliche Höhen von 200 Punkten, können jedoch durch Anklicken vertikal bis zu einer Höhe von 400 Punkten erweitert werden. ViewA und ViewB werden durch Aktualisieren der Höhenbeschränkung (von 200 auf 400) erweitert. Hier ist der entsprechende Ausschnitt:

if(self.contentVisible) {
    heightConstraint.constant -= ContentHeight;
    // + additional View's internal constraints update to hide additional content 
    self.contentVisible = NO;
} else {
    heightConstraint.constant += ContentHeight;
    // + additional View's internal constraints update to show additional content
    self.contentVisible = YES;
}

[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:.25f animations:^{
    [self.view layoutIfNeeded];
}];

Mein Problem ist, dass wenn beide Ansichten erweitert werden, ich in der Lage sein muss, einen Bildlauf durchzuführen, um den gesamten Inhalt zu sehen, und im Moment funktioniert der Bildlauf nicht. Wie kann ich die Bildlaufansicht mithilfe von Einschränkungen aktualisieren, um die Änderungen der ViewA- und ViewB-Höhen wiederzugeben?

Die einzige Lösung, die ich mir bisher vorstellen kann, besteht darin, die Höhe der ContainerView nach der Animation manuell einzustellen. Dies ist die Summe der Höhen von ViewA + ViewB + Button. Aber ich glaube, dass es eine bessere Lösung gibt?

Vielen Dank

38
lukas

Ich verwende die reine Struktur wie folgt

-view
  -scrollView
    -view A
    -view B
    -Button

Stellen Sie sicher, dass die Schaltfläche (THE LAST view) über eine Einschränkung verfügt (vertikaler Abstand von der Unterseite bis zum Übersichtsbild, dh der Bildlaufansicht). In diesem Fall gilt unabhängig davon, welche Änderungen für Ansicht A und Ansicht B vorgenommen wurden, die Höhe von ScrollView wird entsprechend geändert.

Ich beziehe mich auf dieses großartige Online Buchseite .

Lesen Sie einfach den Abschnitt "Erstellen einer Bildlaufansicht", Sie sollten eine Idee haben. 

Ich hatte das ähnliche Problem, dass ich eine Detailansicht erstellte und Interface Builder mit Auto-Layout zu verwenden, so gut für die Aufgabe geeignet ist!

Viel Glück!

(Zusätzliche Ressourcen:

Stack-Überlaufdiskussion über automatisches Layout für Bildlaufansicht .

iOS 6 hat ein Versionshinweise , das über die Unterstützung von Auto Layout für UIScrollView spricht.

Kostenloses Online iOS-Bucherklärung zum Blättern. Das hat mir tatsächlich sehr geholfen!

58
lorixx

Nehmen wir an, wir haben eine Hierarchie wie diese (Label1 ist eine Unteransicht von ContentView; ContentView ist eine Unteransicht von ScrollView, ScrollView ist eine Unteransicht der Ansicht des Viewcontrollers):

ViewController's View ScrollView ContentView Label1 Label2 Label3

ScrollView ist beim Autolayout auf normale Weise auf die Ansicht des Viewcontrollers beschränkt.

ContentView wird oben/links/rechts/unten zur Bildlaufansicht gepinnt. Das heißt, Sie haben Einschränkungen, durch die die obere/untere/führende/nachlaufende Kante der ContentView auf die gleichen Kanten in der ScrollView beschränkt wird. Hier ist ein Schlüssel: Diese Einschränkungen beziehen sich auf die contentSize des ScrollView, nicht auf die Frame-Größe, wie sie in der Ansicht des Viewcontrollers angezeigt wird. Dem ContentView wird also nicht mitgeteilt, dass es dieselbe Frame-Größe wie der angezeigte ScrollView-Frame hat. Es wird vielmehr dem ScrollView mitgeteilt, dass ContentView der Inhalt ist. Wenn also ContentView größer als der ScrollView-Frame ist, werden Sie wie beim Blättern mit scrollView.contentSize scrollen als scrollView.frame wird der Inhalt scrollbar.

Hier ist ein weiterer Schlüssel: Jetzt müssen Sie genügend Einschränkungen zwischen ContentView, Label1-3 und allem anderen außer dem ScrollView haben, damit ContentView seine Breite und Höhe aus diesen Einschränkungen herausfinden kann.

Wenn Sie beispielsweise einen Satz von Etiketten mit vertikalem Bildlauf verwenden möchten, legen Sie eine Einschränkung fest, um die Breite der ContentView an die Breite der ViewController-Ansicht anzupassen, die die Breite berücksichtigt. Um auf die Höhe zu achten, verbinden Sie Label1 oben mit ContentView oben, Label2 oben mit Label1 unten, Label3 oben mit Label2 unten und schließlich (und vor allem) Label3 unten mit ContentView. Jetzt hat es genug Informationen, um die Höhe des ContentView zu berechnen.

Ich hoffe, dass dies jemandem einen Anhaltspunkt gibt, als ich die obigen Beiträge durchgelesen habe und immer noch nicht herausfinden konnte, wie die Breiten- und Höheneinschränkungen von ContentView richtig festgelegt werden. Was mir fehlte, war das Befestigen der Unterseite von Label3 an der Unterseite von ContentView. Andernfalls, wie könnte ContentView wissen, wie groß es ist (da Label3 gerade dann schwebend wäre und es keine Einschränkung gibt, um ContentView mitzuteilen, wo die untere y-Position liegt)?.

25
Doug Voss

Dies ist ein Beispiel dafür, wie ich ein UIScrollView mit Autolayout und einer Containeransicht erstellt habe. Ich habe es kommentiert, um es klarer zu machen:

container ist eine Standard-UIView und body ist eine UITextView

- (void)viewDidLoad
{
    [super viewDidLoad];

    //add scrollview
    [self.view addSubview:self.scrollView];

    //add container view
    [self.scrollView addSubview:self.container];

    //body as subview of container (body size is undetermined)
    [self.container addSubview:self.body];

    NSDictionary *views = @{@"scrollView" : self.scrollView, @"container" : self.container, @"body" : self.body};
    NSDictionary *metrics = @{@"margin" : @(100)};

    //constrain scrollview to superview, pin all edges
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:kNilOptions metrics:metrics views:views]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:kNilOptions metrics:metrics views:views]];

    //pin all edges of the container view to the scrollview (i've given it a horizonal margin as well for my purposes)
    [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[container]|" options:kNilOptions metrics:metrics views:views]];
    [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-margin-[container]-margin-|" options:kNilOptions metrics:metrics views:views]];

    //the container view must have a defined width OR height, here i am constraining it to the frame size of the scrollview, not its bounds
    //the calculation for constant is so that it's the width of the scrollview minus the margin * 2
    [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:-([metrics[@"margin"] floatValue] * 2)]];

    //now as the body grows vertically it will force the container to grow because it's trailing Edge is pinned to the container's bottom Edge
    //it won't grow the width because the container's width is constrained to the scrollview's frame width
    [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[body]|" options:kNilOptions metrics:metrics views:views]];
    [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[body]|" options:kNilOptions metrics:metrics views:views]];
}

In meinem Beispiel ist 'body' eine UITextView, aber es könnte alles andere sein. Wenn Sie zufällig eine UITextView verwenden, müssen Sie beachten, dass eine vertikale Höheneinstellung erforderlich ist, die in viewDidLayoutSubviews festgelegt wird. Fügen Sie also in viewDidLoad die folgende Einschränkung hinzu und behalten Sie einen Verweis darauf:

self.bodyHeightConstraint = [NSLayoutConstraint constraintWithItem:self.body attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:nil multiplier:1.0f constant:100.0f];
[self.container addConstraint:self.bodyHeightConstraint];

Dann berechnen Sie in viewDidLayoutSubviews die Höhe und aktualisieren die Konstante der Einschränkung:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    [self.bodyHeightConstraint setConstant:[self.body sizeThatFits:CGSizeMake(self.container.width, CGFLOAT_MAX)].height];

    [self.view layoutIfNeeded];
}

Der zweite Layout-Durchlauf ist erforderlich, um die Größe der UITextView zu ändern.

7
jwswart

Verwenden Sie diesen Code. ScrollView setContentSize sollte im Haupt-Thread async genannt werden.

Schnell:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    DispatchQueue.main.async {
        var contentRect = CGRect.zero

        for view in self.scrollView.subviews {
           contentRect = contentRect.union(view.frame)
        }

        self.scrollView.contentSize = contentRect.size
    }
}

Ziel c:

 - (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];

     dispatch_async(dispatch_get_main_queue(), ^ {
                        CGRect contentRect = CGRectZero;

                        for(UIView *view in scrollView.subviews)
                           contentRect = CGRectUnion(contentRect,view.frame);

                           scrollView.contentSize = contentRect.size;
                       });
}
1

Meine Variante für die Scrollansicht mit! Dynamic! Höhe:

1) Fügen Sie scroll view zu Ihrer UIView hinzu. Pin alle Einschränkungen (oben, unten, Lead, Trail).

2) Addiere UIView zu Scroll View. Pin alle Einschränkungen (oben, unten, Lead, Trail). Es wird Ihr Content view sein. Sie können es auch umbenennen.

3) Ziehen Sie den Cursor von Content view auf Scroll view - Equal width.

4) Fügen Sie Ihrer UIView Inhalte hinzu. Legen Sie die erforderlichen Einschränkungen fest. Und! Fügen Sie am unteren Element die untere EinschränkungNICHTGreater or equal (>=) hinzu (wie die meisten Leute sprechen), ABEREqual! Setzen Sie es beispielsweise auf 20

In meiner Situation habe ich UIImageView inhaltlich. Ich habe es height mit Code verbunden. Und wenn ich 1000 mag, ist der Bildlauf sichtbar. Und alles funktioniert.

Funktioniert für mich wie ein Zauber. Irgendwelche Fragen - willkommen zu Kommentaren.

0
Vlad Pulichev

In jedem Moment sollte die Bildlaufansicht die Inhaltsgröße kennen. Die Inhaltsgröße wird aus den Unteransichten der Bildlaufansicht abgeleitet. Es ist sehr praktisch, Controller-Eigenschaften den Einschränkungen in der Xib-Datei zuzuordnen, die die Höhen der Unteransichten beschreiben. Im Code (einem Animationsblock) können Sie einfach Konstanten dieser Einschränkungseigenschaften ändern. Wenn Sie die gesamte Einschränkung ändern müssen, behalten Sie einen Verweis darauf, damit Sie sie später im übergeordneten Container aktualisieren können. 

0