跳到主要内容

32篇带“engineering”标签的文章

查看所有标签

引入新的 iOS WebViews

·3 分钟阅读
Facebook 软件工程师

长期以来,Apple一直不鼓励使用UIWebView,而是推荐WKWebView。在未来几个月将发布的iOS 12中,UIWebView将被正式弃用。React Native的iOS WebView实现严重依赖UIWebView类。因此,鉴于这些进展,我们为WebView React Native组件构建了一个使用WKWebView的新原生iOS后端。

这些更改的最后部分已在此提交中落地,并将随0.57版本发布。

要选择此新实现,请使用useWebKit prop。

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

改进

UIWebView没有合法的方法来促进WebView中运行的JavaScript与React Native之间的通信。当从WebView发送消息时,我们依赖一种hack方法将它们传递给React Native。简而言之,我们将消息数据编码到一个具有特殊方案的URL中,然后将WebView导航到该URL。在原生端,我们拦截并取消了此导航,从URL中解析了数据,最后调用了React Native。此实现容易出错且不安全。我很高兴地宣布,我们利用WKWebView的功能完全取代了它。

WKWebView相对于UIWebView的其他优点包括更快的JavaScript执行和多进程架构。有关更多详细信息,请参阅此2014年WWDC

注意事项

如果您的组件使用以下属性,那么在切换到WKWebView时可能会遇到问题。目前,我们建议您避免使用这些属性

不一致的行为

automaticallyAdjustContentInsetscontentInsets (提交)

当您向WKWebView添加contentInsets时,它不会改变WKWebView的视口。视口的大小与框架保持相同。而UIWebView的视口大小实际上会改变(如果内容内边距为正,则会变小)。

backgroundColor (提交)

使用WebView的新iOS实现,如果您使用此属性,您的背景颜色可能会闪烁。此外,WKWebView渲染透明背景的方式与UIWebview不同。请查看提交说明以获取更多详细信息。

不支持

scalesPageToFit (提交)

WKWebView 不支持 `scalesPageToFit` 属性,因此我们无法在 WebView React Native 组件中实现此功能。

辅助功能 API 更新

·7分钟阅读
Ziqi Chen
加州大学伯克利分校学生

动机

随着技术的进步和移动应用在日常生活中变得越来越重要,创建无障碍应用的需求也同样变得越来越重要。

React Native有限的无障碍API一直是开发人员的一大痛点,因此我们对无障碍API进行了一些更新,使其更容易创建包容性移动应用。

现有API的问题

问题一:两个完全不同但相似的属性——accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

accessibilityComponentTypeaccessibilityTraits 是两个属性,用于告知 Android 上的 TalkBack 和 iOS 上的 VoiceOver 用户正在与哪种 UI 元素进行交互。这些属性最大的两个问题是:

  1. 它们是两个不同的属性,使用方法不同,但目的相同。在之前的API中,它们是两个独立的属性(每个平台一个),这不仅不方便,而且让许多开发人员感到困惑。iOS上的accessibilityTraits允许17种不同的值,而Android上的accessibilityComponentType只允许4种值。此外,这些值大部分没有重叠。甚至这两个属性的输入类型也不同。accessibilityTraits允许传入一个特征数组或单个特征,而accessibilityComponentType只允许单个值。
  2. Android上的功能非常有限。使用旧属性,Talkback 唯一能识别的 UI 元素是“按钮”、“单选按钮已选中”和“单选按钮未选中”。

问题二:不存在的辅助功能提示:

辅助功能提示(Accessibility Hints)帮助使用 TalkBack 或 VoiceOver 的用户理解当他们对辅助功能元素执行操作时会发生什么,这些信息仅凭辅助功能标签是无法明显的。这些提示可以在设置面板中开启和关闭。以前,React Native 的 API 完全不支持辅助功能提示。

问题三:忽略反转颜色:

一些视力受损的用户会在手机上使用反转颜色以获得更大的屏幕对比度。Apple为iOS提供了一个API,允许开发者忽略某些视图。这样,当用户开启反转颜色设置时,图像和视频不会失真。此API目前不受React Native支持。

新API的设计

解决方案一:合并accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

为了解决accessibilityComponentTypeaccessibilityTraits之间的混淆,我们决定将它们合并为一个属性。这很有意义,因为它们在技术上具有相同的预期功能,并且通过合并它们,开发人员在构建无障碍功能时不再需要担心特定于平台的复杂性。

背景

在 iOS 上,UIAccessibilityTraits 是一个可以设置在任何 NSObject 上的属性。通过 JavaScript 属性传递给原生的17个特性中的每一个,都被映射到 Objective-C 中的一个 UIAccessibilityTraits 元素。特性由一个长整型表示,并且设置的每个特性都进行或运算。

然而,在 Android 上,AccessibilityComponentType 是 React Native 创建的一个概念,它不直接映射到 Android 中的任何属性。辅助功能由一个辅助功能委托处理。每个视图都有一个默认的辅助功能委托。如果您想自定义任何辅助功能操作,您必须创建一个新的辅助功能委托,覆盖您想要自定义的特定方法,然后将您正在处理的视图的辅助功能委托设置为与新委托关联。当开发人员设置 AccessibilityComponentType 时,原生代码会根据传入的组件创建一个新的委托,并将视图设置为具有该辅助功能委托。

所做的更改

对于我们的新属性,我们希望创建这两个属性的超集。我们决定主要根据现有属性accessibilityTraits来建模新属性,因为accessibilityTraits的值明显更多。这些特性的Android功能将通过修改辅助功能委托进行填充。

iOS上的accessibilityTraits可以设置为UIAccessibilityTraits的17个值。然而,我们没有将所有这些值都包含在新属性的可能值中。这是因为设置其中一些特性的效果实际上并不为人所知,而且其中许多值几乎从未使用过。

UIAccessibilityTraits 的值通常有两种用途。它们要么描述了 UI 元素的角色,要么描述了 UI 元素所处的状态。我们观察到之前属性的大多数用法通常使用一个表示角色的值,并将其与“状态选中”、“状态禁用”或两者结合使用。因此,我们决定创建两个新的辅助功能属性:accessibilityRoleaccessibilityState

辅助功能角色

新属性accessibilityRole用于告诉Talkback或Voiceover UI元素的角色。此新属性可以取以下值之一

  • 按钮
  • 链接
  • 搜索
  • 图片
  • 键盘键
  • 文本
  • 可调整
  • 标题
  • 总结
  • 图片按钮

此属性只允许传入一个值,因为UI元素通常逻辑上不会承担其中多个角色。例外是图片和按钮,因此我们添加了一个组合两者的角色“图片按钮”。

辅助功能状态

新属性accessibilityStates用于告诉Talkback或Voiceover UI元素的状态。此属性接受一个包含以下一个或两个值的数组

  • 已选中
  • 已禁用

解决方案二:添加辅助功能提示

为此,我们添加了一个新属性:accessibilityHint。设置此属性将允许Talkback或Voiceover向用户朗读提示。

辅助功能提示

此属性以字符串形式接收要读取的辅助功能提示。

