Skip to content

Commit

Permalink
fix: page custom widgets (#602)
Browse files Browse the repository at this point in the history
* fix page custom widgets

* update page custom widgets
  • Loading branch information
luisgustavozup committed Jun 4, 2021
1 parent 448a117 commit 28ed370
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 19 deletions.
246 changes: 227 additions & 19 deletions content/pt/resources/customization/beagle-for-ios/custom-widgets.md
Expand Up @@ -12,52 +12,260 @@ description: >-

O Beagle já possui alguns widgets básicos que podem ser usados para alterar a sua aplicação UI através do backend. No entanto, você pode adicionar novos componentes para fazer as views da sua aplicação fiquem "visíveis" ao Beagle e que possam também ser usadas no backend.

## Como criar componentes \(custom views\) e widgets?
### Passo 1: Criar o componente nativo.

### Passo 1: Criar o Widget
Abaixo temos a definição da classe do componente Box.

Segue abaixo um exemplo de um componente customizado que representa um UILabel:
```swift
import Foundation
import UIKit

class Box: UIView {

// 1 Class parameter.
private var title: String

// 2 Initialization part of the class.
public init(title: String) {
self.title = title
super.init(frame: .zero)
setupView()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// 3 Method to add component to hierarchy and pass position.
private func setupView() {
addSubview(label)

label.text = title
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}

// 4 Component `UILabel` created.
private lazy var label: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 20, weight: .bold)
label.backgroundColor = .red
label.textAlignment = .center
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
}
```


### Passo 2: Criar o Widget.

Agora temos o componente `Box`, para trasformar para um componente Beagle temos adotar o protocolo `Widget`, que é um protocolo que conforma com `Decodable` e é responsável por decodificar as propriedades que seu widget expõem ao backend.

Crie uma struct BoxWidget adotando protocolo `Widget`, a interface widget irá adicionar a property de **widgetProperties** e o método **toView**.

* **widgetProperties:** A propriedade de aplicar estilo, id e acessibilidade.

* **toView:** Método para retornar a view do componente criado.

Temos a estrutura da struct `BoxWidget` com os parâmetros `title` e `widgetProperties` e o componente `Box` criado no método `toView`.

```swift
struct MyCustomComponent: ServerDrivenComponent {
let text: String
import Foundation
import UIKit
import Beagle

struct BoxWidget: Widget {

// Class parameter.
let title: String
var widgetProperties: WidgetProperties

// toView method of interface the widget.
func toView(renderer: BeagleRenderer) -> UIView {
let label = UILabel(frame: .zero)
label.text = text
label.numberOfLines = 0
return label
let boxComponent = Box(title: title)

}
}

```

Podemos ver que o `MyCustomComponent` é do tipo `ServerDrivenComponent`, que é um protocolo que conforma com `Decodable` e é responsável por decodificar as propriedades que seu widget expõem ao backend.
Temos que criar a parte de inicialização e decodificação do componente, tem duas maneiras possíveis usando o `sourcery` gerador de código para a linguagem Swift, ou fazendo manualmente.

**Sourcery:**

### Passo 2: Registrar o Widget
Para usar o `Sourcery` pode encontrar nesse [**link**]({{< ref path="/resources/customization/beagle-for-ios/sourcery" lang="pt" >}})

É obrigatório **registrá-lo no Beagle.** Dentro do arquivo de configuração utilize o método **`registerCustomComponent().`** O primeiro parâmetro é uma string que referencia como o seu BFF irá chama-lo e o segundo parâmetro é a classe criada do componente.
**Manual:**

Para fazer manual tem que criar o init e a decodificação dos parametros `title` e `widgetProperties` da struct `BoxWidget`.
O widgetProperties tem sua propria parte decodificação, entao é preciso apenas passar o decoder para o objeto `WidgetProperties`.

```swift
Beagle.registerCustomComponent(
"MyCustomComponent",
componentType: MyCustomComponent.self
)

// Initialization part of the class.
public init(
title: String,
widgetProperties: WidgetProperties = WidgetProperties()
) {
self.title = title
self.widgetProperties = widgetProperties
}

// Enum with parameters for decoding.
enum CodingKeys: String, CodingKey {
case title
}

// Initialization for decoding
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

title = try container.decode(String.self, forKey: .title)
widgetProperties = try WidgetProperties(from: decoder)
}
```

Para integrar o componente ao beagle é preciso utilizar o `sizeThatFits` ou `AutoLayoutWrapper`.

{{< tabs id="T0" >}}
{{% tab name="AutoLayoutWrapper" %}}


### AutoLayoutWrapper

**`AutoLayoutWrapper:`** O objeto calcula o tamanho levando em consideração as contraints do componente.
Para isso primeiro é preciso desabilitar o `translatesAutoresizingMaskIntoConstraints` da view do componente, e depois adicionar a view do componente dentro do `AutoLayoutWrapper`.

```swift
yourComponent.translatesAutoresizingMaskIntoConstraints = false
let beagleWrapper = AutoLayoutWrapper(view: yourComponent)
```

{{% /tab %}}

{{% tab name="SizeThatFits" %}}


### SizeThatFits

**`sizeThatFits:`** Método para implementar sua lógica de tamanho, usado na classe do componente customizado.

```swift
override func sizeThatFits(_ size: CGSize) -> CGSize {
systemLayoutSizeFitting(size)
}
```
{{% /tab %}}
{{< /tabs >}}

Agora terminando as configurações, vamos usar o `AutoLayoutWrapper` do beagle para configurar o tamanho, pois o componente customizado não possue o `sizeThatFits` implementado.

```swift
boxComponent.translatesAutoresizingMaskIntoConstraints = false
let beagleWrapper = AutoLayoutWrapper(view: boxComponent)
```

A classe completa do Widget.

```swift
import Foundation
import UIKit
import Beagle

struct BoxWidget: Widget {

// Class parameter.
let title: String
var widgetProperties: WidgetProperties

// Initialization part of the class.
public init(
title: String,
widgetProperties: WidgetProperties = WidgetProperties()
) {
self.title = title
self.widgetProperties = widgetProperties
}

// Enum with parameters for decoding.
enum CodingKeys: String, CodingKey {
case title
}

// Initialization for decoding
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

title = try container.decode(String.self, forKey: .title)
widgetProperties = try WidgetProperties(from: decoder)
}

// toView method of interface the widget.
func toView(renderer: BeagleRenderer) -> UIView {

// Native component declaration.
let boxComponent = Box(title: title)

// Setting the beagle wrapper.
boxComponent.translatesAutoresizingMaskIntoConstraints = false
let beagleWrapper = AutoLayoutWrapper(view: boxComponent)

// Returning BeagleWrapper and component.
return beagleWrapper
}
}
```


### Passo 3: Registrar o Widget.

É obrigatório **registrá-lo no Beagle.** Dentro do arquivo de configuração do beagle utilize o `dependencies` para registar.

{{% alert color="info" %}}
Para saber mais sobre o dependencies. [**Beagle Dependencies**]({{< ref path="/resources/customization/beagle-for-ios/beagles-dependencies" lang="pt" >}}).
{{% /alert %}}

O método `register` possui dois construtores, o primeiro passando apenas o `component` e segundo recebendo o `component` e `named`.

* **component:** Passa a classe do componente.

* **named:** Parâmetro para setar o nome do componente. Não é obrigatório passar. Um caso é quando o nome do componente é registrado diferente com que você criou no backend. Ele será usado na deserializações para encontrar seu componente.

**Maneiras de Registrar**
```swift
// 1
dependencies.decoder.register(component: BoxWidget.self)

// 2
dependencies.decoder.register(component: BoxWidget.self, named: "BoxWidgetComponent")
```

Após registrar o seu componente de customização, você pode usá-lo via server-driven.

### Passo 3: Exibir o Componente
### Passo 4: Exibir o Componente.

Você pode usar o seu componente declarativamente assim como passá-lo por uma instância até o `BeagleScreenViewController` ou chamá-lo via método `toView()` para apresentar o`UIView` que aparece dentro do seu próprio view controller.

```swift
let beagleScreenViewController = Beagle.screen(
.declarative(
.init(child:
MyCustomComponent(text: "Hello Beagle!")
BoxWidget(title: "Title my box!")
)
)
)
```

Se você usar componentes mais complexos que estejam no `UIViews` ou outros componentes não mencionados, o processo seria parecido, você apenas precisa providenciar um tipo de `ServerDrivenComponent` ou `Widget`.
{{% alert color="info" %}}
Para saber mais como exibir o Componente. [**Como exibir uma tela**]({{< ref path="/get-started/using-beagle/ios" lang="pt" >}}).
{{% /alert %}}

Exemplo renderizado:

![](/shared/custom-component-box-ios.png)

Se você usar componentes mais complexos que estejam no `UIViews` ou outros componentes não mencionados, o processo seria parecido.
Binary file added static/shared/custom-component-box-ios.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 28ed370

Please sign in to comment.