Can React Native produce UIButton instances in Swift? - ios

I want to use React Native to create some buttons that are wired to trigger methods in a Swift app. Is there a way to access TouchableHighlight instances from within a Swift UIViewController or ViewController class?

There are various options for buttons which you can find on npmjs.org. Below are a couple of options.
https://www.npmjs.com/package/react-native-button
https://www.npmjs.com/package/react-native-icon-button
But you won't find a UIButton instance most likely since that is iOS specific. What you get is a touchable element which acts like a button. You can inspect the code of these modules on GitHub to see how they work.

You can do this without installing additional Node modules by wrapping a <Text> component with a <View> component:
import React from 'react'
class ExampleButton extends React.Component {
constructor(props) {
super(props);
this.pressButton = this.pressButton.bind(this);
}
pressButton() {
console.log("The button was pressed!");
}
render() {
return(
<View>
<Text onPress={this.pressButton}>
Button Text
</Text>
</View>
);
}
}

Related

How do I get a React Native TextInput by its testID in an ios UITest

I'm trying to edit the text of a TextInput React Native component in an ios UITest. I've added the testID prop to the component.
Two questions:
Where does the testID prop appear in the native ios code?
How can I select the TextInput by this testID and add text to it?
App.js
import React from "react";
import { StyleSheet, TextInput, View } from "react-native";
export default function App() {
return (
<View>
<TextInput testID="username" placeholder="Enter your username" />
</View>
);
}
TestappUITests.swift
import XCTest
class TestappUITests: XCTestCase {
...
func testExample() {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Write test code here
}
}
RN uses the testID prop as the accessibiltiyIdentifier on the ios element.
You can view it in xcode by opening the 'Debug View Hierarchy' feature in xcode and selecting the element.
You have to use a XCUIElementQuery to select the TextInput component inside of an ios UITest. The TextInput is a RCTUITextField so you need to use the textFields Instance Property.
Then you can use the RN testId to select the element. From then on manipulating is easy with the tap and typeText methods.
import XCTest
class TestappUITests: XCTestCase {
...
func testExample() {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Give element focus
app.textFields["username"].tap()
// Type text in the element
app.textFields["username"].typeText("super-user");
}
}
If you are trying to get a different type of component by testID, you can check the 'Debug View Hierarchy' to find out what type of element you have. And then work out what query you should use instead of textFields.
FYI, the .otherElements Instance Property is used to select React Native Views.

NativeScript Button Not Working on iOS But Works on Android