在 iOS 上,设置此属性将设置视图上相应的原生属性 AccessibilityHint。如果 iPhone 中开启了辅助功能提示,则 Voiceover 将朗读该提示。

在 Android 上,设置此属性会将提示的值附加到辅助标签的末尾。这种实现的优点是它模仿了 iOS 上提示的行为,但缺点是这些提示不能像 iOS 上那样在 Android 的设置中关闭。

我们在Android上做出这个决定的原因是,通常情况下,辅助功能提示与特定动作(例如,点击)相关联,我们希望在不同平台之间保持行为一致。

问题三的解决方案

辅助功能忽略反转颜色

我们已将 Apple 的 API `AccessibilityIgnoresInvertColors` 暴露给 JavaScript,因此现在当您有一个不想反转颜色的视图(例如图片)时,您可以将此属性设置为 true,它就不会被反转。

新用法

这些新属性将在React Native 0.57版本中可用。

如何升级

如果您当前正在使用accessibilityComponentTypeaccessibilityTraits,这里是您可以采取的步骤来升级到新属性。

1. 使用jscodeshift

最简单的用例可以通过运行jscodeshift脚本来替换。

这个脚本替换了以下实例

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

带有

accessibilityRole= “trait”

此脚本还删除了AccessibilityComponentType的实例(假设您在所有设置AccessibilityComponentType的地方都同时设置了AccessibilityTraits)。

2. 使用手动代码修改

对于使用AccessibilityTraits但没有对应AccessibilityRole值的情况,以及将多个特性传递给AccessibilityTraits的情况,需要进行手动代码修改。

通常,

accessibilityTraits= {[“button”, “selected”]}

将被手动替换为

accessibilityRole=“button”
accessibilityStates={[“selected”]}

这些属性已经在Facebook的代码库中使用。Facebook的代码修改出乎意料地简单。jscodeshift脚本修复了大约一半的实例,另一半则手动修复。总的来说,整个过程不到几个小时。

希望您会发现更新后的API很有用!并请继续让应用程序无障碍!#包容

2018 年 React Native 现状

·阅读时长5分钟
Sophie Alpert
Facebook React 工程经理

距离我们上次发布 React Native 的状态更新已经有一段时间了。

在Facebook,我们比以往任何时候都更多地使用React Native,并且用于许多重要项目。我们最受欢迎的产品之一是Marketplace,它是我们应用程序中的顶级标签之一,每月有8亿人使用。自2015年创建以来,Marketplace的整个部分都是用React Native构建的,包括应用程序不同部分的100多个全屏视图。

我们还将 React Native 用于应用程序的许多新部分。如果您观看了上个月的 F8 主题演讲,您会认出“献血”、“危机响应”、“隐私快捷方式”和“健康检查”——所有这些都是最近使用 React Native 构建的功能。Facebook 主应用程序之外的项目也在使用 React Native。新的 Oculus Go VR 头戴设备包含一个完全用 React Native 构建的配套移动应用程序,更不用说 React VR 为头戴设备本身提供了许多体验。

当然,我们还使用许多其他技术来构建我们的应用程序。LithoComponentKit是我们应用程序中广泛使用的两个库;两者都提供了类似React的组件API来构建原生屏幕。React Native的目标从来都不是取代所有其他技术——我们专注于使React Native本身更好,但我们乐于看到其他团队借鉴React Native的思想,例如将即时重载引入非JavaScript代码。

架构

当我们在2013年启动React Native项目时,我们将其设计为在JavaScript和原生之间只有一个“桥接”,它是异步、可序列化和批处理的。就像React DOM将React状态更新转换为对DOM API的命令式、可变调用,例如document.createElement(attrs).appendChild()一样,React Native旨在返回一个列出要执行的突变(mutation)的单个JSON消息,例如[["createView", attrs], ["manageChildren", ...]]。我们将整个系统设计为从不依赖于获取同步响应,并确保列表中的所有内容都可以完全序列化为JSON并返回。我们这样做是为了它带给我们的灵活性:在这种架构之上,我们能够构建诸如Chrome调试之类的工具,这些工具通过WebSocket连接异步运行所有JavaScript代码。

在过去的五年中,我们发现这些初始原则使一些功能的构建变得更加困难。异步桥接意味着您无法将JavaScript逻辑直接与许多期望同步响应的本地API集成。批处理桥接(对本地调用进行排队)意味着React Native应用程序更难调用本地实现的功能。可序列化桥接意味着不必要的复制,而不是在两个世界之间直接共享内存。对于完全使用React Native构建的应用程序,这些限制通常是可以忍受的。但是对于React Native与现有应用程序代码之间复杂集成的应用程序,它们令人沮丧。

我们正在对 React Native 进行大规模的重新架构,以使框架更具灵活性,并在混合 JavaScript/原生应用程序中更好地与原生基础架构集成。通过这个项目,我们将运用过去 5 年所学到的知识,逐步将我们的架构提升到更现代的水平。我们正在重写 React Native 的许多内部组件,但大多数更改都在幕后:现有的 React Native 应用程序将继续运行,几乎或根本不需要更改。

为了让 React Native 更轻量级,并更好地融入现有原生应用,这次重构主要有三个内部变化。首先,我们正在改变线程模型。每个 UI 更新不再需要在三个不同的线程上执行工作,而是可以在任何线程上同步调用 JavaScript 进行高优先级更新,同时将低优先级工作保留在主线程之外,以保持响应性。其次,我们正在将 异步渲染 功能整合到 React Native 中,以允许多个渲染优先级并简化异步数据处理。最后,我们正在简化桥接,使其更快、更轻量;原生和 JavaScript 之间的直接调用效率更高,这将更容易构建调试工具,例如跨语言堆栈跟踪。

一旦这些更改完成,将可能实现更紧密的集成。今天,在不进行复杂 hack 的情况下,无法集成原生导航和手势处理或原生组件(如 UICollectionView 和 RecyclerView)。在我们对线程模型进行更改后,构建此类功能将变得简单明了。

我们将在今年晚些时候,待其接近完成时,发布更多关于这项工作的细节。

社区

除了Facebook内部社区,我们很高兴看到Facebook之外有蓬勃发展的React Native用户和贡献者群体。我们希望更多地支持React Native社区,通过更好地服务React Native用户,并让项目更容易贡献。

正如我们的架构更改将帮助 React Native 与其他原生基础设施更清晰地互操作一样,React Native 在 JavaScript 端也应该更轻量级,以更好地适应 JavaScript 生态系统,这包括使 VM 和打包器可交换。我们知道破坏性更改的速度可能难以跟上,因此我们希望找到减少主要版本发布的方法。最后,我们知道有些团队正在寻求更详尽的文档,例如启动优化等主题,我们的专业知识尚未记录下来。预计在未来一年内会看到这些变化。

如果您正在使用 React Native,您就是我们社区的一员;请继续告诉我们如何才能让 React Native 为您做得更好。

React Native只是移动开发人员工具箱中的一个工具,但我们坚信它——而且我们每天都在让它变得更好,去年有500多名贡献者提交了2500多次提交。

将 TypeScript 与 React Native 结合使用

