iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 12
0
Modern Web

使用 Modern Web 技術來打造 Native App系列 第 12 篇

Day 12:Navigation Part I:Navigation 簡介與 Navigator

  • 分享至 

  • xImage
  •  

前言

在 React Native 裡面,大多的概念以及 Component 都相當好上手,但 Navigation 是個例外。Routing and Navigation 這個主題也被列在 Hard React Native Problems 這個 Repo 之中,其困難程度可見一斑。而這篇將簡單介紹這個概念跟內建的 Component Navigator。

Navigation

在前面篇章介紹到的 Demo App 都只有一個畫面,不過這極少在真實的 App 上發生,一但有了第二個畫面,就必須開始思考要如何做切換。Navigation 就是指在 App 上的畫面切換,概念可以對比到 Web 的 Routing,但又有些不同的地方,尤其可能會結合一些元件、動畫以及不同的使用者體驗操作,在 React Router 曾經對如何支援 React Native 有過激烈的討論。

Navigator、NavigatorIOS、NavigationExperimental 的差別?

Navigator 是用 JavaScript 實作的 Navigation 方案,因此能輕鬆的跨平台也能簡單的客製化樣式。也是這其中最老牌而且簡單易用的一個。

NavigatorIOS 是利用 iOS UIKit 的 UINavigationController 來實作的,所以有 Native 的感覺,不過因為只支援 iOS 所以價值不高。值得一提的是,wix 開發的 react-native-navigation 實作了跨平台的 Native Navigation,或許也是個有趣的選擇。

Navigator 跟 NavigatorIOS 都是 Stateful 的,NavigationExperimental 則是一個有別於它們的方式,是用 Reducer 來處理 Navigation State。因為在下一篇還會提到,這邊就先點到為止。

有興趣深入可以看看官方文件的介紹。

Navigator

第一個 Scene

Scene 是指手機一個滿版的畫面,前面介紹的 Demo,例如 Day08 的出師表都只有一個 Scene。

先試著把出師表搬進 Navigator 裡面,我們必須設定讓 renderScene 回傳出師表的 JSX,並先放置一個 initialRoute:

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Navigator,
} from 'react-native';

export default class NavigatorExample extends Component {
  render() {
    return (
      <Navigator
        initialRoute={{ index: 0 }}
        renderScene={(route, navigator) =>
          <View style={styles.container}>
            <Text style={styles.title}>出師表</Text>
            <Text style={styles.paragraph}>
              臣亮言:先帝創業未半,而中道崩殂。
              今天下三分,益州疲弊,此誠危急存亡之秋也。
              然侍衛之臣,不懈於內;忠志之士,忘身於外者,蓋追先帝之殊遇,欲報之於陛下也。
              誠宜開張聖聽,以光先帝遺德,恢弘志士之氣;
            </Text>
            <Text style={styles.paragraph}>
              不宜妄自菲薄,引喻失義,以塞忠諫之路也。
              宮中府中,俱為一體,陟罰臧否,不宜異同。
              若有作姦犯科,及為忠善者,宜付有司,論其刑賞,以昭陛下平明之治,不宜篇私,使內外異法也。
            </Text>
          </View>
        }
      />
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#EDE7C9',
    padding: 20,
  },
  title: {
    flex: 1,
    paddingBottom: 5,
    fontSize: 20,
  },
  paragraph: {
    flex: 10,
    backgroundColor: '#D25141',
    color: '#F9CF7A',
    fontSize: 24,
    lineHeight: 30,
    padding: 20,
  },
});

可以先做出只有一個 Scene 的 Navigator,呈現畫面如下:

加上第二個 Scene

先試著加上第二篇文章雅量。不過因為用到類似的結構就先把內文變成資料 Object:

const articles = [
  {
    index: 0,
    title: '出師表',
    paragraph1:
`臣亮言:先帝創業未半,而中道崩殂。
今天下三分,益州疲弊,此誠危急存亡之秋也。
然侍衛之臣,不懈於內;忠志之士,忘身於外者,蓋追先帝之殊遇,欲報之於陛下也。
誠宜開張聖聽,以光先帝遺德,恢弘志士之氣;`,
    paragraph2:
`不宜妄自菲薄,引喻失義,以塞忠諫之路也。
宮中府中,俱為一體,陟罰臧否,不宜異同。
若有作姦犯科,及為忠善者,宜付有司,論其刑賞,以昭陛下平明之治,不宜篇私,使內外異法也。`,
  },
  {
    index: 1,
    title: '雅量',
    paragraph1:
`朋友買了一件衣料,綠色的底子帶白色方格,當她拿給我們看時,一位對圍棋十分感與趣的同學說:
「啊,好像棋盤似的。」
「我看倒有點像稿紙。」我說。
「真像一塊塊綠豆糕。」一位外號叫「大食客」的同學緊接著說。`,
    paragraph2:
`我們不禁哄堂大笑,同樣的一件衣料,每個人卻有不同的感覺。那位朋友連忙把衣料用紙包好,她覺得衣料就是衣料,不是棋盤,也不是稿紙,更不是綠豆糕。`,
  }
];

