< How to dynamically adjust the color of an SF Symbol | How to let users import videos using PhotosPicker > |
Updated for Xcode 14.2
New in iOS 16
SwiftUI’s PhotosPicker
brings up the system-standard photo import user interface, allowing users to select one or more images or videos to bring in to your app.
To use it to bring in an image, you need to import the PhotosUI
module, then create some storage for a PhotosPickerItem
to save what the user selected, and also an Image
property to store the loaded asset. You can then watch for the PhotosPickerItem
changing, and load its contents like this:
import PhotosUI
import SwiftUI
struct ContentView: View {
@State private var avatarItem: PhotosPickerItem?
@State private var avatarImage: Image?
var body: some View {
VStack {
PhotosPicker("Select avatar", selection: $avatarItem, matching: .images)
if let avatarImage {
avatarImage
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
}
}
.onChange(of: avatarItem) { _ in
Task {
if let data = try? await avatarItem?.loadTransferable(type: Data.self) {
if let uiImage = UIImage(data: data) {
avatarImage = Image(uiImage: uiImage)
return
}
}
print("Failed")
}
}
}
}
Download this as an Xcode project
Yes, you need to load a Data
object, bounce it through UIImage
, then convert that into an Image
– the API could be a whole lot simpler, particularly given that Image
already conforms to Transerable
, but I’m afraid we’re stuck with it.
If you want more control over the data that is selected, adjust the matching
parameter based on what you’re looking for:
matching: .screenshots
if you only want screenshots.matching: .any(of: [.panoramas, .screenshots])
if you want either of those types.matching: .not(.videos)
if you want any media that isn’t a video.matching: .any(of: [.images, .not(.screenshots)]))
if you want all kinds of images except screenshots.If you want to let the user select multiple images, you should use an array of PhotosPickerItem
objects then load them individually using a similar process:
import PhotosUI
import SwiftUI
struct ContentView: View {
@State private var selectedItems = [PhotosPickerItem]()
@State private var selectedImages = [Image]()
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(0..<selectedImages.count, id: \.self) { i in
selectedImages[i]
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
}
}
}
.toolbar {
PhotosPicker("Select images", selection: $selectedItems, matching: .images)
}
.onChange(of: selectedItems) { _ in
Task {
selectedImages.removeAll()
for item in selectedItems {
if let data = try? await item.loadTransferable(type: Data.self) {
if let uiImage = UIImage(data: data) {
let image = Image(uiImage: uiImage)
selectedImages.append(image)
}
}
}
}
}
}
}
}
Download this as an Xcode project
SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.