·阅读约8分钟
Ash Furrow
Artsy 软件工程师

JavaScript!我们都爱它。但我们中的一些人也喜欢类型。幸运的是,存在为JavaScript添加强类型的方法。我最喜欢的是TypeScript,但React Native开箱即支持Flow。您喜欢哪种是个人偏好问题,它们各自有自己的方法为JavaScript添加类型的魔法。今天,我们将探讨如何在React Native应用程序中使用TypeScript。

这篇文章以微软的TypeScript-React-Native-Starter仓库作为指南。

更新:自此博客文章撰写以来,事情变得更加容易。您可以通过运行一个命令来替换本文中描述的所有设置

npx react-native init MyAwesomeProject --template react-native-template-typescript

然而,Babel 对 TypeScript 的支持确实存在一些限制,上述博客文章对此有详细介绍。本文中概述的步骤仍然有效,并且 Artsy 仍在生产中使用 react-native-typescript-transformer,但使用 React Native 和 TypeScript 最快的入门方法是使用上述命令。如果需要,您可以随时切换。

无论如何,玩得开心!原始博客文章继续如下。

先决条件

因为您可能在多个不同的平台上开发,并针对多种不同类型的设备,所以基本设置可能会比较复杂。您应该首先确保您可以在没有TypeScript的情况下运行一个纯粹的React Native应用程序。请按照React Native网站上的说明进行入门。当您成功部署到设备或模拟器后,您就可以开始一个TypeScript React Native应用程序了。

您还需要Node.jsnpmYarn

初始化

一旦您尝试搭建一个普通的React Native项目,您就可以开始添加TypeScript了。让我们继续进行。

react-native init MyAwesomeProject
cd MyAwesomeProject

添加TypeScript

下一步是将 TypeScript 添加到您的项目中。以下命令将:

  • 将 TypeScript 添加到您的项目
  • React Native TypeScript Transformer 添加到您的项目
  • 初始化一个空的 TypeScript 配置文件,我们接下来会配置它
  • 添加一个空的 React Native TypeScript Transformer 配置文件,我们接下来会配置它
  • 添加 React 和 React Native 的类型定义

好的,让我们继续运行这些命令。

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

tsconfig.json 文件包含 TypeScript 编译器的所有设置。上面命令创建的默认设置大部分都很好,但请打开文件并取消注释以下行

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

rn-cli.config.js 包含 React Native TypeScript Transformer 的设置。打开它并添加以下内容

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

迁移到 TypeScript

将生成的`App.js`和`__tests_/App.js`文件重命名为`App.tsx`。`index.js`需要使用`.js`扩展名。所有新文件都应该使用`.tsx`扩展名(如果文件不包含任何JSX,则使用`.ts`)。

如果您现在尝试运行应用程序,您将收到一个错误,例如“object prototype may only be an object or null”。这是由于未能从 React 导入默认导出以及在同一行导入命名导出造成的。打开 App.tsx 并修改文件顶部的导入

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

其中一些与 Babel 和 TypeScript 如何与 CommonJS 模块互操作的差异有关。将来,两者将在相同的行为上稳定下来。

此时,您应该能够运行React Native应用程序。

添加 TypeScript 测试基础结构

React Native 附带了 Jest,因此为了使用 TypeScript 测试 React Native 应用,我们需要将 ts-jest 添加到我们的 devDependencies 中。

yarn add --dev ts-jest

然后,我们将打开 package.json 并将 jest 字段替换为以下内容

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

这将配置 Jest 以使用 `ts-jest` 运行 `.ts` 和 `.tsx` 文件。

安装依赖项类型声明

为了获得最佳的 TypeScript 体验,我们希望类型检查器能够理解我们依赖项的形状和 API。一些库会发布带有 `.d.ts` 文件(类型声明/类型定义文件)的包,这些文件可以描述底层 JavaScript 的形状。对于其他库,我们需要明确安装 `@types/` npm 范围中的相应包。

例如,这里我们需要 Jest、React、React Native 和 React Test Renderer 的类型。

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

我们将这些声明文件包保存到我们的**开发**依赖项中,因为这是一个 React Native **应用程序**,它只在开发过程中使用这些依赖项,而不在运行时使用。如果我们要发布一个库到 NPM,我们可能需要将其中一些类型依赖项作为常规依赖项添加。

您可以在此处阅读更多关于获取.d.ts文件的信息。

忽略更多文件

为了您的源代码控制,您将希望开始忽略 .jest 文件夹。如果您正在使用 git,我们可以直接将条目添加到我们的 .gitignore 文件中。

# Jest
#
.jest/

作为一个检查点,考虑将您的文件提交到版本控制。

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

添加组件

让我们为我们的应用程序添加一个组件。我们来创建一个 `Hello.tsx` 组件。这是一个教学组件,不是您在实际应用程序中会编写的东西,而是一些非平凡的例子,展示了如何在 React Native 中使用 TypeScript。

创建一个 components 目录并添加以下示例。

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

哇!这很多,但让我们分解一下:

  • 我们没有渲染像 `div`、`span`、`h1` 等 HTML 元素,而是渲染像 `View` 和 `Button` 这样的组件。这些是可以在不同平台上工作的原生组件。
  • 样式是使用React Native提供的StyleSheet.create函数指定的。React的样式表允许我们使用Flexbox控制布局,并使用类似于CSS的其他结构来设置样式。

添加组件测试

现在我们有了一个组件,让我们尝试测试它。

我们已经安装了 Jest 作为测试运行器。我们将为我们的组件编写快照测试,让我们添加快照测试所需的附加组件

yarn add --dev react-addons-test-utils

现在,让我们在 components 目录中创建一个 __tests__ 文件夹,并为 Hello.tsx 添加一个测试

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

测试首次运行时,它将创建渲染组件的快照并将其存储在components/__tests__/__snapshots__/Hello.tsx.snap文件中。当您修改组件时,您需要更新快照并检查更新以防止意外更改。您可以在此处阅读更多关于测试React Native组件的信息。

下一步

请查看官方React教程和状态管理库Redux。这些资源在编写React Native应用时会很有帮助。此外,您可能想看看ReactXP,这是一个完全用TypeScript编写的组件库,支持Web上的React以及React Native。

祝您在更类型安全的React Native开发环境中玩得开心!

为 React Native 构建 <InputAccessoryView>

·7分钟阅读
Peter Argany
Facebook 软件工程师

动机

三年前,GitHub 上有一个 issue 被提出,要求 React Native 支持输入辅助视图。

在接下来的几年里,出现了无数的“+1”,各种变通方法,但 RN 在这个问题上没有具体改变——直到今天。从 iOS 开始,我们正在公开一个 API,用于访问原生输入辅助视图,我们很高兴分享我们是如何构建它的。

背景

输入辅助视图究竟是什么?阅读Apple的开发者文档,我们了解到它是一个自定义视图,当接收者成为第一响应者时,它可以锚定在系统键盘的顶部。任何继承自UIResponder的类都可以将.inputAccessoryView属性重新声明为读写,并在此处管理自定义视图。响应者基础设施挂载视图,并使其与系统键盘保持同步。解散键盘的手势,例如拖动或点击,在框架级别应用于输入辅助视图。这使我们能够构建具有交互式键盘解散功能的内容,这是iMessage和WhatsApp等顶级消息应用程序的不可或缺的功能。

