Tuesday 24 October 2017

Uicollectionview Paging Indicator Forex


Wir haben einen horizontalen Scroller mit UICollectionView implementiert. Wir wollten die linke Seitenmarkierung und die rechte Seitennummer anzeigen, um anzuzeigen, dass sich links oder rechts mehr Zellen befinden. Um dies zu erreichen, haben wir das folgende Stück Code in der Methode geschrieben, aber es funktioniert nicht richtig. Die maximale Anzahl von Zellen, die gezeigt werden können, beträgt 5 (d. h. Seitengröße ist 5). Diese Lösung funktioniert ordnungsgemäß, wenn wir langsam scrollen, aber nicht funktionieren, wenn wir sehr schnell scrollen. Bitte schlagen Sie vor, wie wir das erreichen können. Bryan Hansen UICollectionView benutzerdefinierte Layout-Tutorium Für ein neues Projekt brauchte ich, um eine primäre Ansicht in unserer iPad-App umzudenken, so entschied ich, die Ins und Outs von UICollectionView (in iOS 6 eingeführt) und ich dachte, es zu lernen Würde für ein gutes Tutorial zu teilen. Mein Projekt endete up benötigen eine benutzerdefinierte UICollectionViewLayout was bedeutete, musste ich mehr der Layout-Logik behandeln als wenn Id verwendet die UICollectionViewFlowLayout, die Apple bietet. Allerdings, die Schaffung eines benutzerdefinierten Layouts ermöglicht weit mehr Design Flexibilität, so dass ist, was Ill nehmen Sie durch hier. Nun gehen Sie durch den Prozess schrittweise, die wichtigsten Konzepte der benutzerdefinierten Layout-Design: Am Ende haben eine voll funktionsfähige Foto-Stack-Layout, wie hier zu sehen. Auch wenn Sie daran interessiert sind, ein Layout zu implementieren, das wesentlich anders ist als das hier vorgestellte, werden die grundlegenden Konzepte ziemlich ähnlich sein. So treten Sie mit Ihrem Lieblingsgetränk zurück und in einem Abend oder zwei youll haben Sie ein volles Verständnis der kundenspezifischen Planungen. Das abschließende Projekt ist auf github vorhanden. Dieses Tutorial ist auf das iPhone fokussiert, aber wenn Sie ein Projekt für das iPad erstellen wollen, sollten die meisten der vorgestellten sollte mit minimalen Änderungen arbeiten. Sobald Ihr Projekt erstellt wird youll haben eine Bare-Bones-iOS-App mit einer Aktie UIViewController. In diesem Entwurf passen alle Inhalte für unseren View-Controller automatisch in die Sammlungsansicht. Wenn Ihr Design andere Elemente wie statische Kopf - oder Fußzeile-Abschnitte hat, dann möchten Sie einen UIViewController mit einem UICollectionView als eine seiner Unteransichten verwenden. Ändern Sie den View-Controller-Header, so dass es Unterklassen UICollectionViewController stattdessen: Sie können den View-Controller zu etwas passenderen umbenennen. Dies geschieht einfach durch Auswahl des Klassennamens im Code und wählen Bearbeiten gt Refactor gt Umbenennen. Sie sollten auch die aufgerufenen xib aktualisieren, wenn Sie initWithNibName aufrufen: in Ihrem App-Delegat, um die Namensänderung wiederzugeben. Wir müssen auch die mit diesem View-Controller verknüpfte xib ändern, sodass sie auf eine UICollectionView verweist und nicht auf die UIView-Bestände, die sie jetzt hat. Wählen Sie die Ansichtssteuerungen xib aus, und öffnen Sie dann die rechte Inspektorscheibe, wenn sie noch nicht geöffnet ist. Suchen Sie eine Sammlung View-Objekt aus dem unteren Bereich des rechten Bereichs und ziehen Sie eine neue in den linken Teilbereich unter der Ansicht, die bereits vorhanden ist. Jetzt haben wir eine Collection View-Instanz, aber unsere alte Ansicht ist immer noch mit dem Controller verbunden. Lets fix, dass. Wählen Sie im linken Teilfenster die alte Ansicht aus, und löschen Sie sie. Ziehen Sie nun aus dem Dateieigentümer in die Sammlungsansicht. Wenn Sie freigeben, wählen Sie die Ansicht aus der Liste aus, um die Sammlungsansicht als Ansichtsregler-Primäransicht zu markieren. Jetzt ist eine gute Zeit, um unsere Xcode-Dateien ein wenig zu organisieren, bevor wir das Hinzufügen weiterer Klasse Dateien. Erstellen Sie eine neue Xcode-Gruppe mit dem Namen "View Controller", und ziehen Sie den View-Controller und die zugehörige xib in diese Gruppe. Da wir unser eigenes, vollständig benutzerdefiniertes Layout erstellen, müssen wir eine Layout-Unterklasse erstellen, die die Layout-Logik aller Subviews verarbeitet . Erstellen Sie innerhalb der View Controller-Gruppe eine neue Objective-C-Klasse mit dem Namen BHPhotoAlbumLayout, die die Unterklassen UICollectionViewLayoutNext enthält, und führen Sie dann einige Änderungen an den View-Controllern xib durch, um unsere benutzerdefinierte Layoutklasse zu unterstützen. Wählen Sie die View-Controller xib erneut aus, und wählen Sie dann die Collection View-Instanz im linken Teilfenster aus. Wählen Sie im rechten Inspectorbereich die Registerkarte Attribute aus, und blättern Sie dann nach unten zum Abschnitt Sammlungsansicht. Ändern Sie in diesem Abschnitt das Layout von Flow zu Custom. Dann geben Sie BHPhotoAlbumLayout in das Feld der offenbarten Klasse ein. Schließlich müssen auch die Instanz dieses Layouts von unserem View-Controller zugreifen, so dass wir einen Property-Outlet in Code erstellen und hook it up in der XIB. In unserem View-Controller Unterklasse importieren die BHPhotoAlbumLayout Header dann fügen Sie eine private Eigenschaft als IBOutlet: Wechseln Sie zurück in die xib-Datei. Klicken Sie unter Objekte. Erweitern Sie die Sammlungsansicht und steuern Sie dann von Datei-Eigentümer zu Fotoalbum-Layout. An dieser Stelle, wenn Sie die App laufen, auch wenn youve alles richtig gemacht, youd immer noch starrte auf einen schwarzen Abgrund nicht zu beruhigend. Das Setzen einer Hintergrundfarbe auf die Sammlungsansicht ist eine gute Möglichkeit, zu überprüfen, ob die Ansicht korrekt eingerichtet ist. In unserem View-Controller innerhalb von viewDidLoad legen Sie die Hintergrundfarbe der Ansicht fest: Jetzt können Sie Ihre App abfeuern und sicherstellen, dass die Auflistungsansicht angezeigt wird, wie sie sollte. Sie sollten die dunkelgraue Hintergrundfarbe sehen, die wir definiert haben, anstatt die Standardschwarze. Als nächstes beginnen Sie mit der Arbeit an den spezifischen Ansichten, die unsere Kollektionsansicht präsentieren wird. UICollectionView und UITableView sind in mancher Hinsicht ziemlich ähnlich. Beide verwenden ein Delegierungsdesign-Muster, um das Durchsuchen und das Zusammenwirken mit einer Sammlung von Ansichten (Zellen) zu erleichtern sowie die Gewährleistung der Leistungsfähigkeit zu akzeptieren, indem ein Mittel bereitgestellt wird, um diese Ansichten zwischenzuspeichern und wiederzuverwenden. Während UITableView jedoch einige Besonderheiten seines visuellen Designs und Layouts erzwingt, verleihen UICollectionViews keinerlei visuelles Design oder Layout, was eine viel größere Anpassung an die Darstellung des Inhalts ermöglicht. Gut passen Sie unsere Präsentationslogik früh genug, aber zuerst gut schaffen einige wiederverwendbare Ansichten für unsere Sammlung Ansicht. Dies ist sehr ähnlich zu erstellen benutzerdefinierte Zellen für eine UITableView. Erstellen Sie eine neue Xcode-Gruppe mit der Bezeichnung View. Erstellen Sie innerhalb dieser Gruppe eine Objective-C-Klasse mit dem Namen BHAlbumPhotoCell, die die Unterklassen UICollectionViewCell enthält. Für jetzt können die Dinge einfach halten und nur eine Hintergrundfarbe auf diese Zelle innerhalb von initWithFrame: Nun, da wir eine einfache Zelle bereit für den Einsatz haben, würden einige Grundlagen der Layout-Logik implementiert bekommen. Nun beginnen, indem Sie einige Eigenschaften, die Anpassung der verschiedenen Aspekte des Layouts ermöglichen erklärt. Fügen Sie dem BHPhotoAlbumLayout-Header die folgenden Eigenschaften hinzu: Legt einige Standardwerte für diese Werte fest, um es für alle Benutzer des Layouts zu vereinfachen. Da jemand das Layout seiner Auflistungsansicht in einer xib-Datei angeben (wie wir), oder es direkt im Code initialisieren kann, müssen wir sowohl die init - als auch die initWithCoder-Methoden außer Kraft setzen. Auch, anstatt Duplizieren von Code, können Sie eine Methode namens Setup, die von einem dieser aufgerufen wird, erstellen. Fügen Sie diese Methoden der BHPhotoAlbumLayout-Implementierung hinzu: Jetzt ging es um das Fleisch und die Kartoffeln unserer Layoutlogik. Wir müssen eine Handvoll Methoden außer Kraft setzen, die Layoutinformationen in die Sammlungsansicht berechnen und zurückgeben. Obwohl es mehr als einen Weg, dies zu tun, empfiehlt Äpfel Ansatz für Layouts, die selten ändern und halten Hunderte von Elementen (anstatt Tausende) ist es, berechnen und Cache alle Layout-Informationen im Voraus und dann Zugriff auf den Cache, wenn die Sammlungsansicht Anforderungen es. Wenn Sie mehr darüber erfahren möchten, lesen Sie bitte die Apples-Dokumentation zum Erstellen benutzerdefinierter Layouts. Nun verwenden Sie ein Wörterbuch, um alle unsere Layout-Informationen zu speichern. Die Struktur wird folgendermaßen aussehen: Erstellen Sie ein Unterverzeichnis für jeden Ansichtstyp und verwenden Sie den Indexpfad für jede einzelne Ansicht als Schlüssel und die zugehörigen Layoutattribute als zugeordneten Wert. Dann in der Top-Level-Wörterbuch gut nutzen die spezifische Art der Ansicht als unser Schlüssel und das Unterverzeichnis, das wir gerade als den Wert erstellt. Dies ermöglicht einen schnellen Zugriff auf die Layoutattribute einer bestimmten Art von Ansicht auf einem bestimmten Indexpfad. Ermöglicht das Hinzufügen der wichtigsten gut für unsere Zellen jetzt. Definieren Sie einen statischen NSString für die BHAlbumPhotoCell am Anfang der BHPhotoAlbumLayout-Implementierung. Darunter fügen Sie das Wörterbuch der obersten Ebene als eine private Eigenschaft hinzu. Jetzt haben wir alles, was wir benötigen, um prepareLayout zu implementieren. Lets dies Override prepareLayout in der BHPhotoAlbumLayout-Implementierung: Die Logik hier ist eigentlich ziemlich straight-forward. Erstens erstellen wir einige veränderliche Wörterbücher. Anschließend durchlaufen wir für jeden Abschnitt in der Auflistungsansicht jedes seiner Elemente und erstellen UICollectionViewLayoutAttributes basierend auf dem aktuellen Indexpfad. Wir setzen den Frame für diese Views-Attribute und fügen diese Attribute dann dem Unter-Dictionary hinzu. Sobald wir durch alle Abschnitte geschleift haben, setzen wir das Unterwörterbuch auf das Wörterbuch der obersten Ebene. Am Ende setzen wir unsere private Wörterbuch-Eigenschaft auf das temporäre Wörterbuch, das wir hier erstellt haben. Vielleicht haben Sie bemerkt, dass wir FrameForAlbumPhotoAtIndexPath: die wir havent noch geschrieben haben. Ich denke, wir sollten das tun. Fügen Sie frameForAlbumPhotoAtIndexPath: der BHPhotoAlbumLayout-Implementierung hinzu: Der Frame wird basierend auf dem übergebenen Indexpfad berechnet. Zunächst bestimmen wir die richtige Zeile und Spalte für das Item. Dann bestimmen wir den kombinierten Gesamtbetrag des horizontalen Abstandes zwischen den Elementen. Dann, wenn wir mehr als 1 Spalte haben, teilen wir den gesamten Abstand, um den Abstand für jedes Element zu erreichen. Nun können wir den horizontalen Versatz unseres Rahmens berechnen. Beachten Sie, dass wir diesen Wert, so dass unser Rahmen landet auf ganzen Pixeln, so dass es sieht scharf. Ein subtiles Detail hier ist, dass wir den Wert an dieser Stelle, nicht in der Zeile vor, wo wir durch die Anzahl der Spalten geteilt Boden. Dadurch wird sichergestellt, dass die Elemente korrekt beabstandet sind, während wir hinübergehen, da wir den Wert nach dem Multiplizieren mit der Spalte umrunden. Als nächstes berechnen wir unseren vertikalen Versatz und geben dann den Rahmen basierend auf den Ursprüngen und der Positionsgröße zurück. Nachdem nun unser Layout festgelegt wurde, müssen wir die Methoden, die diese an die Sammlungsansicht zurückgeben, überschreiben, wenn dies erforderlich ist. Das erste, das überschrieben wird, ist layoutAttributesForElementsInRect. Diese Methode muss alle Layoutattribute zurückgeben, die für einen bestimmten Inhaltsbereich der Sammlungsansicht erforderlich sind. Da es sich bei allen bisher um Zellen handelt, müssen wir einfach herausfinden, welche Zellen innerhalb des Rect passiert sind. Das ist nun schön und einfach, da wir alle Layoutattribute in unserem LayoutInfo-Wörterbuch zur Verfügung haben. Override layoutAttributesForElementsInRect: in BHPhotoAlbumLayout wie folgt: Wir beginnen mit dem Erstellen eines veränderlichen Arrays, in dem wir alle Attribute, die zurückgegeben werden sollen, speichern können. Als nächstes würden wir die schöne Block-basierte Wörterbuch-Enumeration nutzen, um durch unser layoutInfo-Wörterbuch zu navigieren. Der äußere Block iteriert durch jedes der hinzugefügten Unterwörterbücher (nur die Zellen im Augenblick), dann iterieren wir durch jede Zelle im Unterwörterbuch. CGRectIntersectsRect macht es einfach, zu überprüfen, ob die Zelle schaute, schneidet mit dem Rect, das weitergegeben wurde. Wenn es tut, fügen wir es dem Array gut zurück. Die nächste Methode, die wir implementieren müssen, ist noch einfacher, weil der Caching-Mechanismus wir angenommen haben. Override layoutAttributesForItemAtIndexPath: in BHPhotoAlbumLayout wie folgt: Alle taten hier ist das Nachschlagen der Unter-Wörterbuch für Zellen und dann die Layout-Attribute für eine Zelle an den übergebenen Index-Pfad. Es könnte nicht einfacher sein Die letzte Methode, die wir überschreiben müssen, um unser Layout zu erhalten und ausgeführt wird, muss die Gesamtinhaltsgröße für alle unsere Inhalte zurückgeben. Überschreiben Sie collectionViewContentSize: in BHPhotoAlbumLayout wie folgt: Dies berechnet und liefert die Gesamtgröße, die benötigt wird, um alles in unserer Sammlungsansicht anzuzeigen. Die Höhe basiert auf der Gesamtzahl der Zeilen und die Breite ist einfach die Breite der Sammlungsansicht selbst. Nachdem die Grundlagen unseres Layouts vollständig sind, müssen wir nun einige gültige Inhalte aus den Methoden der Datenquelle in unserer View-Controller-Unterklasse zurückgeben. Vor dem Zurückgeben von Zellen in unsere Auflistungsansicht müssen wir zunächst unseren View-Controller als Datenquelle definieren und für unsere Sammlungsansicht delegieren. Öffnen Sie die xib, die mit unserem View-Controller verbunden ist, und steuern Sie den Ziehvorgang aus der Sammlungsansicht in den Dateieneigentümer. Klicken Sie auf die DataSource-Steckdose, und wiederholen Sie erneut, um die Delegate-Steckdose festzulegen. In unserer Ansicht Controller-Header deklarieren, dass es das Protokoll für UICollectionViewDataSource und UICollectionViewDelegate implementiert. Jetzt sollten wir unsere Zellklasse mit der Auflistungsansicht registrieren. Das sagt der Kollektion, worauf es ankommt. Dies erfordert eine Kennung für die Zelle. Wechseln Sie zu unserer View-Controller-Implementierung und importieren Sie BHAlbumPhotoCell. h und definieren Sie einen String für die Zell-ID: Jetzt am Ende von viewDidLoad auf unserem View-Controller registrieren Sie die Zellklasse für den von uns definierten Identifier: Als nächstes müssen wir drei Datenquellen-Methoden implementieren Die vertraut mit denen, die mit UITableView vor gearbeitet haben aussehen wird. Implementieren Sie numberOfSectionsInCollectionView: auf unserem View-Controller: Für jetzt einfach eine statische Zahl zurückgeben, gut erhalten realen Inhalt arbeitet bald genug. Als nächstes implementieren Sie numberOfItemsInSection: auf unserem View-Controller: Implementieren Sie dann collectionView: cellForItemAtIndexPath: auf unserem View-Controller: Alright, seine endgültige Zeit, den Simulator auszulösen und zu sehen, ob unser Layout funktioniert. Wenn Sie die Dinge richtig gemacht haben, sollten Sie eine Gruppe von grauen Quadraten in 2 Spalten sehen, die Sie durch vertikal scrollen können. Nun wurden immer irgendwo Alright Dinge sind großartig, so weit, aber haben Sie versucht, drehen Sie Ihr Gerät auf Landschaft Hmm nicht ganz, was auf der Suche nach. Anstelle von 2 Spalten und einem Haufen von ungenutzten Platz auf der rechten Seite, itd besser zu zeigen, 3 Spalten, wenn in der Landschaft. Will das machen? Override willRotateToInterfaceOrientation: duration: auf unserem View-Controller: Zuerst wird überprüft, ob sich das Gerät in Landscape befindet, und wenn ja, legen wir die Anzahl der Spalten auf 3 und passen dann die ItemInsets ein wenig an, damit die Dinge gleichmäßig verteilt werden. Wenn wir in Portrait waren, setzen wir die Spalten auf 2 und verwenden die gleichen Werte, die wir standardmäßig in unserem Layout eingestellt hatten. Lets geben diesem einen Whirl, Fire up Ihre App Was gibt Warum haben wir nicht das Layout, das wir angegeben Darüber hinaus, wenn Sie versuchen zu scrollen, Dinge handeln seltsam. Das Problem ist, dass unsere Layout-Attribute nicht aktualisiert wurden, um den neuen Stand unserer Ansicht widerzuspiegeln. Dies unterstreicht eine der Aufgaben, die wir tun müssen, wenn Änderungen vorgenommen werden, die das Layout der Elemente in unserer Kollektionsansicht beeinflussen. Wir müssen das Layout ungültig machen, was dazu führt, dass alle Layoutattribute neu berechnet werden. Dies geschieht durch Aufrufen von invalidateLayout auf der UICollectionViewLayout-Instanz und es muss geschehen, wenn sich eine der benutzerdefinierten Eigenschaften, die wir unserem Layout hinzugefügt haben, ändert. Um dies zu tun, sollten wir explizit die Setter für diese Eigenschaften implementieren und dort auch das Layout ungültig machen. Implementieren Sie Setter für jede der benutzerdefinierten Eigenschaften auf BHPhotoAlbumLayout: Jeder Setter überprüft, ob der eingestellte Wert identisch ist mit dem, was er bereits gesetzt ist, und gibt zurück, wenn dies der Fall ist. Wenn es anders ist, wird die Instanzvariable gesetzt und dann das Layout ungültig. Führen Sie jetzt Ihre App aus, sie sollte sich wie erwartet verhalten, wenn Sie das Gerät drehen. Mit unserem Layout kommen nett kommen, lassen Sie sich in ein wenig Mühe in Richtung immer einige echte Inhalte anzuzeigen. Diese grauen Quadrate sind nett und alle, aber was wir wirklich sehen wollen, sind einige fantastische Fotos Um die Bereitstellung von Fotos zu unserer Sammlung zu erleichtern, würden einige Klassen benötigt, um sie darzustellen. Diese werden als Teil des Modells innerhalb der Modell-View-Controller-konzeptionellen Organisation unserer App betrachtet. Da der Schwerpunkt dieses Tutorials auf Collection Views Im nicht gehen, viel Zeit auf die Modellklassen zu verbringen. Normalerweise würden Sie etwas wie CoreData oder eine andere persistente Speicherlösung verwenden, um diesen Aspekt Ihrer App zu behandeln. Für dieses Tutorial einfach laden Sie die Modell-Klassen und fügen Sie sie zu Ihrem Projekt. Erstellen Sie eine Gruppe mit dem Namen Model. Dann ziehen Sie Dateien für BHPhoto und BHAlbum in die Gruppe. Vergewissern Sie sich, dass das Kontrollkästchen für Ihre App unter Zu Zielen hinzufügen aktiviert ist, und wenn Sie die Dateien von einem Speicherort außerhalb Ihrer Projektdateien verschieben, stellen Sie sicher, dass die Option Objekte kopieren in den Zielgruppenordner aktiviert ist. Diese Klassen sind ziemlich einfach. BHPhoto hat eine Thumbnail-URL und ein Bild für ein Foto. BHAlbum hat einen Namen und eine Reihe von Fotos und die Möglichkeit, ein Foto hinzuzufügen oder zu entfernen. Jetzt können wir einige Alben erstellen und jedem ein Foto hinzufügen, so dass wir etwas interessanteres zu sehen bekommen. An der Spitze unserer View-Controller-Implementierung importieren Sie BHAlbum. h und BHPhoto. h. Als nächstes erstellen Sie eine private mutable Array-Eigenschaft, um alle Alben zu halten: Fügen Sie dann innerhalb der viewDidLoad-Methode auf unserem View-Controller nach dem Setzen der Hintergrundfarbe auf die Sammlungsansicht Folgendes hinzu: Diese Fotos werden aus dem GitHub-Repository für dieses Projekt geladen Sind einige meiner Lieblingsfotos, die ich in den letzten Jahren übernommen habe. Wir müssen auch die Sammlungsansicht dataSource-Methoden auf unserem View-Controller aktualisieren, so dass der richtige Abschnitt und die Anzahl der Elemente zurückgegeben werden: Alright, weve bekam ein paar Fotoalben mit ein paar Fotos, die jeweils für uns bereit waren. Jetzt müssen wir unsere Fotozelle aktualisieren, damit sie sie anzeigen kann. Fügen Sie eine UIImageView-Eigenschaft als readonly zum BHAlbumPhotoCell-Header hinzu und definieren Sie sie dann als readwrite in der Implementierung neu: Dies folgt der Konvention, die Apple für Subviews auf UITableViewCell verwendet, sodass die Konsumenten dieser Klasse die Eigenschaften der bereitgestellten Bildansicht ändern können Bildansicht selbst. Als nächstes richten Sie die Bildansicht und einige andere Ästhetik auf unserer Zelle. Um zu ziehen von einigen der gewünschten Look, würden einige Eigenschaften auf unserer Zell-Backup-Schicht zu ändern. Dazu müssen wir das QuartzCore-Framework importieren. Fügen Sie den Import an der Spitze der BHAlbumPhotoCell-Implementierung hinzu: Aktualisieren Sie dann initWithFrame: auf BHAlbumPhotoCell in das Match: Hier wurden einige verschiedene Eigenschaften auf unserer Zellen-Ebene gesetzt, um einen schönen Schlagschatten und Grenzeffekt zu geben. Wir instanziieren auch unsere Bildansicht, setzen einige Eigenschaften für ihre Anzeige und fügen sie dann als eine Unteransicht der Zellen contentView hinzu. Das letzte Bit der Arbeit, die wir auf unserer Zelle machen müssen, überschreibt prepareForReuse und setzt jedes Bild zurück, das auf der imageView gesetzt wurde. Dies stellt sicher, dass unsere Zellen frisch aussehen, auch wenn sie von der collectionView wiederverwendet werden. Fügen Sie prepareForReuse zu BHAlbumPhotoCell hinzu: Wir müssen super aufrufen, wenn Sie diese Methode überschreiben, damit die Zelle ordnungsgemäß eingerichtet wird, bevor sie wiederverwendet wird. Süß, jetzt können wir ein Bild auf unserer Fotozelle setzen. Auf unserem View-Controller-Update collectionView: cellForItemAtIndexPath: passend zum Folgenden: Hier holen wir das Album für diesen Abschnitt und dann das Foto für diesen Eintrag. Sobald wir haben, können wir das Fotos Bild auf die Zelle setzen. Fire up Ihre App und sehen, was Sie erhalten Nachdem die Fotos fertig geladen haben, sollten sie in der Sammlungsansicht sichtbar sein. Apropos Beladen, haben Sie bemerkt, wie lange es dauerte, um diese Fotos zu sehen Weve im Wesentlichen Grund unsere App-Reaktionsfähigkeit auf einen Halt, wie waren jetzt unsere Nutzer warten, bis alle sichtbaren Fotos zu laden, bevor sie sehen können oder mit nichts interagieren. Das wird nicht geschnitten. Ermöglicht die Refactoring unserer Ansicht-Controller, so dass Fotos in den Hintergrund geladen werden, anstatt in der Haupt-Warteschlange. Es gibt ein paar verschiedene Ansätze, die wir hier nehmen könnten, aber Ive gewählt, um eine Block-basierte NSOperationQueue, die uns setzen Prioritäten für die Operationen, die wir ausführen verwenden. Zuerst müssen wir eine private Eigenschaft für die Operation Warteschlange an der Spitze unserer View-Controller. Now können die NSOperationQueue initialisieren und legen Sie es auf unserer Eigenschaft. Da unsere Operationen völlig unabhängig voneinander sind, können Sie auch die maximale Anzahl von gleichzeitigen Operationen einstellen, die es bei 3 ausführen kann, wodurch die Last noch schneller wird. Fügen Sie diese Zeilen unten in viewDidLoad auf unserem View-Controller hinzu. Weiter, wo wir zuvor das Foto-Bild direkt geladen haben, jedes Mal, wenn wir eine Zelle geladen haben, nun die Operation Warteschlange so verwenden, dass sie im Hintergrund lädt. Ersetzen Sie die alte Implementierung von collectionView: cellForItemAtIndexPath: auf unserem Viewcontroller mit dem folgenden: Dont wird durch alle Nebenläufigkeit eingeschüchtert, Operationen, Dispatching und whatnot geht hier gut, vielleicht ein wenig eingeschüchtert, aber nur, weil es schwierig für fortgeschrittene Programmierer sein kann auch. Alle waren dabei, eine NSBlockOperation mit einem Codeblock zu erstellen, der später von der NSOperationQueue ausgeführt wird. Weil sich auf das Innere des Blocks bezogen war, müssen wir auch einen schwachen Bezug darauf aufbauen. Dieses gesamte Setup wird es unserer Benutzeroberfläche ermöglichen, auf unsere Benutzer zu reagieren. Wenn der Block ausgeführt wird, lädt er zuerst das Foto, wie wir es zuvor getan haben. Dann über die Grand Central Dispatch verschicken wir asynchron einige Arbeiten zurück an die Haupt-Warteschlange, um das Bild auf unserer Fotozelle zu setzen. Dies ist notwendig, weil alles, was die Benutzeroberfläche berührt, auf der Hauptwarteschlange ausgeführt werden muss. Aus Performance-Gründen wurden nur auf das Bild gesetzt, wenn die Zelle arbeiten mit ist noch in Sicht, so dass, wenn der Benutzer scrollt wirklich schnell, werden wir nicht verlangsamen, indem Sie Bilder, die sie nicht einmal sehen Ein letztes Detail ist, dass, weil die Kann das Laden einer Weile nach dem ursprünglichen Aufruf von cellForItemAtIndexPath beendet werden: Wir sollten uns nicht auf die photoCell verlassen, die die eigentliche Zelle für unseren Indexpfad nicht mehr enthält. Möglicherweise wurde es bereits wiederverwendet, wenn der Benutzer fortgesetzt scrollt. Stattdessen erhalten wir die Zelle basierend auf dem Indexpfad wieder und setzen dann das Bild auf die Zellenimageansicht. (Eine Notiz, wenn unsere App erlaubt, Fotos hinzuzufügen, zu entfernen oder neu zu arrangieren, würden wir nicht einmal davon ausgehen können, dass der ursprüngliche Indexpfad noch gültig ist, stattdessen müssten wir das auch noch mal anhand des spezifischen Albums und nachsehen Foto.) Mit der NSBlockOperation erstellt nun nun die Operation, um unsere Warteschlange, die die Ausführung des Blocks für uns behandeln wird. Lets geben diesem einen Lauf und sehen, wie es führt. Die Bilder sollten jetzt spürbar schneller geladen werden, da 3 gleichzeitig geladen werden und das Scrollen seidig glatt sein sollte. Nette Arbeit Nun, da wir für jedes Album ein Foto haben, können wir den ganzen Stapel von Fotos laden und scharf sehen. Um dies zu tun, sollten einige andere Eigenschaften auf UICollectionViewLayoutAttributes nutzen. Zusätzlich zum Setzen des Frames können wir auch Werte für transform3D, alpha, zIndex, hidden setzen. Wenn die arent genug können Sie eine Unterklasse erstellen und fügen Sie benutzerdefinierte Eigenschaften als auch, obwohl ich will nicht, dass hier. Um den Stapel-Look zu erreichen waren gut für die Verwendung von transform3D und zIndex. Konzeptionell, was wir haben wollen, sind Fotos von oben nach unten gestapelt in jedem Abschnitt mit jedem Foto mit einer leichten Rotation angewendet. Dies kombiniert mit der Schicht Schatten Weve bereits in die Zelle hinzugefügt, ist alles, was notwendig ist, damit es schön aussehen. Erste gut behandeln die Rotation Wir wollen, dass die Rotationen auf jedem Foto Art von planlos und natürlich, als ob jemand nur sie auf einen Tisch fallen sah. Nun erzeugen einige zufällige Rotationen, um dies zu erreichen, aber der einzige Fang ist, dass wir diese Rotationen benötigen, um konsistent zu bleiben, sobald sie generiert wurden, so dass wir Zellen neu laden können, ohne dass sie sich unerwartet ändern. Um dies zu tun, erzeugen Sie eine bestimmte Anzahl von Umdrehungen, wenn wir zuerst unser Layout erstellen und dann gut daran festhalten, sie in einer vorhersagbaren Weise durch die Verwendung einer Konstante zu verwenden. Deklarieren Sie diese Konstanten unten, wo Sie Ihre Header an der Spitze der BHPhotoAlbumLayout-Implementierung importieren: Erstellen Sie dann eine Private-Array-Eigenschaft in der BHPhotoAlbumLayout-Implementierung außerdem: Wurden dieses Array mit einigen CATransform3D-Rotationen laden, aber wir müssen das QuartzCore-Framework importieren In unser Projekt zuerst für diese zu arbeiten. Wählen Sie Ihre Projektdatei aus, und wählen Sie oben in der mittleren Spalte die Registerkarte Phasen erstellen. Erweitern Sie den Link Binär mit Bibliotheken, und klicken Sie auf die Schaltfläche unten links. Suchen Sie das QuartzCore. framework und fügen Sie es zu Ihrem Projekt hinzu: Als nächstes fügen Sie am Ende der Setup-Methode auf BHPhotoAlbumLayout Folgendes hinzu: Zuerst erstellen wir ein temporäres, veränderliches Array, dem wir Objekte hinzufügen. Dann laufen wir durch unsere Schleife, die eine Rotation jedes Mal. Wir erstellen einen zufälligen Prozentsatz zwischen -1.1 und 1.1 und verwenden dann, um eine gezwicktes CATransform3D zu erstellen. Ich geeked ein wenig und fügte hinzu, einige Logik, um sicherzustellen, dass der Prozentsatz der Rotation, die wir zufällig generieren, ist mindestens 0,6 anders als die zuvor generiert. Dadurch wird sichergestellt, dass Fotos in einem Stapel nicht das Unglück haben, dass alle auf dieselbe Weise gedreht werden. Sobald wir unsere Transformation haben, fügen wir sie dem temporären Array hinzu, indem wir sie in einen NSValue wickeln und dann spülen und wiederholen. Nachdem alle 32 Rotationen hinzugefügt sind, setzen wir unsere private Array-Eigenschaft. Jetzt müssen wir es nur benutzen. Ive entschieden, um die Transformation auf unsere UICollectionViewLayoutAttributes setzen ähnlich wie wir seinen Rahmen setzen, durch die Verwendung einer privaten Helfer-Methode. Fügen Sie die folgende Methode in Richtung der Unterseite der BHPhotoAlbumLayout-Implementierung hinzu: Wurden die Verwendung der Schritt-Konstante verwenden, die wir früher angegeben haben, um einige Rotationswerte zwischen den Abschnitten zu springen. Dies ermöglicht es uns, unsere Gelder wert aus dem Rotations-Array, indem Sie in der ersten Drehung in der Array auf der Grundlage der Abschnitt und Element der übergebene Indexpfad. Wir modifizieren dann diesen Offset durch den RotationCount, um sicherzustellen, dass wir innerhalb der Arrays Grenzen bleiben. Weve Weve smart gewählt einen Schritt von 3 und eine totale Rotationszählung von 32 youll beachten, dass, wenn wir am Ende mit vielen Abschnitten, gut effektiv nutzen jede Rotation in der Array als Startoffset, optimal verschleiern die Tatsache, dass wir wiederholt Rotation Werte verwenden . Jetzt müssen wir sie nur auf unsere Layoutattribute setzen. Jetzt in prepareLayout. Direkt darunter, wo wir den Rahmen auf unserer UICollectionViewLayoutAttributes setzen können wir die transform3D mit unserer transformForAlbumPhotoAtIndex: Methode: Lets geben diesem einen Whirl und sehen, was wir bekommen. Alright, weve erhielt eine Vielzahl der Umdrehungen, die auf unsere Fotos angewendet werden, jedoch können Sie beachtet haben, dass die Rahmenränder ein Stückchen jaggy schauen. Dies kann behoben werden, indem wir unsere Zellen Schicht zu rastern. Aktualisieren Sie die Methode initWithFrame: auf BHAlbumPhotoCell, indem Sie zwei neue Zeilen hinzufügen: Jetzt sollten wir einige reibungslos gedrehte Fotos aus unserer Sicht sehen. Heck, jetzt, dass weve unsere Fotos drehen schön, können Sie punch up die Zahl in jedem Abschnitt, um einige süße, süße Stapelaktion zu sehen In viewDidLoad auf unserer Ansicht-Controller, aktualisieren Sie den Beginn der inneren for-Schleife, um die Anzahl der Fotos in erhöhen Jeder Abschnitt: Nachdem Sie die Anzahl der Fotos erhöht haben, bemerkten Sie einen Unterschied in der Ladezeit für die Bilder Sein wahres, wir haben mehr Bilder zu laden, aber wir kümmern uns auch mehr um das obere Foto in jedem Stapel als die, die darunter liegen . Wie über, die wir bekommen, dass man vor den anderen zu laden Einer der Gründe, warum ich entschied mich für eine NSOperationQueue als meine Mittel, um die Fotos asynchron laden ist, weil wir eine Priorität für jede Operation, die wir erstellen können. Mit diesem können wir nur erhöhen die Priorität der Beladung der oberen Foto in jedem Abschnitt. In collectionView: cellForItemAtIndexPath: legen Sie auf unserem View-Controller die Priorität der NSBlockOpertation fest, bevor Sie sie der Warteschlange hinzufügen: Für das obere Element setzen wir die Priorität auf high, die anderen auf normal. Lasst die App wieder laufen und sehen, ob dies unsere Ladezeit verbessert hat. Hmm, es scheint nicht zu viel von einem Unterschied. Vielleicht ist die Priorität der Beladung nicht das einzige Problem war konfrontiert In der Tat, die Änderung der Prioritäten unserer Operation Aufgaben ist nur die Hälfte der Schlacht. Wir müssen auch sicherstellen, dass das, was wir als das Top-Foto zu betrachten, tatsächlich am oberen Rand unserer Ansicht platziert ist. Dazu müssen wir den zIndex unserer Zellen korrekt angeben. Der ZIndex gibt vor, ob eine Zelle über oder unter einer anderen Zelle liegt. Standardmäßig haben alle unsere Zellen einen zIndex von 0, was bedeutet, dass ihre Reihenfolge völlig willkürlich ist. Da wir eine variable Anzahl von Zellen in jedem Abschnitt haben können, verwenden wir einen Basiswert für unseren zIndex und erhöhen ihn dann basierend auf wie hoch oben im Stapel die Zelle landen sollte, wobei das obere Element am höchsten ist. Fügen Sie eine Ganzzahl-Konstante an der Spitze der BHPhotoAlbumLayout-Implementierung hinzu: Dann in prepareLayout auf BHPhotoAlbumLayout setzen Sie den zIndex auf die Layerattribute: Führen Sie nun Ihre App aus, um zu bestätigen, dass die Fotos schnell geladen wurden. Alright machten Fortschritte, nur ein paar weitere Dinge zu arbeiten, bevor wir einen voll ausgestatteten Fotoalbum-Browser haben Alle diese tollen Fotos sind sicher, einfach auf die Augen, aber dieses Design fordert auch den Album-Namen unter jedem Foto-Stack angezeigt werden. Um das Hinzufügen dieses Titels zu unserer Kollektionsansicht zu erleichtern, wollten wir einen weiteren View-Typ in die Mischung einführen. Sammlungsansichten können bis zu drei Typen von wiederverwendbaren Ansichtsunterklassen in ihrem Layout enthalten: Zellen (die wir für unsere Fotostapel verwendet haben), Ergänzende Ansichten und Dekorbilder. Wenn für Ihre Ansicht beliebige Daten vom Ansichts-Controller zur Anzeige benötigt werden, müssen Sie entweder Zellen oder Ergänzende Ansichten verwenden. Dekorationsansichten dagegen sind hauptsächlich zu ästhetischen Zwecken gedacht und sollten keine Daten aus dem View Controller verwenden. Da unsere Titelsichten den Namen jedes Albums anzeigen, müssen wir dazu Ergänzungsansichten verwenden. Die meisten der Arbeit gut tun wird sehr ähnlich wie die Einrichtung, die wir für unsere Zellen. Beginnen wir mit dem Ändern unseres Layouts, um Platz für die Albumtitel zu schaffen. Dazu müssen wir noch mehr vertikalen Abstand zwischen dem Boden eines Stapels und dem Anfang des darunter liegenden hinzufügen. Anstatt die Höhe fest und unflexibel zu machen, können wir eine Eigenschaft hinzufügen, die den Verbrauchern unserer Layout-Klasse erlaubt, die Höhe selbst einzustellen. In der BHPhotoAlbumLayout-Kopfzeile fügen Sie eine Eigenschaft für titleHeight hinzu: Wieder einmal müssen Sie unser Layout ungültig machen, wenn sich diese Eigenschaft ändern sollte, also können Sie explizit den Setter für die Titelhöhe implementieren: Fügen Sie die folgenden unterhalb der anderen Settermethoden auf BHPhotoAlbumLayout hinzu: Sinnvoller Standard für diese Eigenschaft innerhalb der Einrichtungsmethode: Wurden auch noch eine andere einzigartige Art String für die Titelansicht benötigt wurden, um hinzuzufügen. Da Ergänzungs - und Dekorationsansichten eine Art String für die Registrierung benötigen, können wir diese Zeichenfolge öffentlich machen, damit wir sie sowohl für die Registrierung als auch für das Layout-Wörterbuch verwenden können. Definieren Sie einen NSString am Anfang des BHPhotoAlbumLayout-Headers und legen Sie ihn dann in der Implementierung fest: Die Aufteilung der Definition und Einstellung stellt sicher, dass die Konsumenten die Konstante und nicht den spezifischen Wert verwenden. Als nächstes müssen wir Layoutattribute für jede Titelansicht in prepareLayout erstellen. Dies erfordert das Hinzufügen eines anderen Unter-Wörterbuch, so dass wir unsere Titel Layout-Attribute in der gleichen Weise wie wir für unsere Zellen gespeichert haben können. At the beginning of prepareLayout on BHPhotoAlbumLayout create another dictionary:Now, since we only need one title per section, lets add it at the same time were adding attributes for the first cell, conveniently using the index path we already have on hand. Update the inner-loop on prepareLayout to match the following:Notice that we need to supply the kind when creating the layout attributes for Supplementary views. This can be used to differentiate Supplementary views used in your layout that happen to use the same class, but require different layout attributes. (Imagine if we also wanted to have a smaller subtitle below the album name telling users how many photos are in the album. We could use the same view class, but we might want to use a decreased height) For our view we only have one type of supplementary view. Finally, we need to set the titleLayoutInfo sub-dictionary on the top level dictionary: Add the following towards the end of prepareLayout. right after setting the cellLayoutInfo on newLayoutInfo:Since were once again calling a private helper method to obtain the frame for our title, we need to go implement that as well. After the other existing frame helper method add frameForAlbumTitleAtIndexPath:Calculating the frame for our title is a bit simpler since we can just base it off the frame of our cells. We do however need to update our cell frame and total content size calculations to take account for the added height of the titles. Update frameForAlbumPhotoAtIndexPath: to take account of the title height when calculating originY : Also, update the height calculation on collectionViewContentSize: to take account of the title height:The last thing we need to do is override the method which obtains layout attributes for a supplementary view at a specific index path. Add the following method on BHPhotoAlbumLayout below where we do this for our cells already:Thats all we need to update on our layout to handle album titles. Because of how weve structured our layoutInfo cache, no changes are necessary for layoutAttributesForElementsInRect: to correctly handle this additional view. If you run the app now, youll see that extra height has been added between each stack. Next we need to create a class for our album title views. Before we had subclassed UICollectionViewCell, but that class is used only for cells of a collection view. For supplementary views we need to subclass UICollectionReusableView. Create a class within the View grouping called BHAlbumTitleReusableView which subclasses UICollectionReusableView. This title view will be pretty basic, well just add one label, giving us a place to set our album name. Create a readonly property called titleLabel to the header of BHAlbumTitleReusableView and then re-define it as readwrite in its implementation:Now well setup our label on the view. Unlike cells where we need to add subviews to the contentView . here we add them directly on the view. Update initWithFrame: on BHAlbumTitleReusableView to the following:We also need to be sure to reset the labels text whenever the view is reused. Override perpareForReuse on BHAlbumTitleReusableView :Alright weve got some titles, before we can use them we need to define a reuse identifier and register them with the collection view. At the top of our view controller implementation define a static NSString for the identifier: Next, import the BHAlbumTitleReusableView header at the top of our view controller implementation. Then add the following towards the end of viewDidLoad :Now well implement another data source method and return instances of BHAlbumTitleReusableView when the collection view asks for them. Add collectionView:viewForSupplementaryElementOfKind:atIndexPath: to our view controller:Theres nothing special going on here, we get the album for this section and set its name on our title views label. Alright, now lets see how our app is looking BOOM Weve got title labels, this thing is really starting to come together. Now that everything is laid out nicely, lets make the view a little more visually interesting by swapping out the dull gray background color for a nice subtle textured look. A great website for finding textures which arent visually overbearing is subtlepatterns. They have dozens of textures in both normal and retina sizes. I found this concrete wall texture to work great. Create a new group named Images within the existing Supporting Files group. Download a texture you like and then add the image files to the Images group. We can easily add a background texture to our collection view by creating a UIColor with a pattern image. In viewDidLoad on our view controller subclass update the background color we set on the collection view to use a pattern:At this point I think weve got a pretty good looking view, but Id also like to walk through how to add decoration views. Although this may be bit contrived, what were going to do now is add a small emblem decoration tucked up above the photo stacks. Its something youll only see when pulling down on the view before it snaps back into place, but its a good way to show how to work these kinds of views in because they may be more beneficial in other layout designs. Now that weve added cells and supplementary views, this will feel somewhat familiar. The primary difference is that our collection views data source wont need to be involved with the logic of the emblem view. The layout and the emblem view itself will handle everything on their own. Create a class named BHEmblemView as a subclass of UICollectionReusableView within the View group. For this view, were just going to display the same image all the time and this can easily be done within initWithFrame: on our emblem view. If you wanted to create a more customizable decoration view, you would need to make use of custom properties, which requires creating a subclass of UICollectionViewLayoutAttributes and then setting those up within the layout. You can use the image I used or use your own. Were also going to create a simple class method to return the size of the image so that we can provide this to our layout class without needing to instantiate an emblem view. Add a class method named defaultSize to the BHEmblemView header: Switch to the implementation of BHEmblemView and add a constant for the image name: Then implement defaultSize :Now well set up the view to display our emblem. Update initWithFrame: on BHEmblemView to match the following:The collection view will handle instantiation of the emblem view using the frame which well set on its layout attributes. Unlike cells and supplementary views, decoration views are registered with the layout rather than the collection view. Import the BHEmblemView header into the BHPhotoAlbumLayout implementation. Define another kind string for our emblem at the top of the BHPhotoAlbumLayout : Register the emblem view at the end of the setup method on BHPhotoAlbumLayout :Unlike the other views weve added, only the layout class deals with decoration views, thus we perform the registration here rather than in the view controller. Now lets create another private frame helper method for our emblem. Add frameForEmblem towards the bottom of BHPhotoAlbumLayout :Here we get the default size for our emblem and then calculate an origin that will place the view centered horizontally and ending 30pts above the very top of the collection view and pass it back. Now that we have our frame, were ready to create some layout attributes for the emblem. Since were only going to have one emblem, we can create our attributes before we loop through all the sections and rows. Once again, well create a sub-dictionary and add it to the layoutInfo dictionary. Update prepareLayout to match the following:The last step is to return layout attributes for a decoration view with a given kind at a given index path. Add layoutAttributesForDecorationViewOfKind:atIndexPath: to BHPhotoAlbumLayout :Thats all we need to do for the emblem. If you fire up the app and pull down on the view, you should see the emblem tucked above. This covers the fundamental steps of creating custom layouts with UICollectionView. I hope its been enlightening From this point you could consider implementing custom animations for adding, deleting or moving items. Maybe it would be cool to tap a stack and have it expand out into a more detailed view for that album. If theres a lot of interest I may consider covering more advanced topics in an additional tutorial. Although I dont have a comments section here, feel free to email me any feedback regarding this tutorial at bryanehansengmail . copy 2012 Bryan Hansen

No comments:

Post a Comment