React Native / Expo Image Picker

Expo Image Picker

Профайл зураг солих, нийтлэлд зураг хавсаргах, баримт бичиг илгээх — энэ бүхэнд image picker хэрэгтэй. expo-image-picker package нь галерейгаас зураг сонгох болон камераас шууд зураг авах хоёр функцийг нэг дор өгдөг. Өмнөх хичээлийн expo-camera-с ялгаатай нь — энэ нь хэрэглэгчийн туршлагад нийцсэн бэлэн UI-тай ирдэг.

expo-image-picker суулгах

bash
npx expo install expo-image-picker

Суулгасны дараа галерей болон камерын зөвшөөрлийг useMediaLibraryPermissions ба useCameraPermissions hook-уудаар авна.

Галерейгаас зураг сонгох

launchImageLibraryAsync() функц нь утасны зургийн галерейг нээдэг. Хэрэглэгч зураг сонгоод гарч ирэх үед result объект буцаана:

jsx
import { useState } from "react";
import * as ImagePicker from "expo-image-picker";
import { View, Text, Image, TouchableOpacity, StyleSheet } from "react-native";

export default function AvatarPicker() {
  const [image, setImage] = useState(null);

  const pickImage = async () => {
    // Зөвшөөрөл авах
    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (status !== "granted") {
      alert("Галерей ашиглахад зөвшөөрөл шаардлагатай");
      return;
    }

    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true, // Хэрэглэгч зургийг тайрч болно
      aspect: [1, 1], // Дөрвөлжин crop
      quality: 0.8, // 0–1 чанарын хэмжэ
    });

    if (!result.canceled) {
      setImage(result.assets[0].uri);
    }
  };

  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.avatarWrap} onPress={pickImage}>
        {image ? (
          <Image source={{ uri: image }} style={styles.avatar} />
        ) : (
          <View style={styles.placeholder}>
            <Text style={styles.placeholderText}>📷</Text>
            <Text style={styles.placeholderLabel}>Зураг сонгох</Text>
          </View>
        )}
      </TouchableOpacity>

      {image && (
        <TouchableOpacity style={styles.btn} onPress={pickImage}>
          <Text style={styles.btnText}>Зураг солих</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#0b1120",
    gap: 20,
  },
  avatarWrap: {
    width: 120,
    height: 120,
    borderRadius: 60,
    overflow: "hidden",
    borderWidth: 2,
    borderColor: "#22d3ee",
  },
  avatar: {
    width: "100%",
    height: "100%",
  },
  placeholder: {
    flex: 1,
    backgroundColor: "#0f172a",
    justifyContent: "center",
    alignItems: "center",
    gap: 4,
  },
  placeholderText: {
    fontSize: 28,
  },
  placeholderLabel: {
    color: "#94a3b8",
    fontSize: 11,
  },
  btn: {
    backgroundColor: "#1e293b",
    paddingHorizontal: 20,
    paddingVertical: 10,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: "#22d3ee",
  },
  btnText: {
    color: "#22d3ee",
    fontWeight: "600",
    fontSize: 14,
  },
});

result.canceled boolean — хэрэглэгч хаасан бол true. result.assets[0].uri нь сонгосон зургийн локал замыг өгнө.

Камераас зураг авах

launchCameraAsync() нь утасны камерыг нээдэг. Галерейтэй адил API, зөвхөн функцийн нэр өөр:

jsx
import { useState } from "react";
import * as ImagePicker from "expo-image-picker";
import {
  View,
  Text,
  Image,
  TouchableOpacity,
  StyleSheet,
  Alert,
} from "react-native";

export default function PhotoCapture() {
  const [photo, setPhoto] = useState(null);

  const takePhoto = async () => {
    const { status } = await ImagePicker.requestCameraPermissionsAsync();
    if (status !== "granted") {
      Alert.alert("Зөвшөөрөл", "Камер ашиглахад зөвшөөрөл шаардлагатай");
      return;
    }

    const result = await ImagePicker.launchCameraAsync({
      allowsEditing: false,
      quality: 1,
    });

    if (!result.canceled) {
      setPhoto(result.assets[0]);
    }
  };

  return (
    <View style={styles.container}>
      {photo ? (
        <View style={styles.previewWrap}>
          <Image source={{ uri: photo.uri }} style={styles.preview} />
          <Text style={styles.meta}>
            {Math.round(photo.width)} × {Math.round(photo.height)} px
          </Text>
          <TouchableOpacity style={styles.btn} onPress={takePhoto}>
            <Text style={styles.btnText}>Дахин авах</Text>
          </TouchableOpacity>
        </View>
      ) : (
        <TouchableOpacity style={styles.cameraBtn} onPress={takePhoto}>
          <Text style={styles.cameraIcon}>📸</Text>
          <Text style={styles.cameraLabel}>Зураг авах</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#0b1120",
  },
  cameraBtn: {
    backgroundColor: "#0f172a",
    borderRadius: 16,
    padding: 32,
    alignItems: "center",
    gap: 12,
    borderWidth: 1,
    borderColor: "#1e293b",
    borderStyle: "dashed",
  },
  cameraIcon: {
    fontSize: 40,
  },
  cameraLabel: {
    color: "#94a3b8",
    fontSize: 14,
  },
  previewWrap: {
    alignItems: "center",
    gap: 12,
    padding: 20,
  },
  preview: {
    width: 280,
    height: 280,
    borderRadius: 12,
  },
  meta: {
    color: "#475569",
    fontSize: 12,
  },
  btn: {
    backgroundColor: "#22d3ee",
    paddingHorizontal: 24,
    paddingVertical: 10,
    borderRadius: 8,
  },
  btnText: {
    color: "#0b1120",
    fontWeight: "700",
  },
});

Галерей эсвэл камер — хэрэглэгчид сонгуулах

Жинхэнэ апп-д хоёуланг санал болгох нь хамгийн сайн туршлага:

jsx
import * as ImagePicker from "expo-image-picker";
import { Alert } from "react-native";

const selectImage = (onSelect) => {
  Alert.alert("Зураг сонгох", "", [
    {
      text: "Галерейгаас",
      onPress: async () => {
        const { status } =
          await ImagePicker.requestMediaLibraryPermissionsAsync();
        if (status !== "granted") return;
        const result = await ImagePicker.launchImageLibraryAsync({
          mediaTypes: ImagePicker.MediaTypeOptions.Images,
          allowsEditing: true,
          aspect: [1, 1],
          quality: 0.8,
        });
        if (!result.canceled) onSelect(result.assets[0].uri);
      },
    },
    {
      text: "Камераас",
      onPress: async () => {
        const { status } = await ImagePicker.requestCameraPermissionsAsync();
        if (status !== "granted") return;
        const result = await ImagePicker.launchCameraAsync({
          allowsEditing: true,
          aspect: [1, 1],
          quality: 0.8,
        });
        if (!result.canceled) onSelect(result.assets[0].uri);
      },
    },
    { text: "Болих", style: "cancel" },
  ]);
};

// Хэрэглэх:
// selectImage((uri) => setAvatarUri(uri));

Alert.alert дахь ActionSheet загвар нь iOS-д native action sheet болж харагддаг. Хоёр мөр кодоор хэрэглэгчид сонголт өгөх боломжтой — энэ ч мөн React Native-н давуу тал!

Дараагийн хичээлд:

Expo Location — GPS ашиглан хэрэглэгчийн байршил авах, координатыг хаягт хөрвүүлэх геолокаци функц сурна.