React ponte nativa para Android e iOS – componente de interface do usuário

Abhishek Nalwaya Blocked Unblock Seguir Seguindo 11 de janeiro

Esta é a parte 2 do tutorial React Native Bridge, parte 1 focada em fazer ponte entre as classes nativas para React. O tutorial pode ser o fundador aqui .

Neste tutorial, vamos nos concentrar em criar o componente React Native entre plataformas , que funcionará tanto no Android quanto no iOS.

Na Parte 1 do React Native Bridge, iniciei com o iOS e, em seguida, expliquei o Android. Desta vez vou começar com o Android e depois explicar ios. (Apenas para ser natural). Mas, no final, o mesmo componente do React UI funcionará no iOS e no Android.

O código deste artigo pode ser encontrado aqui -> https://github.com/nalwayaabhishek/LightViewApp

Crie o LightApp

Para entender melhor o Native Module, criaremos um exemplo simples do LightViewApp usando a CLI react-native.

 $ reactiva-nat init LightViewApp 
$ cd LightApp

Em seguida, criaremos um componente no Swift para iOS e no Java para Android, e isso será usado neste aplicativo em um componente React. Este será um exemplo de plataforma cruzada e o mesmo código React funcionará tanto no iOS quanto no Android.

Como criamos um esqueleto básico do projeto, em seguida dividimos este artigo em duas seções:

Seção 1 – componente Native Bridge UI no Android

Seção 2 – componente Native Bridge UI no iOS

Seção 1 – componente Native Bridge UI no Android

Nesta seção, focaremos no Android e criaremos uma ponte entre o Swift / Objective C e seu React Component. Tem estes três passos:

Etapa 1) Criar componente de interface do usuário do bulbo

Passo 2) Passando adereços ao componente

Etapa 3) passando o estado nativo para reagir componente

Etapa 1) Criar um componente de visualização de bulbo

Para começar, criaremos uma classe BulbView no swift, que herdará da classe Button do Android. E, em seguida, use isso em nosso código de reagir como componente <Bulb /> .

Abra o Android Studio e clique em Abrir um projeto existente do Android Studio e selecione a pasta do Android dentro de nosso LightViewApp. Uma vez baixada a dependência do gradle , crie uma classe Java BulbView.java como mostrado:

Depois que o arquivo for criado, atualize o seguinte código no BulbView.java :

 pacote com.lightviewapp; 

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;


classe pública BulbView estende o botão {

public BulbView (Contexto de contexto) {
super (contexto);
this .setTextColor (Cor. AZUL );
this .setText ( "Este botão é criado a partir do código JAVA" );
}

BulbView público (Contexto de contexto, AttributeSet attrs) {
super (contexto, attrs);
}

BulbView público (Contexto de contexto, AttributeSet attrs, int defStyle) {
super (contexto, attrs, defStyle);
}

}

Criamos uma classe java BulbView que é herdada do Button e sobrescreve o construtor. Podemos personalizar a cor do botão e o texto do botão.

Em seguida, criaremos um BulbManager para expor e atualizar o código a seguir no BulbManager.java:

 pacote com.lightviewapp; 

import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;


classe pública BulbManager estende SimpleViewManager <BulbView> {

@Sobrepor
public String getName () {
retornar "Bulb" ;
}

@Sobrepor
protegido BulbView createViewInstance (ThemedReactContext reactContext) {

devolve o novo BulbView (reactContext);

}
}

Nós herdamos a classe BulbManager do SimpleViewManager . Um SimpleViewManager é uma interface React Native responsável por instanciar e atualizar visualizações no aplicativo. O SimpleViewManager é uma classe genérica que usa nossa visão.

Nós sempre precisamos de uma classe Custom Manager que deve ser herdada do SimpleViewManager ou de sua classe pai ViewManager

