
Fabriqué avec ❤️ par XmartLabs. Il s'agit de la recréation de XLform dans Swift.
简体中文
![]() | ![]() | ![]() |
|---|
Pour plus d'informations, consultez notre article de blog qui présente Eureka .
Vous pouvez cloner et exécuter l'exemple de projet pour voir des exemples de la plupart des fonctionnalités d'Eureka.
![]() | ![]() |
|---|
En étendant FormViewController vous pouvez ensuite simplement ajouter des sections et des lignes à la variable form .
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 )
}
}
}Dans l'exemple, nous créons deux sections avec des lignes standard, le résultat est le suivant:

Vous pouvez créer un formulaire en configurant simplement la propriété form par vous-même sans s'étendre à partir de FormViewController , mais cette méthode est généralement plus pratique.
Pour modifier le comportement de cela, vous devez définir les options de navigation de votre contrôleur. Le FormViewController a une variable navigationOptions qui est une enum et peut avoir une ou plusieurs des valeurs suivantes:
canBecomeFirstResponder() La valeur par défaut est enabled & skipCanNotBecomeFirstResponderRow
Pour permettre le défilement en douceur sur les lignes hors écran, activez-la via la propriété animateScroll . Par défaut, le FormViewController saute immédiatement entre les lignes lorsque l'utilisateur frappe les boutons suivants ou précédents dans l'accès à la navigation du clavier, y compris lorsque la ligne suivante est hors écran.
Pour définir la quantité d'espace entre le clavier et la ligne en surbrillance suivant un événement de navigation, définissez la propriété rowKeyboardSpacing . Par défaut, lorsque le formulaire défile vers une vue hors écran, aucun espace ne sera laissé entre le haut du clavier et le bas de la ligne.
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
}
} Si vous souhaitez modifier l'ensemble de la vue d'accessoires de navigation, vous devrez remplacer la variable navigationAccessoryView dans votre sous-classe de FormViewController .
L'objet Row contient une valeur d'un type spécifique. Par exemple, un SwitchRow détient une valeur Bool , tandis qu'un TextRow détient une valeur String .
// 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 comprend des opérateurs personnalisés pour faciliter la création de formulaires:
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 comprend les constructeurs de résultats pour faciliter la création de formulaires:
// 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 comprend des rappels pour modifier l'apparence et le comportement d'une ligne.
Une Row est une abstraction utilisée par Eureka qui contient une valeur et contient la Cell de vue. La Cell gère la vue et sous-classe UITableViewCell .
Voici un exemple:
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 ()
Appelé lorsque la valeur d'une ligne change. Vous pourriez être intéressé à ajuster certains paramètres ici ou même à faire apparaître ou disparaître d'autres lignes.
oncellselection ()
Appelé à chaque fois que l'utilisateur tape sur la ligne et il est sélectionné. Notez que cela sera également appelé pour les lignes désactivées, vous devez donc démarrer votre code à l'intérieur de ce rappel avec quelque chose comme guard !row.isDisabled else { return }
CellSetup ()
Appelé une seule fois lorsque la cellule est configurée pour la première fois. Définissez les paramètres permanents ici.
Cellupdate ()
Appelé chaque fois que la cellule apparaît à l'écran. Vous pouvez modifier l'apparence ici en utilisant des variables qui peuvent ne pas être présentes sur CellSetUp ().
oncellhighlightchanged ()
Appelé chaque fois que la cellule ou une sous-vue devient ou démissionne le premier répondeur.
onrowValidationChanged ()
Appelé chaque fois que les erreurs de validation associées à une ligne changent.
onexpandinlinerow ()
Appelé avant d'étendre la ligne en ligne. S'applique aux lignes conformant le protocole InlineRowType .
onCollapseInlinerow ()
Appelé avant d'effondrer la ligne en ligne. S'applique aux lignes conformant le protocole InlineRowType .
onPresent ()
Appelé par une ligne juste avant de présenter un autre contrôleur de vue. S'applique aux lignes conformant le protocole PresenterRowType . Utilisez-le pour configurer le contrôleur présenté.
Vous pouvez définir une String de titre ou une View personnalisée sous forme d'en-tête ou de pied de page d'une Section .
Section ( " Title " )
Section ( header : " Title " , footer : " Footer Title " )
Section ( footer : " Footer Title " ) Vous pouvez utiliser une vue personnalisée à partir d'un fichier .xib :
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
} Ou un UIView personnalisé créé par programmation
Section ( ) { section in
var header = HeaderFooterView < MyCustomUIView > ( . class )
header . height = { 100 }
header . onSetupView = { view , _ in
view . backgroundColor = . red
}
section . header = header
}Ou tout simplement construire la vue avec un rappel
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
} ( )
}
Dans ce cas, nous nous cachons et montrons des sections entières.
Pour ce faire, chaque ligne a une variable hidden de Condition de type facultative qui peut être définie à l'aide d'une fonction ou NSPredicate .
Utilisation du cas de function de Condition :
Condition . function ( [ String ] , ( Form ) - > Bool ) Le tableau de String à passer doit contenir les balises des lignes dont dépend cette ligne. Chaque fois que la valeur de l'une de ces lignes change, la fonction est réévaluée. La fonction prend ensuite le Form et renvoie un Bool indiquant si la ligne doit être cachée ou non. C'est la façon la plus puissante de configurer la propriété hidden car elle n'a aucune limitation explicite de ce qui peut être fait.
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 )
} La variable hidden peut également être définie avec un nspredicate. Dans la chaîne de prédicat, vous pouvez référencer les valeurs des autres lignes par leurs balises pour déterminer si une ligne doit être cachée ou visible. Cela ne fonctionnera que si les valeurs des lignes que le prédicat doivent vérifier sont nsobjects (String et int fonctionneront car ils sont réduits à leurs homologues OBJC, mais les énumérements ne fonctionneront pas). Pourquoi pourrait-il alors être utile d'utiliser des prédicats lorsqu'ils sont plus limités? Eh bien, ils peuvent être beaucoup plus simples, plus courts et lisibles que les fonctions. Regardez cet exemple:
$0 . hidden = Condition . predicate ( NSPredicate ( format : " $switchTag == false " ) ) Et nous pouvons l'écrire encore plus court car Condition est conforme à ExpressibleByStringLiteral :
$0 . hidden = " $switchTag == false "Remarque: nous remplacerons la valeur de la ligne dont la balise est «Switchtag» au lieu de «$ switchtag»
Pour que tout cela fonctionne, toutes les lignes impliquées doivent avoir une balise car la balise les identifiera.
Nous pouvons également cacher une ligne en faisant:
$0 . hidden = true car Condition est conforme à l' ExpressibleByBooleanLiteral .
Le fait de ne pas régler la variable hidden laissera la ligne toujours visible.
Si vous définissez manuellement la condition cachée (ou désactivée) une fois le formulaire affiché, vous devrez peut-être appeler row.evaluateHidden() pour forcer Eureka à réévaluer la nouvelle condition. Voir cette section FAQ pour plus d'informations.
Pour les sections, cela fonctionne tout de même. Cela signifie que nous pouvons configurer la propriété hidden de la section pour l'afficher / le masquer dynamiquement.
Pour désactiver les lignes, chaque ligne a une variable disabled qui est également une propriété de type de Condition facultative. Cette variable fonctionne également de la même manière que la variable hidden afin qu'elle nécessite que les lignes aient une balise.
Notez que si vous souhaitez désactiver une ligne en permanence, vous pouvez également définir la variable disabled sur true .
Pour afficher une liste d'options, Eureka comprend une section spéciale appelée SelectableSection . Lors de la création de celui, vous devez passer le type de ligne à utiliser dans les options et le selectionType . Le selectionType est une énumération qui peut être soit multipleSelection ou singleSelection(enableDeselection: Bool) où le paramètre enableDeselection détermine si les lignes sélectionnées peuvent être désélectionnées ou non.
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
}
} Pour créer une telle section, vous devez créer une ligne conforme au protocole SelectableRowType .
public protocol SelectableRowType : RowType {
var selectableValue : Value ? { get set }
} Cette selectableValue est l'endroit où la valeur de la ligne sera stockée en permanence. La variable value sera utilisée pour déterminer si la ligne est sélectionnée ou non, étant «SelectableValue» si elle est sélectionnée ou nul autrement. Eureka comprend le ListCheckRow qui est utilisé par exemple. Dans les lignes personnalisées du projet Exemples, vous pouvez également trouver le ImageCheckRow .
Pour obtenir facilement la ligne / s sélectionnée d'une SelectableSection il existe deux méthodes: selectedRow() et selectedRows() qui peuvent être appelées pour obtenir la ligne sélectionnée au cas où il s'agit d'une section SingleSelection ou de toutes les lignes sélectionnées s'il s'agit d'une section MultipleSelection .
De plus, vous pouvez configurer la liste des options à regrouper par sections à l'aide de propriétés suivantes de SelectorViewController :
sectionKeyForValue - une fermeture qui devrait renvoyer la clé pour une valeur de ligne particulière. Cette clé est ensuite utilisée pour casser les options par sections.
sectionHeaderTitleForKey - Une fermeture qui renvoie le titre d'en-tête pour une section pour une clé particulière. Par défaut renvoie la clé elle-même.
sectionFooterTitleForKey - Une fermeture qui renvoie le titre de pied de page pour une section pour une clé particulière.
Eureka prend en charge plusieurs valeurs pour un certain champ (comme les numéros de téléphone en contact) en utilisant des sections multivales. Il nous permet de créer facilement des sections insérables, supprimées et réorganisables.

