Universelle Lösung fĂŒr UITableViewRowAction und UIContextualAction



Viele sind auf das Problem gestoßen, eine schöne SchaltflĂ€chenzuordnung fĂŒr eine UITableViewCell zu implementieren, wenn diese nach links verschoben wird. Einige verwendeten die StandardfunktionalitĂ€t "out of the box", andere kĂŒmmerten sich um ihre eigene Implementierung, wĂ€hrend andere mit Unicode-Zeichen verwaltet wurden. In diesem Artikel werde ich Ihnen erklĂ€ren, wie Sie die maximale Anpassung von UITableViewRowAction erreichen.

ZunĂ€chst eine kleine Theorie: UITableViewRowAction ist eine Klasse, mit der der Benutzer bestimmte Aktionen mit dieser Zelle ausfĂŒhren kann, wenn er eine Zelle nach links verschiebt (z. B. Löschen, Ändern usw.). Jede Instanz dieser Klasse stellt eine auszufĂŒhrende Aktion dar und enthĂ€lt Text, Stil und einen Klick-Handler. Mit dem iOS SDK können wir die UITableViewRowAction wie folgt anpassen:

public convenience init(style: UITableViewRowActionStyle, title: String?, handler: @escaping (UITableViewRowAction, IndexPath) -> Swift.Void) 

Sie können auch backgroundColor festlegen.

Bestehende Methoden.


Betrachten Sie die vorhandenen Implementierungsbeispiele oder das, was ich im Internet gefunden habe.
Methodennummer 1 . Nutzen Sie die FunktionalitÀt "out of the box". Dies reicht aus, um normale SchaltflÀchen mit Text und Hintergrund zu verwenden. Es sieht zu einfach und langweilig aus:


Methode Nummer 2 . Legen Sie backgroundColor mit UIColor fest (patternImage: UIImage) . Dies wird Schönheit erreichen, aber es wird die Vielseitigkeit beeintrĂ€chtigen, eine Reihe von EinschrĂ€nkungen und Schwierigkeiten hinzufĂŒgen. Da die Breite der UITableViewRowAction anhand der GrĂ¶ĂŸe des Texts berechnet wird, mĂŒssen Sie den Titel als eine Reihe von Leerzeichen mit einer bestimmten Breite angeben. Wir können diese Methode nicht fĂŒr Zellen mit dynamischen Höhen verwenden. Wenn die Zelle an Höhe zunimmt, sieht sie ungefĂ€hr so ​​aus:



Methode Nummer 3 . Verwenden Sie Unicode-Zeichen. Es ist nicht erforderlich, die GrĂ¶ĂŸe des Texts mit Leerzeichen zu berechnen, und diese Methode eignet sich fĂŒr Zellen mit dynamischer Höhe. Es sieht gut aus:



Wenn wir jedoch den Text unter dem Symbol erstellen möchten, passt diese Methode nicht zu uns.

Methodennummer 4 . Verwenden Sie CoreGraphics, um UI-Komponenten zu rendern. Die Idee ist, ein Bild mit der gewĂŒnschten Anordnung von Elementen (Text und Bild) manuell zu zeichnen. In diesem Fall mĂŒssen Sie die GrĂ¶ĂŸen und Koordinaten fĂŒr die Position jedes Elements berechnen. Mit dieser Methode können wir sowohl Text als auch ein Bild erstellen und sie nach Bedarf anordnen. Diese Methode ist jedoch schwer zu implementieren. Wir wollen etwas Einfacheres.

Andere Implementierungen . Es gibt noch einige fertige Lösungen. Sie verwenden ihre eigene Implementierung der Zelle mit ScrollView und anderer Logik. Zum Beispiel SwipeCellKit oder MGSwipeTableCell . Diese Lösungen ermöglichen jedoch nicht das Erreichen eines Standard-iOS-Verhaltens, da sie im Gegensatz zur UITableViewRowAction, die im Tabellendelegierten erstellt wird, nur eine Zelle abdecken. Wenn Sie beispielsweise mit einer dieser Lösungen Aktionen fĂŒr die Linksverschiebung fĂŒr mehrere Zellen öffnen, mĂŒssen Sie diese Aktionen in jeder dieser Zellen manuell schließen, was Ă€ußerst unpraktisch ist. Dazu mĂŒssen Sie Ihre eigene Logik hinzufĂŒgen, um offene Aktionen beim Wischen zu steuern. Wir möchten eine einfache Lösung erstellen, die keine zusĂ€tzlichen Kosten erfordert, als ob wir die Standardimplementierung von UITableViewRowAction verwenden.

