
Feito com ❤️ by XMartLabs. Esta é a recriação do Xlform no Swift.
简体中文
![]() | ![]() | ![]() |
|---|
Para obter mais informações, observe a nossa postagem no blog, que apresenta Eureka .
Você pode clonar e executar o projeto de exemplo para ver exemplos da maioria dos recursos de Eureka.
![]() | ![]() |
|---|
Ao estender FormViewController , você pode simplesmente adicionar seções e linhas à variável 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 )
}
}
}No exemplo, criamos duas seções com linhas padrão, o resultado é o seguinte:

Você pode criar um formulário apenas configurando a propriedade form sem se estender do FormViewController , mas esse método é normalmente mais conveniente.
Para alterar o comportamento disso, você deve definir as opções de navegação do seu controlador. O FormViewController possui uma variável navigationOptions que é uma enumeração e pode ter um ou mais dos seguintes valores:
canBecomeFirstResponder() O valor padrão está enabled & skipCanNotBecomeFirstResponderRow
Para ativar a rolagem suave até as linhas fora da tela, habilite-a através da propriedade animateScroll . Por padrão, o FormViewController salta imediatamente entre as linhas quando o usuário atinge os botões próximos ou anteriores no acesso à navegação do teclado, inclusive quando a próxima linha estiver fora da tela.
Para definir a quantidade de espaço entre o teclado e a linha destacada após um evento de navegação, defina a propriedade rowKeyboardSpacing . Por padrão, quando o formulário rola para uma exibição fora da tela, nenhum espaço será deixado entre a parte superior do teclado e a parte inferior da linha.
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
}
} Se você deseja alterar toda a visualização de acessórios de navegação, precisará substituir a variável navigationAccessoryView na sua subclasse do FormViewController .
O objeto Row contém um valor de um tipo específico. Por exemplo, um SwitchRow contém um valor Bool , enquanto um TextRow possui um 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 inclui operadores personalizados para facilitar a criação da forma:
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 inclui os construtores de resultados para facilitar a criação da 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 inclui retornos de chamada para alterar a aparência e o comportamento de uma linha.
Uma Row é uma abstração que Eureka usa que contém um valor e contém a Cell de exibição. A Cell gerencia a visão e as subclasse UITableViewCell .
Aqui está um exemplo:
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 ()
Chamado quando o valor de uma linha muda. Você pode estar interessado em ajustar alguns parâmetros aqui ou até fazer com que outras linhas apareçam ou desapareçam.
ONCELLSELECTION ()
Chamado cada vez que o usuário toca na linha e é selecionado. Observe que isso também será chamado para linhas desativadas, então você deve iniciar seu código dentro deste retorno de chamada com algo como guard !row.isDisabled else { return }
CellSetup ()
Chamado apenas uma vez quando a célula é configurada pela primeira vez. Defina configurações permanentes aqui.
CellUpDate ()
Chamado cada vez que a célula aparece na tela. Você pode alterar a aparência aqui usando variáveis que podem não estar presentes no CellSetup ().
ONCELLHIGHLIGHTCHANGED ()
Chamado sempre que a célula ou qualquer subview se tornar ou renunciar ao primeiro respondedor.
OnRowValidationChanged ()
Chamado sempre que os erros de validação associados a uma linha alterações.
Onexpandinlinerow ()
Chamado antes de expandir a linha inline. Aplica -se a linhas em conformidade no protocolo InlineRowType .
OnCollapseInlinerow ()
Chamado antes de desmoronar a linha embutida. Aplica -se a linhas em conformidade no protocolo InlineRowType .
onpresess ()
Chamado por uma linha pouco antes de apresentar outro controlador de exibição. Aplica -se a linhas em conformidade com o protocolo PresenterRowType . Use -o para configurar o controlador apresentado.
Você pode definir uma String de título ou uma View personalizada como cabeçalho ou rodapé de uma Section .
Section ( " Title " )
Section ( header : " Title " , footer : " Footer Title " )
Section ( footer : " Footer Title " ) Você pode usar uma visualização personalizada de um arquivo .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 um UIView personalizado criado programaticamente
Section ( ) { section in
var header = HeaderFooterView < MyCustomUIView > ( . class )
header . height = { 100 }
header . onSetupView = { view , _ in
view . backgroundColor = . red
}
section . header = header
}Ou apenas construa a vista com um retorno de chamada
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
} ( )
}
Nesse caso, estamos escondendo e mostrando seções inteiras.
Para realizar isso, cada linha possui uma variável hidden de Condition de tipo opcional que pode ser definida usando uma função ou NSPredicate .
Usando o caso function de Condition :
Condition . function ( [ String ] , ( Form ) - > Bool ) A matriz de String a passar deve conter as tags das linhas em que essa linha depende. Cada vez que o valor de qualquer uma dessas linhas altera a função é reavaliada. A função assume o Form e retorna um Bool indicando se a linha deve estar oculta ou não. Esta é a maneira mais poderosa de configurar a propriedade hidden , pois não possui limitações explícitas do que pode ser feito.
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 )
} A variável hidden também pode ser definida com um nspredicate. Na string predicada, você pode fazer referência a valores de outras linhas por suas tags para determinar se uma linha deve ser oculta ou visível. Isso só funcionará se os valores das linhas que o predicado tiver que verificar forem o NSOBjects (String e Int funcionarão, pois são preenchidos com seus colegas OBJC, mas as enumes não funcionarão). Por que poderia ser útil usar predicados quando eles são mais limitados? Bem, eles podem ser muito mais simples, mais curtos e legíveis que as funções. Veja este exemplo:
$0 . hidden = Condition . predicate ( NSPredicate ( format : " $switchTag == false " ) ) E podemos escrevê -lo ainda mais curto, já que Condition estão em conformidade com o ExpressibleByStringLiteral :
$0 . hidden = " $switchTag == false "Nota: substituiremos o valor da linha cuja tag é 'switchtag' em vez de '$ switchtag'
Para que tudo isso funcione, todas as linhas implicadas devem ter uma tag, pois a tag as identificará.
Também podemos esconder uma fila fazendo:
$0 . hidden = true Como Condition está em conformidade com ExpressibleByBooleanLiteral .
Não definir a variável hidden deixará a linha sempre visível.
Se você definir manualmente a condição oculta (ou desativada) após a exibição do formulário, talvez seja necessário ligar para row.evaluateHidden() para forçar Eureka a reavaliar a nova condição. Veja esta seção de perguntas frequentes para obter mais informações.
Para seções, isso funciona da mesma forma. Isso significa que podemos configurar a seção Propriedade hidden para mostrá -la/ocultar dinamicamente.
Para desativar linhas, cada linha possui uma variável disabled que também é uma propriedade de tipo de Condition opcional. Essa variável também funciona da mesma forma que a variável hidden , para que as linhas tenham uma tag.
Observe que, se você deseja desativar uma linha permanentemente, também poderá definir a variável disabled como true .
Para exibir uma lista de opções, Eureka inclui uma seção especial chamada SelectableSection . Ao criar um, você precisa passar o tipo de linha para usar nas opções e no selectionType . O selectionType é uma enumeração que pode ser multipleSelection ou singleSelection(enableDeselection: Bool) onde o parâmetro enableDeselection determina se as linhas selecionadas podem ser desmarcadas ou não.
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 criar essa seção, você deve criar uma linha que esteja em conformidade com o protocolo SelectableRowType .
public protocol SelectableRowType : RowType {
var selectableValue : Value ? { get set }
} Este selectableValue é onde o valor da linha será armazenado permanentemente. A variável value será usada para determinar se a linha está selecionada ou não, sendo 'selectableValue' se selecionado ou nulo caso contrário. Eureka inclui o ListCheckRow , que é usado, por exemplo. Nas linhas personalizadas do projeto de exemplos, você também pode encontrar o ImageCheckRow .
Para obter facilmente as linhas/s selecionadas de uma SelectableSection existem dois métodos: selectedRow() e selectedRows() que podem ser chamados para obter a linha selecionada, caso seja uma seção SingleSelection ou todas as linhas selecionadas se for uma seção MultipleSelection .
Além disso, você pode configurar a lista de opções a serem agrupadas por seções usando as seguintes propriedades do SelectorViewController :
sectionKeyForValue - Um fechamento que deve retornar a chave para um valor de linha específico. Essa chave é usada posteriormente para quebrar opções por seções.
sectionHeaderTitleForKey - Um fechamento que retorna o título do cabeçalho para uma seção para uma chave específica. Por padrão, retorna a chave em si.
sectionFooterTitleForKey - Um fechamento que retorna o título do rodapé para uma seção para uma chave específica.
O Eureka suporta vários valores para um determinado campo (como números de telefone em um contato) usando seções multivalugas. Ele nos permite criar seções inseríveis, exclusíveis e reordenadas.