Afin de créer une section multivalerie, nous devons utiliser le type de MultivaluedSection au lieu du type Section ordinaire. MultivaluedSection étend Section et dispose de propriétés supplémentaires pour configurer le comportement de la section multivalerie.
Plongeons-nous dans un exemple de code ...
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 "
}
}Extrait de code précédent montre comment créer une section multivalerie. Dans ce cas, nous voulons insérer, supprimer et réorganiser les lignes comme l'indique l'argument multivaledOptions.
addButtonProvider nous permet de personnaliser la ligne de bouton qui insère une nouvelle ligne lorsque TAPPED et multivaluedOptions contient une valeur .Insert .
La propriété de fermeture multivaluedRowToInsertAt est appelée par eureka chaque fois qu'une nouvelle ligne doit être insérée. Afin de fournir la ligne à ajouter dans une section multivalerie, nous devons définir cette propriété. Eureka passe l'index en tant que paramètre de fermeture. Notez que nous pouvons retourner n'importe quel type de ligne, même les lignes personnalisées, même si dans la plupart des cas, les lignes de section multivalerie sont du même type.
Eureka ajoute automatiquement une ligne de bouton lorsque nous créons une section multivalerie insérable. Nous pouvons personnaliser à quoi ressemble la ligne de bouton comme nous l'avons expliqué auparavant. La propriété showInsertIconInAddButton indique si le bouton plus (style insérer) doit apparaître à gauche du bouton, vrai par défaut.
Il y a des considérations que nous devons avoir à l'esprit lors de la création de sections insérables. Toute ligne ajoutée à la section multivalerie insérable doit être placée au-dessus de la ligne qu'Eureka ajoute automatiquement à insérer de nouvelles lignes. Cela peut être facilement réalisé en ajoutant ces lignes supplémentaires à la section à l'intérieur de la fermeture de l'initialisateur de la section (dernier paramètre de l'initialiseur de la section), alors Eureka ajoute le bouton d'insertion d'ajout à la fin de la section.
Par défaut, Eureka définira l' isEditing de TableView sur true uniquement s'il y a une vente multivalerie dans le formulaire. Cela se fera en viewWillAppear de la première fois qu'un formulaire est présenté.
Pour plus d'informations sur la façon d'utiliser des sections multivales, veuillez jeter un œil au projet d'exemple Eureka qui contient plusieurs exemples d'utilisation.
Si vous souhaitez utiliser un bouton d'ajout qui n'est pas un ButtonRow , vous pouvez utiliser GenericMultivaluedSection<AddButtonType> , où AddButtonType est le type de la ligne que vous souhaitez utiliser comme bouton Ajouter. Ceci est utile si vous souhaitez utiliser une ligne personnalisée pour modifier l'interface utilisateur du bouton.
Exemple:
GenericMultivaluedSection < LabelRow > ( multivaluedOptions : [ . Reorder , . Insert , . Delete ] , {
$0 . addButtonProvider = { section in
return LabelRow ( ) {
$0 . title = " A Label row as add button "
}
}
// ...
}Eureka 2.0.0 présente la fonction de validations intégrées tant demandée.
Une ligne a une collection de Rules et une configuration spécifique qui détermine quand les règles de validation doivent être évaluées.
Il existe certaines règles fournies par défaut, mais vous pouvez également en créer de nouvelles.
Les règles fournies sont:
Voyons comment configurer les règles de validation.
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
}
} Comme vous pouvez le voir dans l'extrait de code précédent, nous pouvons configurer autant de règles que nous le souhaitons dans la ligne en invoquant la fonction add(rule:) .
Row fournit également func remove(ruleWithIdentifier identifier: String) pour supprimer une règle. Pour l'utiliser, nous devons attribuer un ID à la règle après l'avoir créé.
Parfois, la collection de règles que nous voulons utiliser sur une ligne est la même que nous voulons utiliser sur de nombreuses autres lignes. Dans ce cas, nous pouvons configurer toutes les règles de validation à l'aide d'un RuleSet qui est une collection de règles de validation.
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
} Eureka nous permet de spécifier lorsque les règles de validation doivent être évaluées. Nous pouvons le faire en configurant la propriété de validationOptions Row, qui peut avoir les valeurs suivantes:
.validatesOnChange - valide chaque fois qu'une valeur de ligne change..validatesOnBlur - (valeur par défaut) valide juste après la démission de la cellule. Sans applicable pour toutes les lignes..validatesOnChangeAfterBlurred - valide chaque fois que la valeur de la ligne change après sa démission du premier répondeur pour la première fois..validatesOnDemand - Nous devons valider manuellement la ligne ou la forme en invoquant la méthode validate() . Si vous souhaitez valider l'intégralité du formulaire (toutes les lignes), vous pouvez invoquer manuellement la méthode validate() .
Chaque ligne dispose de la propriété validationErrors qui peut être utilisée pour récupérer toutes les erreurs de validation. Cette propriété contient simplement la liste des erreurs de validation de la dernière exécution de validation de la ligne, ce qui signifie qu'elle n'évalue pas les règles de validation de la ligne.
Comme prévu, les règles doivent utiliser les mêmes types que l'objet Row. Soyez très prudent pour vérifier le type de ligne utilisé. Vous pourriez voir une erreur de compilateur ("Label arugment incorrect dans l'appel (Ayez` `Règle: 'Règlement attendu': ')" Cela ne pointe pas vers le problème lors du mélange de types.
En utilisant des actions de balayage, nous pouvons définir plusieurs actions leadingSwipe et trailingSwipe par ligne. Comme les actions de balayage dépendent des caractéristiques du système iOS, leadingSwipe sont disponibles sur iOS 11.0+ seulement.
Voyons comment définir des actions de balayage.
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
} Les actions de balayage ont besoin tableView.isEditing est défini sur false . Eureka définira ceci sur true s'il y a une vente multivalerie dans la forme (dans le viewWillAppear ). Si vous avez à la fois des valeurs multivales et des actions de balayage sous la même forme, vous devez définir isEditing en fonction de vos besoins.
Il est très courant que vous ayez besoin d'une ligne différente de celles incluses dans Eureka. Si tel est le cas, vous devrez créer votre propre ligne, mais cela ne devrait pas être difficile. Vous pouvez lire ce tutoriel sur la façon de créer des lignes personnalisées pour commencer. Vous voudrez peut-être également jeter un œil à Eurekacommunity qui comprend des lignes supplémentaires prêtes à être ajoutées à Eureka.
Pour créer une ligne avec un comportement et une apparence personnalisés, vous voudrez probablement créer des sous-classes de Row et Cell .
N'oubliez pas que Row est l'abstraction utilisée par Eureka, tandis que la Cell est la réalité UITableViewCell en charge de la vue. Comme la Row contient la Cell , la Row et Cell doivent être définies pour le même type de valeur .
// 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 " )
}
} Le résultat: 
Tout comme les rappels CellSetup et CellUpdate, la Cell a les méthodes de configuration et de mise à jour où vous pouvez le personnaliser.
Une ligne en ligne est un type de ligne spécifique qui affiche dynamiquement une ligne en dessous, normalement une ligne en ligne change entre un mode élargi et effondré chaque fois que la ligne est tapotée.
Donc, pour créer une ligne en ligne, nous avons besoin de 2 lignes, la ligne qui est "toujours" visible et la ligne qui se développera / s'effondrera.
Une autre exigence est que le type de valeur de ces 2 lignes doit être le même. Cela signifie que si une ligne contient une valeur String , l'autre doit également avoir une valeur String .
Une fois que nous avons ces 2 lignes, nous devons faire en sorte que le type de ligne supérieur soit conforme à InlineRowType . Ce protocole vous oblige à définir une fonction InlineRow et une fonction setupInlineRow . Le type InlineRow sera le type de la ligne qui se développera / s'effondrera. Prenez ceci comme exemple:
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 }
}
} L' InlineRowType ajoutera également quelques méthodes à votre ligne en ligne:
func expandInlineRow ( )
func collapseInlineRow ( )
func toggleInlineRow ( ) Ces méthodes devraient bien fonctionner, mais si vous souhaitez les remplacer, gardez à l'esprit que c'est toggleInlineRow qui doit appeler expandInlineRow et collapseInlineRow .
Enfin, vous devez invoquer toggleInlineRow() lorsque la ligne est sélectionnée, par exemple l'emportant sur customDidSelect :
public override func customDidSelect ( ) {
super . customDidSelect ( )
if !isDisabled {
toggleInlineRow ( )
}
}Remarque: Une ligne de présentateur est une ligne qui présente un nouveau UIViewController.
Pour créer une ligne de présentateur personnalisée, vous devez créer une classe conforme au protocole PresenterRowType . Il est fortement recommandé de sous-classer SelectorRow car il est conforme à ce protocole et ajoute d'autres fonctionnalités utiles.
Le protocole de TreeTerrowType est défini comme suit:
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 }
} Le surpresentCallback sera appelé lorsque la ligne sera sur le point de présenter un autre contrôleur de vue. Cela se fait dans le SelectorRow , donc si vous ne le sous-classez pas, vous devrez l'appeler vous-même.
Le presentationMode est ce qui définit la façon dont le contrôleur est présenté et quel contrôleur est présenté. Cette présentation peut être d'utiliser un identifiant de segue, une classe de segues, présentant un contrôleur modalement ou poussant vers un contrôleur de vue spécifique. Par exemple, un CustomPushrow peut être défini comme ceci:
Voyons un exemple.
/// 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 )
} )
}
}Parfois, nous voulons changer l'apparence de l'interface utilisateur de l'une de nos lignes, mais sans modifier le type de ligne et toute la logique associée à une ligne. Il existe actuellement une façon de le faire si vous utilisez des cellules qui sont instanciées à partir de fichiers NIB . Actuellement, aucune des principales lignes d'Eureka n'est instanciée à partir de fichiers NIB, mais certaines des lignes personnalisées d'Eurekacommunity sont en particulier le postaladdressrow qui y a été déplacé.
Ce que vous avez à faire est:
cellProvider pour utiliser cette plume. Vous devriez le faire dans l'initiateur, soit dans chaque instanciation en béton, soit en utilisant le defaultRowInitializer . Par exemple: <<< PostalAddressRow ( ) {
$0 . cellProvider = CellProvider < PostalAddressCell > ( nibName : " CustomNib " , bundle : Bundle . main )
}Vous pouvez également créer une nouvelle ligne pour cela. Dans ce cas, essayez de hériter de la même superclasse que la ligne que vous souhaitez changer pour hériter de sa logique.
Il y a certaines choses à considérer lorsque vous faites ceci:
Unknown class <YOUR_CLASS_NAME> in Interface Builder file , il se peut que vous deviez instancier ce nouveau type quelque part dans votre code pour le charger dans le temps d'exécution. Appel let t = YourClass.self a aidé dans mon cas. Rangée d'étiquette![]() | Ligne de bouton![]() | Coche ![]() |
Commutateur![]() | Rangée de curseur![]() | Ventre ![]() |
Rangée de la zone de texte ![]() |
Ces lignes ont un champ de texte sur le côté droit de la cellule. La différence entre chacun d'eux consiste en une configuration de capitalisation, de correction automatique et de type de clavier différent.
![]() | Textrow Namerow Urlrome Introduction Phonerow Mot de passe Décimalrow Twitterrow Comptrow Zipcoderow |
Tous les sous-types FieldRow ci-dessus ont une propriété formatter de type NSFormatter qui peut être définie pour déterminer comment la valeur de cette ligne doit être affichée. Un formateur personnalisé pour les nombres avec deux chiffres après la marque décimale est inclus avec Eureka ( DecimalFormatter ). L'exemple de projet contient également un CurrencyFormatter qui affiche un nombre sous forme de devise en fonction du lieu de l'utilisateur.
Par défaut, le réglage du formatter d'une ligne n'affecte que la façon dont une valeur est affichée lorsqu'elle n'est pas modifiée. Pour formater également la valeur pendant la modification de la ligne, définissez useFormatterDuringInput sur true lors de l'initialisation de la ligne. Le formatage de la valeur lors de sa modification peut nécessiter la mise à jour de la position du curseur et Eureka fournit le protocole suivant auquel votre formateur doit être conforme afin de gérer la position du curseur:
public protocol FormatterProtocol {
func getNewPosition ( forPosition forPosition : UITextPosition , inTextInput textInput : UITextInput , oldValue : String ? , newValue : String ? ) -> UITextPosition
} De plus, les sous-types FieldRow ont une propriété useFormatterOnDidBeginEditing . Lorsque vous utilisez un DecimalRow avec un formateur qui permet des valeurs décimales et se conforme aux paramètres locaux de l'utilisateur (par exemple DecimalFormatter ), si useFormatterDuringInput est false , useFormatterOnDidBeginEditing doit être défini sur true afin que la marque décimale de la valeur en cours d'édition correspond à la marque décimale du clavier.
Les lignes de date maintiennent une date et nous permettent de configurer une nouvelle valeur via le contrôle UIDIDEPicker. Le mode du UidatePicker et la façon dont la vue de sélecteur de dattes est indiquée est ce qui change entre eux.
Rigueur ![]() Picker montré dans le clavier. | Date Row (en ligne) ![]() La ligne se développe. | Date Row (Sicer) ![]() Le cueilleur est toujours visible. |
Avec ces 3 styles (normal, en ligne et cueilleur), Eureka comprend:
Ce sont des lignes avec une liste d'options associées à partir de laquelle l'utilisateur doit choisir.
<<< ActionSheetRow < String > ( ) {
$0 . title = " ActionSheetRow "
$0 . selectorTitle = " Pick a number "
$0 . options = [ " One " , " Two " , " Three " ]
$0 . value = " Two " // initially selected
} Ligne d'alerte![]() Affichera une alerte avec les options à choisir. | Ligne de feuille d'action![]() Affichera une feuille d'action avec les options à choisir. | Rangée ponctuelle![]() Poussera vers un nouveau contrôleur à partir de l'endroit où choisir les options répertoriées à l'aide de lignes de vérification. | Ligne de sélecteur multiple![]() Comme Pushrow mais permet la sélection de plusieurs options. |
Ligne segmentée![]() | Ligne segmentée (avec titre)![]() | Rangée de cueillette![]() Présente les options d'un type générique via une vue de sélecteur (Il y a aussi une ligne en ligne de sélecteur) |
Faites-nous savoir, nous serions heureux de le mentionner ici. :)