export default class NavigatorExample extends Component {
  render() {

    return (
      <Navigator
        initialRoute={articles[1]}
        renderScene={(route, navigator) =>
          <View style={styles.container}>
            <Text style={styles.title}>{route.title}</Text>
            <Text style={styles.paragraph}>
              {route.paragraph1}
            </Text>
            <Text style={styles.paragraph}>
              {route.paragraph2}
            </Text>
          </View>
        }
      />
    );
  }
}

暫時先讓 initialRoute 取用 articles[1] 的值,就可以看見雅量:

加上切換方式

接下來,我們多加一個 Button 進來,然後在 onPress 的 Handler 上面判斷頁面來進行 navigator.push 或 navigator.pop:

<Navigator
  initialRoute={articles[0]}
  initialRouteStack={articles}
  renderScene={(route, navigator) =>
    <View style={styles.container}>
      <View style={styles.header}>
        <Button
          title="Switch"
          onPress={() => {
            if (route.index === 0) {
              navigator.push(articles[1]);
            } else {
              navigator.pop();
            }
          }}
        />
        <Text style={styles.title}>{route.title}</Text>
      </View>
      <Text style={styles.paragraph}>
        {route.paragraph1}
      </Text>
      <Text style={styles.paragraph}>
        {route.paragraph2}
      </Text>
    </View>
  }
/>

navigator.push 跟 navigator.pop 時也會有不一樣的效果:

也可以用把 Navigator.NavigationBar 傳到 navigationBar Prop 來達成一樣的功能,它可以直接指定 LeftButton、RightButton 跟 Title:

<Navigator
  initialRoute={articles[0]}
  initialRouteStack={articles}
  renderScene={(route, navigator) =>
    <View style={styles.container}>
      <Text style={styles.paragraph}>
        {route.paragraph1}
      </Text>
      <Text style={styles.paragraph}>
        {route.paragraph2}
      </Text>
    </View>
  }
  navigationBar={
    <Navigator.NavigationBar
      routeMapper={{
        LeftButton: (route, navigator, index, navState) =>
          <Text onPress={() => {
            if (route.index === 0) {
              navigator.push(articles[1]);
            } else {
              navigator.pop();
            }
          }}>Switch</Text>,
        RightButton: (route, navigator, index, navState) => null,
        Title: (route, navigator, index, navState) =>
          <Text style={styles.title}>{route.title}</Text>
      }}
    />
  }
/>

Scene 切換方式

要改變動畫或是手勢的行為可以用 configureScene 這個 Prop 來達到,例如下面範例中的 FloatFromBottom:

<Navigator
  renderScene={(route, navigator) =>
    // ...
  }
  configureScene={(route, routeStack) =>
    Navigator.SceneConfigs.FloatFromBottom}
/>

Navigator.SceneConfigs 有以下的屬性可以拿來當作回傳值:

  • PushFromRight (default)
  • FloatFromRight
  • FloatFromLeft
  • FloatFromBottom
  • FloatFromBottomAndroid
  • FadeAndroid
  • SwipeFromLeft
  • HorizontalSwipeJump
  • HorizontalSwipeJumpFromRight
  • HorizontalSwipeJumpFromLeft
  • VerticalUpSwipeJump
  • VerticalDownSwipeJump

基於 Navigator 的 Library

比較早期的 Library 有許多是建在 Navigator 之上,例如 Exponent 所做的 ExNavigator 或是 React Native Router Flux v2 以前的版本,不過漸漸的大部分正在遷移到 NavigationExperimental。

結語

就如同 Router 是複雜應用程式的入門磚,Navigator 一樣可以幫我們一窺複雜應用程式的樣貌,下一篇會繼續介紹這個主題,讓 Navigation 的輪廓能更完整。


上一篇
Day 11:IDE (Integrated Development Environment) for React Native
下一篇
Day 13:Navigation Part II:新的 API - NavigationExperimental
系列文
使用 Modern Web 技術來打造 Native App 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言