将视图锚定到键盘顶部有两种常见用例。第一种是创建键盘工具栏,例如Facebook撰写器背景选择器。

在这种情况下,键盘聚焦在文本输入字段上,输入辅助视图用于提供额外的键盘功能。此功能与输入字段的类型相关。在地图应用程序中,它可能是地址建议,或者在文本编辑器中,它可能是富文本格式工具。


拥有此场景中的 Objective-C UIResponder 应该很清楚。已成为第一响应者,在底层,它变成了UITextViewUITextField的实例。

第二种常见情况是固定文本输入

这里,文本输入实际上是输入辅助视图本身的一部分。这通常用于消息应用程序中,用户可以在滚动浏览之前的消息线程时撰写消息。


在这个例子中,的拥有者是谁?还能是UITextViewUITextField吗?文本输入在输入辅助视图**内部**,这听起来像一个循环依赖。单独解决这个问题就是另一篇博客文章了。剧透:拥有者是一个通用的UIView子类,我们手动告诉它成为第一响应者

API设计

我们现在知道`<InputAccessoryView>`是什么,以及我们想如何使用它。下一步是设计一个对两种用例都适用,并且与现有React Native组件(如`<TextInput>`)良好协作的API。

对于键盘工具栏,有几件事需要考虑:

  1. 我们希望能够将任何通用 React Native 视图层次结构提升到 <InputAccessoryView> 中。
  2. 我们希望这个通用且分离的视图层次结构能够接受触摸并能够操纵应用程序状态。
  3. 我们希望将<InputAccessoryView>链接到特定的<TextInput>
  4. 我们希望能够在多个文本输入之间共享一个<InputAccessoryView>,而无需复制任何代码。

我们可以使用类似于React portals的概念来实现#1。在此设计中,我们将React Native视图传送到由响应者基础设施管理的UIView层次结构。由于React Native视图渲染为UIViews,这实际上非常简单——我们只需覆盖

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex

并将所有子视图导入新的UIView层次结构。对于#2,我们为<InputAccessoryView>设置了一个新的RCTTouchHandler。状态更新通过使用常规事件回调来实现。对于#3和#4,我们在创建<TextInput>组件期间,使用nativeID字段在原生代码中定位辅助视图UIView层次结构。此函数使用底层原生文本输入的.inputAccessoryView属性。这样做有效地将<InputAccessoryView><TextInput>在其ObjC实现中链接起来。

支持固定文本输入(场景2)增加了更多的限制。对于这种设计,输入辅助视图有一个文本输入作为子视图,因此通过 nativeID 链接不是一个选项。相反,我们将一个通用的屏幕外 UIView.inputAccessoryView 设置为我们的原生 <InputAccessoryView> 层次结构。通过手动告诉这个通用的 UIView 成为第一响应者,响应器基础设施会挂载该层次结构。这个概念在前面提到的博客文章中得到了详尽的解释。

陷阱

当然,在构建这个API时并非一帆风顺。以下是我们遇到的一些陷阱,以及我们如何修复它们。

构建这个 API 的一个初步想法是监听 NSNotificationCenterUIKeyboardWill(Show/Hide/ChangeFrame) 事件。这种模式在一些开源库中以及 Facebook 应用的某些内部部分中使用。不幸的是,UIKeyboardDidChangeFrame 事件没有及时调用来更新滑动时的 <InputAccessoryView> 框架。此外,键盘高度的变化也未被这些事件捕获。这导致了一类错误,表现如下:

在 iPhone X 上,文本键盘和表情键盘高度不同。大多数使用键盘事件来操作文本输入框的应用程序都必须修复上述错误。我们的解决方案是致力于使用`.inputAccessoryView`属性,这意味着响应器基础设施会处理类似这样的帧更新。


我们遇到的另一个棘手的 bug 是避开 iPhone X 上的 Home Bar。你可能会想,“Apple 为此开发了 safeAreaLayoutGuide,这很简单!”我们也同样天真。第一个问题是,原生 <InputAccessoryView> 实现直到它即将出现的那一刻才有一个可供锚定的窗口。没关系,我们可以覆盖 -(BOOL)becomeFirstResponder 并在那里强制执行布局约束。遵循这些约束会使辅助视图向上移动,但又出现了另一个 bug:

输入辅助视图成功避开了Home Bar,但现在不安全区域后面的内容可见。解决方案在于这个雷达。我将原生<InputAccessoryView>层次结构包装在一个不符合safeAreaLayoutGuide约束的容器中。原生容器覆盖了不安全区域中的内容,而<InputAccessoryView>则停留在安全区域边界内。


使用示例

这里有一个示例,它构建了一个键盘工具栏按钮来重置<TextInput>状态。

class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}

render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}

固定文本输入的另一个例子可以在仓库中找到。

我何时能使用此功能?

此功能实现的完整提交在此<InputAccessoryView> 将在即将发布的 v0.55.0 版本中提供。

快乐打字 :)

将 AWS 与 React Native 结合使用

·10 分钟阅读
Richard Threlkeld
AWS 移动部门高级技术产品经理

AWS在科技行业中以云计算服务提供商而闻名。这些服务包括计算、存储和数据库技术,以及完全托管的无服务器产品。AWS移动团队一直与客户和JavaScript生态系统成员密切合作,使云计算连接的移动和网络应用程序更安全、可扩展、更易于开发和部署。我们从一个完整的入门工具包开始,但最近还有一些新的进展。

这篇博客文章将探讨一些对 React 和 React Native 开发者来说有趣的内容:

  • AWS Amplify,一个用于使用云服务的 JavaScript 应用程序的声明式库
  • AWS AppSync,一个具有离线和实时功能的完全托管 GraphQL 服务

AWS Amplify

使用Create React Native App和Expo等工具可以非常轻松地启动React Native应用程序。但是,当您尝试将用例与基础设施服务匹配时,将它们连接到云可能具有挑战性。例如,您的React Native应用程序可能需要上传照片。这些照片是否需要按用户保护?那可能意味着您需要某种注册或登录过程。您想要自己的用户目录还是使用社交媒体提供商?也许您的应用程序还需要在用户登录后调用具有自定义业务逻辑的API。

为了帮助 JavaScript 开发人员解决这些问题,我们发布了一个名为 AWS Amplify 的库。其设计打破了按 AWS 特定实现分类的传统,而是按任务“类别”划分。例如,如果您希望用户注册、登录,然后上传私人照片,您只需将 AuthStorage 类别引入您的应用程序即可

import { Auth } from 'aws-amplify';

Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));

Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));