E em todas as classes do Manager, deve haver sempre estas duas coisas:

  • Um nome de método getName, que retorna o nome da string, que será exposto ao JavaScript
  • Implemente o método createViewInstance (ThemedReactContext reactContext) no qual criamos uma instância do componente e retornamos o objeto.

A classe do gerenciador também será usada na próxima seção, onde iremos passar adereços do React Component.

O próximo passo é registrar o módulo, se um módulo não estiver registrado, ele não estará disponível no JavaScript. Crie um arquivo clicando em Arquivo de Menu -> Novo -> Classe Java e o nome do arquivo como BulbPackage e, em seguida, clique em OK. Em seguida, adicione o seguinte código ao BulbPackage.java

 pacote com.lightviewapp; 
importar com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
classe pública BulbPackage implementa ReactPackage {
@Sobrepor
public List <NativeModule> createNativeModules (ReactApplicationContext reactContext) {
retornar coleções. emptyList ();
}
@Sobrepor
public List <ViewManager> createViewManagers (ReactApplicationContext reactContext) {
retornar Coleções. <ViewManager> singletonList (
novo BulbManager ()
);
}
}

Precisamos substituir a função createNativeModules e adicionar o objeto Bulb à matriz modules. Se isso não for adicionado aqui, ele não estará disponível em JavaScript.

Pacote BulbPackage deve ser fornecida no método getPackages do arquivo MainApplication.java. Este arquivo existe sob a pasta android no diretório do aplicativo reagir-nativo. Atualize o seguinte código em android / app / src / main / java / com / LightViewApp /MainApplication.java

 public class MainApplication extends Application implementa ReactApplication { 
...

@Sobrepor
Lista protegida <ReactPackage> getPackages () {
Retornar Arrays. <ReactPackage> asList (
novo MainReactPackage (),
novo BulbPackage ()
);
}

....
}

Agora vamos atualizar o código JavaScript e acessar o componente < Bulb> do nosso componente React. Para fazer isso abra App.js e atualize com o seguinte código:

 import React, {Component} from 'react'; 
import {StyleSheet, Text, View, requireNativeComponent} de 'react-native';
 const Bulb = requireNativeComponent ("Bulb") 
 classe padrão de exportação App estende o componente { 
 render () { 
 Retorna ( 
 <Ver estilo = {styles.container}> 
 <View style = {styles.top} /> 
 <Estilo do bulbo = {styles.bottom} /> 
 </ View> 
 ); 
}
 } 
 Estilos const = StyleSheet.create ({ 
 container: { 
 flex: 1, 
 backgroundColor: '# F5FCFF', 
 } 
 topo: { 
 flex: 1, 
 alignItems: "center", 
 justifyContent: "center", 
 } 
 inferior: { 
 flex: 1, 
 alignItems: "center", 
 justifyContent: "center", 
 } 
 }); 

Agora, execute o aplicativo nativo de resposta:

Woo ? podemos ver que <Bulb> processou um botão de interface do usuário que criamos em Java. O tamanho e a posição do botão são decididos a partir da folha de estilo que definimos no componente React.

Passo 2) Passando adereços ao componente

Nesta seção, vamos estender o componente Bulb para aceitar adereços. Nós vamos adicionar um adereço isOn ao componente Bulb UI, que irá mudar a cor do botão, criamos na última seção.

 <Estilo do bulbo = {styles.bottom} isOn = {true} /> 

Primeiro, vamos manipular nosso botão para aceitar cliques usando o OnClickListener. OnClickListener registra um retorno de chamada a ser invocado quando fazemos alguma operação no Button e onClick será chamado quando clicado em um botão. Ao clicar no botão, alteramos o valor de isOn . Qual, então, vai mudar a cor e o texto do botão.

Abra o BulbView e atualize o seguinte código:

 pacote com.lightviewapp; 

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;

