React Native/React Native_study

[React Native] #7 Google Maps 를 활용한 화면 구성

bocoder
728x90
반응형

본 게시글에서는 Google Maps 를 호출해 화면에 보여주도록 한다.

 

1. Google Maps 를 불러올 module 추가

* react-native-maps  : https://github.com/react-native-maps/react-native-maps 

yarn add react-native-maps

 

 

2. <MapView> 관련 소스를 추가

* initialRegion : 지도의 초기 좌표값이며, 우리나라가 한 화면에 담기도록 latitudeDelta, longitudeDelta 값을 5로 조정함

* 좌표값은 Google Maps 에서 확인 가능

* useState 참고 : https://ko.reactjs.org/docs/hooks-reference.html

* provider={PROVIDER_GOOGLE} 을 설정하지 않으면, ios 에서는 Apple Maps 를 display 하므로 주의

 

# screens > Map > index.js

import React, { useState } from "react";
import { StyleSheet } from "react-native";
import styled from "styled-components";

//components
import Header from 'components/Header'

//google map
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';

const index = ({ navigation }) => {

  const [initialRegion, setInitialRegion] = useState({
    latitude: 35.91395373474155,
    longitude: 127.73829440215488,
    latitudeDelta: 5,
    longitudeDelta: 5,
  })

  return (
    <Wrapper>
      <Header title='지도' />
      <MapView
        initialRegion={initialRegion}
        style={[styles.map]}
        provider={PROVIDER_GOOGLE}
        showsUserLocation={true}
        showsMyLocationButton={true}
      />
    </Wrapper>
  );
};

export default index;

const Wrapper = styled.View`
  flex: 1;
  flex-direction: column;
`;

const styles = StyleSheet.create({
  map: {
  	flex: 1,
    width: '100%',
    height: '100%',
  },
});

 

* Google Maps 가 적절한 크기로 화면에 display 됨

* 하지만, Android 에서는 showsMyLocationButton={true} 옵션이 정상 작동 하지 않음이 확인됨

< iOS / Android >

 

 

3. [ Android ]에서 showsMyLocationButton 표시하기

* redering 순서 때문으로, onMapReady 옵션을 이용하여 Map이 셋팅 된 후 mapWidth 를 업데이트하여 해결

* 참고 : https://github.com/react-native-maps/react-native-maps/issues/1033

 

# screens > Map > index.js

import React, { useState } from "react";
import { StyleSheet } from "react-native";
import styled from "styled-components";

//components
import Header from 'components/Header'

//google map
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';

const index = ({ navigation }) => {

  const [initialRegion, setInitialRegion] = useState({
    latitude: 35.91395373474155,
    longitude: 127.73829440215488,
    latitudeDelta: 5,
    longitudeDelta: 5,
  })
  const [mapWidth, setMapWidth] = useState('99%');

  // Update map style to force a re-render to make sure the geolocation button appears
  const updateMapStyle = () => {
    setMapWidth('100%')
  }

  return (
    <Wrapper>
      <Header title='지도' />
      <MapView
        initialRegion={initialRegion}
        style={[styles.map, { width: mapWidth }]}
        provider={PROVIDER_GOOGLE}
        showsUserLocation={true}
        showsMyLocationButton={true}
        onMapReady={() => {
          updateMapStyle()
        }}
      />
    </Wrapper>
  );
};

export default index;

const Wrapper = styled.View`
  flex: 1;
  flex-direction: column;
`;
const styles = StyleSheet.create({
  map: {
    flex: 1,
    width: '100%',
    height: '100%',
  },
});

 

* 아래와 같이 Android 에서도 정상적으로 표시됨

* 동일한 module 이지만 button style 이 다르므로, 상황에 따라 OS별 customizing이 필요함

< iOS / Android >

 

 

4. expo를 통해 expo-location 설치 및 현재 위치 정보 가져오기

* expo-location : https://docs.expo.io/versions/v41.0.0/sdk/location/

expo install expo-location

 

# screens > Map > index.js

import React, { useState, useEffect } from "react";
import { StyleSheet } from "react-native";
import styled from "styled-components";

//components
import Header from 'components/Header'

//google map
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';

//location
import * as Location from 'expo-location';

