
Hecho con ❤️ por XMartLabs. Esta es la recreación de XLFORM en Swift.
简体中文
![]() | ![]() | ![]() |
|---|
Para obtener más información, mire nuestra publicación de blog que presenta a Eureka .
Puede clonar y ejecutar el proyecto de ejemplo para ver ejemplos de la mayoría de las características de Eureka.
![]() | ![]() |
|---|
Al extender FormViewController , simplemente puede agregar secciones y filas a 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 )
}
}
}En el ejemplo creamos dos secciones con filas estándar, el resultado es este:

Puede crear un formulario simplemente configurando la propiedad form usted mismo sin extender desde FormViewController , pero este método suele ser más conveniente.
Para cambiar el comportamiento de esto, debe establecer las opciones de navegación de su controlador. FormViewController tiene una variable navigationOptions .
canBecomeFirstResponder() El valor predeterminado está enabled & skipCanNotBecomeFirstResponderRow
Para habilitar el desplazamiento suave a las filas fuera de la pantalla, habilitarlo a través de la propiedad animateScroll . De manera predeterminada, el FormViewController salta inmediatamente entre filas cuando el usuario golpea los botones siguientes o anteriores en la accesoria de navegación del teclado, incluso cuando la siguiente fila está fuera de la pantalla.
Para establecer la cantidad de espacio entre el teclado y la fila resaltada después de un evento de navegación, configure la propiedad rowKeyboardSpacing . Por defecto, cuando el formulario se desplaza a una vista fuera de pantalla, no quedará espacio entre la parte superior del teclado y la parte inferior de la fila.
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 desea cambiar toda la vista de accesorios de navegación, deberá anular la variable navigationAccessoryView en su subclase de FormViewController .
El objeto Row contiene un valor de un tipo específico. Por ejemplo, un SwitchRow contiene un valor Bool , mientras que un TextRow contiene un valor 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 incluye operadores personalizados para facilitar la creación de formularios:
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 incluye a los constructores de resultados para facilitar la creación de forma:
// 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 incluye devoluciones de llamada para cambiar la apariencia y el comportamiento de una fila.
Una Row es una abstracción que usa Eureka que contiene un valor y contiene la Cell de vista. La Cell administra la vista y subclase UITableViewCell .
Aquí hay un ejemplo:
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 ()
Llamado cuando cambia el valor de una fila. Es posible que esté interesado en ajustar algunos parámetros aquí o incluso hacer que algunas otras filas aparezcan o desaparezcan.
OnCellSelection ()
Llamado cada vez que el usuario aprovecha la fila y se selecciona. Tenga en cuenta que esto también se solicitará para filas deshabilitadas, por lo que debe iniciar su código dentro de esta devolución de llamada con algo como guard !row.isDisabled else { return }
CellSetup ()
Llamado solo una vez cuando la celda se configura por primera vez. Establezca configuraciones permanentes aquí.
CellUpdate ()
Llamado cada vez que la celda aparece en la pantalla. Puede cambiar la apariencia aquí usando variables que pueden no estar presentes en CellSetup ().
OncellhighLightChanged ()
Llamado cuando la célula o cualquier subvisión se convierte o renuncia al primer respondedor.
onRowValidationChanged ()
Llamado cada vez que los errores de validación asociados con una fila cambian.
Onexpandinlinerow ()
Llamado antes de expandir la fila en línea. Se aplica al protocolo InlineRowType que conforman.
OnCollapseInlinerow ()
Llamado antes de colapsar la fila en línea. Se aplica al protocolo InlineRowType que conforman.
onPresent ()
Llamado por una fila justo antes de presentar otro controlador de vista. Se aplica a las filas que conforman el protocolo PresenterRowType . Úselo para configurar el controlador presentado.
Puede establecer una String de título o una View personalizada como encabezado o pie de página de una Section .
Section ( " Title " )
Section ( header : " Title " , footer : " Footer Title " )
Section ( footer : " Footer Title " ) Puede usar una vista personalizada desde un archivo .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
} O un UIView personalizado creado programáticamente
Section ( ) { section in
var header = HeaderFooterView < MyCustomUIView > ( . class )
header . height = { 100 }
header . onSetupView = { view , _ in
view . backgroundColor = . red
}
section . header = header
}O simplemente construya la vista con una devolución de llamada
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
} ( )
}
En este caso estamos escondiendo y mostrando secciones completas.
Para lograr esto, cada fila tiene una variable hidden de Condition de tipo opcional que se puede establecer utilizando una función o NSPredicate .
Usando el caso de function de Condition :
Condition . function ( [ String ] , ( Form ) - > Bool ) La matriz de String para pasar debe contener las etiquetas de las filas de las que depende de esta fila. Cada vez que cambia el valor de cualquiera de esas filas, se reevalúa la función. La función luego toma el Form y devuelve un Bool que indica si la fila debe estar oculta o no. Esta es la forma más poderosa de establecer la propiedad hidden , ya que no tiene limitaciones explícitas de lo que se puede hacer.
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 también se puede establecer con un NSPredicate. En la cadena de predicado puede hacer referencia a los valores de otras filas por sus etiquetas para determinar si una fila debe estar oculta o visible. Esto solo funcionará si los valores de las filas que el predicado tiene que verificar son NSObjects (String e INT funcionarán a medida que se unan a sus contrapartes OBJC, pero los enums no funcionarán). ¿Por qué podría ser útil usar predicados cuando son más limitados? Bueno, pueden ser mucho más simples, más cortos y legibles que las funciones. Mira este ejemplo:
$0 . hidden = Condition . predicate ( NSPredicate ( format : " $switchTag == false " ) ) Y podemos escribirlo aún más corto, ya que Condition se ajusta a ExpressibleByStringLiteral :
$0 . hidden = " $switchTag == false "Nota: Sustituiremos el valor de la fila cuya etiqueta es 'switchtag' en lugar de '$ switchtag'
Para que todo esto funcione, todas las filas implicadas deben tener una etiqueta ya que la etiqueta las identificará.
También podemos ocultar una fila haciendo:
$0 . hidden = true como Condition se ajusta al ExpressibleByBooleanLiteral .
No establecer la variable hidden dejará la fila siempre visible.
Si establece manualmente la condición oculta (o deshabilitada) después de que se haya mostrado el formulario, es posible que deba llamar row.evaluateHidden() . Vea esta sección de preguntas frecuentes para obtener más información.
Para las secciones esto funciona de la misma manera. Eso significa que podemos configurar la propiedad hidden de la sección para mostrarla/ocultarla dinámicamente.
Para deshabilitar las filas, cada fila tiene una variable disabled que también es una propiedad de tipo de Condition opcional. Esta variable también funciona igual que la variable hidden para que requiera que las filas tengan una etiqueta.
Tenga en cuenta que si desea deshabilitar una fila de forma permanente, también puede establecer la variable disabled en true .
Para mostrar una lista de opciones, Eureka incluye una sección especial llamada SelectableSection . Al crear uno, debe pasar el tipo de fila para usar en las opciones y selectionType . El selectionType es un enum que puede ser multipleSelection o singleSelection(enableDeselection: Bool) donde el parámetro enableDeselection determina si las filas seleccionadas se pueden soltar o no.
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
}
} Para crear dicha sección, debe crear una fila que conforma el protocolo SelectableRowType .
public protocol SelectableRowType : RowType {
var selectableValue : Value ? { get set }
} Este selectableValue es donde el valor de la fila se almacenará permanentemente. La variable value se utilizará para determinar si la fila está seleccionada o no, siendo 'selectableValue' si se selecciona o nil de otra manera. Eureka incluye el ListCheckRow que se usa, por ejemplo. En las filas personalizadas del proyecto Ejemplos también puede encontrar el ImageCheckRow .
Para obtener fácilmente las filas seleccionadas de una SelectableSection hay dos métodos: selectedRow() y selectedRows() que se puede llamar para obtener la fila seleccionada en caso de que sea una sección SingleSelection o todas las filas seleccionadas si es una sección MultipleSelection .
Además, puede configurar la lista de opciones que se agruparán mediante secciones utilizando las siguientes propiedades de SelectorViewController :
sectionKeyForValue : un cierre que debería devolver la clave para un valor de fila particular. Esta clave se usa más tarde para romper las opciones por secciones.
sectionHeaderTitleForKey : un cierre que devuelve el título del encabezado para una sección para una clave particular. De forma predeterminada devuelve la clave en sí.
sectionFooterTitleForKey : un cierre que devuelve el título del pie de página para una sección para una clave particular.
Eureka admite múltiples valores para un cierto campo (como los números de teléfono en un contacto) mediante el uso de secciones multivaloras. Nos permite crear fácilmente secciones insertables, eliminables y reordenables.