classe pública BulbView estende o botão {
public Boolean isOn = false;
public void setIsOn (Boolean initialBulbStatus) {
isOn = initialBulbStatus;
updateButton ();
}
 public BulbView (Contexto de contexto) { 
super (contexto);
this.setTextColor (Cor. AZUL );
this.setOnClickListener (changeStatusListener);
updateButton ();

}


BulbView público (Contexto de contexto, AttributeSet attrs) {
super (contexto, attrs);
}

BulbView público (Contexto de contexto, AttributeSet attrs, int defStyle) {
super (contexto, attrs, defStyle);
}

private OnClickListener changeStatusListener = novo OnClickListener () {
public void onClick (Ver v) {
ison =! isOn ;
updateButton ();
}
};

private void updateButton () {
if ( isOn ) {
setBackgroundColor (Cor. AMARELO );
setText ("Switch OFF");
} outro {
setBackgroundColor (Cor. PRETO );
setText ("Switch ON");
}
}

}

Em seguida, vamos aceitar isOn prop de JavaScript e atribuir o valor de isOn a nossa classe Bulb. Abra o BulbManager.java e adicione o seguinte código:

 classe pública BulbManager estende SimpleViewManager <BulbView> { 
 .... 
 @ReactProp (name = "isOn" ) 
public void setBulbStatus (BulbView bulbView, Boolean isOn) {
bulbView.setIsOn (isOn);
}
 .... 
}

Em seguida, atualize o código de reação no App.js :

 <Estilo do bulbo = {styles.bottom} isOn = {true} /> 

Agora execute o aplicativo no simulador Android:

Podemos ver o estado do bulbo é passado para o Android. Você pode tentar alterar o valor de isOn = false e tentar atualizar a tela.

Passando o Estado Nativo para Reagir Componente

Agora vamos adicionar o valor do status da lâmpada (ON ou OFF) à tela React. Para fazer isso, adicionaremos a função _onStatusChange ao componente Bulb e isso será chamado quando o status do botão for alterado. Vamos passar a função em componente assim:

 <Estilo do bulbo = {styles.bottom} isOn = {this.state.isOn} onStatusChange = {this._onStatusChange} /> 

Vamos começar e atualizar o BulbView.js

 pacote com.lightviewapp; 
...
 import com.facebook.react.bridge.Arguments; 
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;


classe pública BulbView estende o botão {
....
private void changeStatus () {
Evento WritableMap = Argumentos. createMap ();
event.putBoolean ("isOn", isOn );
ReactContext reactContext = (ReactContext) getContext ();
reactContext.getJSModule (RCTEventEmitter.class) .receiveEvent (
getId (),
"mudança de status",
evento);

if ( isOn ) {
setBackgroundColor (Cor. AMARELO );
setText ("Switch OFF");
} outro {
setBackgroundColor (Cor. PRETO );
setText ("Switch ON");
}

}
...

}

Quando ocorre um evento nativo, o código nativo emite um evento para a representação em JavaScript da Visualização e as duas exibições são vinculadas ao valor retornado do método getId() .

Para mapear o nome do evento s tatusChange para o atributo de retorno de chamada onStatusChange em JavaScript, registre-o substituindo o método getExportedCustomBubblingEventTypeConstants em seu ViewManager. Então vamos atualizar BulbManager.java

 .... 

import com.facebook.react.common.MapBuilder;
import java.util.Map;


classe pública BulbManager estende SimpleViewManager <BulbView> {
...

@Sobrepor
public map getExportedCustomBubblingEventTypeConstants () {
retornar o MapBuilder. construtor ()
.colocar(
"mudança de status",
MapBuilder. de (
"phasedRegistrationNames",
MapBuilder. of ("bubbled", "onStatusChange")))
.construir();
}

}

