react nativeで一人前のiOSアプリ開発者を目指す 第5回 天気予報アプリを改良する

こんにちは、個別指導塾コミット塾長、AWESOME開発担当の船津です。
今回はネイティブのgeolocationAPIを使って天気予報アプリを少し改良してみたいと思います。

やりたいことは…

最初に開くと、現在地を取得して天気を表示してくれる。
都市名を入力するとその地域の天気を表示してくれる
「現在地から天気を取得するボタン」を設置

作成するファイルは以下の3つ

index.ios.js アプリ登録
forecast.js 天気予報表示
Firstproject.js APIを操作したり、フォームを作ったり

index.ios.js forecast.jsについては第二回とほぼ変わらないのでこちらを見て下さい。

初めてアプリを作る方はまず
react-native init FirstProjectをしてください

それではFirstProject.jsの中身を見ていきます。

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  TextInput,
  View,
  Image,
  TouchableHighlight
} from 'react-native';

var Forecast = require('./Forecast');
var forecastContent = null;
// アドレスを変数にしておきます
var API_STEM = 'http://api.openweathermap.org/data/2.5/weather?';
// APIキーも変数にしておきます
var API_KEY = 'hogehogehoge';


class FirstProject extends Component {
  constructor() {
    super();
    this.state = {
      zip: '',
      forecast: null
    };
    this._getForcastForZip = this._getForcastForZip.bind(this);
    this._getCurrentPosition = this._getCurrentPosition.bind(this);
    this._getForcastForCoords = this._getForcastForCoords.bind(this);
  }
	// アプリを開いた時に_getCurrentPositionを呼び出します。
  componentDidMount() {
    this._getCurrentPosition();
  }
  // 天気予報APIから情報を取得します
  _getForcast(url,cb){
    fetch(url)
      .then((response) => response.json())
      .then((responseJSON) => {
        this.setState({
          forecast: {
            main: responseJSON.weather[0].main,
            description: responseJSON.weather[0].description,
            temp: Math.floor(responseJSON.main.temp - 273),
            city: responseJSON.name
          }
        });
      })
      .catch((error) => {
        console.warn(error);
      });
  }
  // zipコード、もしくは都市名を入力した時の処理です。
  _getForcastForZip(event){
    var zip = event.nativeEvent.text;
    this.setState({
      zip:zip
    });
    this._getForcast(<code>${API_STEM}q=${zip}&amp;amp;amp;APPID=${API_KEY}</code>);
  }
	// 位置情報を引数として天気予報APIから情報を取得します。
  _getForcastForCoords(lat, lon){
    this._getForcast(<code>${API_STEM}lat=${lat}&amp;amp;amp;lon=${lon}&amp;amp;amp;APPID=${API_KEY}</code>);
  }
  // iosのネイティブAPIを利用して現在地を取得し、setStateで値を保持、天気予報APIの呼び出しまで行います。
  _getCurrentPosition(){
    navigator.geolocation.getCurrentPosition(
      (initialPosition) =&amp;amp;gt; {
        this.setState({
          latitude: initialPosition.coords.latitude,
          longitude: initialPosition.coords.longitude,
        });
        this._getForcastForCoords(this.state.latitude,this.state.longitude);
      },
      (error) =&amp;amp;gt; alert(error.message),
      {enableHighAccuracy:true, timeout: 20000, maximumAge:1000}
    );
  }
  
	// ほぼ第2回と同じですが、都市名をcityとしてsetStateしています。
render() {
    if (this.state.forecast !== null) {
      forecastContent = <Forecast
      main={this.state.forecast.main}
      description={this.state.forecast.description}
      temp={this.state.forecast.temp}
      city={this.state.forecast.city}/>;
    }
    return (
      <View style={styles.container}>
        <Image
          source={{uri: 'https://d19vfv6p26udb5.cloudfront.net/wp-content/uploads/2016/06/21145923/RIMG0202-450x600.jpg'}}
          resizeMode='cover'
          style={styles.backdrop}>
          <View style={styles.overlay}>
            <View style={styles.row}>
              <Text style={styles.mainText}>
                Current Weather For
              </Text>
              <View style={styles.zipContainer}>
                <TextInput
                  placeholder="Please input city name or zip code"
                  style={[styles.zipCode, styles.mainText]}
                  returnKeyType="go"
                  onSubmitEditing={this._getForcastForZip}/>
              </View>
            </View>
            // react nativeではボタンはtouchabkeHighlightで表現するようです。現在地の位置情報から天気予報APIを呼び出します。
            <TouchableHighlight onPress={this._getCurrentPosition} style={styles.button}>
              <Text>
                Use Current Location
              </Text>
            </TouchableHighlight>
            {forecastContent}
          </View>
        </Image>
      </View>
    );
  }
}

var baseFontSize = 16;
var styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    paddingTop: 30,
  },
  backdrop: {
    flex: 1,
    flexDirection: 'column',
    alignSelf: 'stretch',
  },
  overlay : {
    paddingTop: 5 ,
    backgroundColor: '#000000',
    opacity: 0.5 ,
    flexDirection: 'column',
    alignItems: 'center',
  },
  row: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'nowrap',
    alignItems: 'flex-start',
    padding: 30,
  },
  mainText: {
    fontSize: baseFontSize,
    color: '#fff',
    alignItems: 'center',
  },
  zipContainer: {
    flex: 2,
    borderColor:'#ade248',
    borderBottomWidth: 1,
    borderRadius: 4,
    marginLeft: 5,
    marginTop: 3,
  },
  zipCode: {
    width: 100 ,
    height: baseFontSize,
  },
  button: {
    flex: 2,
    padding: 15,
    marginBottom: 20,
    borderWidth: 2,
    borderColor: 'white',
    opacity: 0.8,
    borderRadius: 4,
    backgroundColor: 'white',
  }
});

module.exports = FirstProject;

完成するとこのような画面になります。

こちらが初期の画面、現在地の天気予報を取得して表示します。

react nativeで一人前のiOSアプリ開発者を目指す 第5回 天気予報アプリを改良する

こちらが都市名を入力した後、ちゃんと変わりますね。

react nativeで一人前のiOSアプリ開発者を目指す 第5回 天気予報アプリを改良する

真ん中のボタンを押すとまた初期の表示に戻ります。

位置情報の取得を別ファイルで管理しようとしましたが、あまりうまくいかなかったので一つのファイルにまとめてあります。
this.propsやthis.stateの使い分け、外部コンポーネントとの値の受け渡し方法、呼び出し方はまだまだわかっていない部分が多いです。またそのあたりの知識がまとまってきたらブログにしようと思います。

改善点

純正アプリのように色々な地点の天気をタブで切り替えられるようにしたいですね。

次回はカメラAPIを使用していきます。

PR

Instagram Feed

Load More
Something is wrong. Response takes too long or there is JS error. Press Ctrl+Shift+J or Cmd+Shift+J on a Mac.

Instagram

専務取締役

funatsukeisuke

WEBと教育を組み合わせて何かおもしろいことをやってやろうと画策しています。AWESOMEでは開発を担当、個別指導塾コミットでは塾長と2足の草鞋を履きながら日々勉強しています。

他の投稿を見る →