
Hergestellt mit ❤️ von XmartLabs. Dies ist die Wiederherstellung von XLFORM in Swift.
简体中文
![]() | ![]() | ![]() |
|---|
Weitere Informationen finden Sie in unserem Blog -Beitrag, der Eureka einführt.
Sie können das Beispielprojekt klonen und ausführen, um Beispiele für die meisten Funktionen von Eureka anzuzeigen.
![]() | ![]() |
|---|
Durch Erweiterung FormViewController können Sie einfach Abschnitte und Zeilen zur form hinzufügen.
import Eureka
class MyFormViewController : FormViewController {
override func viewDidLoad ( ) {
super . viewDidLoad ( )
form +++ Section ( " Section1 " )
<<< TextRow ( ) { row in
row . title = " Text Row "
row . placeholder = " Enter text here "
}
<<< PhoneRow ( ) {
$0 . title = " Phone Row "
$0 . placeholder = " And numbers here "
}
+++ Section ( " Section2 " )
<<< DateRow ( ) {
$0 . title = " Date Row "
$0 . value = Date ( timeIntervalSinceReferenceDate : 0 )
}
}
}Im Beispiel erstellen wir zwei Abschnitte mit Standardzeilen, das Ergebnis ist Folgendes:

Sie können ein Formular erstellen, indem Sie einfach die form selbst einrichten, ohne sich von FormViewController zu erstrecken, aber diese Methode ist in der Regel bequemer.
Um das Verhalten davon zu ändern, sollten Sie die Navigationsoptionen Ihres Controllers festlegen. Der FormViewController verfügt über eine navigationOptions -Variable, die eine Aufzählung ist und eine oder mehrere der folgenden Werte haben kann:
canBecomeFirstResponder() Der Standardwert ist enabled & skipCanNotBecomeFirstResponderRow
Aktivieren Sie sie über die animateScroll Eigenschaft. Standardmäßig springt der FormViewController sofort zwischen Zeilen, wenn der Benutzer die nächsten oder vorherigen Schaltflächen in der Tastaturnavigationserklärung trifft, einschließlich der Zeit, wann die nächste Zeile aus dem Bildschirm ist.
Um den Platz zwischen der Tastatur und der hervorgehobenen Zeile nach einem Navigationsereignis festzulegen, setzen Sie die rowKeyboardSpacing -Eigenschaft. Wenn das Formular standardmäßig zu einer Offscreen -Ansicht scrollt, bleibt zwischen der Oberseite der Tastatur und der Unterseite der Zeile kein Leerzeichen übrig.
class MyFormViewController : FormViewController {
override func viewDidLoad ( ) {
super . viewDidLoad ( )
form = ...
// Enables the navigation accessory and stops navigation when a disabled row is encountered
navigationOptions = RowNavigationOptions . Enabled . union ( . StopDisabledRow )
// Enables smooth scrolling on navigation to off-screen rows
animateScroll = true
// Leaves 20pt of space between the keyboard and the highlighted row after scrolling to an off screen row
rowKeyboardSpacing = 20
}
} Wenn Sie die gesamte Navigationszubehöransicht ändern möchten, müssen Sie die navigationAccessoryView -Variable in Ihrer Unterklasse von FormViewController überschreiben.
Das Row enthält einen Wert eines bestimmten Typs. Ein SwitchRow hält beispielsweise einen Bool -Wert, während ein TextRow einen String enthält.
// Get the value of a single row
let row : TextRow ? = form . rowBy ( tag : " MyRowTag " )
let value = row . value
// Get the value of all rows which have a Tag assigned
// The dictionary contains the 'rowTag':value pairs.
let valuesDictionary = form . values ( )Eureka beinhaltet benutzerdefinierte Betreiber, um die Erstellung der Form zu erleichtern:
form +++ Section ( )
// Chain it to add multiple Sections
form +++ Section ( " First Section " ) +++ Section ( " Another Section " )
// Or use it with rows and get a blank section for free
form +++ TextRow ( )
+++ TextRow ( ) // Each row will be on a separate sectionform +++ Section ( )
<<< TextRow ( )
<<< DateRow ( )
// Or implicitly create the Section
form +++ TextRow ( )
<<< DateRow ( ) // Append Sections into a Form
form += [ Section ( " A " ) , Section ( " B " ) , Section ( " C " ) ]
// Append Rows into a Section
section += [ TextRow ( ) , DateRow ( ) ]Eureka enthält Ergebnisbauer, um die Form der Form zu erleichtern:
// Section + Section
form = ( Section ( " A " ) +++ {
URLRow ( " UrlRow_f1 " ) { $0 . title = " Url " }
if something {
TwitterRow ( " TwitterRow_f2 " ) { $0 . title = " Twitter " }
} else {
TwitterRow ( " TwitterRow_f1 " ) { $0 . title = " Twitter " }
}
AccountRow ( " AccountRow_f1 " ) { $0 . title = " Account " }
} )
// Form + Section
form +++ {
if something {
PhoneRow ( " PhoneRow_f1 " ) { $0 . title = " Phone " }
} else {
PhoneRow ( " PhoneRow_f2 " ) { $0 . title = " Phone " }
}
PasswordRow ( " PasswordRow_f1 " ) { $0 . title = " Password " }
} @ FormBuilder
var form : Form {
Section ( " Section A " ) { section in
section . tag = " Section_A "
}
if true {
Section ( " Section B " ) { section in
section . tag = " Section_B "
}
}
NameRow ( " NameRow_f1 " ) { $0 . title = " Name " }
}Eureka enthält Rückrufe, um das Aussehen und das Verhalten einer Reihe zu ändern.
Eine Row ist eine Abstraktion, die Eureka verwendet, die einen Wert enthält und die Cell enthält. Die Cell verwaltet die Ansicht und Unterklassen UITableViewCell .
Hier ist ein Beispiel:
let row = SwitchRow ( " SwitchRow " ) { row in // initializer
row . title = " The title "
} . onChange { row in
row . title = ( row . value ?? false ) ? " The title expands when on " : " The title "
row . updateCell ( )
} . cellSetup { cell , row in
cell . backgroundColor = . lightGray
} . cellUpdate { cell , row in
cell . textLabel ? . font = . italicSystemFont ( ofSize : 18.0 )
} 
onchange ()
Aufgerufen, wenn sich der Wert einer Zeile ändert. Möglicherweise sind Sie daran interessiert, einige Parameter hier anzupassen oder sogar einige andere Zeilen erscheinen oder verschwinden.
Oncellelection ()
Jedes Mal, wenn der Benutzer in die Zeile aufnimmt und es ausgewählt wird. Beachten Sie, dass dies auch für deaktivierte Zeilen gefordert wird, sodass Sie Ihren Code in diesem Rückruf mit so etwas wie guard !row.isDisabled else { return } starten sollten
cellSetup ()
Nur einmal aufgerufen, wenn die Zelle zum ersten Mal konfiguriert ist. Stellen Sie hier dauerhafte Einstellungen fest.
cellUpdate ()
Jedes Mal aufgerufen, wenn die Zelle auf dem Bildschirm angezeigt wird. Sie können das Erscheinungsbild hier mit Variablen ändern, die bei CellSetUp () möglicherweise nicht vorhanden sind.
OncellhighlightChanged ()
Aufgerufen, wenn die Zelle oder eine Unteransicht zum Ersthelfer werden oder zurücktreten.
OnrowvalidationChanged ()
Aufgerufen, wenn sich die Validierungsfehler ändert.
Onexpandinlinerow ()
Angerufen, bevor die Inline -Reihe erweitert wird. Gilt für Zeilen, die das Protokoll InlineRowType entsprechen.
Oncollapseinlinerow ()
Angerufen, bevor Sie die Inline -Zeile zusammenbrechen. Gilt für Zeilen, die das Protokoll InlineRowType entsprechen.
Onpresent ()
Von einer Zeile kurz vor der Präsentation eines anderen Ansichtscontrollers aufgerufen. Gilt für Zeilen, die das Protokoll für das gegenwärtige PresenterRowType entsprechen. Verwenden Sie es, um den präsentierten Controller einzurichten.
Sie können eine String oder eine benutzerdefinierte View als Header oder Fußzeile eines Section festlegen.
Section ( " Title " )
Section ( header : " Title " , footer : " Footer Title " )
Section ( footer : " Footer Title " ) Sie können eine benutzerdefinierte Ansicht aus einer .xib -Datei verwenden:
Section ( ) { section in
var header = HeaderFooterView < MyHeaderNibFile > ( . nibFile ( name : " MyHeaderNibFile " , bundle : nil ) )
// Will be called every time the header appears on screen
header . onSetupView = { view , _ in
// Commonly used to setup texts inside the view
// Don't change the view hierarchy or size here!
}
section . header = header
} Oder eine benutzerdefinierte UIView programmatisch erstellt
Section ( ) { section in
var header = HeaderFooterView < MyCustomUIView > ( . class )
header . height = { 100 }
header . onSetupView = { view , _ in
view . backgroundColor = . red
}
section . header = header
}Oder erstellen Sie einfach die Ansicht mit einem Rückruf
Section ( ) { section in
section . header = {
var header = HeaderFooterView < UIView > ( . callback ( {
let view = UIView ( frame : CGRect ( x : 0 , y : 0 , width : 100 , height : 100 ) )
view . backgroundColor = . red
return view
} ) )
header . height = { 100 }
return header
} ( )
}
In diesem Fall verstecken und zeigen wir ganze Abschnitte.
Um diese zu erreichen, verfügt jede Zeile über eine hidden Variable des optionalen Condition , der mithilfe einer Funktion oder NSPredicate festgelegt werden kann.
Verwenden des function der Condition :
Condition . function ( [ String ] , ( Form ) - > Bool ) Das zu übergebene String sollte die Tags der Zeilen enthalten. Diese Zeile hängt davon ab. Jedes Mal, wenn der Wert einer dieser Zeilen ändert, wird die Funktion neu bewertet. Die Funktion nimmt dann das Form an und gibt einen Bool zurück, der angibt, ob die Zeile verborgen sein sollte oder nicht. Dies ist die leistungsstärkste Möglichkeit, die hidden Eigenschaft aufzubauen, da es keine expliziten Einschränkungen hat, was getan werden kann.
form +++ Section ( )
<<< SwitchRow ( " switchRowTag " ) {
$0 . title = " Show message "
}
<<< LabelRow ( ) {
$0 . hidden = Condition . function ( [ " switchRowTag " ] , { form in
return ! ( ( form . rowBy ( tag : " switchRowTag " ) as? SwitchRow ) ? . value ?? false )
} )
$0 . title = " Switch is on! "
} 
public enum Condition {
case function ( [ String ] , ( Form ) -> Bool )
case predicate ( NSPredicate )
} Die hidden Variable kann auch mit einem NSPREDIATE festgelegt werden. In der Prädikatzeichenfolge können Sie die Werte anderer Zeilen nach ihren Tags verweisen, um festzustellen, ob eine Zeile verborgen oder sichtbar sein sollte. Dies funktioniert nur, wenn die Werte der Zeilen, die das Prädikat überprüfen muss, NSObjects sind (String und INT werden funktionieren, da sie an ihre OBJC -Gegenstücke überbrückt werden, aber Enums werden nicht funktionieren). Warum könnte es dann nützlich sein, Prädikate zu verwenden, wenn sie eingeschränkter sind? Nun, sie können viel einfacher, kürzer und lesbar sein als Funktionen. Sehen Sie sich dieses Beispiel an:
$0 . hidden = Condition . predicate ( NSPredicate ( format : " $switchTag == false " ) ) Und wir können es noch kürzer schreiben, da Condition dem ExpressibleByStringLiteral entspricht:
$0 . hidden = " $switchTag == false "Hinweis: Wir ersetzen den Wert der Zeile, dessen Tag "Switchtag" anstelle von "$ switchtag" ist
Damit all dies funktioniert, müssen alle verwickelten Zeilen ein Tag haben, sobald das Tag identifiziert wird.
Wir können auch eine Reihe verbergen, indem wir:
$0 . hidden = true als Condition entspricht ExpressibleByBooleanLiteral .
Wenn Sie die hidden Variable nicht festlegen, wird die Zeile immer sichtbar.
Wenn Sie den versteckten (oder deaktivierten) Zustand nach der Anlage des Formulars manuell festlegen, müssen Sie möglicherweise row.evaluateHidden() anrufen. Weitere Informationen finden Sie in diesem FAQ -Abschnitt.
Für Abschnitte funktioniert dies genauso. Das bedeutet, dass wir eine versteckte Eigenschaft hidden Eigenschaft einrichten können, um sie dynamisch anzuzeigen/auszublenden.
Um Zeilen zu deaktivieren, verfügt jede Zeile über eine disabled Variable, die auch eine optionale Condition -Eigenschaft ist. Diese Variable funktioniert auch genauso wie die hidden Variable, sodass die Zeilen ein Tag haben müssen.
Beachten Sie, dass Sie, wenn Sie eine Zeile dauerhaft deaktivieren möchten, auch disabled Variable auf true festlegen können.
Um eine Liste der Optionen anzuzeigen, enthält Eureka einen speziellen Abschnitt mit dem Namen SelectableSection . Wenn Sie eine erstellen, müssen Sie die Art der Zeile übergeben, die in den Optionen und in der selectionType verwendet werden soll. Der selectionType ist eine Aufzählung, die entweder multipleSelection oder singleSelection(enableDeselection: Bool) wobei der enableDeselection Parameter für die Auswahl bestimmt, ob die ausgewählten Zeilen abgewiesen werden können oder nicht.
form +++ SelectableSection < ListCheckRow < String > > ( " Where do you live " , selectionType : . singleSelection ( enableDeselection : true ) )
let continents = [ " Africa " , " Antarctica " , " Asia " , " Australia " , " Europe " , " North America " , " South America " ]
for option in continents {
form . last! <<< ListCheckRow < String > ( option ) { listRow in
listRow . title = option
listRow . selectableValue = option
listRow . value = nil
}
} Um einen solchen Abschnitt zu erstellen, müssen Sie eine Zeile erstellen, die das Protokoll SelectableRowType entspricht.
public protocol SelectableRowType : RowType {
var selectableValue : Value ? { get set }
} In diesem selectableValue wird der Wert der Zeile dauerhaft gespeichert. Die value wird verwendet, um festzustellen, ob die Zeile ausgewählt ist oder nicht, da es "selyableValue" ist, wenn es ausgewählt ist oder sonst nicht. Eureka enthält die ListCheckRow die zum Beispiel verwendet wird. In den benutzerdefinierten Zeilen des Beispiel -Projekts finden Sie auch die ImageCheckRow .
Um die ausgewählten Zeilen/s einer SelectableSection leicht zu erhalten, gibt es zwei Methoden: selectedRow() und selectedRows() , die aufgerufen werden können, um die ausgewählte Zeile zu erhalten, falls es sich um einen Abschnitt SingleSelection handelt, oder alle ausgewählten Zeilen, wenn es sich um einen MultipleSelection -Auswahlabschnitt handelt.
Zusätzlich können Sie die Liste der Optionen einrichten, die nach Abschnitten unter Verwendung der folgenden Eigenschaften von SelectorViewController gruppiert werden sollen:
sectionKeyForValue - Eine Schließung, die den Schlüssel für einen bestimmten Zeilenwert zurückgeben sollte. Dieser Schlüssel wird später verwendet, um Optionen nach Abschnitten zu brechen.
sectionHeaderTitleForKey - Ein Abschluss, der den Header -Titel für einen Abschnitt für einen bestimmten Schlüssel zurückgibt. Standardmäßig gibt der Schlüssel selbst zurück.
sectionFooterTitleForKey - Eine Schließung, die den Fußzeilen -Titel für einen Abschnitt für einen bestimmten Schlüssel zurückgibt.
Eureka unterstützt mehrere Werte für ein bestimmtes Feld (z. B. Telefonnummern in einem Kontakt) mithilfe von Abschnitten mit mehreren Hochschulen. Es ermöglicht es uns, einfach einfügbare, löschbare und nachweisbare Abschnitte zu erstellen.

