2016-02-05 3 views
11
Auto Layout mit

Ich versuche, eine UIScrollView nur machen läßt in horizontaler Richtung Zoomen. Die Bildlaufansicht wird mit einem reinen automatischen Layoutansatz eingerichtet. Die übliche Vorgehensweise ist, wie in einer Reihe von Stapelüberlauf Antworten (z this one) und other Ressourcen vorgeschlagen. Ich habe dies erfolgreich in Apps verwendet, bevor das automatische Layout vorhanden war. Der Ansatz ist wie folgt: In der Inhaltsansicht des Scroll-Ansicht, ich setTransform() außer Kraft setzen, und ändern Sie das übergebene Transformation auf der Skala in x-Richtung:Wie UIScrollView Zoom in nur eine Richtung zu machen, wenn

override var transform: CGAffineTransform { 
    get { return super.transform } 
    set { 
     var t = newValue 
     t.d = 1.0 
     super.transform = t 
    } 
} 

I zurückgesetzt auch den Inhalt der Ansicht blättern versetzt, so dass es doesn ‚t bewegen vertikal während des Zoom:

func scrollViewDidZoom(scrollView: UIScrollView) { 
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height) 
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0) 
} 

Diese sehr schön funktioniert, wenn nicht mit automatischen Layout:

Zooming without autolayout

jedoch bei der Verwendung von automatischen Layout, der Inhalt Offset endet falsch up während des Zoomens. Während die Inhaltsansicht nur Skalen in horizontaler Richtung, es bewegt sich vertikal:

Zooming with autolayout

Ich habe ein Beispielprojekt setzen on GitHub (verwendet, um die Videos in dieser Frage zu machen). Es enthält zwei Storyboards, Main.storyboard und NoAutolayout.storyboard, die außer dass Main.storyboard identisch ist Automatisches Layout eingeschaltet, während NoAutolayout nicht. Wechseln Sie zwischen ihnen, indem Sie die Projekteinstellung für das Haupt-Interface ändern, und Sie können das Verhalten in beiden Fällen sehen.

Ich würde das automatische Layout lieber nicht abschalten, da es eine Reihe von Problemen bei der Implementierung meiner Benutzeroberfläche auf eine viel nettere Weise löst, als dies beim manuellen Layout der Fall ist. Gibt es eine Möglichkeit, den vertikalen Inhaltsoffset während des Zoomens mit automatischem Layout korrekt (Null) zu halten?

EDIT: Ich habe ein drittes Drehbuch, AutolayoutVariableColumns.storyboard, zum Beispielprojekt hinzugefügt. Dies fügt ein Textfeld hinzu, um die Anzahl der Spalten in der GridView zu ändern, wodurch sich die tatsächliche Inhaltsgröße ändert. Dies zeigt genauer das Verhalten, das ich in der echten App benötige, die diese Frage ausgelöst hat.

Antwort

8

Denken Sie, ich habe es herausgefunden. Sie müssen eine Übersetzung in der Transformation anwenden, um die Zentrierung zu versetzen, die UIScrollView beim Zoomen versucht.

Fügen Sie diese auf Ihre Gridview:

var unzoomedViewHeight: CGFloat? 
override func layoutSubviews() { 
    super.layoutSubviews() 
    unzoomedViewHeight = frame.size.height 
} 

override var transform: CGAffineTransform { 
    get { return super.transform } 
    set { 
     if let unzoomedViewHeight = unzoomedViewHeight { 
      var t = newValue 
      t.d = 1.0 
      t.ty = (1.0 - t.a) * unzoomedViewHeight/2 
      super.transform = t 
     } 
    } 
} 

die Transformation zu berechnen, müssen wir die ungezoomten Höhe der Ansicht wissen. Hier nehme ich nur die Bildgröße während layoutSubviews() und nehme an, dass sie die Unzoom-Höhe enthält. Es gibt wahrscheinlich einige Randfälle, wo das nicht stimmt; möglicherweise ein besserer Ort in der Ansicht Update-Zyklus, um die Höhe zu berechnen, aber das ist die Grundidee.

+0

Dank Anna, das ist ein interessanter Ansatz, und es scheint, als könnte es gut funktionieren! Ich werde es versuchen und werde es melden. –

+0

