In this tutorial, we’ll walk through creating a simple photo gallery app that displays a grid of images and allows users to view and zoom into individual photos.
What We’re Building
Our app will have two main screens:
- A grid view showing thumbnails of all our photos
- A detail view where users can see a larger version of a photo and zoom in
Let’s break down the key components and explain how they work.
The Photo Model
First, we need to define what a photo is in our app:
struct Photo: Identifiable { let id = UUID() let name: String }
This Photo
struct is simple:
- It has an
id
(a unique identifier) which SwiftUI uses to keep track of each photo. - It has a
name
, which we’ll use to load the image file.
We also create some sample data:
let samplePhotos = (1...20).map { Photo(name: "photo\($0)") }
This creates 20 Photo
objects with names like “photo1”, “photo2”, etc.
The Main View
Our main view, ContentView
, displays the grid of photos:
struct ContentView: View { let columns = [GridItem(.adaptive(minimum: 100))] var body: some View { NavigationView { ScrollView { LazyVGrid(columns: columns, spacing: 20) { ForEach(samplePhotos) { photo in NavigationLink(destination: PhotoDetailView(photo: photo)) { Image(photo.name) .resizable() .scaledToFill() .frame(width: 100, height: 100) .clipShape(RoundedRectangle(cornerRadius: 10)) } } } .padding() } .navigationTitle("Photo Gallery") } } }
Let’s break this down:
- We use a
NavigationView
to allow navigation between the grid and detail views. - Inside, we have a
ScrollView
with aLazyVGrid
. This creates a scrollable grid of items. - The
ForEach
loop creates aNavigationLink
for each photo. This link leads to the detail view when tapped. - Each photo is displayed as an
Image
, resized to fit a 100×100 frame with rounded corners.
The Detail View
When a user taps a photo, they see the PhotoDetailView
:
struct PhotoDetailView: View { let photo: Photo @State private var scale: CGFloat = 1.0 var body: some View { VStack { ZoomableImageView(imageName: photo.name, scale: $scale) } .frame(maxWidth: .infinity, maxHeight: .infinity) .navigationTitle("Photo Details") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Reset Zoom") { scale = 1.0 } } } } }
This view:
- Displays a
ZoomableImageView
(which we’ll explain next). - Shows the photo’s name as the navigation title.
- Adds a “Reset Zoom” button to the navigation bar.
The Zoomable Image View
The ZoomableImageView
allows users to zoom in on the photo:
struct ZoomableImageView: View { let imageName: String @Binding var scale: CGFloat var body: some View { Image(imageName) .resizable() .scaledToFit() .scaleEffect(scale) .gesture( MagnificationGesture() .onChanged { value in scale = value.magnitude } ) .onTapGesture(count: 2) { if scale > 1 { scale = 1 } else { scale = 2 } } } }
Here’s what’s happening:
- The image is displayed and made resizable to fit the screen.
- We apply a
scaleEffect
based on thescale
value. - A
MagnificationGesture
allows the user to pinch to zoom. - A double-tap gesture toggles between normal size and 2x zoom.
Putting It All Together
When you run this app, you’ll see a grid of photo thumbnails. Tapping on a photo takes you to a detail view where you can:
- See the full photo
- Pinch to zoom in or out (Use Option key to Zoom in Simulator)
- Double-tap to quickly toggle between normal and 2x zoom
- Tap the “Reset Zoom” button to return to the original size
This simple photo gallery app demonstrates several key SwiftUI concepts:
- Creating and using custom views
- Implementing navigation between views
- Using gestures for user interaction
- Creating a grid layout
- Basic state management with
@State
and@Binding
Feel free to expand on this app by adding your own photos, implementing more features, or customizing the design to make it your own!
Comments
One response to “Building a Simple Photo Gallery App in SwiftUI”
[…] an iOS app that allows users to search for photos using the Pixabay API, display the results in a photo gallery, and view individual photos in detail. We’ll use SwiftUI for the user interface, Alamofire […]