Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: page custom widgets #602

Merged
merged 2 commits into from Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Na mesma sessão do android da criação de custom widgets, ainda existe esse titulo com a pergunta

### 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)
Comment on lines +214 to +215
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sao duas propriedades requeridas entao para funcionar o auto layout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depende da maneira que o usuário implementa, usando o AutoLayoutWrapper é dessa maneira, usando o sizeThatFits ele coloca o método no componente customizado dele.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mas se ele for usar o AutoLayoutWrapper não seria mais facil ja adicionar essa linha para ele entao?
translatesAutoresizingMaskIntoConstraints = false


// 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)
luisgustavozup marked this conversation as resolved.
Show resolved Hide resolved

// 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(
luisgustavozup marked this conversation as resolved.
Show resolved Hide resolved
.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.