Para crear una sección multivalida, tenemos que usar el tipo de sección MultivaluedSection en lugar del tipo Section regular. MultivaluedSection extiende Section y tiene algunas propiedades adicionales para configurar el comportamiento de la sección multivalización.
Vamos a sumergirnos en un ejemplo de código ...
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 "
}
}El fragmento de código anterior muestra cómo crear una sección multivalida. En este caso, queremos insertar, eliminar y reordenar las filas como lo indica el argumento multivaluedoptions.
addButtonProvider nos permite personalizar la fila de botones que inserta una nueva fila cuando se toca y contiene multivaluedOptions .Insert
Eureka llama a la propiedad de cierre multivaluedRowToInsertAt cada vez que se necesita insertar una nueva fila. Para proporcionar la fila para agregar a la sección multivalor, debemos establecer esta propiedad. Eureka pasa el índice como parámetro de cierre. Tenga en cuenta que podemos devolver cualquier tipo de fila, incluso filas personalizadas, aunque en la mayoría de los casos las filas de sección multivalor sean del mismo tipo.
Eureka agrega automáticamente una fila de botones cuando creamos una sección multivaluda insertable. Podemos personalizar cómo se ve esta fila de botón como explicamos antes. La propiedad showInsertIconInAddButton indica si el botón Plus (estilo de inserción) debe aparecer a la izquierda del botón, verdadero de forma predeterminada.
Hay algunas consideraciones que debemos tener en mente al crear secciones insertables. Cualquier fila agregada a la sección multivalor insertable debe colocarse sobre la fila que Eureka agrega automáticamente para insertar nuevas filas. Esto se puede lograr fácilmente agregando estas filas adicionales a la sección desde el interior del cierre del inicializador de la sección (último parámetro del inicializador de la sección), por lo que Eureka agrega el botón ADRIS al final de la sección.
Por defecto, Eureka establecerá el isEditing de TableView en verdadero solo si hay una Sección MultivalUede en el formulario. Esto se hará en viewWillAppear la primera vez que se presenta un formulario.
Para obtener más información sobre cómo usar secciones multivalorales, eche un vistazo al proyecto de ejemplo Eureka que contiene varios ejemplos de uso.
Si desea usar un botón ADD que no es un ButtonRow , puede usar GenericMultivaluedSection<AddButtonType> , donde AddButtonType es el tipo de fila que desea usar como botón Agregar. Esto es útil si desea usar una fila personalizada para cambiar la interfaz de usuario del botón.
Ejemplo:
GenericMultivaluedSection < LabelRow > ( multivaluedOptions : [ . Reorder , . Insert , . Delete ] , {
$0 . addButtonProvider = { section in
return LabelRow ( ) {
$0 . title = " A Label row as add button "
}
}
// ...
}Eureka 2.0.0 presenta la función de validaciones incorporadas muy solicitada.
Una fila tiene una colección de Rules y una configuración específica que determina cuándo deben evaluarse las reglas de validación.
Hay algunas reglas proporcionadas por defecto, pero también puede crear otras nuevas por su cuenta.
Las reglas proporcionadas son:
Veamos cómo configurar las reglas de validación.
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
}
} Como puede ver en el fragmento de código anterior, podemos configurar tantas reglas como queramos en una fila invocando la función add(rule:)
La fila también proporciona func remove(ruleWithIdentifier identifier: String) para eliminar una regla. Para usarlo, debemos asignar una ID a la regla después de crearla.
A veces, la colección de reglas que queremos usar en una fila es la misma que queremos usar en muchas otras filas. En este caso, podemos configurar todas las reglas de validación utilizando un RuleSet que es una colección de reglas de validación.
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 nos permite especificar cuándo deben evaluarse las reglas de validación. Podemos hacerlo configurando la propiedad de validationOptions Row, que puede tener los siguientes valores:
.validatesOnChange : valida cada vez que cambia un valor de fila..validatesOnBlur - (valor predeterminado) se valida justo después de que la celda renuncia al primer respondedor. No aplicable para todas las filas..validatesOnChangeAfterBlurred : valida cada vez que el valor de la fila cambia después de que renuncia al primer respondedor por primera vez..validatesOnDemand : debemos validar manualmente la fila o forma invocando el método validate() . Si desea validar todo el formulario (todas las filas), puede invocar manualmente el método Form validate() .
Cada fila tiene la propiedad validationErrors que se puede utilizar para recuperar todos los errores de validación. Esta propiedad solo contiene la lista de errores de validación de la última ejecución de validación de la fila, lo que significa que no evalúa las reglas de validación de la fila.
Como se esperaba, las reglas deben usar los mismos tipos que el objeto de fila. Tenga mucho cuidado al verificar el tipo de fila utilizado. Es posible que vea un error del compilador ("Etiqueta de combinación incorrecta en la llamada (tener 'regla:' Reglas esperadas ':')" que no apunta al problema al mezclar tipos.
Al usar acciones de deslizamiento, podemos definir múltiples acciones leadingSwipe y trailingSwipe por fila. Como las acciones de deslizamiento dependen de las características del sistema iOS, leadingSwipe está disponible solo en iOS 11.0+.
Veamos cómo definir acciones de deslizamiento.
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
} Las acciones de deslizamiento necesitan tableView.isEditing se establecerán en false . Eureka establecerá esto en true si hay una Sección Multivaluede en la forma (en el viewWillAppear ). Si tiene sesiones multivalorales y acciones de deslizamiento en la misma forma, debe establecer isEditing de acuerdo con sus necesidades.
Es muy común que necesite una fila que sea diferente de las incluidas en Eureka. Si este es el caso, tendrá que crear su propia fila, pero esto no debería ser difícil. Puede leer este tutorial sobre cómo crear filas personalizadas para comenzar. Es posible que también desee echar un vistazo a Eurekacommunity que incluye algunas filas adicionales listas para agregar a Eureka.
Para crear una fila con comportamiento y apariencia personalizados, probablemente desee crear subclases de Row y Cell .
Recuerde que la Row es la abstracción que usa Eureka, mientras que la Cell es el UITableViewCell real a cargo de la vista. Como la Row contiene la Cell , tanto Row como Cell deben definirse para el mismo tipo de valor .
// 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 " )
}
} El resultado: 
Al igual que las devoluciones de llamada CellSetup y CellUpdate, la Cell tiene los métodos de configuración y actualización donde puede personalizarlo.
Una fila en línea es un tipo específico de fila que muestra dinámicamente una fila debajo de ella, normalmente una fila en línea cambia entre un modo expandido y colapsado cada vez que se aprovecha la fila.
Entonces, para crear una fila en línea, necesitamos 2 filas, la fila que es "siempre" visible y la fila que se expandirá/colapsará.
Otro requisito es que el tipo de valor de estas 2 filas debe ser el mismo. Esto significa que si una fila contiene un valor String , entonces el otro también debe tener un valor String .
Una vez que tengamos estas 2 filas, debemos hacer que el tipo de fila superior se ajuste a InlineRowType . Este protocolo requiere que defina un typealias InlineRow y una función setupInlineRow . El tipo InlineRow será el tipo de fila que se expandirá/colapsará. Tome esto como ejemplo:
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 }
}
} InlineRowType también agregará algunos métodos a su fila en línea:
func expandInlineRow ( )
func collapseInlineRow ( )
func toggleInlineRow ( ) Estos métodos deberían funcionar bien, pero si desea anularlos, tenga en cuenta que es toggleInlineRow el que tiene que llamar expandInlineRow y collapseInlineRow .
Finalmente debe invocar toggleInlineRow() cuando se selecciona la fila, por ejemplo, anular customDidSelect :
public override func customDidSelect ( ) {
super . customDidSelect ( )
if !isDisabled {
toggleInlineRow ( )
}
}Nota: Una fila del presentador es una fila que presenta un nuevo UIViewController.
Para crear una fila de presentador personalizada, debe crear una clase que conforma el protocolo PresenterRowType . Se recomienda altamente subclase SelectorRow , ya que se ajusta a ese protocolo y agrega otra funcionalidad útil.
El protocolo de presentewrowtype se define de la siguiente manera:
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 }
} El OnPresentCallback se llamará cuando la fila está a punto de presentar otro controlador de vista. Esto se hace en SelectorRow por lo que si no lo subclase, tendrá que llamarlo usted mismo.
El presentationMode es lo que define cómo se presenta el controlador y qué controlador se presenta. Esta presentación puede estar utilizando un identificador de Segue, una clase Segue, que presenta un controlador modalmente o empuje a un controlador de vista específico. Por ejemplo, un CustomPushrow se puede definir así:
Veamos un ejemplo ..
/// 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 )
} )
}
}A veces queremos cambiar el aspecto de la interfaz de usuario de una de nuestras filas, pero sin cambiar el tipo de fila y toda la lógica asociada a una fila. Actualmente hay una forma de hacer esto si está utilizando celdas que se instancian desde los archivos NIB . Actualmente, ninguna de las filas centrales de Eureka se instancia desde los archivos NIB, pero algunas de las filas personalizadas en Eurekacommunity son, en particular, el Postaladdressrow que se movió allí.
Lo que tienes que hacer es:
cellProvider para usar esta punta. Debe hacer esto en el Inicialador, ya sea en cada instanciación concreta o usando el defaultRowInitializer . Por ejemplo: <<< PostalAddressRow ( ) {
$0 . cellProvider = CellProvider < PostalAddressCell > ( nibName : " CustomNib " , bundle : Bundle . main )
}También puede crear una nueva fila para esto. En ese caso, trate de heredar de la misma superclase que la fila que desea cambiar para heredar su lógica.
Hay algunas cosas a considerar cuando haces esto:
Unknown class <YOUR_CLASS_NAME> in Interface Builder file , podría ser que tenga que instanciar ese nuevo tipo en algún lugar de su código para cargarlo en el tiempo de ejecución. Llamando let t = YourClass.self Ayudó en mi caso. Fila de etiqueta![]() | Fila de botones![]() | Verifique la fila ![]() |
Fila de interrupción![]() | Slider Row![]() | Fila de paso a paso ![]() |
Fila de área de texto ![]() |
Estas filas tienen un campo de texto en el lado derecho de la celda. La diferencia entre cada uno de ellos consiste en una configuración de capitalización, autocorrección y tipo de teclado diferente.
![]() | Textrow Namerow Urlrow Introducir Fonerow Contraseña Correo electrónico Decimalrow TwitterRow Contactar Zipcoderow |
Todos los subtipos FieldRow anteriores tienen una propiedad formatter de tipo NSFormatter que se puede configurar para determinar cómo se debe mostrar el valor de esa fila. Se incluye un formateador personalizado para números con dos dígitos después de la marca decimal con Eureka ( DecimalFormatter ). El proyecto de ejemplo también contiene una CurrencyFormatter que muestra un número como moneda de acuerdo con la localidad del usuario.
Por defecto, establecer formatter de una fila solo afecta cómo se muestra un valor cuando no se edita. Para formatear también el valor mientras se edita la fila, establezca useFormatterDuringInput en true al inicializar la fila. El formateo del valor tal como se está editando puede requerir actualizar la posición del cursor y Eureka proporciona el siguiente protocolo al que debe cumplir su formato para manejar la posición del cursor:
public protocol FormatterProtocol {
func getNewPosition ( forPosition forPosition : UITextPosition , inTextInput textInput : UITextInput , oldValue : String ? , newValue : String ? ) -> UITextPosition
} Además, los subtipos FieldRow tienen una propiedad useFormatterOnDidBeginEditing . Cuando se utiliza un DecimalRow con un formato que permite valores decimales y se ajusta a la configuración regional del usuario (por ejemplo, DecimalFormatter ), si useFormatterDuringInput es false , useFormatterOnDidBeginEditing debe establecerse en true para que la marca decimal en el valor que se edita se coincida con la marca decimal en la teclado.
Las filas de fecha contienen una fecha y nos permiten configurar un nuevo valor a través del control UidatePicker. El modo del UCidPicker y la forma en que se muestra la vista de selección de fecha es qué cambia entre ellos.
Fila de fecha ![]() Seleccionador que se muestra en el teclado. | Fila de fecha (en línea) ![]() La fila se expande. | Fecha de cita (seleccionador) ![]() El selector siempre es visible. |
Con esos 3 estilos (Normal, Inline y Picker), Eureka incluye:
Estas son filas con una lista de opciones asociadas desde las cuales el usuario debe elegir.
<<< ActionSheetRow < String > ( ) {
$0 . title = " ActionSheetRow "
$0 . selectorTitle = " Pick a number "
$0 . options = [ " One " , " Two " , " Three " ]
$0 . value = " Two " // initially selected
} Fila de alerta![]() Mostrará una alerta con las opciones para elegir. | Fila de hoja de acción![]() Mostrará una hoja de acción con las opciones para elegir. | Fila de empuje![]() Presionará a un nuevo controlador de dónde elegir las opciones enumeradas con las filas de verificación. | Fila selectora múltiple![]() Como PushRow pero permite la selección de múltiples opciones. |
Fila segmentada![]() | Fila segmentada (con título)![]() | Fila de selección![]() Presenta opciones de un tipo genérico a través de una vista de selección (También hay fila en línea de Picker) |
Háganos saberlo, nos alegraría mencionarlo aquí. :)