Ein schneller Test scheint darauf hinzuweisen, dass dies funktioniert. Ich werde versuchen, es morgen in mein aktuelles Projekt zu integrieren. Danke noch einmal! –

+0

Sicher! Lassen Sie es mich wissen, wenn Sie es zwicken - ich versuche, das gleiche Problem zu lösen. (und achte darauf, dass du meine Antwort akzeptierst ;-) –

0

Ich bin mir nicht sicher, ob dies Ihre Anforderung von 100% Autolayout erfüllt, aber hier ist eine Lösung, wo die UIScrollView mit Autolayout und gridView als Unteransicht hinzugefügt wurde.

Ihre ViewController.swift sollte wie folgt aussehen:

// Add an outlet for the scrollView in interface builder. 
@IBOutlet weak var scrollView: UIScrollView! 

// Remove the outlet and view for gridView. 
//@IBOutlet var gridView: GridView! 

// Create the gridView here: 
var gridView: GridView! 

// Setup some views 
override func viewDidLoad() { 
    super.viewDidLoad() 
    // The width for the gridView is set to 1000 here, change if needed. 
    gridView = GridView(frame: CGRect(x: 0, y: 0, width: 1000, height: self.view.bounds.height)) 
    scrollView.addSubview(gridView) 
    scrollView.contentSize = CGSize(width: gridView.frame.width, height: scrollView.frame.height) 
    gridView.contentMode = .ScaleAspectFill 
} 

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { 
    return gridView 
} 

func scrollViewDidZoom(scrollView: UIScrollView) { 
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0) 
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height) 
} 
+0

Danke für die nette Antwort. Leider ist der Grund, warum ich das automatische Layout verwenden möchte, dass "GridView" (nicht wirklich so einfach wie GridView in meinem Beispielprojekt) eine Größe hat, die sich auf seinen Inhalt ändert. Autolayouts Unterstützung für 'intrinsicContentSize()' ist dafür perfekt. Ohne sie müssen die Ansicht (en) manuell angepasst werden, wenn sich ihr Inhalt ändert, was ziemlich schmerzhaft ist. Es macht mir nichts aus, das automatische Layout für _scroll view_ off zu deaktivieren, solange die Inhaltsansicht über die eigene Inhaltsgröße skaliert werden kann. –

+0

Ah, verstehe. Ich werde Sie wissen lassen, wenn ich eine Möglichkeit finde, das automatische Layout auch auf der 'gridView' zu verwenden! – xoudini

3

Versuchen translatesAutoresizingMaskIntoConstraints

func scrollViewDidZoom(scrollView: UIScrollView) { 
    gridView.translatesAutoresizingMaskIntoConstraints = true 
} 

Im Beispielprojekt Einstellung es funktioniert. Wenn dies nicht ausreicht, versuchen, neue Zwänge zu schaffen programmatisch in scrollViewDidEndZooming: helfen könnte.

Auch wenn dies nicht hilft, bitte das Beispielprojekt aktualisieren, damit wir das Problem mit variabler intrinsicContentSize()

Dieser Artikel von Ole Begemann wiedergeben können mir sehr geholfen
How I Learned to Stop Worrying and Love Cocoa Auto Layout

WWDC 2015 Video
Mysteries of Auto Layout, Part 1
Mysteries of Auto Layout, Part 2

Und zum Glück gibt es dafür eine Flagge. Es heißt translatedAutoResizingMask IntoConstraints [ohne Leerzeichen].

Es ist ein bisschen ein Bissen, aber es tut ziemlich viel, was es sagt. Dadurch verhalten sich Ansichten so, wie sie es unter dem Legacy-Layout-System getan haben, aber in einer Auto-Layout-Welt.

+0

Danke für die Antwort. Ich verstehe nicht ganz, warum das funktioniert, aber ich denke, es ist im Wesentlichen ein zerbrechlicher Hack. Wenn Sie 'transmittedAutoresizingMaskIntoConstraints' in' viewDidLoad() 'einschalten, erhalten Sie sofort eine Conflicting-Constraints-Exception. In jedem Fall ist 'translatedAutoresizingMaskIntoConstraints' nicht dazu gedacht, für Ansichten verwendet zu werden, die direkt am automatischen Layout beteiligt sind, da sich das GridView hier befindet. Ich werde das Beispielprojekt mit einer Variablen 'intrinsicContentSize()' aktualisieren. –

