SPM package is now called ExyteMediaPicker
instead of MediaPicker
, sorry for any inconveniences.
[Media]
)..sheet(isPresented: $showMediaPicker) {
MediaPicker(
isPresented: $showMediaPicker,
onChange: { medias = $0 }
)
}
The lbrary will return an array of Media
structs for you to use as you see fit. It has the following fields and methods (all the methods use async/await API):
type
- .image or .videoduration
- nil for .imagegetURL()
- returns URL
to media (automatically stores in temporary directory if needed)getThumbnailURL()
- returns URL
to media's thumbnail (for image just returns the image itself)getData()
- returns media's Data
representationgetThumbnailData()
- returns media's thumbnail Data
representationThis library lets you use both photo library and camera
Default photos grid screen has a standard header which contains the 'Done' and 'Cancel' buttons, and a simple switcher between Photos and Albums. Use it for a basic out-of-the box picker (see default picker in example project for an usage example). This can be customized (see "Init - view builders" section)
After making one photo, you see a preview of it and a little plus icon, by tapping it you return back to camera mode and can continue making as many photos as you like. Press "Done" once you're finished and you will be able to scroll through all the photos you've taken before confirming you'd like to use them. This preview screen of photos you've taken can also be customized (see "Init - view builders" section)
isPresented
- a binding to determine whether the picker should be displayed or not
onChange
- a closure that returns the selected media every time the selection changes
You can pass 1-3 view builders in order to add your own buttons and other elements to media picker screens. You can pass all, some or none of these when creating your MediaPicker
(see the custom picker in the example project for usage example). First screen you can customize is default photos grid view. Pass albumSelectionBuilder
closure like this to replace the standard one with your own view:
MediaPicker(
isPresented: $isPresented,
onChange: { selectedMedia = $0 },
albumSelectionBuilder: { defaultHeaderView, albumSelectionView, isInFullscreen in
VStack {
if !isInFullscreen {
defaultHeaderView
}
albumSelectionView
Spacer()
footerView
}
.background(Color.black)
}
)
albumSelectionBuilder
gives you two views to work with:
defaultHeaderView
- a default looking header
with photos/albums mode switcheralbumSelectionView
- the photos grid itselfisInFullscreen
- is fullscreen photo details screen displayed. Use for example to hide the header while in fullscreen mode.The second customizable screen is the one you see after taking a photo. Pass cameraSelectionBuilder
like this:
MediaPicker(
isPresented: $isPresented,
onChange: { selectedMedia = $0 },
cameraSelectionBuilder: { addMoreClosure, cancelClosure, cameraSelectionView in
VStack {
HStack {
Spacer()
Button("Done", action: { isPresented = false })
}
cameraSelectionView
HStack {
Button("Cancel", action: cancelClosure)
Spacer()
Button(action: addMoreClosure) {
Text("Take more photos")
}
}
}
}
)
cameraSelectionBuilder
gives you these parameters:
addMoreClosure
- you can call this closure on tap of your own button, it will work same as default plus icon on camera selection preview screencancelClosure
- show confirmation and return to photos grid screen if confirmedcameraSelectionView
- swipable camera photos preview collection itselfThe last one is live camera screen
MediaPicker(
isPresented: $isPresented,
onChange: { selectedMedia = $0 },
cameraViewBuilder: { cameraSheetView, cancelClosure, showPreviewClosure, takePhotoClosure, startVideoCaptureClosure, stopVideoCaptureClosure, toggleFlash, flipCamera in
cameraSheetView
.overlay(alignment: .topLeading) {
HStack {
Button("Cancel") { cancelClosure() }
.foregroundColor(Color("CustomPurple"))
Spacer()
Button("Done") { showPreviewClosure() }
.foregroundColor(Color("CustomPurple"))
}
.padding()
}
.overlay(alignment: .bottom) {
HStack {
Button("Take photo") { takePhotoClosure() }
.greenButtonStyle()
Button(videoIsBeingRecorded ? "Stop video capture" : "Capture video") {
videoIsBeingRecorded ? stopVideoCaptureClosure() : startVideoCaptureClosure()
videoIsBeingRecorded.toggle()
}
.greenButtonStyle()
}
.padding()
}
}
)
cameraViewBuilder
live camera capture view and a lot of closures to do with as you please:
cameraSheetView
- live camera capture viewcancelClosure
- if you want to display "are you sure" before closingshowPreviewClosure
- shows preview of taken photoscancelClosure
- if you want to display "are you sure" before closingstartVideoCaptureClosure
- starts video capture, you'll need a bollean varialbe to track recording statestopVideoCaptureClosure
- stops video capturetoggleFlash
- flash off/onflipCamera
- camera back/frontshowLiveCameraCell
- show live camera feed cell in the top left corner of the gallery grid
mediaSelectionType
- limit displayed media type: .photo, .video or both
mediaSelectionStyle
- a way to display selected/unselected media state: a counter or a simple checkmark
mediaSelectionLimit
- the maximum selection quantity allowed, 'nil' for unlimited selection
showFullscreenPreview
- if true - tap on media opens fullscreen preview, if false - tap on image immediately selects this image and closes the picker
applyFilter((Media) async -> Media?)
- pass a closure to apply to each of medias individually. Closures's return type is Media?
: return Media
the closure gives to you if you want it to be displayed on photo grid, or nil
if you want to exclude it. The code you apply to each media can be asyncronous (using async/await syntactics, check out FilterMediaPicker
in example project)
applyFilter(([Media]) async -> [Media])
- same but apply the closure to whole medias array. Can also be used for reodering.
If your app restricts screen rotation, you can skip this section.
We recommend locking orientation for MediaPicker, because default rotation animations don't look good on the camera screen. At the moment SwiftUI doesn't provide a way of locking screen orientation, so the library has an initializer with an orientationHandler
parameter - a closure that is called when you enter/leave the camera screen inside MediaPicker. In this closure you need to use AppDelegate to lock/unlock the rotation - see example project for implementation.
albums
- a list of user's albums (like in Photos app), if you want to display them differently than showingDefaultHeader
does.
pickerMode
- set this if you don't plan to use the default header. Available options are:
mediaPickerTheme
- color settings. Example usage (see MediaPickerTheme
for all available settings):
MediaPicker(...)
.mediaPickerTheme(
main: .init(
background: .black
),
selection: .init(
emptyTint: .white,
emptyBackground: .black.opacity(0.25),
selectedTint: Color("CustomPurple")
)
)
Here is an example of how you can customize colors and elements to create a custom looking picker:
To try out the MediaPicker examples:
https://github.com/exyte/MediaPicker.git
Examples/Examples.xcworkspace
in the Xcodedependencies: [
.package(url: "https://github.com/exyte/ExyteMediaPicker.git")
]
pod 'ExyteMediaPicker'
github "Exyte/MediaPicker"
PopupView - Toasts and popups library
Grid - The most powerful Grid container
ScalingHeaderScrollView - A scroll view with a sticky header which shrinks as you scroll
AnimatedTabBar - A tabbar with a number of preset animations
Chat - Chat UI framework with fully customizable message cells, input view, and a built-in media picker
OpenAI Wrapper lib for OpenAI REST API
AnimatedGradient - Animated linear gradient
ConcentricOnboarding - Animated onboarding flow
FloatingButton - Floating button menu
ActivityIndicatorView - A number of animated loading indicators
ProgressIndicatorView - A number of animated progress indicators
FlagAndCountryCode - Phone codes and flags for every country
SVGView - SVG parser
LiquidSwipe - Liquid navigation animation