在上面的代码中,您可以看到 Amplify 帮助您完成的一些常见任务示例,例如使用带有电子邮件或短信的多因素身份验证 (MFA) 代码。目前支持的类别有:

  • **Auth**:提供凭据自动化。开箱即用的实现使用AWS凭据进行签名,以及来自Amazon Cognito的OIDC JWT令牌。支持MFA功能等常见功能。
  • **分析**:只需一行代码,即可在 Amazon Pinpoint 中获取已认证或未认证用户的跟踪。您可以根据需要将其扩展用于自定义指标或属性。
  • **API**:通过利用AWS Signature Version 4,提供与 RESTful API 的安全交互。API 模块在无服务器基础设施与Amazon API Gateway配合使用时效果出色。
  • **存储**:简化在Amazon S3中上传、下载和列出内容的命令。您还可以轻松地按用户将数据分组为公共或私人内容。
  • 缓存:一个跨 Web 应用程序和 React Native 的 LRU 缓存接口,使用特定于实现的持久性。
  • i18n 和日志:提供国际化和本地化功能,以及调试和日志记录功能。

Amplify 的优点之一是它在为您的特定编程环境设计时编码了“最佳实践”。例如,我们发现与客户和 React Native 开发人员合作时,在开发过程中为了快速实现功能而采取的捷径会延续到生产堆栈。这可能会损害可伸缩性或安全性,并强制进行基础设施重构和代码重构。

我们如何帮助开发人员避免这种情况的一个例子是AWS Lambda 的无服务器参考架构。这些架构展示了在构建后端时,如何将 Amazon API Gateway 和 AWS Lambda 结合使用的最佳实践。这种模式被编码到 Amplify 的 API 类别中。您可以使用此模式与多个不同的 REST 端点交互,并将标头一直传递到您的 Lambda 函数,以实现自定义业务逻辑。我们还发布了一个 AWS Mobile CLI,用于使用这些功能启动新的或现有的 React Native 项目。要开始使用,只需通过 **npm** 安装,然后按照配置提示进行操作

npm install --global awsmobile-cli
awsmobile configure

另一个针对移动生态系统的编码最佳实践示例是密码安全性。默认的 Auth 类别实现利用 Amazon Cognito 用户池进行用户注册和登录。该服务实现了 安全远程密码协议,作为在身份验证尝试期间保护用户的方式。如果您愿意阅读 该协议的数学原理,您会注意到在计算密码验证器时必须使用大素数,通过原根生成一个组。在 React Native 环境中,JIT 已禁用。这使得此类安全操作的 BigInteger 计算性能较低。为了解决这个问题,我们发布了可以在您的项目中链接的 Android 和 iOS 原生桥接

npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js

我们也很高兴地看到 Expo 团队已将此功能纳入其最新 SDK,因此您无需弹出即可使用 Amplify。

最后,具体针对 React Native(和 React)开发,Amplify 包含高阶组件 (HOC),用于轻松包装功能,例如用于应用的注册和登录

import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends React.Component {
...
}

export default withAuthenticator(App);

底层组件也以`<Authenticator />`的形式提供,这让您可以完全控制UI的定制。它还提供了一些关于管理用户状态的属性,例如用户是否已登录或正在等待MFA确认,以及当状态改变时可以触发的回调。

同样,您会找到可用于不同用例的通用 React 组件。您可以根据需要自定义这些组件,例如,在 `Storage` 模块中显示 Amazon S3 中的所有私有图像。

<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>

您可以通过 props 控制许多组件功能,如前面所示,包括公共或私人存储选项。甚至还具有在用户与某些 UI 组件交互时自动收集分析的功能。

return <S3Album track/>

AWS Amplify 偏爱约定优于配置的开发风格,采用全局初始化例程或类别级别初始化。最快的入门方法是使用 aws-exports 文件。但是,开发人员也可以独立使用现有资源库。

欲深入了解其理念并观看完整演示,请查看 AWS re:Invent 的视频。

AWS AppSync

在AWS Amplify发布后不久,我们还发布了AWS AppSync。这是一个完全托管的GraphQL服务,具有离线和实时功能。尽管您可以使用不同的客户端编程语言(包括原生Android和iOS)中的GraphQL,但它在React Native开发人员中非常受欢迎。这是因为数据模型很好地契合了单向数据流和组件层次结构。

AWS AppSync使您能够连接到自己的AWS账户中的资源,这意味着您拥有并控制您的数据。这是通过使用数据源完成的,该服务支持Amazon DynamoDBAmazon ElasticsearchAWS Lambda。这使您可以在单个GraphQL API中将功能(例如NoSQL和全文搜索)组合为一个模式。这使您能够混合和匹配数据源。AppSync服务还可以从模式进行配置,因此如果您不熟悉AWS服务,您可以编写GraphQL SDL,点击一个按钮,即可自动启动并运行。

AWS AppSync 中的实时功能通过带有众所周知、基于事件模式的 GraphQL 订阅来控制。由于 AWS AppSync 中的订阅通过模式和 GraphQL 指令进行控制,并且一个模式可以使用任何数据源,这意味着您可以从使用 Amazon DynamoDB 和 Amazon Elasticsearch Service 的数据库操作中触发通知,或者从基础设施的其他部分使用 AWS Lambda 触发通知。

与AWS Amplify类似,您可以使用企业安全功能在您的GraphQL API上使用AWS AppSync。该服务允许您使用API密钥快速入门。然而,当您投入生产时,它可以转换为使用AWS Identity and Access Management (IAM)或Amazon Cognito用户池中的OIDC令牌。您可以在解析器级别通过类型上的策略来控制访问。您甚至可以使用逻辑检查进行细粒度访问控制运行时检查,例如检测用户是否是特定数据库资源的拥有者。还有一些功能可以检查组员资格以执行解析器或单个数据库记录访问。

为了帮助 React Native 开发者更多地了解这些技术,AWS AppSync 控制台主页上提供了一个内置的 GraphQL 示例模式,您可以启动它。该示例部署了一个 GraphQL 模式,配置了数据库表,并自动为您连接了查询、变更和订阅。此外,还有一个功能齐全的AWS AppSync 的 React Native 示例(以及一个React 示例),使您可以在几分钟内启动客户端和云组件。

使用 `AWSAppSyncClient` 入门非常简单,它与 Apollo Client 插件集成。 `AWSAppSyncClient` 处理 GraphQL API 的安全和签名、离线功能以及订阅握手和协商过程

import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from 'aws-appsync-react';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";

const client = new AWSAppSyncClient({
url: awsconfig.graphqlEndpoint,
region: awsconfig.region,
auth: {type: AUTH_TYPE.API_KEY, apiKey: awsconfig.apiKey}
});

AppSync控制台提供了可下载的配置文件,其中包含您的GraphQL端点、AWS区域和API密钥。然后,您可以将客户端与React Apollo一起使用。

const WithProvider = () => (
<ApolloProvider client={client}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
);

此时,您可以使用标准的 GraphQL 查询。

query ListEvents {
listEvents{
items{
__typename
id
name
where
when
description
comments{
__typename
items{
__typename
eventId
commentId
content
createdAt
}
nextToken
}
}
}
}

上面的示例展示了对 AppSync 提供的示例应用程序模式的查询。它不仅展示了与 DynamoDB 的交互,还包括数据分页(包括加密令牌)以及 EventsComments 之间的类型关系。由于应用程序配置了 AWSAppSyncClient,数据会自动离线持久化,并在设备重新连接时同步。