const index = ({ navigation }) => {

  const [initialRegion, setInitialRegion] = useState({
    latitude: 35.91395373474155,
    longitude: 127.73829440215488,
    latitudeDelta: 5,
    longitudeDelta: 5,
  })
  const [mapWidth, setMapWidth] = useState('99%');
  const [location, setLocation] = useState(null);
  const [errorMsg, setErrorMsg] = useState(null);


  // Update map style to force a re-render to make sure the geolocation button appears
  const updateMapStyle = () => {
    setMapWidth('100%')
  }

  // Get current location information 
  useEffect(() => {
    (async () => {
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        setErrorMsg('Permission to access location was denied');
        return;
      }

      let location = await Location.getCurrentPositionAsync({});
      setLocation(location);
    })();
  }, []);

  let text = 'Waiting..';
  if (errorMsg) {
    text = errorMsg;
  } else if (location) {
    text = JSON.stringify(location);
    console.log('[LOG] current location : ' + text);
  }

  return (
    <Wrapper>
      <Header title='지도' />
      <MapView
        initialRegion={initialRegion}
        style={[styles.map, { width: mapWidth }]}
        provider={PROVIDER_GOOGLE}
        showsUserLocation={true}
        showsMyLocationButton={true}
        onMapReady={() => {
          updateMapStyle()
        }}
      />
    </Wrapper>
  );
};

export default index;

const Wrapper = styled.View`
  flex: 1;
  flex-direction: column;
`;
const styles = StyleSheet.create({
  map: {
    flex: 1,
    width: '100%',
    height: '100%',
  },
});

* 현재 위치값을 가져와 활용할 수 있으나, OS 별로 response 필드가 조금 상이하기 때문에 참고

 

 

5. 지도 tap 으로 이동 시, 화면 새로고침 설정

* Bottom tap의 [지도 -> 홈 -> 지도] 화면 전환 시,  기존에 이동했던 위치를 기억하여 display 됨 

* Tab.Screen의 unmountOnBlur 옵션을 true 로 변경하여 새로고침 되도록 설정 (defaluts to  false)

* Bottom tab 참고 : https://reactnavigation.org/docs/bottom-tab-navigator/

 

# router > index.js

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

//stacks
import HomeStack from "router/Home/index";
import MapStack from "router/Map/index";
import WeatherDataStack from "router/WeatherData/index";
import SettingStack from "router/Setting/index";

//icons
import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';

const Tab = createBottomTabNavigator();

const Root = ({ navigation }) => {
    return (
        <Tab.Navigator
            tabBarOptions={{
                showLabel: false,
            }}
        >
            <Tab.Screen
                name="HomeStack"
                component={HomeStack}
                options={{
                    tabBarIcon: ({ focused }) => (
                        <Ionicons
                            name="ios-home"
                            style={{ color: focused ? "#00B386" : "#404040" }}
                            size={30}
                        />
                    ),
                    unmountOnBlur: true,
                }}
            />
            <Tab.Screen
                name="MapStack"
                component={MapStack}
                options={{
                    tabBarIcon: ({ focused }) => (
                        <Ionicons
                            name="ios-location"
                            style={{ color: focused ? "#00B386" : "#404040" }}
                            size={30}
                        />
                    ),
                    unmountOnBlur: true,
                }}

            />
            <Tab.Screen
                name="WeatherDataStack"
                component={WeatherDataStack}
                options={{
                    tabBarIcon: ({ focused }) => (
                        <MaterialCommunityIcons
                            name="weather-partly-rainy"
                            style={{ color: focused ? "#00B386" : "#404040" }}
                            size={30}
                        />
                    ),
                    unmountOnBlur: true,
                }}
            />
            <Tab.Screen
                name="SettingStack"
                component={SettingStack}
                options={{
                    tabBarIcon: ({ focused }) => (
                        <Ionicons
                            name="ios-settings"
                            style={{ color: focused ? "#00B386" : "#404040" }}
                            size={30}
                        />
                    ),
                    unmountOnBlur: true,
                }}
            />
        </Tab.Navigator>
    );
}


export default function index() {
    return (
        <NavigationContainer>
            <Root />
        </NavigationContainer>
    );
};

 

끝.

728x90
반응형