Fangen wir an


Versuchen wir, eine universelle Lösung zu erstellen, mit der Sie UITableViewRowAction mit jedem von uns benötigten Design erstellen können.

Erstellen Sie unsere Nachkommenklasse aus UITableViewRowAction und definieren Sie die Methode zur Erstellung der Hauptinstanz:

 typealias RowActionTapHandler = (RowAction, IndexPath) -> Void class RowAction: UITableViewRowAction { static func rowAction(with actionHandler: @escaping RowActionTapHandler) -> Self { let rowAction = self.init(style: .default, title: nil) { action, indexPath in guard let action = action as? RowAction else { return } actionHandler(action, indexPath) } return rowAction } } 

Da UITableViewRowAction keinen primĂ€ren Initialisierer hat, können wir der Einfachheit halber eine statische Methode erstellen, in der wir auf standardmĂ€ĂŸige Weise eine UITableViewRowAction erstellen und einen actionHandler hineinwerfen, um den Klick zu verarbeiten.

Versuchen wir nun, die SchaltflĂ€che UITableViewRowAction universell anzupassen. Die Idee ist, das Aussehen der SchaltflĂ€che aus dem fertigen UIView zu erhalten (das in einer regulĂ€ren xib konfiguriert werden kann). So können wir eine beliebige SchaltflĂ€che erstellen, die auf individuellen Vorlieben basiert (beliebige Schriftart des Textes, GrĂ¶ĂŸe und Anordnung der Elemente usw.).

Wir haben zwei Dinge, die wir in UITableViewRowAction Àndern können: backgroundColor und title . Derzeit besteht die einzige Möglichkeit, das Aussehen der SchaltflÀche UITableViewRowAction zu Àndern, darin, die Eigenschaft backgroundColor zu Àndern. Daher ist es unser Ziel, Folgendes zu transformieren: UIView -> UIImage -> UIColor. Wir können die Farbe mit dem Bild wie folgt erhalten: UIColor (patternImage: UIImage) . Und wir können das Bild von UIView erhalten, indem wir es mit dem Standard-CoreGraphics-Framework rendern.

ZunĂ€chst entscheiden wir jedoch, wie die GrĂ¶ĂŸe der SchaltflĂ€che festgelegt werden soll: "Out of the Box" gibt es diese Funktion nicht, und die GrĂ¶ĂŸe in der UITableViewRowAction wird anhand der GrĂ¶ĂŸe des Textes des Titelfelds berechnet.

Nachdem ich Experimente durchgefĂŒhrt hatte (Messung der berechneten und tatsĂ€chlichen Breite der SchaltflĂ€che), fand ich ein leeres Unicode-Zeichen mit der kleinsten Breite ungleich Null: U + 200A

Wir werden es verwenden und eine Konstante erstellen:

 private enum Constants { static let placeholderSymbol = "\u{200A}" } 

Wir werden eine Funktion schreiben, um eine Folge von Leerzeichen zu erhalten, die der GrĂ¶ĂŸe entsprechen, die wir benötigen:

 private static let placeholderSymbolWidth: CGFloat = { let flt_max = CGFloat.greatestFiniteMagnitude let maxSize = CGSize(width: flt_max, height: flt_max) let attributes = [NSFontAttributeName : UIFont.systemFont(ofSize: UIFont.systemFontSize)] let boundingRect = Constants.placeholderSymbol.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) return boundingRect.width }() private func emptyTitle(for size: CGSize) -> String { var usefulWidth = size.width - Constants.minimalActionWidth usefulWidth = usefulWidth < 0 ? 0 : usefulWidth let countOfSymbols = Int(floor(usefulWidth * Constants.shiftFactor / RowAction.placeholderSymbolWidth)) return String(repeating: Constants.placeholderSymbol, count: countOfSymbols) } 

Die Variable placeholderSymbolWidth ermittelt die Breite unseres Unicode-Zeichens, und die Funktion emptyTitle (fĂŒr GrĂ¶ĂŸe: CGSize) -> String berechnet die Anzahl der Leerzeichen entsprechend der GrĂ¶ĂŸengrĂ¶ĂŸe und gibt eine Zeichenfolge dieser Zeichen zurĂŒck. Die obige Formel verwendet die ShiftFactor- Konstante. Dies ist der Abweichungskoeffizient der Breite vom Nennwert. Tatsache ist, dass mit zunehmender Breite der UITableViewRowAction ein kleiner Fehler zunimmt (d. H. Die tatsĂ€chliche Breite der SchaltflĂ€che unterscheidet sich von der berechneten Breite durch die ĂŒbliche Formel). Um dies zu vermeiden, verwenden wir diese experimentell abgeleitete Konstante.