+0

Ich habe das Beispielprojekt mit einem Storyboard aktualisiert, mit dem die Anzahl der Spalten in der Rasteransicht geändert werden kann, was sich auf die tatsächliche Inhaltsgröße auswirkt. (Ich habe das Projekt auch nach GitHub verlegt). Beachten Sie, dass mit dieser Änderung Ihre Lösung zusammenbricht. Wenn Sie zoomen und dann die Anzahl der Spalten ändern, führt die aus der Autoresize-Maske übersetzte Breiteneinschränkung dazu, dass GridView nicht an die neue Spaltenanzahl angepasst wird. –

+0

@ Annas Antwort ist viel besser als der Versuch, zwischen Legacy- und Auto-Layout-System zu wechseln. –

1

Obwohl @ Anna-Lösung arbeitet, bietet UIScrollView einen Weg mit Auto Layout des Arbeitens. Aber weil Scroll-Ansichten, die ein wenig anders aus anderen Ansichten arbeiten, sind Einschränkungen auch anders interpretiert:

  • Constraints zwischen den Kanten/Rand der Scroll-Ansicht und ihrem Inhalt werden an dieInhaltsbereich der Scroll-Ansicht.
  • Constraints zwischen der Höhe , Breite oder Zentren befestigen an die Rahmen der Bildlaufansicht.
  • Einschränkungen zwischen Bildlaufansicht und Ansichten außerhalb Bildlaufansicht funktioniert wie eine normale Ansicht.

Also, wenn Sie eine Subview Scroll Hinblick auf seine Kanten/Ränder gepinnt hinzufügen, wird das subview Inhaltsbereich des Scroll-Ansicht oder Inhalt Ansicht.

von Apple schlägt vor, den folgenden Ansatz in Working with Scroll Views:

  1. auf die Szene, um die Bildlaufansicht hinzufügen.
  2. Zeichnen Sie Einschränkungen, um die Größe und Position der Bildlaufansicht wie üblich zu definieren.
  3. Fügen Sie eine Ansicht zur Bildlaufansicht hinzu. Legen Sie das Xcode-spezifische Label der Ansicht auf Content View fest.
  4. Die oberen, unteren, vorderen und hinteren Kanten der Inhaltsansicht an die entsprechenden Kanten der Bildlaufansicht anheften. Die Inhaltsansicht definiert nun den Inhaltsbereich der Bildlaufansicht.
  5. (...)
  6. (...)
  7. der Ansicht blättern Inhalt in der Inhaltsansicht auslegen. Verwenden Sie Einschränkungen, um den Inhalt wie gewohnt in der Inhaltsansicht zu positionieren.

In Ihrem Fall die Grid muss im Inhalt sehen sein:

Interface Builder

Sie die Rasteransicht Einschränkungen halten können, die Sie wurden mit, aber jetzt, an die Inhaltsansicht angehängt. Halten Sie den Code beim horizontalen Zoomen genau so wie er ist. Ihr transform überbrückt es sehr gut.

+0

Ich verstehe das alles. Die Frage bezieht sich auf horizontal-nur _zooming_ das ist ein ganz anderes Biest als horizontal-nur scrollen. In der echten App, die diese Frage inspiriert hat, sollte _scrolling_ in beide Richtungen funktionieren und die Höhe der Inhaltsansicht ist variabel.Es ist nur das Zoomen, das auf die horizontale Richtung beschränkt sein sollte. –

+0

Kein Problem. Das horizontale Scrollen war nur eine Vermutung. Über das horizontale Zoomen hast du es schon gelöst. Was bleibt, ist das horizontale Zoomen mit Auto-Layout zu lösen und mit dieser Lösung können Sie es ohne Annas machen. Ich habe getestet und es hat gut funktioniert. Wenn es einen Punkt unklar ist, lass es mich wissen und ich werde den Beitrag bearbeiten. –

+0

Annas Lösung eignet sich sehr gut für horizontales Zoomen mit einem reinen Auto-Layout-Ansatz. Wenn Sie einen anderen Ansatz verfolgen, würde mich das interessieren. Deine Antwort hier erwähnt das Zoomen nicht. –