您可以在此视频中深入了解其背后的客户端技术和 React Native 演示

反馈

这些库背后的团队渴望了解这些库和服务对您有何帮助。他们也想知道我们还能做些什么,让您在使用云服务进行 React 和 React Native 开发时更轻松。请通过 GitHub 联系 AWS 移动团队,了解AWS AmplifyAWS AppSync

在 React Native 中实现 Twitter 的应用加载动画

·12 分钟阅读
Eli White
Eli White
Meta 软件工程师

Twitter的iOS应用有一个我非常喜欢的加载动画。

应用程序准备好后,Twitter 标志会令人愉悦地展开,露出应用程序。

我想弄清楚如何用 React Native 重现这个加载动画。


为了理解**如何**构建它,我首先必须理解加载动画的不同部分。最容易看出细微之处的方法是放慢速度。

这里有几个主要部分需要我们弄清楚如何构建。

  1. 鸟的缩放。
  2. 随着小鸟长大,下面的应用程序也随之显示
  3. 最后稍微缩小应用程序

我花了很长时间才弄清楚如何制作这个动画。

我最初**错误地**认为蓝色背景和Twitter小鸟是应用程序**之上**的一个图层,并且随着小鸟的增大,它变得透明,从而露出了下面的应用程序。这种方法行不通,因为Twitter小鸟变得透明会显示蓝色图层,而不是下面的应用程序!

幸运的是,亲爱的读者,您不必经历我同样的沮丧。您将获得这个直接进入精彩部分的教程!


正确的方法

在开始编码之前,了解如何分解这一点很重要。为了帮助可视化此效果,我将其重新创建在 CodePen 中(嵌入在几段中),以便您可以交互式地查看不同的图层。

这种效果主要有三层。第一层是蓝色背景层。尽管这看起来出现在应用程序的顶部,但它实际上在后面。

然后我们有一个纯白层。最后,在最前面,是我们的应用程序。


这个动画的主要技巧是使用Twitter标志作为mask,并遮盖应用程序和白色图层。我不会深入探讨遮罩的细节,网上有很多资源可以查看

在此背景下,遮罩的基本原理是:遮罩的不透明像素显示其遮罩的内容,而透明像素则隐藏其遮罩的内容。

我们使用 Twitter 标志作为遮罩,并让它遮盖两个图层:纯白图层和应用程序图层。

为了显示应用程序,我们将遮罩放大,直到它大于整个屏幕。

当遮罩放大时,我们逐渐增加应用程序图层的不透明度,显示应用程序并隐藏其后面的纯白色图层。为了完成效果,我们将应用程序图层起始比例设置为 > 1,并在动画结束时将其缩小到 1。然后我们将非应用程序图层隐藏起来,因为它们将永远不会再被看到。

俗话说,一张图片胜过千言万语。那么,一个互动可视化价值多少字呢?点击“下一步”按钮浏览动画。显示图层可以为您提供侧视图。网格有助于可视化透明图层。

现在,针对 React Native

好的。既然我们已经知道了要构建什么以及动画如何工作,那么我们就可以开始编写代码了——这也是您真正来到这里的原因。

这个谜题的主要部分是MaskedViewIOS,一个核心的React Native组件。

import {MaskedViewIOS} from 'react-native';

<MaskedViewIOS maskElement={<Text>Basic Mask</Text>}>
<View style={{backgroundColor: 'blue'}} />
</MaskedViewIOS>;

MaskedViewIOS接受`maskElement`和`children`属性。子元素由`maskElement`遮罩。请注意,遮罩不必是图像,它可以是任何任意视图。上述示例的行为是渲染蓝色视图,但它仅在`maskElement`中的“Basic Mask”字样处可见。我们只是制作了复杂的蓝色文本。

我们想要做的是渲染我们的蓝色层,然后在顶部渲染我们的带有Twitter标志的遮罩应用程序和白色层。

{
fullScreenBlueLayer;
}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Image source={twitterLogo} />
</View>
}>
{fullScreenWhiteLayer}
<View style={{flex: 1}}>
<MyApp />
</View>
</MaskedViewIOS>;

这将为我们提供下面看到的图层。

现在是动画部分

我们拥有了使这能正常工作所需的所有部分,下一步是为它们制作动画。为了让这个动画感觉良好,我们将利用 React Native 的 Animated API。

Animated 允许我们以声明式的方式在 JavaScript 中定义动画。默认情况下,这些动画在 JavaScript 中运行,并告诉原生层每帧进行哪些更改。尽管 JavaScript 会尝试每帧更新动画,但它可能无法足够快地完成,从而导致帧丢失(卡顿)发生。这不是我们想要的!

Animated 有一个特殊行为,允许您获得没有这种卡顿的动画。Animated 有一个名为 useNativeDriver 的标志,它在动画开始时将您的动画定义从 JavaScript 发送到原生,从而允许原生端处理动画的更新,而无需每帧都与 JavaScript 往返交互。useNativeDriver 的缺点是您只能更新特定的属性集,主要是 transformopacity。您无法使用 useNativeDriver 动画化背景颜色等内容,至少目前还不能——我们将来会添加更多,当然您也可以随时为您的项目所需属性提交 PR,造福整个社区 😀。

由于我们希望这个动画流畅,我们将在这些约束内工作。要更深入地了解useNativeDriver的底层工作原理,请查看我们宣布它的博客文章

分解我们的动画

我们的动画有4个组成部分

  1. 放大鸟,显示应用程序和纯白层
  2. 应用程序淡入
  3. 缩小应用程序
  4. 完成后隐藏白色层和蓝色层

使用 Animated,有两种主要方法来定义您的动画。第一种是使用Animated.timing,它允许您精确地说明动画将运行多长时间,以及一个缓动曲线来平滑运动。另一种方法是使用基于物理的API,例如Animated.spring。使用Animated.spring,您可以指定弹簧中的摩擦力和张力等参数,然后让物理定律运行您的动画。

我们希望同时运行多个动画,并且它们之间都密切相关。例如,我们希望在遮罩进行到一半时,应用程序开始淡入。由于这些动画密切相关,我们将使用Animated.timing和一个单一的Animated.Value

`Animated.Value` 是一个原生值的包装器,Animated 用它来了解动画的状态。通常,您希望一个完整的动画只拥有一个这样的值。大多数使用 Animated 的组件会将该值存储在状态中。

由于我将此动画视为在整个动画的不同时间点发生的步骤,因此我们将Animated.Value的起始值设为0(表示0%完成),结束值设为100(表示100%完成)。

我们的初始组件状态将如下。

state = {
loadingProgress: new Animated.Value(0),
};

当我们准备开始动画时,我们告诉Animated将这个值动画到100。

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true, // This is important!
}).start();

然后,我尝试估算出动画不同部分的粗略值,以及在整体动画的不同阶段它们应该具有的值。下表列出了动画的不同部分,以及我认为它们在不同时间点应该具有的值。

