Dimensionierung einer Containeransicht mit einem Controller dynamischer Größe in einer Bildlaufansicht

73

Ich versuche, eine Containeransicht mit einem Controller mit dynamischer Höhe in einer UIScrollView zu erstellen und deren Größe mithilfe des automatischen Layouts automatisch zu ändern.

Storyboard zur Veranschaulichung des Setups

View Controller A ist die Bildlaufansicht, in der die Containeransicht sowie weitere Inhalte enthalten sind.

View Controller B ist der View Controller, für den ich eine dynamische Größe haben möchte und bei dem der gesamte Inhalt in der Bildlaufansicht von View Controller A in voller Höhe angezeigt werden soll.

Ich habe einige Probleme damit, dass die dynamische Größe von B die Größe der Containeransicht in A automatisch festlegt. Wenn ich jedoch eine Höhenbeschränkung für die Containeransicht in A festlege Containeransicht in A bis zum Beispiel 250,

Es wäre die erwartete Ausgabe, wenn View Controller B auch eine Höhe von 250 hätte. Es funktioniert auch gut für Höhe 1000, soweit ich weiß, sind alle Einschränkungen für das automatische Layout ordnungsgemäß eingerichtet. Da die Höhe eigentlich dynamisch sein sollte, möchte ich leider vermeiden, überhaupt eine Höhenbeschränkung festzulegen.

Ich bin mir nicht sicher, ob es Einstellungen für View Controller BI gibt, mit denen die Größe je nach Inhalt automatisch aktualisiert werden kann, oder ob es andere Tricks gibt, die ich verpasst habe. Jede Hilfe wäre sehr dankbar!

Gibt es eine Möglichkeit, die Größe der Containeransicht in A entsprechend der Größe von View Controller B zu bestimmen, ohne eine Höhenbeschränkung festzulegen?

TemptingFriendlyGrison
quelle
Sie können Ihre Höhenbeschränkungen als IBOutlet festlegen und sie dynamisch in Ihrem Code anpassen
Randy
Dies ist der nächste Weg, den ich zu einer Lösung gekommen bin, aber ist dies der einfachste Weg, dies zu tun? Ich hatte gehofft, dass es etwas geben würde, das ich verpasst habe, um das Problem leichter zu lösen, als manuell eine Höhenbeschränkung festzulegen.
TemptingFriendlyGrison
1
Ich denke, Sie können hier auch PreferredContentSize verwenden - ändern Sie es einfach weiter, wissen Sie?
Fattie

Antworten:

114

Ja, das gibt es. Ich habe es geschafft, dieses Verhalten in einem meiner eigenen Projekte zu erreichen.

Alles, was Sie tun müssen, ist dem System mitzuteilen, dass es keine Einschränkungen hinzufügen soll, die den festen Rahmensatz für Ihre Stammansicht in Interface Builder nachahmen. Der beste Ort, um dies zu tun, ist in Ihrem Container View Controller, wenn Ihr Embed-Segue ausgelöst wird:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // You might want to check if this is your embed segue here
    // in case there are other segues triggered from this view controller. 
    segue.destinationViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
}

Wichtig:

Sie müssen sicherstellen, dass die Ansicht, die Sie in den Container laden, von oben nach unten eingeschränkt ist, und Sie müssen die Priorität einer der vertikalen Einschränkungen auf einen Wert unter 1000 festlegen. (Es wird empfohlen, immer die untere Einschränkung zu verwenden dafür.) Dies ist notwendig, weil sich Interface Builder sonst beschweren wird - aus gutem Grund:

Zur Entwurfszeit hat Ihre Stammansicht eine feste Größe (Höhe). Wenn nun alle Ihre Unteransichten eine feste Höhe haben und mit festen Einschränkungen verbunden sind, die alle dieselbe Priorität haben, können alle diese Anforderungen nur erfüllt werden, wenn die Höhe Ihrer festen Stammansicht zufällig genau der Gesamthöhe Ihrer Unteransichten und den vertikalen Einschränkungen entspricht. Wenn Sie die Priorität einer der Einschränkungen auf 999 Interface Builder senken, wissen Sie, welche Einschränkung aufgehoben werden muss. Zur Laufzeit jedoch - wenn die translatesAutoresizingMaskIntoConstraintsEigenschaft wie oben angegeben festgelegt ist - gibt es keinen festen Rahmen mehr für Ihre Stammansicht, und das System verwendet stattdessen Ihre 999-Prioritätsbeschränkung.

Screenshot des Interface Builder

Mischa
quelle
2
Das hat gut funktioniert, danke! Hatte einige Einschränkungen im Editor mit der Bildlaufansicht, aber diese wurden behoben, wenn ich eine Höhenbeschränkung für den Container festlegte, den ich beim Erstellen entfernt hatte.
TemptingFriendlyGrison
14
Sie sollten in der Lage sein, diese Einschränkungsprobleme zu beheben, ohne Einschränkungen in Ihrer Containeransicht festlegen und entfernen zu müssen, wenn Sie im Interface Builder zum Größeninspektor gehen, nach unten scrollen und "Platzhalter" aus der Dropdown- Liste " Intrinsic Content Size " auswählen . Sie können dann eine feste intrinsische Größe für Ihre Containeransicht festlegen, die nur vom Interface Builder zum Layout der Ansicht verwendet wird, aber zur Laufzeit nicht angewendet wird.
Mischa
Unter iOS 9 stellte ich fest, dass das Setzen von translatesAutoresizingMaskIntoConstraints = NO im PARENT-Ansichts-Controller (anstelle des untergeordneten Containers VC) den Trick ausführte. Ich habe meine unterste Einschränkungspriorität bei 1000 gehalten, wodurch die gesamte Containeransicht wachsen konnte. Ansonsten ging der Inhalt der Containeransicht über den unteren Rand meiner Containeransicht hinaus und ich sah andere Verrücktheiten im Layout. Danke, dass du das herausgefunden hast !!!
Kento
Dies funktioniert nicht für mich, nehmen iboutlet der Containeransicht und beurteilen Höhe arbeiten
siva krishna
Tolle Hilfe ! Vielen Dank!
Gunjot Singh
25

Aus der Antwort von @Mischa konnte ich die Höhe einer containerView abhängig von ihrem Inhalt dynamisch machen:

Schreiben Sie im viewController der containerView:

  override func loadView() {
    super.loadView()
    view.translatesAutoresizingMaskIntoConstraints = false
  }

Und achten Sie darauf, dass alle vertikalen Einschränkungen in IB festgelegt sind. Dazu müssen Sie view.translatesAutoresizingMaskIntoConstraints = false von außerhalb des View-Controllers nicht festlegen.

In meinem Fall habe ich versucht, die Größe des Containers in eine tableView im viewController zu ändern. Da die tableView abhängig von ihrer Übersicht eine flexible Höhe hat (also alles in Ordnung für IB), habe ich die vertikalen Einschränkungen im Code folgendermaßen abgeschlossen:

  @IBOutlet private var tableView: UITableView! {
    didSet {
      tableView.addConstraint(tableViewHeight)
    }
  }
  private lazy var tableViewHeight: NSLayoutConstraint = NSLayoutConstraint(item: self.tableView, attribute: NSLayoutAttribute.Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 0)

Beobachten Sie dann die contentSize-Höhe der Tabellenansicht und passen Sie die Konstante der tableViewHeight-Einschränkung bei Bedarf programmgesteuert an.

Acecilia
quelle
Danke @acecilia, das ist in der Tat ein toller Tipp. Prost
Fattie
17

Swift 4, Xcode 9

Die akzeptierte Antwort allein hat das Problem für mich nicht gelöst.

Meine Hierarchie: ScrollView -> Inhaltsansicht (UIView) -> Ansichten | Containeransicht | Weitere Ansichten.