Cocoapods es un gerente de dependencia para proyectos de cacao.
Especifique eureka en Podfile de su proyecto:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios , '9.0'
use_frameworks!
pod 'Eureka'Luego ejecute el siguiente comando:
$ pod installSwift Package Manager es una herramienta para administrar la distribución del código Swift.
Después de configurar su archivo Manifest Package.swift , puede agregar eureka como dependencia agregándolo al valor de dependencias de su Package.swift .
Dependencias: [.Package (URL: "https://github.com/xmartlabs/eureka.git", de: "5.5.0")]
Cartago es un gerente de dependencia simple y descentralizado para el cacao.
Especifique Eureka en Cartfile de su proyecto:
github "xmartlabs/Eureka" ~> 5.5
$ git submodule add https://github.com/xmartlabs/Eureka.gitAbra la carpeta Eureka creada por el comando Git Submodule anterior y arrastre el eureka.xcodeproj al proyecto del proyecto del proyecto XCode de su aplicación.
Seleccione el eureka.xCodeproj en el navegador del proyecto y verifique las coincidencias del objetivo de implementación con el objetivo de implementación de su aplicación.
Seleccione su proyecto en la navegación Xcode y luego seleccione su objetivo de aplicación desde la barra lateral. A continuación, seleccione la pestaña "General" y haga clic en el botón + en la sección "Binarios incrustados".
¡Seleccione Eureka.framework y terminamos!
eureka-forms ).Antes de contribuir, consulte el archivo contribuyente para obtener más información.
¡Si usa Eureka en su aplicación, nos encantaría saber al respecto! Llévanos una línea en Twitter.
Cada fila tiene la siguiente propiedad:
/// 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 ) }
} Puede establecer displayValueFor según el valor de la cadena que desea mostrar.
Podemos obtener una fila en particular invocando cualquiera de las siguientes funciones expuestas por la clase Form :
public func rowBy < T : Equatable > ( tag : String ) -> RowOf < T > ?
public func rowBy < Row : RowType > ( tag : String ) -> Row ?
public func rowBy ( tag : String ) -> BaseRow ?Por ejemplo:
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 " ) Invocar setValues(values: [String: Any?]) Que está expuesto por la clase Form .
Por ejemplo:
form . setValues ( [ " IntRowTag " : 8 , " TextRowTag " : " Hello world! " , " PushRowTag " : Company ( name : " Xmartlabs " ) ] ) Donde "IntRowTag" , "TextRowTag" , "PushRowTag" son etiquetas de fila (cada una identifica de manera única una fila) y 8 , "Hello world!" , Company(name:"Xmartlabs") son el valor de fila correspondiente para asignar.
El tipo de valor de una fila debe coincidir con el tipo de valor del valor del diccionario correspondiente, de lo contrario se asignará nil.
Si el formulario ya se mostró, tenemos que recargar las filas visibles, ya sea recargando la vista de tabla tableView.reloadData() o invocando updateCell() a cada fila visible.
Después de establecer una condición, esta condición no se evalúa automáticamente. Si desea que lo haga inmediatamente, puede llamar .evaluateHidden() o .evaluateDisabled() .
Estas funciones se llaman cuando se agrega una fila al formulario y cuando una fila depende de los cambios. Si la condición se cambia cuando se muestra la fila, debe reevaluarse manualmente.
Mira este problema.
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 ( ) Se proporcionan selectableRowSetup , las propiedades selectableRowCellUpdate y selectableRowCellSetup para poder personalizar las celdas selectables SelectorViewController y MultipleSectorViewController.
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!
}
} Como hemos dicho, los tipos Form y Section se ajustan a MutableCollection y RangeReplaceableCollection . Un formulario es una colección de secciones y una sección es una colección de filas.
La extensión del protocolo RangeReplaceableCollection proporciona muchos métodos útiles para modificar la recolección.
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 )
}Estos métodos se utilizan internamente para implementar los operadores personalizados como se muestra a continuación:
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 )
}Puede ver cómo se implementan el resto de los operadores personalizados aquí.
Depende de usted decidir si desea usar operadores personalizados de Eureka o no.
El formulario siempre se muestra en UITableView . Puede configurar su controlador View en un guión gráfico y agregar una vista de uable donde desea que esté y luego conectar la salida a la variable tableView de FormViewController. Esto le permite definir un marco personalizado (posiblemente con restricciones) para su formulario.
Todo esto también se puede hacer cambiando programáticamente el marco, los márgenes, etc. de la tableView de su formviewController.
¡Entonces podemos hacer que Eureka sea aún mejor! 
Esto se puede encontrar en el archivo ChangeLog.md.