Twitter 小鸟遮罩应该从比例 1 开始,它会先变小,然后尺寸急剧增大。因此,在动画进行到 10% 时,它的比例应该为 0.8,然后到结束时急剧增大到比例 70。选择 70 说实话是相当随意的,它需要足够大才能让小鸟完全显示屏幕,而 60 不够大 😀。不过,有趣的是,数字越高,它看起来增长得越快,因为它必须在相同的时间内到达那里。这个数字经过了一些试验和错误才使这个标志看起来不错。不同大小的标志/设备将需要不同的最终比例,以确保整个屏幕都能显示。

应用程序应该保持不透明一段时间,至少在Twitter标志变小的过程中。根据官方动画,我希望在小鸟缩放到一半时开始显示它,并很快完全显示出来。因此,在15%时我们开始显示它,在整个动画进行到30%时它完全可见。

应用程序的比例从1.1开始,并在动画结束时缩小到其常规比例。

现在,代码来了。

我们上面所做的实质上是将动画进度百分比的值映射到各个部分的相应值。我们使用 Animated 的 `.interpolate` 来实现这一点。我们根据 `this.state.loadingProgress` 创建了 3 个不同的样式对象,每个动画部分一个。

const loadingProgress = this.state.loadingProgress;

const opacityClearToVisible = {
opacity: loadingProgress.interpolate({
inputRange: [0, 15, 30],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
// clamp means when the input is 30-100, output should stay at 1
}),
};

const imageScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 10, 100],
outputRange: [1, 0.8, 70],
}),
},
],
};

const appScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 100],
outputRange: [1.1, 1],
}),
},
],
};

现在我们有了这些样式对象,我们可以在渲染帖子前面视图片段时使用它们。请注意,只有`Animated.View`、`Animated.Text`和`Animated.Image`能够使用使用`Animated.Value`的样式对象。

const fullScreenBlueLayer = (
<View style={styles.fullScreenBlueLayer} />
);
const fullScreenWhiteLayer = (
<View style={styles.fullScreenWhiteLayer} />
);

return (
<View style={styles.fullScreen}>
{fullScreenBlueLayer}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Animated.Image
style={[styles.maskImageStyle, imageScale]}
source={twitterLogo}
/>
</View>
}>
{fullScreenWhiteLayer}
<Animated.View
style={[opacityClearToVisible, appScale, {flex: 1}]}>
{this.props.children}
</Animated.View>
</MaskedViewIOS>
</View>
);

耶!现在我们的动画片段看起来如我们所愿。现在我们只需清理那些不再需要的蓝色和白色图层。

要知道何时可以清理它们,我们需要知道动画何时完成。幸运的是,我们调用`Animated.timing`的`.start`方法接受一个可选的回调函数,该函数在动画完成时运行。

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true,
}).start(() => {
this.setState({
animationDone: true,
});
});

现在我们在state中有一个值来判断动画是否完成,我们可以修改我们的蓝色和白色图层来使用它。

const fullScreenBlueLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenBlueLayer]} />
);
const fullScreenWhiteLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenWhiteLayer]} />
);

瞧!我们的动画现在正常工作了,并且一旦动画完成,我们就清理了未使用的图层。我们已经构建了Twitter应用程序的加载动画!

等等,我的代码不起作用!

别担心,亲爱的读者。我同样讨厌那些只给出部分代码而不提供完整源代码的指南。

这个组件已经发布到 npm,并且在 GitHub 上名为 react-native-mask-loader。想在手机上试用,可以在 Expo 上获取

更多阅读 / 扩展学习

  1. 这本 gitbook 是一个很好的资源,在阅读 React Native 文档后可以了解更多关于 Animated 的知识。
  2. 实际的 Twitter 动画似乎在蒙版显示接近尾声时加速。尝试修改加载器以使用不同的缓动函数(或弹簧!)来更好地匹配该行为。
  3. 蒙版当前的结束缩放是硬编码的,可能无法在平板电脑上显示整个应用程序。根据屏幕尺寸和图像尺寸计算结束缩放将是一个很棒的 PR。

React Native 月报 #6

·阅读时间:4 分钟
Tomislav Tenodi
Speck 创始人

React Native 月度会议仍在如火如荼地进行中!请务必查看此帖子底部的下一届会议通知。

Expo

  • 祝贺 Devin AbbottHoussein Djirdeh 预发布了“全栈 React Native”书籍!它通过构建几个小型应用程序来引导你学习 React Native。
  • 发布了 reason-react-native-scripts 的第一个(实验性)版本,以帮助人们轻松试用 ReasonML
  • Expo SDK 24 发布了!它使用了 React Native 0.51,并包含了一系列新功能和改进:独立应用程序中的图片打包(首次加载无需缓存!)、图片处理 API(裁剪、调整大小、旋转、翻转)、人脸检测 API、新的发布渠道功能(设置给定渠道的活动发布版本并回滚)、用于跟踪独立应用程序构建的 Web 仪表板,以及修复了 OpenGL Android 实现和 Android 多任务处理器的长期存在的错误,这只是其中的一些例子。
  • 从今年一月开始,我们将为 React Navigation 分配更多资源。我们坚信,仅使用 React 组件和 Animated、react-native-gesture-handler 等基本元素来构建 React Native 导航是可能且可取的,我们对计划中的一些改进感到非常兴奋。如果你想为社区做出贡献,请查看 react-native-mapsreact-native-svg,它们都需要一些帮助!

Infinite Red

