Skip to content

Commit 7e9bb4d

Browse files
committed
docs(react): update taking photos page
1 parent 2cefaad commit 7e9bb4d

File tree

1 file changed

+122
-116
lines changed

1 file changed

+122
-116
lines changed

docs/react/your-first-app/2-taking-photos.md

Lines changed: 122 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -4,93 +4,91 @@ sidebar_label: Taking Photos
44
---
55

66
<head>
7-
<title>Take Photos From The Camera on React Apps - Ionic Documentation</title>
7+
<title>Build Camera API for iOS, Android & Web | Ionic Capacitor Camera</title>
88
<meta
99
name="description"
10-
content="To take photos from the device's camera on a React app, begin by building it for the web, then make some small tweaks for mobile use on iOS and Android devices."
10+
content="Add the ability to take photos with your device's camera using the Ionic Capacitor Camera API for mobile iOS, Android, and the web. Learn how here."
1111
/>
1212
</head>
1313

14-
Now for the fun part - adding the ability to take photos with the device’s camera using the Capacitor [Camera API](https://capacitorjs.com/docs/apis/camera). We’ll begin with building it for the web, then make some small tweaks to make it work on mobile (iOS and Android).
14+
Now for the fun part - adding the ability to take photos with the device’s camera using the Capacitor [Camera API](../../native/camera.md). We’ll begin with building it for the web, then make some small tweaks to make it work on mobile (iOS and Android).
1515

16-
To do so, we will create our own custom React hook that will manage the photos for the gallery.
16+
## Photo Gallery Hook
1717

18-
:::note
19-
If you are not familiar with React Hooks, [Introducing React Hooks](https://react.dev/reference/react/hooks) from the official React docs is a good resource to start with.
20-
:::
18+
We will create a [custom React hook](https://react.dev/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component) to manage the photos for the gallery.
2119

2220
Create a new file at `src/hooks/usePhotoGallery.ts` and open it up.
2321

24-
A custom hook is just a function that uses other React hooks. And that's what we will be doing! We will start by importing the various hooks and utilities we will be using from React core, the Ionic React Hooks project, and Capacitor:
22+
Next, define a new method, `usePhotoGallery()`, that will contain the core logic to take a device photo and save it to the filesystem. Let’s start by opening the device camera.
2523

26-
```tsx
27-
// CHANGE: Add the following imports
24+
```ts
2825
import { useState, useEffect } from 'react';
29-
import { isPlatform } from '@ionic/react';
30-
31-
// CHANGE: Add the following imports
3226
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
3327
import { Filesystem, Directory } from '@capacitor/filesystem';
3428
import { Preferences } from '@capacitor/preferences';
35-
import { Capacitor } from '@capacitor/core';
36-
37-
export function usePhotoGallery() {}
38-
```
39-
40-
Next, create a function named usePhotoGallery:
41-
42-
```tsx
43-
import { useState, useEffect } from 'react';
44-
import { isPlatform } from '@ionic/react';
45-
46-
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
47-
import { Filesystem, Directory } from '@capacitor/filesystem';
48-
import { Preferences } from '@capacitor/preferences';
49-
import { Capacitor } from '@capacitor/core';
5029

5130
export function usePhotoGallery() {
52-
// CHANGE: Add the usePhotoGallery function.
53-
const takePhoto = async () => {
31+
const addNewToGallery = async () => {
5432
// Take a photo
55-
const photo = await Camera.getPhoto({
33+
const capturedPhoto = await Camera.getPhoto({
5634
resultType: CameraResultType.Uri,
5735
source: CameraSource.Camera,
5836
quality: 100,
5937
});
6038
};
6139

6240
return {
63-
takePhoto,
41+
addNewToGallery,
6442
};
6543
}
6644
```
6745

68-
Our `usePhotoGallery` hook exposes a method called takePhoto, which in turn calls into Capacitor's getPhoto method.
69-
7046
Notice the magic here: there's no platform-specific code (web, iOS, or Android)! The Capacitor Camera plugin abstracts that away for us, leaving just one method call - `getPhoto()` - that will open up the device's camera and allow us to take photos.
7147

72-
The last step we need to take is to use the new hook from the Tab2 page. Go back to Tab2.tsx and import the hook:
48+
Next, in `Tab2.tsx`, import the `usePhotoGallery` method and destructure it to call its `addNewToGallery` method.
7349

7450
```tsx
75-
// Keep the other imports
76-
77-
// CHANGE: Import the usePhotoGallery hook
51+
import { camera, trash, close } from 'ionicons/icons';
52+
import {
53+
IonContent,
54+
IonHeader,
55+
IonPage,
56+
IonTitle,
57+
IonToolbar,
58+
IonFab,
59+
IonFabButton,
60+
IonIcon,
61+
IonGrid,
62+
IonRow,
63+
IonCol,
64+
IonImg,
65+
IonActionSheet,
66+
} from '@ionic/react';
67+
// CHANGE: Add `usePhotoGallery` import.
7868
import { usePhotoGallery } from '../hooks/usePhotoGallery';
69+
import './Tab2.css';
7970

8071
const Tab2: React.FC = () => {
81-
// CHANGE: Get access to `takePhoto` method by using the hook
82-
const { takePhoto } = usePhotoGallery();
72+
// CHANGE: Destructure `addNewToGallery()` from `usePhotoGallery()`.
73+
const { addNewToGallery } = usePhotoGallery();
8374

8475
return (
8576
<IonPage>
8677
<IonHeader>
8778
<IonToolbar>
88-
<IonTitle>Tab 2</IonTitle>
79+
<IonTitle>Photo Gallery</IonTitle>
8980
</IonToolbar>
9081
</IonHeader>
91-
<IonContent>
82+
<IonContent fullscreen>
83+
<IonHeader collapse="condense">
84+
<IonToolbar>
85+
<IonTitle size="large">Photo Gallery</IonTitle>
86+
</IonToolbar>
87+
</IonHeader>
88+
9289
<IonFab vertical="bottom" horizontal="center" slot="fixed">
93-
<IonFabButton onClick={() => takePhoto()}>
90+
{/* CHANGE: Add a click event listener to the floating action button. */}
91+
<IonFabButton onClick={() => addNewToGallery()}>
9492
<IonIcon icon={camera}></IonIcon>
9593
</IonFabButton>
9694
</IonFab>
@@ -102,109 +100,114 @@ const Tab2: React.FC = () => {
102100
export default Tab2;
103101
```
104102

105-
Save the file, and if you’re not already, restart the development server in your browser by running `ionic serve`. On the Photo Gallery tab, click the Camera button. If your computer has a webcam of any sort, a modal window appears. Take a selfie!
103+
If it's not running already, restart the development server in your browser by running `ionic serve`. On the Photo Gallery tab, click the Camera button. If your computer has a webcam of any sort, a modal window appears. Take a selfie!
106104

107105
![A photo gallery app displaying a webcam selfie.](/img/guides/first-app-cap-ng/camera-web.png 'Webcam Selfie in Photo Gallery')
108106

109107
_(Your selfie is probably much better than mine)_
110108

111-
After taking a photo, it disappears. We still need to display it within our app and save it for future access.
109+
After taking a photo, it disappears right away. We need to display it within our app and save it for future access.
112110

113111
## Displaying Photos
114112

115-
First we will create a new type to define our Photo, which will hold specific metadata. Add the following UserPhoto interface to the `usePhotoGallery.ts` file, somewhere outside of the main function:
113+
Return to `usePhotoGallery.ts`.
116114

117-
```tsx
118-
export functino usePhotoGallery {
119-
// Same old code from before.
115+
Outside of the `usePhotoGallery` method definition (the very bottom of the file), create a new interface, `UserPhoto`, to hold our photo metadata.
116+
117+
```ts
118+
export function usePhotoGallery {
119+
// Same old code from before.
120120
}
121121

122-
// CHANGE: Add the interface.
122+
// CHANGE: Add the `UserPhoto` interface.
123123
export interface UserPhoto {
124124
filepath: string;
125125
webviewPath?: string;
126126
}
127127
```
128128

129-
Back at the top of the function (right after the call to `usePhotoGallery`, we will define a state variable to store the array of each photo captured with the Camera.
129+
Above the `addNewToGallery()` method, define an array of `UserPhoto`, which will contain a reference to each photo captured with the Camera. Make it a state variable using React's [useState hook](https://react.dev/reference/react/useState).
130130

131-
```tsx
131+
```ts
132132
export function usePhotoGallery {
133-
// CHANGE: Add the photos array.
134-
const [photos, setPhotos] = useState<UserPhoto[]>([]);
133+
// CHANGE: Add the photos array.
134+
const [photos, setPhotos] = useState<UserPhoto[]>([]);
135135

136-
// Same old code from before.
136+
// Same old code from before.
137137
}
138138
```
139139

140-
When the camera is done taking a picture, the resulting Photo returned from Capacitor will be stored in the `photo` variable. We want to create a new photo object and add it to the photos state array. We make sure we don't accidentally mutate the current photos array by making a new array, and then call `setPhotos` to store the array into state. Update the `takePhoto` method and add this code after the getPhoto call:
141-
142-
```tsx
143-
// Same old code from before.
140+
Over in the `addNewToGallery()` method, add the newly captured photo to the beginning of the `photos` array. Then, update the `userPhotoGallery` return statement with the `photos` array.
144141

142+
```ts
145143
export function usePhotoGallery() {
146144
const [photos, setPhotos] = useState<UserPhoto[]>([]);
147-
// CHANGE: Create new fileName variable with date and .jpeg
148-
const fileName = Date.now() + '.jpeg';
149145

150-
const takePhoto = async () => {
151-
// Same old code from before.
146+
const addNewToGallery = async () => {
147+
// Take a photo
148+
const capturedPhoto = await Camera.getPhoto({
149+
resultType: CameraResultType.Uri,
150+
source: CameraSource.Camera,
151+
quality: 100,
152+
});
152153

153-
// CHANGE: Add in newPhotos after getPhoto call
154-
const newPhotos = [
154+
// CHANGE: Create the `fileName` with current timestamp.
155+
const fileName = Date.now() + '.jpeg';
156+
// CHANGE: Create `savedImageFile` matching `UserPhoto` interface.
157+
const savedImageFile = [
155158
{
156159
filepath: fileName,
157-
webviewPath: photo.webPath,
160+
webviewPath: capturedPhoto.webPath,
158161
},
159162
...photos,
160163
];
161-
setPhotos(newPhotos);
164+
165+
// CHANGE: Update the `photos` array with the new photo.
166+
setPhotos(savedImageFile);
162167
};
163168

164-
// CHANGE: Update return statement to include photos.
165169
return {
170+
addNewToGallery,
171+
// CHANGE: Update return statement to include `photos` array.
166172
photos,
167-
takePhoto,
168173
};
169174
}
170-
171-
// Same old code from before.
172175
```
173176

174177
`usePhotoGallery.ts` should now look like this:
175178

176-
```tsx
179+
```ts
177180
import { useState, useEffect } from 'react';
178-
import { isPlatform } from '@ionic/react';
179181
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
180182
import { Filesystem, Directory } from '@capacitor/filesystem';
181183
import { Preferences } from '@capacitor/preferences';
182-
import { Capacitor } from '@capacitor/core';
183184

184185
export function usePhotoGallery() {
185186
const [photos, setPhotos] = useState<UserPhoto[]>([]);
186-
const fileName = Date.now() + '.jpeg';
187187

188-
const takePhoto = async () => {
189-
const photo = await Camera.getPhoto({
188+
const addNewToGallery = async () => {
189+
// Take a photo
190+
const capturedPhoto = await Camera.getPhoto({
190191
resultType: CameraResultType.Uri,
191192
source: CameraSource.Camera,
192193
quality: 100,
193194
});
194195

195-
const newPhotos = [
196+
const fileName = Date.now() + '.jpeg';
197+
const savedImageFile = [
196198
{
197199
filepath: fileName,
198-
webviewPath: photo.webPath,
200+
webviewPath: capturedPhoto.webPath,
199201
},
200202
...photos,
201203
];
202-
setPhotos(newPhotos);
204+
205+
setPhotos(savedImageFile);
203206
};
204207

205208
return {
209+
addNewToGallery,
206210
photos,
207-
takePhoto,
208211
};
209212
}
210213

@@ -214,47 +217,50 @@ export interface UserPhoto {
214217
}
215218
```
216219

217-
Next, move over to `Tab2.tsx` so we can display the image on the screen. With the photo(s) stored into the main array we can display the images on the screen. Add a [Grid component](https://ionicframework.com/docs/api/grid) so that each photo will display nicely as photos are added to the gallery, and loop through each photo in the Photos array, adding an Image component (`<IonImg>`) for each. Point the `src` (source) to the photos path:
220+
Next, switch to `Tab2.tsx` to display the images. We'll add a [Grid component](../../api/grid.md) to ensure the photos display neatly as they're added to the gallery. Inside the grid, loop through each photo in the `UserPhoto`'s `photos` array. For each item, add an [Image component](../../api/img.md) and set its `src` property to the photo's path.
218221

219222
```tsx
220-
// Same old code from before.
221-
222-
// CHANGE: Import usePhotoGallery Hook
223-
import { usePhotoGallery } from '../hooks/usePhotoGallery';
224-
225223
const Tab2: React.FC = () => {
226-
// CHANGE: Get access to photos from usePhotoGallery
227-
const { photos, takePhoto } = usePhotoGallery();
228-
229-
return (
230-
<IonPage>
231-
<IonHeader>
232-
<IonToolbar>
233-
<IonTitle>Tab 2</IonTitle>
234-
</IonToolbar>
224+
// CHANGE: Add `photos` array to destructure from `usePhotoGallery()`.
225+
const { photos, addNewToGallery } = usePhotoGallery();
226+
227+
return (
228+
<IonPage>
229+
<IonHeader>
230+
<IonToolbar>
231+
<IonTitle>Photo Gallery</IonTitle>
232+
</IonToolbar>
233+
</IonHeader>
234+
<IonContent fullscreen>
235+
<IonHeader collapse="condense">
236+
<IonToolbar>
237+
<IonTitle size="large">Photo Gallery</IonTitle>
238+
</IonToolbar>
235239
</IonHeader>
236-
<IonContent>
237-
<!-- CHANGE: Add grid component and loop through each photo in the Photos Array. -->
238-
<IonGrid>
239-
<IonRow>
240-
{photos.map((photo, index) => (
241-
<IonCol size="6" key={photo.filepath}>
242-
<IonImg src={photo.webviewPath} />
243-
</IonCol>
244-
))}
245-
</IonRow>
246-
</IonGrid>
247-
<IonFab vertical="bottom" horizontal="center" slot="fixed">
248-
<IonFabButton onClick={() => takePhoto()}>
249-
<IonIcon icon={camera}></IonIcon>
250-
</IonFabButton>
251-
</IonFab>
252-
</IonContent>
253-
</IonPage>
254-
);
240+
241+
{/* CHANGE: Add a grid component to display the photos. */}
242+
<IonGrid>
243+
<IonRow>
244+
{/* CHANGE: Create a new column and image component for each photo. */}
245+
{photos.map((photo) => (
246+
<IonCol size="6" key={photo.filepath}>
247+
<IonImg src={photo.webviewPath} />
248+
</IonCol>
249+
))}
250+
</IonRow>
251+
</IonGrid>
252+
253+
<IonFab vertical="bottom" horizontal="center" slot="fixed">
254+
<IonFabButton onClick={() => addNewToGallery()}>
255+
<IonIcon icon={camera}></IonIcon>
256+
</IonFabButton>
257+
</IonFab>
258+
</IonContent>
259+
</IonPage>
260+
);
255261
};
256262
```
257263

258-
Save all files. Within the web browser, click the Camera button and take another photo. This time, the photo is displayed in the Photo Gallery!
264+
Within the web browser, click the camera button and take another photo. This time, the photo is displayed in the Photo Gallery!
259265

260266
Up next, we’ll add support for saving the photos to the filesystem, so they can be retrieved and displayed in our app at a later time.

0 commit comments

Comments
 (0)