Um einen mehrfached -Abschnitt zu erstellen, müssen wir anstelle des regulären Section MultivaluedSection -Typ verwenden. MultivaluedSection erweitert Section und verfügt über einige zusätzliche Eigenschaften, um das Verhalten mit mehreren Abschnitten zu konfigurieren.
Lassen Sie uns in ein Code -Beispiel eintauchen ...
form +++
MultivaluedSection ( multivaluedOptions : [ . Reorder , . Insert , . Delete ] ,
header : " Multivalued TextField " ,
footer : " .Insert adds a 'Add Item' (Add New Tag) button row as last cell. " ) {
$0 . addButtonProvider = { section in
return ButtonRow ( ) {
$0 . title = " Add New Tag "
}
}
$0 . multivaluedRowToInsertAt = { index in
return NameRow ( ) {
$0 . placeholder = " Tag Name "
}
}
$0 <<< NameRow ( ) {
$0 . placeholder = " Tag Name "
}
}Früherer Code -Snippet zeigt, wie Sie einen mehrwertigen Abschnitt erstellen. In diesem Fall möchten wir Zeilen einfügen, löschen und neu ordnen, wie Argument mit mehreren Leuchten angibt.
addButtonProvider können wir die Schaltfläche anpassen, in der eine neue Zeile eingefügt wird, wenn Tacked und multivaluedOptions .Insert -Wert enthält.
multivaluedRowToInsertAt Clospeure -Eigenschaft wird von Eureka jedes Mal aufgerufen, wenn eine neue Zeile eingefügt werden muss. Um die Zeile zur Verfügung zu stellen, um sie in einen mehrkündigten Abschnitt hinzuzufügen, sollten wir diese Eigenschaft festlegen. Eureka übergibt den Index als Schließungsparameter. Beachten Sie, dass wir jede Art von Zeile zurückgeben können, sogar benutzerdefinierte Zeilen, obwohl in den meisten Fällen mehrfachige Abschnittzeilen vom gleichen Typ sind.
Eureka fügt automatisch eine Schaltflächenzeile hinzu, wenn wir einen einfügenbaren Abschnitt mit mehreren Multivaluierten erstellen. Wir können anpassen, wie diese Schaltfläche wie zuvor erklärt aussieht. showInsertIconInAddButton -Eigenschaft zeigt an, ob plus Taste (Style) links neben der Taste angezeigt werden sollte, standardmäßig.
Es gibt einige Überlegungen, die wir beim Erstellen von Einfügenabschnitten im Auge haben müssen. Jede Zeile, die dem Abschnitt mit einfügbarem mehrfachem Abschnitt hinzugefügt wird, sollte über der Zeile platziert werden, die Eureka automatisch hinzugefügt wird, um neue Zeilen einzufügen. Dies kann leicht erreicht werden, indem diese zusätzlichen Zeilen aus dem Initialisiererverschluss des Abschnitts (letzter Parameter des Abschnitts -Initialisierers) zum Abschnitt hinzugefügt werden. Anschließend fügt Eureka die Schaltfläche Einfügen bei Adds -Einfügen am Ende des Abschnitts hinzu.
Standardmäßig setzt Eureka die isEditing des TableView nur dann auf True, wenn ein MultivaluedSektion in der Form vorhanden ist. Dies erfolgt in viewWillAppear , wenn zum ersten Mal ein Formular präsentiert wird.
Weitere Informationen zur Verwendung von Multivaluierten -Abschnitten finden Sie in einem Beispielprojekt Eureka, das mehrere Verwendungsbeispiele enthält.
Wenn Sie eine Schaltfläche hinzufügen möchten, die kein ButtonRow ist, können Sie GenericMultivaluedSection<AddButtonType> verwenden, wobei AddButtonType der Typ der Zeile ist, die Sie als Taste hinzufügen möchten. Dies ist nützlich, wenn Sie eine benutzerdefinierte Zeile verwenden möchten, um die Benutzeroberfläche der Schaltfläche zu ändern.
Beispiel:
GenericMultivaluedSection < LabelRow > ( multivaluedOptions : [ . Reorder , . Insert , . Delete ] , {
$0 . addButtonProvider = { section in
return LabelRow ( ) {
$0 . title = " A Label row as add button "
}
}
// ...
}Eureka 2.0.0 führt die viel angeforderte Funktion integriert.
Eine Zeile verfügt über eine Sammlung von Rules und eine bestimmte Konfiguration, die feststellt, wann die Validierungsregeln bewertet werden sollen.
Es gibt standardmäßig einige Regeln, aber Sie können auch selbst neue erstellen.
Die bereitgestellten Regeln sind:
Lassen Sie uns sehen, wie Sie die Validierungsregeln einrichten.
override func viewDidLoad ( ) {
super . viewDidLoad ( )
form
+++ Section ( header : " Required Rule " , footer : " Options: Validates on change " )
<<< TextRow ( ) {
$0 . title = " Required Rule "
$0 . add ( rule : RuleRequired ( ) )
// This could also have been achieved using a closure that returns nil if valid, or a ValidationError otherwise.
/*
let ruleRequiredViaClosure = RuleClosure<String> { rowValue in
return (rowValue == nil || rowValue!.isEmpty) ? ValidationError(msg: "Field required!") : nil
}
$0.add(rule: ruleRequiredViaClosure)
*/
$0 . validationOptions = . validatesOnChange
}
. cellUpdate { cell , row in
if !row . isValid {
cell . titleLabel ? . textColor = . systemRed
}
}
+++ Section ( header : " Email Rule, Required Rule " , footer : " Options: Validates on change after blurred " )
<<< TextRow ( ) {
$0 . title = " Email Rule "
$0 . add ( rule : RuleRequired ( ) )
$0 . add ( rule : RuleEmail ( ) )
$0 . validationOptions = . validatesOnChangeAfterBlurred
}
. cellUpdate { cell , row in
if !row . isValid {
cell . titleLabel ? . textColor = . systemRed
}
} Wie Sie im vorherigen Code -Snippet sehen können, können wir so viele Regeln einrichten, wie wir in einer Zeile erwarten, indem wir Row's add(rule:) Funktion aufrufen.
Zeile bietet auch func remove(ruleWithIdentifier identifier: String) um eine Regel zu entfernen. Um es zu verwenden, müssen wir nach dem Erstellen der Regel eine ID zuweisen.
Manchmal ist die Sammlung von Regeln, die wir in einer Zeile verwenden möchten, die gleichen, die wir auf vielen anderen Zeilen verwenden möchten. In diesem Fall können wir alle Validierungsregeln mit einem RuleSet einrichten, bei dem es sich um eine Sammlung von Validierungsregeln handelt.
var rules = RuleSet < String > ( )
rules . add ( rule : RuleRequired ( ) )
rules . add ( rule : RuleEmail ( ) )
let row = TextRow ( ) {
$0 . title = " Email Rule "
$0 . add ( ruleSet : rules )
$0 . validationOptions = . validatesOnChangeAfterBlurred
} Mit Eureka können wir angeben, wann die Validierungsregeln bewertet werden sollen. Wir können dies tun, indem wir die Eigenschaft von validationOptions Row einrichten, die die folgenden Werte haben kann:
.validatesOnChange - validiert, wenn sich ein Zeilenwert ändert..validatesOnBlur - (Standardwert) bestätigt gleich nach dem Rücktritt der Zelle. Nicht für alle Zeilen anwendbar..validatesOnChangeAfterBlurred - bestätigt, wenn sich der Zeilenwert nach dem ersten Responder ändert..validatesOnDemand - Wir sollten die Zeile oder das Formular manuell validieren, indem wir validate() -Methoden aufrufen. Wenn Sie das gesamte Formular (alle Zeilen) validieren möchten, können Sie die Formular validate() manuell aufrufen.
Jede Zeile hat die validationErrors -Eigenschaft, mit der alle Validierungsfehler abgerufen werden können. Diese Eigenschaft enthält nur die Liste der Validierungsfehler der neuesten Zeilenvalidierungsausführung, was bedeutet, dass sie die Validierungsregeln der Zeile nicht bewertet.
Wie erwartet müssen die Regeln dieselben Typen wie das Zeilenobjekt verwenden. Achten Sie besonders darauf, den verwendeten Zeilentyp zu überprüfen. Möglicherweise sehen Sie einen Compiler -Fehler ("Falsches Augment -Etikett in Call (Regel:" Erwartete "Regeln:"), die beim Mischen von Typen nicht auf das Problem hinweist.
Durch die Verwendung von SWIPE -Aktionen können wir mehrere leadingSwipe und trailingSwipe -Aktionen pro Reihe definieren. Da Swipe -Aktionen von iOS -Systemfunktionen abhängen, ist leadingSwipe nur für iOS 11.0+ verfügbar.
Mal sehen, wie man Swipe -Aktionen definiert.
let row = TextRow ( ) {
let deleteAction = SwipeAction (
style : . destructive ,
title : " Delete " ,
handler : { ( action , row , completionHandler ) in
//add your code here.
//make sure you call the completionHandler once done.
completionHandler ? ( true )
} )
deleteAction . image = UIImage ( named : " icon-trash " )
$0 . trailingSwipe . actions = [ deleteAction ]
$0 . trailingSwipe . performsFirstActionWithFullSwipe = true
//please be aware: `leadingSwipe` is only available on iOS 11+ only
let infoAction = SwipeAction (
style : . normal ,
title : " Info " ,
handler : { ( action , row , completionHandler ) in
//add your code here.
//make sure you call the completionHandler once done.
completionHandler ? ( true )
} )
infoAction . actionBackgroundColor = . blue
infoAction . image = UIImage ( named : " icon-info " )
$0 . leadingSwipe . actions = [ infoAction ]
$0 . leadingSwipe . performsFirstActionWithFullSwipe = true
} Swipe -Aktionen benötigen tableView.isEditing auf false . Eureka setzt dies auf true , wenn es eine mehrwertige Abteilung in der Form gibt (im viewWillAppear ). Wenn Sie sowohl mehrwertige Multivalueds- als auch Aktionen in derselben Form haben, sollten Sie isEditing entsprechend Ihren Anforderungen festlegen.
Es ist sehr häufig, dass Sie eine Reihe benötigen, die sich von denen in Eureka unterscheidet. Wenn dies der Fall ist, müssen Sie Ihre eigene Zeile erstellen, dies sollte jedoch nicht schwierig sein. Sie können dieses Tutorial zum Erstellen von benutzerdefinierten Zeilen lesen, um loszulegen. Möglicherweise möchten Sie auch Eurekacommunity ansehen, die einige zusätzliche Reihen enthält, die für Eureka hinzugefügt werden können.
Um eine Zeile mit benutzerdefiniertem Verhalten und Erscheinungsbild zu erstellen, möchten Sie wahrscheinlich Unterklassen von Row und Cell erstellen.
Denken Sie daran, dass Row die Abstraktion ist, die Eureka verwendet, während die Cell die tatsächliche UITableViewCell ist, die für die Ansicht verantwortlich ist. Da die Row die Cell enthält, müssen sowohl Row als auch Cell für denselben Werttyp definiert werden.
// Custom Cell with value type: Bool
// The cell is defined using a .xib, so we can set outlets :)
public class CustomCell : Cell < Bool > , CellType {
@ IBOutlet weak var switchControl : UISwitch !
@ IBOutlet weak var label : UILabel !
public override func setup ( ) {
super . setup ( )
switchControl . addTarget ( self , action : #selector ( CustomCell . switchValueChanged ) , for : . valueChanged )
}
func switchValueChanged ( ) {
row . value = switchControl . on
row . updateCell ( ) // Re-draws the cell which calls 'update' bellow
}
public override func update ( ) {
super . update ( )
backgroundColor = ( row . value ?? false ) ? . white : . black
}
}
// The custom Row also has the cell: CustomCell and its correspond value
public final class CustomRow : Row < CustomCell > , RowType {
required public init ( tag : String ? ) {
super . init ( tag : tag )
// We set the cellProvider to load the .xib corresponding to our cell
cellProvider = CellProvider < CustomCell > ( nibName : " CustomCell " )
}
} Das Ergebnis: 
Genau wie bei den Callbacks CellSetup und CellUpdate verfügt die Cell über die Methoden für Setups und aktualisiert, wo Sie sie anpassen können.
Eine Inline -Zeile ist eine bestimmte Art von Zeile, die dynamisch eine Zeile darunter anzeigt. Normalerweise ändert sich eine Inline -Zeile zwischen einem erweiterten und einem zusammengebrochenen Modus, wenn die Zeile abgebrochen wird.
Um eine Inline -Zeile zu erstellen, benötigen wir 2 Zeilen, die Zeile, die "immer" sichtbar ist, und die Zeile, die erweitert/zusammenbricht.
Eine weitere Anforderung ist, dass der Werttyp dieser 2 Zeilen gleich sein muss. Dies bedeutet, wenn eine Zeile einen String -Wert enthält, muss der andere auch einen String -Wert haben.
Sobald wir diese 2 Zeilen haben, sollten wir den Typ in der oberen Zeile dem InlineRowType entsprechen. Dieses Protokoll erfordert, dass Sie eine InlineRow -Typealias und eine setupInlineRow -Funktion definieren. Der InlineRow -Typ ist der Typ der Zeile, der sich erweitert/zusammenbricht. Nehmen Sie dies als Beispiel:
class PickerInlineRow < T > : Row < PickerInlineCell < T > > where T : Equatable {
public typealias InlineRow = PickerRow < T >
open var options = [ T ] ( )
required public init ( tag : String ? ) {
super . init ( tag : tag )
}
public func setupInlineRow ( _ inlineRow : InlineRow ) {
inlineRow . options = self . options
inlineRow . displayValueFor = self . displayValueFor
inlineRow . cell . height = { UITableViewAutomaticDimension }
}
} Der InlineRowType fügt Ihrer Inline -Zeile auch einige Methoden hinzu:
func expandInlineRow ( )
func collapseInlineRow ( )
func toggleInlineRow ( ) Diese Methoden sollten gut funktionieren, aber wenn Sie sie überschreiben möchten, denken Sie daran, dass es sich um toggleInlineRow handelt, das expandInlineRow und collapseInlineRow aufrufen muss.
Schließlich müssen Sie toggleInlineRow() aufrufen, wenn die Zeile ausgewählt ist, z. B. überschrieben customDidSelect :
public override func customDidSelect ( ) {
super . customDidSelect ( )
if !isDisabled {
toggleInlineRow ( )
}
}HINWEIS: Eine Moderator -Zeile ist eine Zeile, die einen neuen UiviewController darstellt.
Um eine benutzerdefinierte Moderator -Zeile zu erstellen, müssen Sie eine Klasse erstellen, die das Protokoll für das PresenterRowType entspricht. Es wird dringend empfohlen, SelectorRow der Unterklasse zu erhalten, da es dem Protokoll entspricht und andere nützliche Funktionen hinzufügt.
Das PresentErrowType -Protokoll ist wie folgt definiert:
public protocol PresenterRowType : TypedRowType {
associatedtype PresentedControllerType : UIViewController , TypedRowControllerType
/// Defines how the view controller will be presented, pushed, etc.
var presentationMode : PresentationMode < PresentedControllerType > ? { get set }
/// Will be called before the presentation occurs.
var onPresentCallback : ( ( FormViewController , PresentedControllerType ) -> Void ) ? { get set }
} Der OnpresentCallback wird aufgerufen, wenn die Zeile einen anderen Ansichtscontroller präsentieren soll. Dies geschieht in der SelectorRow Wenn Sie es nicht unterklassen, müssen Sie es selbst nennen.
Der presentationMode definiert, wie der Controller präsentiert wird und welcher Controller präsentiert wird. Diese Präsentation kann eine Segue -Kennung, eine Segue -Klasse, verwenden, die einen Controller modell präsentiert oder zu einem bestimmten Ansichtscontroller drückt. Zum Beispiel kann ein CustomPushrow so definiert werden:
Lassen Sie uns ein Beispiel sehen.
/// Generic row type where a user must select a value among several options.
open class SelectorRow < Cell : CellType > : OptionsRow < Cell > , PresenterRowType where Cell : BaseCell {
/// Defines how the view controller will be presented, pushed, etc.
open var presentationMode : PresentationMode < SelectorViewController < SelectorRow < Cell > > > ?
/// Will be called before the presentation occurs.
open var onPresentCallback : ( ( FormViewController , SelectorViewController < SelectorRow < Cell > > ) -> Void ) ?
required public init ( tag : String ? ) {
super . init ( tag : tag )
}
/**
Extends `didSelect` method
*/
open override func customDidSelect ( ) {
super . customDidSelect ( )
guard let presentationMode = presentationMode , !isDisabled else { return }
if let controller = presentationMode . makeController ( ) {
controller . row = self
controller . title = selectorTitle ?? controller . title
onPresentCallback ? ( cell . formViewController ( ) ! , controller )
presentationMode . present ( controller , row : self , presentingController : self . cell . formViewController ( ) ! )
} else {
presentationMode . present ( nil , row : self , presentingController : self . cell . formViewController ( ) ! )
}
}
/**
Prepares the pushed row setting its title and completion callback.
*/
open override func prepare ( for segue : UIStoryboardSegue ) {
super . prepare ( for : segue )
guard let rowVC = segue . destination as Any as? SelectorViewController < SelectorRow < Cell > > else { return }
rowVC . title = selectorTitle ?? rowVC . title
rowVC . onDismissCallback = presentationMode ? . onDismissCallback ?? rowVC . onDismissCallback
onPresentCallback ? ( cell . formViewController ( ) ! , rowVC )
rowVC . row = self
}
}
// SelectorRow conforms to PresenterRowType
public final class CustomPushRow < T : Equatable > : SelectorRow < PushSelectorCell < T > > , RowType {
public required init ( tag : String ? ) {
super . init ( tag : tag )
presentationMode = . show ( controllerProvider : ControllerProvider . callback {
return SelectorViewController < T > ( ) { _ in }
} , onDismiss : { vc in
_ = vc . navigationController ? . popViewController ( animated : true )
} )
}
}Manchmal wollen wir das UI -Aussehen einer unserer Zeilen ändern, ohne den Zeilentyp und die gesamte Logik zu ändern, die einer Zeile zugeordnet ist. Derzeit gibt es eine Möglichkeit, dies zu tun , wenn Sie Zellen verwenden, die von NIB -Dateien instanziiert werden . Derzeit wird keine der Kernzeilen von Eureka aus NIB -Dateien instanziiert, aber einige der benutzerdefinierten Zeilen in Eurekacommunity sind insbesondere das dort verschobene Postaladdressrow.
Was Sie tun müssen, ist:
cellProvider -Variablen, um diese NIB zu verwenden. Sie sollten dies im Initialiser tun, entweder in jeder Beton -Instanziierung oder mit dem defaultRowInitializer . Zum Beispiel: <<< PostalAddressRow ( ) {
$0 . cellProvider = CellProvider < PostalAddressCell > ( nibName : " CustomNib " , bundle : Bundle . main )
}Dafür können Sie auch eine neue Zeile erstellen. Versuchen Sie in diesem Fall, aus derselben Superklasse zu erben wie die Zeile, die Sie ändern möchten, um seine Logik zu erben.
Es gibt einige Dinge zu berücksichtigen, wenn Sie dies tun:
Unknown class <YOUR_CLASS_NAME> in Interface Builder file sagt, müssen Sie diesen neuen Typ in Ihrem Code so instanziieren, dass Sie ihn in der Laufzeit laden müssen. Calling let t = YourClass.self hat in meinem Fall geholfen. Etikettenreihe![]() | Tastezeile![]() | Überprüfen Sie die Reihe ![]() |
Zeile schalten![]() | Slider -Reihe![]() | Stepper Row ![]() |
Textbereichreihe ![]() |
Diese Zeilen haben ein Textfeld auf der rechten Seite der Zelle. Der Unterschied zwischen jedem von ihnen besteht in einer anderen Konfiguration von Kapitalisation, Autokorrektur und Tastaturtyp.
![]() | Textrow Namerow URLROW Introw Phonerow Passwortrow E -MailRow Dezimalrow Twitterrow Accountrow Zipcoderow |
Alle oben genannten FieldRow haben eine formatter -Eigenschaft vom Typ NSFormatter , der feststellen kann, wie der Wert dieser Zeile angezeigt werden soll. Eine benutzerdefinierte Formatatterin für Zahlen mit zwei Ziffern nach der Dezimalmarke ist Eureka ( DecimalFormatter ) enthalten. Das Beispielprojekt enthält auch eine CurrencyFormatter , die eine Zahl als Währung gemäß dem Gebietsschema des Benutzers anzeigt.
Standardmäßig beeinflusst das Einstellen des formatter einer Zeile nur, wie ein Wert angezeigt wird, wenn er nicht bearbeitet wird. Um den Wert auch zu formatieren, während die Zeile bearbeitet wird, setzen Sie useFormatterDuringInput auf true bei der Initialisierung der Zeile fest. Das Formatieren des Werts, der bearbeitet wird, muss möglicherweise die Cursorposition aktualisieren, und Eureka bietet das folgende Protokoll, das Ihr Formatierer entsprechen sollte, um die Cursorposition zu behandeln:
public protocol FormatterProtocol {
func getNewPosition ( forPosition forPosition : UITextPosition , inTextInput textInput : UITextInput , oldValue : String ? , newValue : String ? ) -> UITextPosition
} Zusätzlich verfügen FieldRow -Subtypen über eine useFormatterOnDidBeginEditing . Bei Verwendung eines DecimalRow mit einem Formatierer, das Dezimalwerte ermöglicht und dem Gebietsschema des Benutzers entspricht (z. B. DecimalFormatter false , muss useFormatterDuringInput auf true festgelegt werden, damit die Dezimalmarke im bearbeiteten Wert mit der Dezimalmarke useFormatterOnDidBeginEditing dem Tastatur entspricht.
Datumsreihen halten ein Datum und ermöglichen es uns, einen neuen Wert über Uidatepicker Control einzurichten. Der Modus des Uidespickers und die Art und Weise, wie die Date Picker -Ansicht angezeigt wird, ändert sich zwischen ihnen.
Datum Zeile ![]() Picker in der Tastatur gezeigt. | Datumsreihe (Inline) ![]() Die Reihe erweitert sich. | Date Row (Picker) ![]() Der Picker ist immer sichtbar. |
Mit diesen 3 Stilen (normal, Inline & Picker) enthält Eureka:
Dies sind Zeilen mit einer Liste von Optionen, aus denen der Benutzer auswählen muss.
<<< ActionSheetRow < String > ( ) {
$0 . title = " ActionSheetRow "
$0 . selectorTitle = " Pick a number "
$0 . options = [ " One " , " Two " , " Three " ]
$0 . value = " Two " // initially selected
} Alarmreihe![]() Zeigt eine Warnung mit den Optionen zur Auswahl. | ActionSheet -Zeile![]() Zeigt ein Aktionsblatt mit den Optionen zur Auswahl. | Push Reihe![]() Wird zu einem neuen Controller von der Auswahl von Optionen überprüft, die mithilfe von Überprüfungszeilen aufgeführt sind. | Mehrfachauswahlreihe![]() Wie Pushrow, ermöglicht aber die Auswahl mehrerer Optionen. |
Segmentierte Zeile![]() | Segmentierte Zeile (W/Titel)![]() | Picker Row![]() Präsentiert Optionen eines generischen Typs über eine Auswahlansicht (Es gibt auch Picker Inline -Reihe) |
Lassen Sie uns davon wissen, wir würden uns freuen, es hier zu erwähnen. :)

Cocoapods ist ein Abhängigkeitsmanager für Kakaoprojekte.
Geben Sie Eureka in Podfile Ihres Projekts an:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios , '9.0'
use_frameworks!
pod 'Eureka'Führen Sie dann den folgenden Befehl aus:
$ pod installSwift Package Manager ist ein Tool zur Verwaltung der Verteilung des Swift -Code.
Nachdem Sie Ihr Package.swift eingerichtet haben. Die Manifestdatei in Paket. Sie können Eureka als Abhängigkeit hinzufügen, indem Sie sie dem Abhängigkeitswert Ihres Package.swift hinzufügen.
Abhängigkeiten: [.package (URL: "https://github.com/xmartlabs/eureka.git", von: "5.5.0")]
Karthago ist ein einfacher, dezentraler Abhängigkeitsmanager für Kakao.
Geben Sie Eureka in Cartfile Ihres Projekts an:
github "xmartlabs/Eureka" ~> 5.5
$ git submodule add https://github.com/xmartlabs/Eureka.gitÖffnen Sie den Eureka -Ordner, der vom vorherigen Befehl git submodule erstellt wurde, und ziehen Sie den Eureka.xcodeProj in den Projektnavigator des Xcode -Projekts Ihrer Anwendung.
Wählen Sie im Projektnavigator den Eureka.xcodeProj aus und überprüfen Sie die Bereitstellungszielübereinstimmung mit Ihrem Anwendungsbereitstellungsziel.
Wählen Sie Ihr Projekt in der Xcode -Navigation aus und wählen Sie dann Ihr Anwendungsziel aus der Seitenleiste aus. Wählen Sie als nächstes die Registerkarte "Allgemein" aus und klicken Sie auf die Schaltfläche + unter dem Abschnitt "Embedded Binare".
Wählen Sie Eureka.framework und wir sind fertig!
eureka-forms ).Überprüfen Sie vor dem Beitrag die beitragende Datei, um weitere Informationen zu erhalten.
Wenn Sie Eureka in Ihrer App verwenden, würden wir gerne davon hören! Schreiben Sie uns eine Linie auf Twitter.
Jede Reihe hat die folgende Eigenschaft:
/// Block variable used to get the String that should be displayed for the value of this row.
public var displayValueFor : ( ( T ? ) -> String ? ) ? = {
return $0 . map { String ( describing : $0 ) }
} Sie können displayValueFor entsprechend dem String -Wert festlegen, den Sie anzeigen möchten.
Wir können eine bestimmte Zeile erhalten, indem wir eine der folgenden Funktionen aufrufen, die von der Form ausgesetzt sind:
public func rowBy < T : Equatable > ( tag : String ) -> RowOf < T > ?
public func rowBy < Row : RowType > ( tag : String ) -> Row ?
public func rowBy ( tag : String ) -> BaseRow ?Zum Beispiel:
let dateRow : DateRow ? = form . rowBy ( tag : " dateRowTag " )
let labelRow : LabelRow ? = form . rowBy ( tag : " labelRowTag " )
let dateRow2 : Row < DateCell > ? = form . rowBy ( tag : " dateRowTag " )
let labelRow2 : BaseRow ? = form . rowBy ( tag : " labelRowTag " ) let section : Section ? = form . sectionBy ( tag : " sectionTag " ) Aufrufen setValues(values: [String: Any?]) Was durch Form ausgesetzt ist.
Zum Beispiel:
form . setValues ( [ " IntRowTag " : 8 , " TextRowTag " : " Hello world! " , " PushRowTag " : Company ( name : " Xmartlabs " ) ] ) Wo "IntRowTag" , "TextRowTag" , "PushRowTag" sind Zeilen -Tags (jeder identifiziert einzigartig eine Reihe) und 8 "Hello world!" , Company(name:"Xmartlabs") sind der entsprechende Zeilenwert zuzuweisen.
Der Werttyp einer Zeile muss mit dem Werttyp des entsprechenden Wörterbuchwerts übereinstimmen, sonst wird NIL zugewiesen.
Wenn das Formular bereits angezeigt wurde, müssen wir die sichtbaren Zeilen neu laden, indem wir die Tabellenansicht tableView.reloadData() neu laden oder updateCell() in jede sichtbare Zeile aufrufen.
Nach dem Einstellen einer Bedingung wird diese Bedingung nicht automatisch bewertet. Wenn Sie dies sofort tun möchten, können Sie .evaluateHidden() oder .evaluateDisabled() anrufen.
Diese Funktionen werden gerade aufgerufen, wenn eine Zeile zum Formular hinzugefügt wird und wenn eine Zeile von Änderungen abhängt. Wenn die Bedingung geändert wird, wenn die Zeile angezeigt wird, muss sie manuell neu bewertet werden.
Sehen Sie sich dieses Problem an.
section . header = HeaderFooterView ( title : " Header title ( variable ) " ) // use String interpolation
//or
var header = HeaderFooterView < UIView > ( . class ) // most flexible way to set up a header using any view type
header . height = { 60 } // height can be calculated
header . onSetupView = { view , section in // each time the view is about to be displayed onSetupView is invoked.
view . backgroundColor = . orange
}
section . header = headersection . reload ( ) selectableRowSetup , auswählen, selectableRowCellUpdate und selectableRowCellSetup -Eigenschaften werden bereitgestellt, um SelectorViewController und MultiplectorCectorViewController -Auswahlzellen anzupassen.
let row = PushRow < Emoji > ( ) {
$0 . title = " PushRow "
$0 . options = [ ?? , ? , ?? , ? , ? , ? ]
$0 . value = ??
$0 . selectorTitle = " Choose an Emoji! "
} . onPresent { from , to in
to . dismissOnSelection = false
to . dismissOnChange = false
to . selectableRowSetup = { row in
row . cellProvider = CellProvider < ListCheckCell < Emoji > > ( nibName : " EmojiCell " , bundle : Bundle . main )
}
to . selectableRowCellUpdate = { cell , row in
cell . textLabel ? . text = " Text " + row . selectableValue! // customization
cell . detailTextLabel ? . text = " Detail " + row . selectableValue!
}
} Wie wir bereits gesagt haben, entsprechen Form und Section MutableCollection und RangeReplaceableCollection . Eine Form ist eine Sammlung von Abschnitten und ein Abschnitt ist eine Sammlung von Zeilen.
RangeReplaceableCollection -Protokollverlängerung bietet viele nützliche Methoden zur Änderung der Sammlung.
extension RangeReplaceableCollection {
public mutating func append ( _ newElement : Self . Element )
public mutating func append < S > ( contentsOf newElements : S ) where S : Sequence , Self . Element == S . Element
public mutating func insert ( _ newElement : Self . Element , at i : Self . Index )
public mutating func insert < S > ( contentsOf newElements : S , at i : Self . Index ) where S : Collection , Self . Element == S . Element
public mutating func remove ( at i : Self . Index ) -> Self . Element
public mutating func removeSubrange ( _ bounds : Range < Self . Index > )
public mutating func removeFirst ( _ n : Int )
public mutating func removeFirst ( ) -> Self . Element
public mutating func removeAll ( keepingCapacity keepCapacity : Bool )
public mutating func reserveCapacity ( _ n : Self . IndexDistance )
}Diese Methoden werden intern verwendet, um die benutzerdefinierten Operatoren wie gezeigt zu implementieren:
public func +++ ( left : Form , right : Section ) -> Form {
left . append ( right )
return left
}
public func += < C : Collection > ( inout lhs : Form , rhs : C ) where C . Element == Section {
lhs . append ( contentsOf : rhs )
}
public func << < ( left : Section , right : BaseRow ) -> Section {
left . append ( right )
return left
}
public func += < C : Collection > ( inout lhs : Section , rhs : C ) where C . Element == BaseRow {
lhs . append ( contentsOf : rhs )
}Sie können sehen, wie der Rest der benutzerdefinierten Operatoren hier implementiert wird.
Es liegt an Ihnen zu entscheiden, ob Sie Eureka -benutzerdefinierte Operatoren verwenden möchten oder nicht.
Das Formular wird immer in einem UITableView angezeigt. Sie können Ihren Ansichtscontroller in einem Storyboard einrichten und einen UitableView hinzufügen, in dem Sie es sein möchten, und dann das Outlet an tableView von formViewController anschließen. Auf diese Weise können Sie einen benutzerdefinierten Rahmen (möglicherweise mit Einschränkungen) für Ihr Formular definieren.
All dies kann auch durch programmgestütztes Rahmen, Ränder usw. des tableView Ihres FormViewController erfolgen.
So können wir Eureka noch besser machen! 
Dies finden Sie in der Datei chantelog.md.