Microsoft

  • 一个 pull request 已经开始,用于将核心 React Native Windows 桥接迁移到 .NET Standard,使其有效地与操作系统无关。希望许多其他 .NET Core 平台可以通过自己的线程模型、JavaScript 运行时和 UIManager(例如 JavaScriptCore、Xamarin.Mac、Linux Gtk# 和三星 Tizen 选项)扩展该桥接。

Wix

  • Detox
    • 为了扩展 E2E 测试,我们希望最大程度地缩短 CI 上的时间,我们正在为 Detox 开发并行化支持。
    • 提交了一个 pull request,以支持自定义构建,从而更好地支持 E2E 上的模拟。
  • DetoxInstruments
    • DetoxInstruments 的杀手级功能开发被证明是一项非常具有挑战性的任务,在任何给定时间获取 JavaScript 回溯需要自定义 JSCore 实现来支持 JS 线程挂起。在 Wix 应用程序内部测试分析器揭示了关于 JS 线程的有趣见解。
    • 该项目目前尚不稳定,无法广泛使用,但正在积极开发中,我们希望很快宣布。
  • React Native Navigation
    • V2 的开发速度大幅提升,到目前为止,我们只有 1 名开发人员投入其 20% 的时间,现在我们有 3 名开发人员全职投入!
  • Android 性能
    • 将 RN 中捆绑的旧 JSCore 替换为其最新版本(webkitGTK 项目的顶端,带有自定义 JIT 配置)使 JS 线程性能提高了 40%。接下来是编译其 64 位版本。这项工作基于 适用于 Android 的 JSC 构建脚本。在此处查看其当前状态 此处

下一次会议

关于将这次会议重新用于讨论单一特定主题(例如导航、将 React Native 模块移入单独的仓库、文档等)已经进行了一些讨论。这样我们觉得可以为 React Native 社区做出最好的贡献。它可能会在下一次会议上举行。如果您希望看到某个主题被涵盖,请随意发推。

React Native 月报 #5

·阅读时间:4 分钟
Tomislav Tenodi
Speck 创始人

React Native 月度会议继续进行!让我们看看各团队都在忙些什么。

Callstack

  • 我们一直在开发 React Native CI。最重要的是,我们已经从 Travis 迁移到 Circle,使 React Native 拥有了一个单一、统一的 CI 管道。
  • 我们组织了 Hacktoberfest - React Native 版,与与会者一起,我们尝试向开源项目提交了许多拉取请求。
  • 我们继续开发 Haul。上个月,我们提交了两个新版本,包括 webpack 3 支持。我们计划添加 CRNAExpo 支持,并改进 HMR。我们的路线图在问题跟踪器上是公开的。如果您想提出改进建议或提供反馈,请告诉我们!

Expo

  • 发布了 Expo SDK 22(使用 React Native 0.49),并为其更新了 CRNA
    • 包括改进的启动屏幕 API、基本的 ARKit 支持、“DeviceMotion”API、iOS11 上的 SFAuthenticationSession 支持以及 更多功能。
  • 您的 Snack 现在可以有多个 JavaScript 文件,并且您只需将图像和其他资产拖放到编辑器中即可上传。
  • react-navigation 贡献代码以添加对 iPhone X 的支持。
  • 当我们使用 Expo 构建大型应用程序时,将注意力集中在粗糙的边缘上。例如
    • 对部署到多个环境的一流支持:staging、production 和任意渠道。渠道将支持回滚和设置给定渠道的活动发布。如果您想成为早期测试者,请告诉我们,@expo_io
    • 我们还在改进我们的独立应用构建基础设施,并增加了在独立应用构建中捆绑图像和其他非代码资产的支持,同时保留了通过空中更新资产的能力。

Facebook

  • 更好的 RTL 支持
    • 我们正在引入许多方向感知的样式。
      • 位置
        • (左|右)→ (开始|结束)
      • 边距
        • margin(Left|Right) → margin(Start|End)
      • 填充
        • padding(Left|Right) → padding(Start|End)
      • 边框
        • borderTop(Left|Right)Radius → borderTop(Start|End)Radius
        • borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
        • border(Left|Right)Width → border(Start|End)Width
        • border(Left|Right)Color → border(Start|End)Color
    • 在 RTL 中,position、margin、padding 和 border 样式的“left”和“right”的含义互换了。几个月内,我们将移除此行为,并使“left”始终表示“左”,“right”始终表示“右”。破坏性更改隐藏在一个标志下。在您的 React Native 组件中使用 I18nManager.swapLeftAndRightInRTL(false) 以选择启用它们。
  • 正在对我们的内部原生模块进行 Flow 类型化,并利用这些类型生成 Java 接口和 ObjC 协议,原生实现必须实现这些接口和协议。我们希望这个代码生成器最早能在明年开源。

Infinite Red

  • 用于帮助 React Native 和其他项目的新开源工具。更多信息请点击此处
  • 正在重构 Ignite 以发布新的样板(代号:Bowser)

Shoutem

  • 改进 Shoutem 的开发流程。我们希望简化从创建应用程序到第一个自定义屏幕的流程,使其变得非常简单,从而降低新 React Native 开发人员的门槛。准备了一些研讨会来测试新功能。我们还改进了 Shoutem CLI 以支持新流程。
  • Shoutem UI 获得了一些组件改进和错误修复。我们还检查了与最新 React Native 版本的兼容性。
  • Shoutem 平台获得了一些显著更新,新的集成作为 开源扩展项目 的一部分提供。我们很高兴看到其他开发者积极开发 Shoutem 扩展。我们积极联系并为他们的扩展提供建议和指导。

下一次会议

下一次会议定于2017年12月6日星期三。如果您对我们如何改进会议成果有任何建议,请随时在 Twitter 上联系我。

React Native 月报 #4

·3 分钟阅读
Mike Grabowski
Mike Grabowski
CTO 兼 Callstack 联合创始人

React Native 月度会议继续进行!以下是各团队的笔记

Callstack

  • React Native EU 已结束。来自 33 个国家的 300 多名参与者参观了弗罗茨瓦夫。演讲可在 YouTube 上找到。
  • 会议结束后,我们正慢慢回到开源日程。值得一提的是,我们正在开发 react-native-opentok 的下一个版本,该版本修复了大多数现有问题。

GeekyAnts

尝试通过以下方式降低开发人员拥抱 React Native 的门槛

  • React Native EU 上发布了 BuilderX.io。BuilderX 是一款直接与 JavaScript 文件配合使用的设计工具(目前仅支持 React Native),可生成美观、可读、可编辑的代码。
  • 推出了 ReactNativeSeed.com,它为您的下一个 React Native 项目提供了一套样板。它提供了各种选项,包括用于数据类型的 TypeScript 和 Flow,用于状态管理的 MobX、Redux 和 mobx-state-tree,以及 CRNA 和纯 React-Native 作为技术栈。

Expo

  • 即将发布 SDK 21,该版本增加了对 react-native 0.48.3 的支持,以及 Expo SDK 中的一系列错误修复/可靠性改进/新功能,包括视频录制、新的启动屏幕 API、对 react-native-gesture-handler 的支持以及改进的错误处理。
  • 关于 react-native-gesture-handlerSoftware MansionKrzysztof Magiera 仍在推进其开发,我们一直在帮助他进行测试并资助他部分开发时间。将其集成到 SDK21 的 Expo 中将使人们能够轻松地在 Snack 中使用它,因此我们很高兴看到人们会创造出什么。
  • 关于改进错误日志记录/处理——有关日志记录的详细信息(特别是“问题 2”),请参阅 Expo 内部 PR 的此 gist,以及 此提交 以了解处理导入 npm 标准库模块失败尝试的更改。通过这种方式,有大量机会改进 React Native 上游的错误消息,我们将致力于后续的上游 PR。社区参与进来也会很棒。
  • native.directory 持续增长,您可以从 GitHub 仓库添加您的项目。
  • 参观北美各地的黑客马拉松,包括 PennAppsHack The NorthHackMIT,以及即将到来的 MHacks

Facebook

  • 正在改进 Android 上的 <Text><TextInput> 组件。(<TextInput> 的原生自动增长;深层嵌套 <Text> 组件的布局问题;更好的代码结构;性能优化)。
  • 我们仍在寻找愿意协助分类问题和拉取请求的额外贡献者。

Microsoft

  • 发布了 CodePush 的代码签名功能。React Native 开发者现在可以对他们在 CodePush 中的应用程序包进行签名。公告可在此处找到。
  • 正在努力完成 CodePush 与 Mobile Center 的集成。同时也在考虑测试/崩溃集成。

下一次会议

下一次会议定于2017年10月10日星期三。由于这只是我们的第四次会议,我们想知道这些笔记如何有益于 React Native 社区。如果您对我们应如何改进会议成果有任何建议,请随时在 Twitter 上联系我。