공식문서: https://developer.apple.com/documentation/uikit/uiimagepickercontroller
UIImagePickerController
UIImagePickerController는 iOS 앱 내에서 사진이나 동영상을 촬영하거나, 기기의 사진 라이브러리에서 이미지를 선택할 수 있도록 시스템 기본 UI를 제공하는 뷰 컨트롤러입니다.
주요 모드(Source Type)
이 컨트롤러의 화면과 역할은 표시하기 전에 설정하는 소스 타입(Source Type)에 따라 달라집니다.
.camera: 새로운 사진이나 동영상을 촬영할 수 있는 카메라 인터페이스를 제공합니다..photoLibrary/.savedPhotoAlbum: 기기에 저장된 기존 사진과 동영상을 고를 수 있는 앨범 인터페이스를 제공합니다.
기본 사용 방법
- 지원 여부 확인:
isSourceTypeAvailable메서드를 사용해 현재 기기에서 카메라나 사진첩 접근이 가능한지 먼저 확인합니다. - 미디어 타입 설정: 사진만 고르게 할 것인지 동영상도 고르게 할 것인지
mediaTypes속성을 통해 설정합니다. - 화면 띄우기(Present):
- iPhone: 모달 방식으로 띄움
- iPad: 카메라는 전체화면을 사용할 수 있지만 사진첩 앨범을 띄울 때는 반드시 팝업 형태로 띄워야 함
- 결과 처리(Delegate): 사용자가 사진을 선택하거나 취소 버튼을 눌렀을 때의 동작을 처리하기 위해
UIImagePickerControllerDelegate를 설정하여 결과값을 넘겨받습니다.
주의사항
- 기본적으로 세로(Portrait) 모드만 지원합니다.
- 기본 UI를 사용하고 싶지 않다면 해당 클래스 대신
AVFoundation이나PhotoKit프레임워크를 사용해야 합니다.
데모 앱
import SwiftUI
import UIKit
import UniformTypeIdentifiers
struct ContentView: View {
// 선택하거나 촬영한 이미지를 SwiftUI 화면에 표시하기 위한 상태값입니다.
@State private var selectedImage: UIImage?
// 하나의 UIImagePickerController로 카메라와 사진첩을 번갈아 열기 위해 sourceType을 저장합니다.
@State private var pickerSource: UIImagePickerController.SourceType = .photoLibrary
@State private var isShowingPicker = false
// 시뮬레이터처럼 카메라를 사용할 수 없는 환경을 안내하기 위한 alert 상태입니다.
@State private var alertMessage = ""
@State private var isShowingAlert = false
var body: some View {
NavigationStack {
VStack(spacing: 24) {
imagePreview
VStack(spacing: 12) {
Button {
presentPicker(for: .camera)
} label: {
Label("사진 촬영", systemImage: "camera")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
Button {
presentPicker(for: .photoLibrary)
} label: {
Label("사진첩에서 선택", systemImage: "photo.on.rectangle")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
}
}
.padding()
.navigationTitle("UIImagePickerController Demo")
.toolbarTitleDisplayMode(.inline)
}
.sheet(isPresented: $isShowingPicker) {
ImagePicker(sourceType: pickerSource, selectedImage: $selectedImage)
.ignoresSafeArea()
}
.alert("사용할 수 없음", isPresented: $isShowingAlert) {
Button("확인", role: .cancel) {}
} message: {
Text(alertMessage)
}
}
private var imagePreview: some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(.secondary.opacity(0.12))
if let selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFit()
.clipShape(RoundedRectangle(cornerRadius: 8))
} else {
ContentUnavailableView(
"선택된 이미지 없음",
systemImage: "photo",
description: Text("사진을 촬영하거나 사진첩에서 이미지를 선택하세요.")
)
}
}
.frame(maxWidth: .infinity)
.aspectRatio(1, contentMode: .fit)
}
private func presentPicker(
for sourceType: UIImagePickerController.SourceType
) {
// UIImagePickerController는 기기마다 지원하는 sourceType이 다를 수 있으므로 먼저 확인합니다.
guard UIImagePickerController.isSourceTypeAvailable(sourceType) else {
alertMessage = sourceType == .camera
? "이 기기에서는 카메라를 사용할 수 없습니다."
: "이 기기에서는 사진첩을 사용할 수 없습니다."
isShowingAlert = true
return
}
pickerSource = sourceType
isShowingPicker = true
}
}
// UIKit의 UIImagePickerController를 SwiftUI에서 sheet로 띄우기 위한 래퍼입니다.
struct ImagePicker: UIViewControllerRepresentable {
let sourceType: UIImagePickerController.SourceType
@Binding var selectedImage: UIImage?
@Environment(\.dismiss) private var dismiss
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
// 이미지 선택/촬영만 다루도록 mediaTypes를 이미지로 제한합니다.
picker.mediaTypes = [UTType.image.identifier]
picker.allowsEditing = false
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(
_ uiViewController: UIImagePickerController,
context: Context
) {
}
func makeCoordinator() -> Coordinator {
Coordinator(selectedImage: $selectedImage, dismiss: dismiss)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@Binding private var selectedImage: UIImage?
private let dismiss: DismissAction
init(selectedImage: Binding<UIImage?>, dismiss: DismissAction) {
_selectedImage = selectedImage
self.dismiss = dismiss
}
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
) {
// 선택 결과에서 원본 이미지를 꺼내 SwiftUI 상태값에 전달합니다.
selectedImage = info[.originalImage] as? UIImage
dismiss()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss()
}
}
}
SwiftUI에서는 UIImagePickerController를 직접 사용할 수 없기 때문에 UIViewControllerRepresentable로 감싸서 사용했습니다. 버튼을 누르면 카메라 또는 사진첩을 sourceType으로 지정해 picker를 띄우고, 선택이 끝나면 delegate 메서드에서 originalImage를 가져와 SwiftUI의 @State값에 저장합니다. 저장된 이미지는 Image(uiImage:)로 화면에 표시합니다.
스크린샷



