Profile Photo Selection from Library
Complete guide to selecting profile photos from device library and displaying them in UIImageView
Note:
This tutorial covers implementing profile photo selection from the device library and displaying the selected image in a UIImageView. This is a common feature in iOS apps for user profile customization.
Overview
Hello!
In this guide, we will implement functionality that allows users to select an image from their library when they tap on a default profile photo, and then display the selected image in a UIImageView.
Reference:
- Swift — CocoaPods — Firebase Integration
- Firebase User Registration
- Firebase User Login
- Firebase User Redirect
Step 1: Making the Photo Tappable
First, add a UIImageView to your Storyboard and connect it to your Swift file by giving it an ID using control or right-click.
To make the image we created interactive for user interaction, let's define the following code in the viewDidLoad function:
override func viewDidLoad() {
super.viewDidLoad()
// Make the image view tappable
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
profileImageView.isUserInteractionEnabled = true
profileImageView.addGestureRecognizer(tapGesture)
}
@objc func imageTapped() {
// Handle tap action
selectImageFromLibrary()
}
The #selector part requires an Objective-C function. This function specifies what to do when tapped with (imageTapped).
Step 2: Photo Selection from Library
For photo selection from the library, we need to call two libraries in our class: UIImagePickerControllerDelegate and UINavigationControllerDelegate.
import UIKit
class ProfileViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@IBOutlet weak var profileImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
setupProfileImage()
}
private func setupProfileImage() {
// Make the image view tappable
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
profileImageView.isUserInteractionEnabled = true
profileImageView.addGestureRecognizer(tapGesture)
// Set default profile image
profileImageView.image = UIImage(named: "default_profile")
profileImageView.layer.cornerRadius = profileImageView.frame.width / 2
profileImageView.clipsToBounds = true
}
@objc func imageTapped() {
selectImageFromLibrary()
}
}
Assign UIImagePickerController to a variable and specify that it will be taken from the library in the specified view. Let's make it happen with present.
private func selectImageFromLibrary() {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.sourceType = .photoLibrary
imagePickerController.allowsEditing = true
present(imagePickerController, animated: true, completion: nil)
}
Step 3: Displaying Selected Photo in UIImageView
After the user makes a selection, we need to display the tapped photo in the UIImageView. For this, you can call the function by typing didFinishPickingMediaWithInfo in an empty code block.
This function defines what to do after the selection. We can perform the operation by specifying the image in the code block and the original photo in the info section. We can write dismiss to close the function.
The code we created will look like the following in the final stage:
// MARK: - UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let selectedImage = info[.editedImage] as? UIImage {
// Use edited image if available
profileImageView.image = selectedImage
} else if let originalImage = info[.originalImage] as? UIImage {
// Use original image if no edited version
profileImageView.image = originalImage
}
// Save the selected image to UserDefaults or Firebase Storage
saveProfileImage()
// Dismiss the picker
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
Step 4: Complete Implementation
Here's the complete implementation for profile photo selection:
import UIKit
class ProfileViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@IBOutlet weak var profileImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var emailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupProfileImage()
loadUserProfile()
}
private func setupProfileImage() {
// Make the image view tappable
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
profileImageView.isUserInteractionEnabled = true
profileImageView.addGestureRecognizer(tapGesture)
// Configure image view appearance
profileImageView.layer.cornerRadius = profileImageView.frame.width / 2
profileImageView.clipsToBounds = true
profileImageView.layer.borderWidth = 2
profileImageView.layer.borderColor = UIColor.systemBlue.cgColor
// Set default image
if let savedImage = loadSavedProfileImage() {
profileImageView.image = savedImage
} else {
profileImageView.image = UIImage(named: "default_profile")
}
}
@objc func imageTapped() {
showImagePickerOptions()
}
private func showImagePickerOptions() {
let alertController = UIAlertController(title: "Select Photo", message: "Choose a photo source", preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) { _ in
self.selectImageFromCamera()
}
let libraryAction = UIAlertAction(title: "Photo Library", style: .default) { _ in
self.selectImageFromLibrary()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cameraAction)
alertController.addAction(libraryAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
private func selectImageFromCamera() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.sourceType = .camera
imagePickerController.allowsEditing = true
present(imagePickerController, animated: true, completion: nil)
} else {
showAlert(title: "Camera Not Available", message: "Camera is not available on this device")
}
}
private func selectImageFromLibrary() {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.sourceType = .photoLibrary
imagePickerController.allowsEditing = true
present(imagePickerController, animated: true, completion: nil)
}
// MARK: - UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let selectedImage = info[.editedImage] as? UIImage {
// Use edited image if available
profileImageView.image = selectedImage
} else if let originalImage = info[.originalImage] as? UIImage {
// Use original image if no edited version
profileImageView.image = originalImage
}
// Save the selected image
saveProfileImage()
// Dismiss the picker
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// MARK: - Image Storage
private func saveProfileImage() {
guard let image = profileImageView.image else { return }
// Save to UserDefaults (for demo purposes)
if let imageData = image.jpegData(compressionQuality: 0.8) {
UserDefaults.standard.set(imageData, forKey: "profileImage")
}
// TODO: Upload to Firebase Storage
uploadImageToFirebase(image)
}
private func loadSavedProfileImage() -> UIImage? {
if let imageData = UserDefaults.standard.data(forKey: "profileImage") {
return UIImage(data: imageData)
}
return nil
}
private func uploadImageToFirebase(_ image: UIImage) {
// TODO: Implement Firebase Storage upload
// This would typically involve:
// 1. Convert image to Data
// 2. Upload to Firebase Storage
// 3. Get download URL
// 4. Save URL to Firestore
}
// MARK: - User Profile
private func loadUserProfile() {
// Load user information
if let user = Auth.auth().currentUser {
nameLabel.text = user.displayName ?? "User"
emailLabel.text = user.email
}
}
private func showAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
Step 5: Enhanced Features
Image Compression and Optimization
private func compressImage(_ image: UIImage) -> Data? {
let maxSize: CGFloat = 1024 // Maximum dimension
let scale = min(maxSize / image.size.width, maxSize / image.size.height)
let newSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image.draw(in: CGRect(origin: .zero, size: newSize))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage?.jpegData(compressionQuality: 0.8)
}
Firebase Storage Integration
import FirebaseStorage
private func uploadImageToFirebase(_ image: UIImage) {
guard let imageData = compressImage(image) else { return }
let storageRef = Storage.storage().reference()
let imageRef = storageRef.child("profile_images/\(Auth.auth().currentUser?.uid ?? "unknown").jpg")
let uploadTask = imageRef.putData(imageData, metadata: nil) { metadata, error in
if let error = error {
print("Error uploading image: \(error)")
return
}
// Get download URL
imageRef.downloadURL { url, error in
if let downloadURL = url {
self.saveProfileImageURL(downloadURL.absoluteString)
}
}
}
// Show upload progress
uploadTask.observe(.progress) { snapshot in
let progress = Double(snapshot.progress!.completedUnitCount) / Double(snapshot.progress!.totalUnitCount)
print("Upload progress: \(progress * 100)%")
}
}
private func saveProfileImageURL(_ url: String) {
// Save to Firestore
if let userID = Auth.auth().currentUser?.uid {
let db = Firestore.firestore()
db.collection("users").document(userID).updateData([
"profileImageURL": url
]) { error in
if let error = error {
print("Error saving profile image URL: \(error)")
} else {
print("Profile image URL saved successfully")
}
}
}
}
Image Filters and Effects
private func applyImageFilter(_ image: UIImage) -> UIImage {
let context = CIContext()
let ciImage = CIImage(image: image)
// Apply a simple filter (you can customize this)
let filter = CIFilter(name: "CIPhotoEffectNoir")
filter?.setValue(ciImage, forKey: kCIInputImageKey)
if let outputImage = filter?.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage)
}
return image
}
Step 6: Permissions
Camera and Photo Library Permissions
Add these keys to your Info.plist:
<key>NSCameraUsageDescription</key>
<string>This app needs access to camera to take profile photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photo library to select profile photos.</string>
Check and Request Permissions
import Photos
private func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized, .limited:
selectImageFromLibrary()
case .denied, .restricted:
showPermissionAlert()
case .notDetermined:
PHPhotoLibrary.requestAuthorization { status in
DispatchQueue.main.async {
if status == .authorized || status == .limited {
self.selectImageFromLibrary()
} else {
self.showPermissionAlert()
}
}
}
@unknown default:
showPermissionAlert()
}
}
private func showPermissionAlert() {
let alert = UIAlertController(
title: "Permission Required",
message: "Please enable photo library access in Settings to select profile photos.",
preferredStyle: .alert
)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { _ in
if let settingsURL = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(settingsURL)
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(settingsAction)
alert.addAction(cancelAction)
present(alert, animated: true, completion: nil)
}
Step 7: Best Practices
Performance Optimization
- Image Compression: Always compress images before storage
- Memory Management: Dispose of large images properly
- Caching: Cache images locally for better performance
- Background Processing: Handle image processing in background
User Experience
- Loading States: Show progress during image upload
- Error Handling: Provide clear error messages
- Image Validation: Check image size and format
- Fallback Images: Provide default images when needed
Troubleshooting
Common Issues:
- "Permission denied" - Check Info.plist permissions
- "Image not displaying" - Verify UIImageView connections
- "Memory issues" - Implement proper image compression
- "Upload failures" - Check Firebase configuration
Note:
Profile photo selection is a common feature that enhances user experience. Remember to handle permissions properly, compress images for performance, and provide clear feedback to users during the process.