I have a Github page that I will link to that has all of my code. So basically, I'm using Angular 8 along with NativeScript to build my first app. I've been following a Udemy tutorial and decided to have a series of components. A Menu component which serves as my Home routing path and a series of child routes. The Menu has a TabView which when each tab is clicked, it opens a different component. Right now, I have an About component, a Store component, and a Join component. When each child component route is opened, I wanted to have a BUTTON attach to each child route that routed to another page when clicked. So a button on the About Component that leads to an About Details component that will just have text about the company.
The problem is either with my routing or the page-router-outlet on iOS. I have an iPad and an Android phone I'm using to test things until I get a new Mac to set up the emulators. When I use the page-router-outlet in a certain location on the about-tab.component.html page, it works fine on Android and opens up the About Details component. But on iOS, nothing happens. I click the button...and nothing moves at all.
I've tried moving the page-router-outlet to different locations on the about-tab.component.html file and it either completely breaks the About tab on iOS and the screen is blank (and doesn't show anything, not even the words), or it stays the same. I created a GitHub page...
https://github.com/eoscryptodev/Best-Laces-Out
I've also tried to move around the location of the routes themselves. I tried making the About Details component a child of the About Component, which is a child of the Home component (Main). I also tried putting the component on the same level as the Home (Menu) component.
app-routing.module.ts
import { NgModule, NO_ERRORS_SCHEMA } from '#angular/core';
import { NativeScriptCommonModule } from 'nativescript-angular/common';
import { NativeScriptRouterModule } from 'nativescript-angular/router';
import { Routes } from '#angular/router';
import { MenuComponent } from './menu/menu.component';
import { AboutComponent } from './menu/about/about.component';
import { ShopComponent } from './menu/shop/shop.component';
import { JoinComponent } from './menu/join/join.component';
import { AboutDetailsComponent } from './menu/about-details/about-details.component';
//import { BottomTabComponent } from './tabs/bottom-tab/bottom-tab.component';
//import { JoinPageComponent } from './pages/join-page/join-page.component';
//FIX: You want the path to go from OurHistory(the menu option) to OutHistoryPage. Also figure out how to add the menu to the bottom of the pages.
// Maybe create an ns-path for the BottomTabBar that can be placed on HTML pages.
const routes: Routes =
[
{ path: '', redirectTo: '/home/(HistoryOutlet:OurHistory//MerchOutlet:OurMerch//ClubOutlet:OurClub)', pathMatch: 'full' },
{ path: 'home', component: MenuComponent, children:
[
{ path: 'OurHistory', component: AboutComponent, outlet: 'HistoryOutlet'},
{ path: 'HistoryDetails', component: AboutDetailsComponent, outlet: 'HistoryOutlet' },
{ path: 'OurMerch', component: ShopComponent, outlet: 'MerchOutlet' },
{ path: 'OurClub', component: JoinComponent, outlet: 'ClubOutlet' },
]}, // a root route with an empty path. The route that is loaded when our app starts. SHOULD BE LOGO IMAGE!!
]
about-tab.component.html
<ScrollView orientation="vertical" height="500">
<StackLayout orientation="vertical" backgroundColor="white">
<page-router-outlet name="HistoryOutlet"></page-router-outlet>
<Label text="This is Our History" id="ID1"></Label>
<Button height="50"
width="200"
backgroundColor="black"
text="Who We Are"
id="about-button"
[nsRouterLink]="['/home', { outlets: { HistoryOutlet:[ 'HistoryDetails' ]}} ]">
</Button>
</StackLayout>
</ScrollView>
about-details.component.html
<ns-action-bar title="Details"></ns-action-bar>
<StackLayout>
<Button
text="Go Back"
[nsRouterLink]="['/home', { outlets: { HistoryOutlet: ['OurHistory'] } } ]"
class="about-details">
</Button>
</StackLayout>
I found my solution in the NativeScript Github docs. Under TabView Navigation. I see where I went wrong. I was close, but this answers everything.
https://github.com/NativeScript/docs/blob/6ac877dc3bd551a8e338855e07597ee24358a462/docs/core-concepts/angular-navigation.md

Twilio Flex Plugin how to add new tab with custom route

I am using Twilio Flex plugin to customize flex ui in react js.
I want to add one custom link in a sidebar with a new custom component with new route URL like '/shops'.
After loading that component in that body i want to load custom shop.
Check the following screen for more details.
Thanks in advance for a help.
I found this video by Twilio which helped.
https://www.youtube.com/watch?v=ZMjKMoy1RPc
The key points are to add a new View to the View Collection and create a new SideLink which links to it.
import { FlexPlugin } from 'flex-plugin';
import { View, SideLink, Actions } from '#twilio/flex-ui';
import React from 'react';
export default class ShopPlugin extends FlexPlugin {
constructor() {
super('ShopPlugin');
}
init(flex, manager) {
flex.ViewCollection.Content.add(
<View name="shop-view" key="shop-view">
<div>Your Shop View Goes Here</div>
</View>
)
flex.SideNav.Content.add(
<SideLink
showLabel={true}
icon="Thumbup"
iconActive="ThumbupBold"
isActive={activeView === 'shop-view'}
onClick={() => {
Actions.invokeAction('NavigateToView', {viewName: 'shop-view'});
}
>
Shops
</SideLink>
)
}
}

Use DartAngular with dart:html

Is it possible to use default dart library html with angular dart?
ie:
class Test1Component implements OnInit{
#override
void ngOnInit() {
ButtonElement button = querySelector('button');
//Broken code, avoid button to be null.
button.onClick.listen(onClick);
}
void onClick(Event e){
print('Button clicked');
}
}
How can I avoid to get a 'null' button without the using any timers?
Basically I'm using only angular just for the Routes and but I'd like to stick with dart:html to control the DOM and events.
Yes, you can do that, but it's usually not a good idea.
Use instead #ViewChild(...) or similar Angular methods to get references to elements in a components view.
<button #myButton>click me</button>
#ViewChildren('myButton')
set myButton(List<Element> value) {
if(value.isNotEmpty) {
print(value.first);
}
}
If you want to just add a click handler using
<button (click)="onClick">click me</button>
would be the better way but it sounds you are somehow adding the button dynamically and adding a click handler declaratively might not work in this case (would need more info)
EDIT:
If someone like me want to use dart:html instead angular ng code, it's possible to use it
import 'package:angular/angular.dart';
import 'dart:html';
// AngularDart info: https://webdev.dartlang.org/angular
// Components info: https://webdev.dartlang.org/components
#Component(
selector: 'container',
template: '<h1>Test 1</h1><button #test1>Bottone test 1</button>',
)
class Test1Component implements OnInit{
#ViewChild('test1')
ButtonElement button;
#override
void ngOnInit() {
//Verified that button is initialized
print(button);
//Initialize click
button.onClick.listen((e) => print("Clicked"));
}
}