Para criar uma seção multivalugurada, precisamos usar o tipo de seção MultivaluedSection , em vez do tipo Section regular. A seção MultivaluedSection estende Section e possui algumas propriedades adicionais para configurar o comportamento da seção multivaluntado.
Vamos mergulhar em um exemplo 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 "
}
}O snippet de código anterior mostra como criar uma seção multivaluga. Nesse caso, queremos inserir, excluir e reordenar linhas como o argumento de multivaluntoções indica.
addButtonProvider nos permite personalizar a linha do botão que insere uma nova linha quando tocada e multivaluedOptions contém o valor .Insert .
A propriedade multivaluedRowToInsertAt é chamada por Eureka cada vez que uma nova linha precisa ser inserida. Para fornecer a linha para adicionar à seção multivalunciada, devemos definir essa propriedade. Eureka passa o índice como parâmetro de fechamento. Observe que podemos retornar qualquer tipo de linha, mesmo linhas personalizadas, embora na maioria dos casos linhas de seções multivalugas sejam do mesmo tipo.
O Eureka adiciona automaticamente uma linha de botão quando criamos uma seção multivalunciada inserível. Podemos personalizar como se parece com essa linha de botão como explicamos antes. A propriedade showInsertIconInAddButton indica se o botão Plus (estilo inserir) deve aparecer à esquerda do botão, true por padrão.
Há algumas considerações que precisamos ter em mente ao criar seções inseríveis. Qualquer linha adicionada à seção Multivalunciada inserível deve ser colocada acima da linha que eureka adiciona automaticamente para inserir novas linhas. Isso pode ser facilmente alcançado adicionando essas linhas adicionais à seção de dentro do fechamento do inicializador da seção (último parâmetro do inicializador da seção) para que eureka adicione o botão Adicionar inserção no final da seção.
Por padrão, eureka definirá o isEditing do TableView apenas se houver uma seção de multivaluais no formulário. Isso será feito no viewWillAppear na primeira vez em que um formulário for apresentado.
Para obter mais informações sobre como usar seções multivalugas, consulte o projeto EXEMPLE DE EUREKA, que contém vários exemplos de uso.
Se você deseja usar um botão Adicionar que não seja um ButtonRow , pode usar GenericMultivaluedSection<AddButtonType> , onde AddButtonType é o tipo de linha que você deseja usar como botão Adicionar. Isso é útil se você deseja usar uma linha personalizada para alterar a interface do usuário do botão.
Exemplo:
GenericMultivaluedSection < LabelRow > ( multivaluedOptions : [ . Reorder , . Insert , . Delete ] , {
$0 . addButtonProvider = { section in
return LabelRow ( ) {
$0 . title = " A Label row as add button "
}
}
// ...
}O Eureka 2.0.0 apresenta o muito solicitado recurso de validações internas.
Uma linha possui uma coleção de Rules e uma configuração específica que determina quando as regras de validação devem ser avaliadas.
Existem algumas regras fornecidas por padrão, mas você também pode criar novas por conta própria.
As regras fornecidas são:
Vamos ver como configurar as regras de validação.
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 você pode ver no snippet de código anterior, podemos configurar quantas regras que desejarmos em uma linha invocando a linha de linha add(rule:) função.
A linha também fornece func remove(ruleWithIdentifier identifier: String) para remover uma regra. Para usá -lo, devemos atribuir um ID à regra após criá -lo.
Às vezes, a coleção de regras que queremos usar em uma linha é a mesma que queremos usar em muitas outras linhas. Nesse caso, podemos configurar todas as regras de validação usando um RuleSet que é uma coleção de regras de validação.
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 quando as regras de validação devem ser avaliadas. Podemos fazê -lo configurando a propriedade da validationOptions Row, que pode ter os seguintes valores:
.validatesOnChange - valida sempre que um valor de linha mudar..validatesOnBlur - (Valor padrão) valida logo após a célula renunciar ao primeiro respondedor. Não aplicável a todas as linhas..validatesOnChangeAfterBlurred - valida sempre que o valor da linha mudar após o resignado de socorristas pela primeira vez..validatesOnDemand - Devemos validar manualmente a linha ou a forma, invocando o método validate() . Se você deseja validar todo o formulário (todas as linhas), pode invocar manualmente o método validate() .
Cada linha possui a propriedade validationErrors que pode ser usada para recuperar todos os erros de validação. Essa propriedade apenas mantém a lista de erros de validação da última execução de validação de linha, o que significa que não avalia as regras de validação da linha.
Como esperado, as regras devem usar os mesmos tipos que o objeto de linha. Tenha cuidado para verificar o tipo de linha usado. Você pode ver um erro do compilador ("etiqueta de ragar incorreta na chamada (regra ':' esperado '.
Usando ações de deslizamento, podemos definir várias ações leadingSwipe e trailingSwipe por linha. Como as ações de furto dependem dos recursos do sistema iOS, leadingSwipe está disponível apenas no iOS 11.0+.
Vamos ver como definir ações de deslizamento.
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
} Ações de deslizamento precisam de tableView.isEditing ser definido como false . Eureka definirá isso como true se houver uma seção de multivaluais no formulário (no viewWillAppear ). Se você possui as ações multivaluais e as ações de deslizar da mesma forma, deve definir isEditing de acordo com suas necessidades.
É muito comum que você precise de uma linha diferente daquelas incluídas em Eureka. Se for esse o caso, você terá que criar sua própria linha, mas isso não deve ser difícil. Você pode ler este tutorial sobre como criar linhas personalizadas para começar. Você também pode querer dar uma olhada na Eurekacommunity, que inclui algumas linhas extras prontas para serem adicionadas ao Eureka.
Para criar uma linha com comportamento e aparência personalizados, você provavelmente desejará criar subclasses de Row e Cell .
Lembre -se de que a Row é a abstração que Eureka usa, enquanto a Cell é a UITableViewCell ACIDADE ESCOLHA DA VIENTE. Como a Row contém a Cell , Row e Cell devem ser definidas para o mesmo 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 " )
}
} O resultado: 
Assim como os retornos de chamada CellSetup e CellUpDate, a Cell possui os métodos de configuração e atualização nos quais você pode personalizá -lo.
Uma linha embutida é um tipo específico de linha que mostra dinamicamente uma linha abaixo dela, normalmente uma linha embutida muda entre um modo expandido e colapso sempre que a linha for tocada.
Portanto, para criar uma linha embutida, precisamos de 2 linhas, a linha que é "sempre" visível e a linha que expandirá/colapso.
Outro requisito é que o tipo de valor dessas 2 linhas seja o mesmo. Isso significa que, se uma linha possuir um valor String , o outro também deverá ter um valor String .
Depois de termos essas duas linhas, devemos fazer com que o tipo de linha superior esteja em conformidade com InlineRowType . Este protocolo exige que você defina uma time de InlineRow e uma função setupInlineRow . O tipo InlineRow será o tipo de linha que expandirá/colapsará. Tome isso como exemplo:
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 }
}
} O InlineRowType também adicionará alguns métodos à sua linha embutida:
func expandInlineRow ( )
func collapseInlineRow ( )
func toggleInlineRow ( ) Esses métodos devem funcionar bem, mas você deseja substituí -los, lembre -se de que é toggleInlineRow que deve chamar expandInlineRow e collapseInlineRow .
Finalmente, você deve invocar toggleInlineRow() quando a linha é selecionada, por exemplo, substituindo customDidSelect :
public override func customDidSelect ( ) {
super . customDidSelect ( )
if !isDisabled {
toggleInlineRow ( )
}
}NOTA: Uma linha do apresentador é uma linha que apresenta um novo uiviewController.
Para criar uma linha de apresentador personalizada, você deve criar uma classe que esteja em conformidade com o protocolo PresenterRowType . É altamente recomendável para a SelectorRow da subclasse, conforme em conformidade com esse protocolo e adiciona outras funcionalidades úteis.
O protocolo PresentRowType é definido da seguinte forma:
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 }
} O OnPrenderCallback será chamado quando a linha estiver prestes a apresentar outro controlador de exibição. Isso é feito na SelectorRow por isso, se você não o subclasse, terá que ligar sozinho.
O presentationMode é o que define como o controlador é apresentado e qual controlador é apresentado. Esta apresentação pode estar usando um identificador Seguugue, uma classe Segue, apresentando um controlador modalmente ou pressionando para um controlador de exibição específico. Por exemplo, um custompushrow pode ser definido assim:
Vamos ver um exemplo ..
/// 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 )
} )
}
}Às vezes, queremos alterar a aparência da interface do usuário de uma de nossas linhas, mas sem alterar o tipo de linha e toda a lógica associada a uma linha. Atualmente, existe uma maneira de fazer isso se você estiver usando células que são instanciadas dos arquivos NIB . Atualmente, nenhuma das principais linhas de Eureka é instanciada dos arquivos NIB, mas algumas das linhas personalizadas da Eurekacommunity são, em particular, o postaladdressrow que foi movido para lá.
O que você precisa fazer é:
cellProvider para usar essa ponta. Você deve fazer isso no inicializador, em cada instanciação de concreto ou usando o defaultRowInitializer . Por exemplo: <<< PostalAddressRow ( ) {
$0 . cellProvider = CellProvider < PostalAddressCell > ( nibName : " CustomNib " , bundle : Bundle . main )
}Você também pode criar uma nova linha para isso. Nesse caso, tente herdar da mesma superclasse que a linha que você deseja alterar para herdar sua lógica.
Há algumas coisas a considerar quando você faz isso:
Unknown class <YOUR_CLASS_NAME> in Interface Builder file , pode ser que você precise instanciar esse novo tipo em algum lugar do seu código para carregá -lo no tempo de execução. Chamando let t = YourClass.self ajudou no meu caso. Linha de etiqueta![]() | Linha do botão![]() | Verifique a linha ![]() |
Linha de interruptor![]() | Slider Row![]() | Stapper Row ![]() |
Linha da área de texto ![]() |
Essas linhas têm um campo de texto no lado direito da célula. A diferença entre cada um deles consiste em uma configuração diferente de capitalização, autocorreção e tipo de teclado.
![]() | Textrow Namerow Urlrow Introw Phonerow PasswordRow EmailRow Decimalrow Twitterrow Accountrow ZipCoderow |
Todos os subtipos FieldRow acima têm uma propriedade formatter do tipo NSFormatter que pode ser definida para determinar como o valor dessa linha deve ser exibido. Um formatador personalizado para números com dois dígitos após a marca decimal é incluída no Eureka ( DecimalFormatter ). O projeto de exemplo também contém um CurrencyFormatter que exibe um número como moeda de acordo com a localidade do usuário.
Por padrão, definir o formatter de uma linha afeta apenas como um valor é exibido quando não está sendo editado. Para formatar também o valor enquanto a linha estiver sendo editada, defina useFormatterDuringInput como true ao inicializar a linha. A formatação do valor como está sendo editada pode exigir a atualização da posição do cursor e Eureka fornece o seguinte protocolo que seu formatador deve estar em conformidade para lidar com a posição do cursor:
public protocol FormatterProtocol {
func getNewPosition ( forPosition forPosition : UITextPosition , inTextInput textInput : UITextInput , oldValue : String ? , newValue : String ? ) -> UITextPosition
} Além disso, os subtipos FieldRow possuem uma propriedade useFormatterOnDidBeginEditing . Ao usar um DecimalRow com um formatador que permite valores decimais e conforme ao local do usuário (por exemplo, DecimalFormatter ), se o useFormatterDuringInput for false , o uso de uso de que useFormatterOnDidBeginEditing deve ser definido como true para que a marca decimal no valor que seja editada corresponda à marca decimal no teclado.
As linhas de data mantêm uma data e nos permitem configurar um novo valor através do controle UIDIDEPicker. O modo do UIDIDEPicker e a maneira como a visualização do seletor de data é mostrada é o que muda entre eles.
Data Linha ![]() Picker mostrado no teclado. | Data Linha (embutida) ![]() A linha se expande. | Linha de data (seletor) ![]() O seletor é sempre visível. |
Com esses 3 estilos (normal, embutido e pegajoso), Eureka inclui:
São linhas com uma lista de opções associadas das quais o usuário deve escolher.
<<< ActionSheetRow < String > ( ) {
$0 . title = " ActionSheetRow "
$0 . selectorTitle = " Pick a number "
$0 . options = [ " One " , " Two " , " Three " ]
$0 . value = " Two " // initially selected
} Linha de alerta![]() Mostrará um alerta com as opções para escolher. | Linha de ação![]() Mostrará uma folha de ação com as opções para escolher. | Empurre a linha![]() Empurrará para um novo controlador de onde escolher as opções listadas usando linhas de verificação. | Linha de seletor múltiplo![]() Como Pushrow, mas permite a seleção de várias opções. |
Linha segmentada![]() | Linha segmentada (com título)![]() | Linha de seleção![]() Apresenta as opções de um tipo genérico através de uma visualização de seletor (Há também a linha inline do pegajador) |
Deixe -nos saber sobre isso, ficaríamos felizes em mencioná -lo aqui. :)