Finalmente, vamos criar a função _onStatusChange em JavaScript, que será chamada assim que o botão for clicado. Atualize o código React em App.js :

 import React, {Component} from 'react'; 
 import {StyleSheet, Text, View, requireNativeComponent} de 'react-native'; 
 const Bulb = requireNativeComponent ("Bulb") 

 classe padrão de exportação App estende o componente { 
 construtor (props) { 
 super (adereços); 
 this._onStatusChange = this._onStatusChange.bind (this); 
 this.state = {isOn: false}; 
 } 
 _onStatusChange = e => { 
 this.setState ({isOn: e.nativeEvent.isOn}); 
 } 
 render () { 
 Retorna ( 
 <Ver estilo = {styles.container}> 
 <Ver estilo = {styles.top}> 
 <Texto> Este estado do Bulb vem do código nativo para JavaScript </ Text> 
 <Text> {this.state.isOn? "Bulb is On": "A lâmpada está desligada"} </ Text> 
 </ View> 
 <Estilo do bulbo = {styles.bottom} isOn = {this.state.isOn} onStatusChange = {this._onStatusChange} /> 
 </ View> 
 ); 
 } 
 } 
 Estilos const = StyleSheet.create ({ 
 container: { 
 flex: 1, 
 backgroundColor: '# F5FCFF', 
 } 
 topo: { 
 flex: 1, 
 alignItems: "center", 
 justifyContent: "center", 
 } 
 inferior: { 
 flex: 1, 
 alignItems: "center", 
 justifyContent: "center", 
 } 
 }); 

Agora execute o aplicativo no Android Simulator

Woo, podemos ver que na seção superior o estado do Bulb é refletido.

Seção 2 – componente Native Bridge UI no iOS

Nesta seção, criaremos o mesmo código JavaScript que usamos para o Android funcionar com o iOS. Desta vez vamos criar classe Bulb View no Swift e expor a mesma implementação em Javascript.

Para começar, criaremos uma classe BulbView no swift, que terá uma variável de classe isOn e algumas outras funções. E então, vamos acessar essa classe rápida do Javascript como um componente. Vamos começar abrindo o arquivo LightViewApp.xcodeproj na pasta ios. Deve abrir o Xcode com o seu código ios.

Depois de abrir o projeto no Xcode, crie um novo arquivo Swift BulbView.swift como mostrado:

Também clicamos em Create Bridging Header, que criará um arquivo LightViewApp-Bridging-Header.h Isso ajudará a se comunicar entre o código Swift e o Objective C. Lembre-se que em um projeto, temos apenas um arquivo de cabeçalho de ponte. Então, se adicionarmos novos arquivos, podemos reutilizar esse arquivo.

Atualize o código a seguir no LightViewApp-Bridging-Header.h :

 #import "React / RCTBridgeModule.h" 
 #import "React / RCTViewManager.h" 
 #import "React / RCTEventEmitter.h" 

O RCTBridgeModule fornecerá uma interface para registrar um módulo de ponte.

Em seguida, atualizaremos o BulbView.swift com o seguinte código:

 importar UIKit 

 classe BulbView: UIView { 
 @objc var onStatusChange: RCTDirectEventBlock? 
 @objc var isOn: Bool = false { 
 didSet { 
 button.setTitle (String (descrevendo: isOn? "Desligar": "Ativar"), para: .normal) 
 button.backgroundColor = isOn? amarelo: preto 
 } 
 } 
 substituir init (frame: CGRect) { 
 super .init (frame: frame) 
 auto .addSubview (botão) 
 } 
 init necessário (codificador aDecoder: NSCoder) { 
 fatalError ("init não foi implementado") 
 } 
 preguiçoso botão var: UIButton = { 
 deixe o botão = UIButton.init (tipo: UIButton.ButtonType.system) 
 button.titleLabel? .font = UIFont.systemFont (ofSize: 20) 
 button.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
 button.addTarget ( 
 auto 
 ação: #selector ( changeBulbStatus ), 
 para: .touchUpInside 
 ) 
 botão de retorno 
 } () 
 @objc func changeBulbStatus () { 
 isOn =! isOn como Bool 
 onStatusChange! (["isOn": isOn]) 
 } 
 } 