How to properly use TabNavigator inside another React Component

Question - How to properly use React Navigation's TabNavigator container component inside another React component that acts just as wrapper component?
What I want to achieve - Basically I want the appbar and tabbar both to be displayed - appbar on the top, tabbar (TabBarTop) just beneath it, a very common design pattern.
I have tried a couple of ways.
Method #1 (Nesting inside StackNavigator)
Tab.js
export const Tab = TabNavigator({
Tab1: { screen: Tab1Container },
Tab2: { screen: Tab2Container }
}, {
tabBarComponent: TapBarTop,
tabBarPosition: 'top'
});
AppBar.js
class AppBarComponent extends Component {
static navigationOptions = { header: null }
render() {
return (
<View>
*some more views, buttons blah blah here
</View>
)
}
}
export default AppBarComponent;
and I use them inside StackNavigator, like
export default StackNavigator({
stack1: { screen: AppBarComponent },
stack2: { screen: Tab }
});
This results in only 1 stack to be displayed at a given time which is exactly how it works. And I don't have anything to do with initialRouteName.
Method #2 (wrapping inside another component)
<View style={{ flex: 1 }}>
<AppBarComponent />
<Tab />
</View>
This on the contrary displays both components but this.props.navigation.navigate('somepath') or push() or pop() or
replace() doesn't work from inside and . But this.props.navigation and its methods all are available inside those components.
PS - I'm using React Navigation v1 and running on iOS
Solution for Method 1
Have a StackNavigator with only one screen and show your TabNavigator in that screen. Then customize header with your custom AppBarComponent.
header
React Element or a function that given HeaderProps returns a React
Element, to display as a header. Setting to null hides header.
Sample
export default StackNavigator({
stack: {
screen: Tab,
navigationOptions: {
header: (HeaderProps) => (<AppBarComponent headerProps={HeaderProps} />)
}
}
});
Solution for Method 2
You can wrap your component which is not a part of the stack with withNavigation HOC.
withNavigation is a higher order component which passes the
navigation prop into a wrapped Component. It's useful when you cannot
pass the navigation prop into the component directly, or don't want to
pass it in case of a deeply nested child.
Sample
class AppBarComponent extends Component {
static navigationOptions = { header: null }
render() {
return (
<View>
*some more views, buttons blah blah here
</View>
)
}
}
export default withNavigation(AppBarComponent);
#bennygenel your answer is definitely useful in addressing the problem. I kind of fed wrong information when I talk about having components inside TabNavigator. Well, instead of dumb components, there were multiple redux containers inside the TabNavigator as screens and the entire TabNavigator wrapped in a Dumb component. I have edited my question sincerely.
Though this.props.navigation exists both inside the redux containers and the wrapper component, the navigation stack reference was different for them which is why methods such as navigate or goBack() were not working.
The solution was simple. Passing the navigation stack reference as screenProps to the wrapper component solves this issue.
<View style={{ flex: 1 }}>
<AppBarComponent />
<Tab screenProps={{ rootNav: this.props.navigation }} />
</View>

Resources