O Cocoapods é um gerente de dependência de projetos de cacau.
Especifique Eureka no Podfile do seu projeto:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios , '9.0'
use_frameworks!
pod 'Eureka'Em seguida, execute o seguinte comando:
$ pod installO Swift Package Manager é uma ferramenta para gerenciar a distribuição do código SWIFT.
Depois de configurar o arquivo de manifesto do Package.swift , você pode adicionar Eureka como uma dependência adicionando -o ao valor de dependências do seu Package.swift .
Dependências: [.package (url: "https://github.com/xmartlabs/eureka.git", de: "5.5.0")]]
Cartago é um gerente de dependência simples e descentralizado para cacau.
Especifique Eureka no Cartfile do seu projeto:
github "xmartlabs/Eureka" ~> 5.5
$ git submodule add https://github.com/xmartlabs/Eureka.gitAberto da pasta Eureka, criada pelo comando Git Submodule anterior e arraste o eureka.xcodeproj para o navegador do projeto do projeto Xcode do seu aplicativo.
Selecione o eureka.xcodeproj no navegador do projeto e verifique se a meta de implantação corresponde ao seu destino de implantação de aplicativos.
Selecione seu projeto na navegação Xcode e, em seguida, selecione seu destino de aplicativo na barra lateral. Em seguida, selecione a guia "Geral" e clique no botão + na seção "Binários incorporados".
Selecione Eureka.framework e terminamos!
eureka-forms ).Antes de contribuir, verifique o arquivo contribuinte para obter mais informações.
Se você usar Eureka em seu aplicativo, gostaríamos de ouvir sobre isso! Deixe -nos uma linha no Twitter.
Cada linha tem a seguinte propriedade:
/// 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 ) }
} Você pode definir displayValueFor para de acordo com o valor da string que deseja exibir.
Podemos obter uma linha específica invocando qualquer uma das seguintes funções expostas pela 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 ?Por exemplo:
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 " ) Invocando setValues(values: [String: Any?]) Que é exposto pela classe Form .
Por exemplo:
form . setValues ( [ " IntRowTag " : 8 , " TextRowTag " : " Hello world! " , " PushRowTag " : Company ( name : " Xmartlabs " ) ] ) Onde "IntRowTag" , "TextRowTag" , "PushRowTag" são tags de linha (cada uma identifica exclusivamente uma linha) e 8 , "Hello world!" , Company(name:"Xmartlabs") são o valor da linha correspondente a atribuir.
O tipo de valor de uma linha deve corresponder ao tipo de valor do valor do dicionário correspondente, caso contrário, a NIL será atribuída.
Se o formulário já foi exibido, precisamos recarregar as linhas visíveis, recarregando a tabela View tableView.reloadData() ou invocando updateCell() para cada linha visível.
Após definir uma condição, essa condição não é avaliada automaticamente. Se você deseja que o faça imediatamente, pode ligar para .evaluateHidden() ou .evaluateDisabled() .
Essas funções são chamadas apenas quando uma linha é adicionada ao formulário e quando uma linha depende de alterações. Se a condição for alterada quando a linha estiver sendo exibida, ela deve ser reavaliada manualmente.
Olhe para esta edição.
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 ( ) São fornecidas propriedades selectableRowSetup , selectableRowCellUpdate e selectableRowCellSetup são fornecidas para poder personalizar as células selecionadas SelectorViewController e MultipleSelectorViewController.
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 dissemos, os tipos de Form e Section estão em conformidade com MutableCollection e RangeReplaceableCollection . Um formulário é uma coleção de seções e uma seção é uma coleção de linhas.
A extensão do protocolo RangeReplaceableCollection fornece muitos métodos úteis para modificar a coleta.
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 )
}Esses métodos são usados internamente para implementar os operadores personalizados, como mostrado abaixo:
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 )
}Você pode ver como o restante dos operadores personalizados é implementado aqui.
Cabe a você decidir se deseja usar ou não operadores personalizados da Eureka.
O formulário é sempre exibido em uma UITableView . Você pode configurar seu controlador de visualização em um storyboard e adicionar uma View Uitable, onde deseja que seja e conectar a saída à variável tableView do FormViewController. Isso permite definir um quadro personalizado (possivelmente com restrições) para o seu formulário.
Tudo isso também pode ser feito com o quadro, margens, etc. da tableView do seu FormViewController.
Então, podemos tornar Eureka ainda melhor! 
Isso pode ser encontrado no arquivo changelog.md.