Ich musste die folgenden Einschränkungen hinzufügen, damit sich ScrollView und Container dynamisch anpassen:

  1. ScrollView : Oben, Unten, Führend, Nach Übersicht (Sichere Bereiche)
  2. Inhaltsansicht : Oben, Unten, Führend, Nachlaufend, Gleiche Breite wie ScrollView, aber auch mit Gleicher Höhe (Einschränkung mit niedrigerer Priorität: 250) .
  3. Ansichten : normale Einschränkungen für das automatische Layout.
  4. Containeransicht : Ansicht von oben, von unten nach unten, Führen und Nachlaufen in einen sicheren Bereich.
  5. Eingebettete VC von Container View : Alle Einschränkungen, die vertikal mit der unteren Einschränkung verbunden sind, haben eine niedrigere Priorität, sind jedoch größer als die gleiche Höhe der Inhaltsansicht! In diesem Fall hat eine Priorität von 500 den Trick getan.
  6. Stellen Sie view.translatesAutoresizingMaskIntoConstraints = falseentweder prepareForSegue()oder loadView()wie andere Antworten ein.

Jetzt habe ich eine dynamisch anpassbare Containeransicht in einer Scroll-Ansicht mit automatischer Größenänderung.

Teodor Ciuraru
quelle
Hallo Teodor, ich habe Ihre Antwort gelesen und denke, dass Sie mir bei meinem Problem helfen können: stackoverflow.com/questions/48797508/twitter-profile-effect Vielen Dank für Ihre Hilfe.
Delarcomarta
Irgendein GitHub-Beispiel?
Muhammad Hassan
Es funktioniert nicht, wenn es eine scrollbare Ansicht in der Containeransicht gibt
Hamid Shahsavari
Ich würde dies 5 Mal positiv bewerten, wenn ich könnte. Diese schrittweise Antwort war einfach und die Lösung für 3 Stunden Durcheinander mit IB-Einschränkungen.
TM Lynch
Erstaunliche Lösung !!! arbeitete für mich. Aber ich sehe jetzt ein anderes Problem. Mein eingebetteter Controller für die Tabellenansicht scrollt an Ort und Stelle, die Bildlaufansicht hat jedoch aufgehört zu scrollen
Jerry
4

Aufbauend auf der großartigen Lösung von @ Mischa für dieses Problem müssen Sie, wenn der Container eine UITableView mit dynamischer Größe einbettet, die Überschreibung von contentSize und intrinsicContentSize sowie die Einstellung translatesAutoresizingMaskIntoConstraints = falsegemäß der akzeptierten Antwort prüfen .

Wenn die übergeordnete Ansicht die Containeransicht lädt und einrichtet, wird die Eigenhöhe jeder der UITableViewCells auf 0 abgeleitet, daher die UITableViewDelegate-Methode von:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

wird nicht aufgerufen. Dadurch wird UITableView so angezeigt, als hätte es keinen Inhalt.

Wenn Sie die intrinsische Inhaltsgröße überschreiben, um die tatsächliche Inhaltsgröße zurückzugeben, wird die Tabellenansicht mit der Größe angezeigt, die für den Inhalt erforderlich ist.

Ein großartiger Artikel von Emilio Peláez geht näher auf dieses Thema ein.

Stuart Pattison
quelle
1

Ich habe diese Lösung ausprobiert und für mich gearbeitet.
Versuchen Sie im Zielansichts-Controller (untergeordneter Ansicht) wie folgt, auf den übergeordneten Ansichts-Controller zuzugreifen:

if let parentVC = self.parent as? EmbededContinerViewController {               
   if let myParent = parentVC.parent as? ParentViewController {
      myParent.subViewHeight.constant += 2000
      myParent.subView.layoutIfNeeded()
   }

}}

Vielleicht ist dies keine normale Lösung, hat aber für mich funktioniert und mein Problem gelöst.
Ich habe diesen Code in Swift 5.1 ausprobiert .

reza_khalafi
quelle