Außerdem hat UITableViewRowAction eine Mindestbreite von 30 (z. B. wenn Sie title = "" festlegen). Diese Breite ist das Minimum, das zum EinrĂŒcken links und rechts vom Titel erforderlich ist.
Aktualisieren Sie unsere Konstanten:

 private enum Constants { static let placeholderSymbol = "\u{200A}" static let minimalActionWidth: CGFloat = 30 static let shiftFactor: CGFloat = 1.1 } 

Lassen Sie uns nun das Bild aus der UIView holen. Wir werden die folgende Funktion schreiben:

 private func image(from view: UIView) -> UIImage? { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, UIScreen.main.scale) guard let context = UIGraphicsGetCurrentContext() else { assertionFailure("Something wrong with CoreGraphics image context"); return nil } context.setFillColor(UIColor.white.cgColor) context.fill(CGRect(origin: .zero, size: view.bounds.size)) view.layer.render(in: context) let img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img } 

Diese Funktion wandelt unsere Ansicht in ein Bild um. In diesem Fall entspricht die GrĂ¶ĂŸe des Bildes der GrĂ¶ĂŸe der Ansicht .

Der letzte Schritt bleibt, in dem wir die Methode zum Festlegen von UITableViewRowAction mithilfe von UIView implementieren:

 func setupForCell(with view: UIView) { guard let img = image(from: view) else { return } self.title = emptyTitle(for: img.size) self.backgroundColor = UIColor(patternImage: img) } 

Hier verwenden wir die zuvor geschriebenen Funktionen, um unsere SchaltflÀche zu konfigurieren und die Hintergrundfarbe anhand des resultierenden Bildes festzulegen.

Das ist alles Wir haben eine universelle Lösung fĂŒr UITableViewRowAction und können sie jetzt mithilfe von UIView auf beliebige Weise anpassen.



Ab iOS 11 hat Apple eine neue Klasse fĂŒr Wischaktionen fĂŒr Zellen in der Tabelle hinzugefĂŒgt - UIContextualAction. Sie können damit sofort das Bild angeben, das bei der Aktion auf dem Wisch angezeigt wird (dies fehlt uns in all den Jahren, in denen UITableViewRowAction verwendet wurde). Er sollte unser Leben leichter machen, aber zusĂ€tzlich zu einer neuen Gelegenheit schuf er zusĂ€tzliche Probleme. Diese Klasse schrĂ€nkt die GrĂ¶ĂŸe des Bildes ein, erlaubt nicht die gleichzeitige Verwendung von Text und Bild, und das Bild muss ein spezielles Format (Vorlagenbild) haben. Daher habe ich meine Lösung fĂŒr die UnterstĂŒtzung von Wischaktionen in iOS 11 fertiggestellt. Um eine UITableViewRowAction oder UIContextualAction zu erstellen, mĂŒssen Sie zunĂ€chst die RowActionFactory-Factory erstellen und sie mithilfe unserer Ansicht konfigurieren (wie oben in der setupForCell-Methode (mit Ansicht: UIView)). und geben Sie die benötigte EntitĂ€t zurĂŒck: UITableViewRowAction oder UIContextualAction (unter Verwendung der Methoden rowAction () bzw. contextualAction (fĂŒr indexPath: IndexPath) ). Das folgende Beispiel zeigt die Verwendung von RowActionFactory:

 @available(iOS 11.0, *) func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { var contextualActions: [UIContextualAction] = [] for actionType in rowActionTypes { // actionType -     let rowActionFactory = RowActionFactory { (indexPath) in tableView.setEditing(false, animated: true) } let rowActionView = <...> //  UIView     rowActionFactory.setupForCell(with: rowActionView) if let contextualAction = rowActionFactory.contextualAction(for: indexPath) { contextualActions.append(contextualAction) } } let swipeActionsConfiguration = UISwipeActionsConfiguration(actions: contextualActions) swipeActionsConfiguration.performsFirstActionWithFullSwipe = false return swipeActionsConfiguration } 

Die fertige Lösung finden Sie hier: TCSCustomRowActionFactory (github.com) .

Source: https://habr.com/ru/post/de414019/


All Articles