Nós criamos uma visão de bulbo que é herdada do UIButton . Também criamos uma variável isOn que define a cor e o texto do botão. Clicar no botão irá chamar o método changeBulbStatus .

Agora crie o Bulb.swift como arquivo Swift:

E atualize o seguinte código

 Fundação de importação 
 @objc (bulbo) 
 bulbo de classe : RCTViewManager { 
 substituir func view () -> UIView! { 
 retornar BulbView () 
 } 
 } 

Nós criamos a classe Bulb que é herdada do RCTViewManager. A classe raiz da maioria das classes View nas hierarquias React Native é RCTViewManager, do qual as subclasses herdam uma interface básica para o sistema de tempo de execução e a capacidade de se comportar como objetos Objective-C. Podemos ver que usamos @objc antes de uma função e classe, isso fará com que aquela classe, função ou objeto esteja disponível para o Objective C

O atributo @objc disponibiliza sua API Swift no Objective-C e no tempo de execução do Objective-C.

Agora crie um novo arquivo em File -> New -> File e selecione Objective-C file e, em seguida, nomeie o arquivo como Bulb.m e atualize o seguinte código:

 #import "React / RCTViewManager.h" 
 @interface RCT_EXTERN_MODULE (Bulbo, RCTViewManager) 
 RCT_EXPORT_VIEW_PROPERTY (isOn, BOOL ) 
 RCT_EXPORT_VIEW_PROPERTY (onStatusChange, RCTDirectEventBlock) 
 @fim 

Reagir nativa não irá expor qualquer função do Bulb para reagir JavaScript, a menos que explicitamente feito. Para fazer isso, usamos a macro RCT_EXPORT_METHOD () . Então, expusemos a classe e a propriedade Bulb ao nosso código Javascript. Como o objeto Swift é convertido em objeto JavaScript, há um tipo de conversa. O RCT_EXPORT_METHOD suporta todos os tipos de objeto JSON padrão:

  • NSString para string
  • NSInteger, float, double, CGFloat, NSNumber para numerar
  • BOOL para boolean
  • NSArray para matriz
  • NSDictionary para objetar com chaves de string e valores de qualquer tipo desta lista
  • RCTResponseSenderBlock para funcionar

Nota importante: Se você tiver pulado a parte do Android desta postagem, será necessário copiar o código React em App.js.

Agora execute o aplicativo no iOS Simulator:

Agora vamos corrigir o aviso mostrado na parte inferior do simulador e no console do navegador:

O módulo Bulb requer a configuração da fila principal, uma vez que substitui o `init`, mas não implementa` requiresMainQueueSetup`. Em uma versão futura, o React Native será o padrão para inicializar todos os módulos nativos em um thread de segundo plano, a menos que seja explicitamente desativado.

Para entender melhor, vamos entender tudo sobre o segmento React Native runs:

  • Tópico principal : onde o trabalho do UIKit
  • Fila de sombras: onde o layout acontece
  • Encadeamento JavaScript: onde seu código JS está realmente em execução

Cada módulo nativo possui sua própria Fila do GCD, a menos que especifique o contrário. Agora, como esse módulo nativo será executado em um thread diferente e nosso thread principal depende dele, ele mostrará esse aviso. E para fazer esse código rodar no MainQueue, abra o Bulb.swift e adicione essa função.

 @objc 
 estático func requiresMainQueueSetup () -> Bool { 
 retorno verdadeiro 
 } 

Você pode mencionar explicitamente return false para executar isso em uma ameaça separada.

Woo !! Nós criamos uma interface de usuário de ponte nativa que é Cross Platform. Se você quiser ler sobre o Native Bridge aqui . Espero que você ache isso útil. Por favor, compartilhe seus pensamentos nos comentários. Se você gostou do post, por favor, compartilhe e dê alguns claps?