Cocoapods est un gestionnaire de dépendances pour les projets de cacao.
Spécifiez Eureka dans Podfile de votre projet:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios , '9.0'
use_frameworks!
pod 'Eureka'Puis exécutez la commande suivante:
$ pod installSwift Package Manager est un outil pour gérer la distribution du code SWIFT.
Après avoir configuré votre fichier Package.swift manifeste, vous pouvez ajouter Eureka comme dépendance en l'ajoutant à la valeur des dépendances de votre Package.swift .
Dépendances: [.package (URL: "https://github.com/xmartlabs/eureka.git", de: "5.5.0")]
Carthage est un simple gestionnaire de dépendances décentralisé pour le cacao.
Spécifiez Eureka dans Cartfile de votre projet:
github "xmartlabs/Eureka" ~> 5.5
$ git submodule add https://github.com/xmartlabs/Eureka.gitOuvrez le dossier Eureka créé par la commande GIT Submodule précédente et faites glisser le eureka.xcodeproj dans le navigateur de projet du projet Xcode de votre application.
Sélectionnez le eureka.xcodeproj dans le Navigator du projet et vérifiez la cible de déploiement correspond à votre objectif de déploiement d'application.
Sélectionnez votre projet dans la navigation Xcode, puis sélectionnez votre cible d'application dans la barre latérale. Sélectionnez ensuite l'onglet "Général" et cliquez sur le bouton + dans la section "Binaires intégrés".
Sélectionnez Eureka.framework et nous avons terminé!
eureka-forms ).Avant de contribuer, vérifiez le fichier contributif pour plus d'informations.
Si vous utilisez Eureka dans votre application, nous aimerions en entendre parler! Envoyez-nous une ligne sur Twitter.
Chaque ligne a la propriété suivante:
/// 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 ) }
} Vous pouvez définir displayValueFor en fonction de la valeur de chaîne que vous souhaitez afficher.
Nous pouvons obtenir une ligne particulière en invoquant l'une des fonctions suivantes exposées par la classe Form :
public func rowBy < T : Equatable > ( tag : String ) -> RowOf < T > ?
public func rowBy < Row : RowType > ( tag : String ) -> Row ?
public func rowBy ( tag : String ) -> BaseRow ?Par exemple:
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 " ) Invoquer setValues(values: [String: Any?]) Qui est exposé par la classe Form .
Par exemple:
form . setValues ( [ " IntRowTag " : 8 , " TextRowTag " : " Hello world! " , " PushRowTag " : Company ( name : " Xmartlabs " ) ] ) Où "IntRowTag" , "TextRowTag" , "PushRowTag" sont des étiquettes de ligne (chacune identifie uniquement une ligne) et 8 , "Hello world!" , Company(name:"Xmartlabs") sont la valeur de ligne correspondante à attribuer.
Le type de valeur d'une ligne doit correspondre au type de valeur de la valeur du dictionnaire correspondant, sinon NIL sera attribué.
Si le formulaire était déjà affiché, nous devons recharger les lignes visibles soit en rechargeant la vue de table tableView.reloadData() ou en invoquant updateCell() sur chaque ligne visible.
Après avoir réglé une condition, cette condition n'est pas automatiquement évaluée. Si vous voulez que cela le fasse immédiatement, vous pouvez appeler .evaluateHidden() ou .evaluateDisabled() .
Ces fonctions sont simplement appelées lorsqu'une ligne est ajoutée au formulaire et lorsqu'il dépend des modifications. Si la condition est modifiée lorsque la ligne est affichée, elle doit être réévaluée manuellement.
Regardez ce problème.
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 , selectableRowCellUpdate et selectableRowCellSetup sont fournis pour pouvoir personnaliser SelectorViewController et MultipleSelectorViewController des cellules sélectionnables.
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!
}
} Comme nous l'avons dit, les types Form et Section sont conformes à MutableCollection et RangeReplaceableCollection . Un formulaire est une collection de sections et une section est une collection de lignes.
L'extension du protocole RangeReplaceableCollection fournit de nombreuses méthodes utiles pour modifier la collecte.
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 )
}Ces méthodes sont utilisées en interne pour implémenter les opérateurs personnalisés comme indiqué ci-dessous:
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 )
}Vous pouvez voir comment les autres opérateurs personnalisés sont mis en œuvre ici.
C'est à vous de décider si vous souhaitez utiliser ou non les opérateurs personnalisés Eureka.
Le formulaire est toujours affiché dans un UITableView . Vous pouvez configurer votre contrôleur de vue dans un storyboard et ajouter un UableTView où vous voulez qu'il soit, puis connecter la prise à la variable tableView de FormViewController. Cela vous permet de définir un cadre personnalisé (éventuellement avec des contraintes) pour votre formulaire.
Tout cela peut également être fait en modifiant programmaticalement un cadre, des marges, etc. de la vue de la tableView de votre formviewController.
Nous pouvons donc rendre Eureka encore mieux! 
Cela peut être trouvé dans le fichier Changelog.md.