MediaBrowser: a photo and video browser for iOS with selection
🏞 A simple iOS photo and video browser with optional grid view, captions and selections written in Swift5.0
At a glance
- What is it?
- MediaBrowser is a MIT licensed Swift 6 component that turns a list of UIImage, PHAsset or URL media into a zoomable, pannable browser with optional captions, a grid view and multi-selection, using SDWebImage for caching. It suits iOS developers who need a gallery screen, and it is UIKit only with a delegate-based API.
- Who is it for?
- MediaBrowser fits an iOS team that needs a gallery or media viewer screen quickly, whether the media comes from the photo library, from disk or from the web, because it takes a plain delegate protocol and a Media array rather than a view model. It is the wrong choice for a SwiftUI codebase, since the API is entirely UIKit and delegate based, and for an app that needs a full asset management flow with editing, cropping and upload.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 40 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One view controller, three sources of media
MediaBrowser is described as a simple iOS photo and video browser with optional grid view, captions and selections, and the introduction states the core contract plainly. It can display one or more images or videos by providing either UIImage objects, PHAsset objects, or URLs to library assets, web images and videos, or local files. The browser handles the downloading and caching of photos from the web seamlessly, photos can be zoomed and panned, and optional customisable captions can be displayed.
The last part of that sentence matters for scope. This is a viewer, and it can also be used to let the user select one or more photos using either the grid or the main image view. So the same component covers a read-only gallery, a picker, and a multi-select picker, which is why the API is shaped the way it is.
The two animated gifs in the README, one for a single photo and one for multiple photos and video, plus the four smaller ones for the grid and the selection variants, show the same controller doing four jobs. The grid view is a separate mode rather than a separate screen, which is why the enableGrid and startOnGrid properties exist in the configuration list.
The audience is an iOS developer who needs a media browsing screen and does not want to write paging, zooming and caching themselves. The homepage is the CocoaDocs documentation site, and the project publishes on Swift Package Manager and CocoaPods.
The licence is MIT, and there is a Korean README at README-KR.md alongside the English one.
The delegate protocol and the Media value type
The usage is short, and the shape of it is the API design.
You create the browser with a delegate and push it onto a navigation controller:
let browser = MediaBrowser(delegate: self)
self.navigationController?.pushViewController(browser, animated: true)And you implement two required methods on the delegate:
func numberOfMedia(in mediaBrowser: MediaBrowser) -> Int {
return mediaArray.count
}
func media(for mediaBrowser: MediaBrowser, at index: Int) -> Media {
guard mediaArray.indices.contains(index) else { return Media() }
return mediaArray[index]
}The two required methods are a count and a fetch by index, which is a data source protocol in all but name. The index-based design has a consequence worth noticing: the browser asks for media lazily, one index at a time, and the guard clause in the example is there because a bounds check on an empty Media is what you return rather than crashing. Combined with the preCachingEnabled and cachingImageCount properties, the browser controls how far ahead it prefetches, and setCurrentIndex(at:) exists to set the current index path when starting, with the note to also set it before preCachingEnabled.
The Media type is the unit of content, and the same type is used for full-size media, for grid thumbnails and for captions. That is a small design decision with real consequences: the thumbnail delegate method returns a Media, and the documentation recommends a small size for it, so the browser can show a low resolution image in the grid while the main view holds the full asset.
The delegate then has a set of optional methods for the things most apps need: mediaBrowserDidFinishModalPresentation for when the user dismisses, thumbnail for the grid, captionView for a custom caption, didDisplayMedia for a paging callback, actionButtonPressed for the action button in the toolbar, isMediaSelected to report selection state, and mediaDid for the selection change callback.
Caching, prefetching and the SDWebImage dependency
Caching is delegated rather than reinvented. The README states that MediaBrowser uses the latest SDWebImage version for caching, and that this was motivated by MWPhotoBrowser. SDWebImage is the standard image loading and caching library for iOS, so a web URL passed in as media gets the same on-disk cache, request deduplication and background decode behaviour the rest of an SDWebImage-based app already has.
The config properties make the cache policy explicit. preCachingEnabled turns prefetching on, cachingImageCount sets how many images are cached ahead of the current index, and placeholderImage takes an image plus a flag for whether it is applied to all media. The loading indicator has its own cluster of properties: inner and outer ring colours, inner and outer ring widths, the font and the font colour for the percentage text, and a flag for whether that value text should be shown at all.
The zoom and paging behaviour is configured through a smaller set. zoomPhotosToFill controls whether zoomed photos fill the frame, displayMediaNavigationArrows turns the paging arrows on, skipNextPagingScrollViewPositioning is a state flag for the paging implementation, and autoPlayOnAppear starts video playback automatically.
Two of the listed properties are internal state rather than configuration, and it is worth knowing which is which before you start setting things. performingLayout, rotating, viewIsActive, didSavePreviousStateOfNavBar, skipNextPagingScrollViewPositioning and viewHasAppearedInitially are all implementation state, exposed for subclasses. leaveStatusBarAlone and isVCBasedStatusBarAppearance also relate to how the browser interacts with the host view controller's status bar behaviour, which matters if the browser is pushed into a controller that manages its own.
A very wide configuration surface
The property table in the README is the longest part of the documentation, and counting it makes the point: there are around fifty public properties, and a large share of them are appearance knobs for the navigation bar, the toolbar, the status bar and the loading indicator.
The navigation cluster covers scrollViewBackgroundColor, navigationBarTranslucent, navigationBarTextColor, navigationBarBackgroundColor, navigationBarTintColor and navigationBarStyle. The toolbar cluster covers toolbarTextColor, toolbarBarTintColor and toolbarBackgroundColor. The status bar has statusBarStyle, statusBarShouldBeHidden, leaveStatusBarAlone and isVCBasedStatusBarAppearance. The loading indicator is described above.
Behavioural properties include displayActionButton and actionButtonImage for the toolbar button, displaySelectionButtons to show the selection controls, alwaysShowControls, enableGrid, enableSwipeToDismiss, startOnGrid, disableGridAnimations, autoPlayOnAppear, hideControlsOnStartup and delayToHideElements, plus captionAlpha and toolbarAlpha for fading the chrome.
There is a design lesson here that is worth more than the list itself. A component that exposes fifty properties has a wide surface for accidental misconfiguration, and the practical risk is that two of them will fight each other. navigationBarTintColor and navigationBarBackgroundColor are the kind of pair where the interaction is undocumented, and statusBarStyle against isVCBasedStatusBarAppearance is another. The alternative for a consumer is to subclass the browser and set what you need in one place, which the presence of exposed internal state flags suggests is the intended extension path.
The documentation points to CocoaDocs as the best place to start for advanced usage, and to the demo project for all usage.
Swift 6 modernization after a long gap
The release history tells a story of a project that sat still and then moved once.
Version 2.3.0 came from 2019-03-31 and is titled Swift 5.0 and bug fixed. Version 2.2.0 from 2018-11-10 is titled Update due to PR. Then there is a gap of more than seven years, and version 3.0.0 from 2026-07-08, titled Swift 6 modernization.
The jump from a 2.x line to a 3.0 line in one step after seven years of silence is unusual, and it tells you the author treated the Swift 6 migration as a breaking change. The requirements say the library is written in Swift 6 and requires iOS 13.0 or later, and the header badges agree, one for iOS 13.0 or later and one for Swift 6.0.
The distribution story is healthy for a component. It supports both Swift Package Manager and CocoaPods, with a Package.swift at the repository root, a MediaBrowser.podspec, a Podfile, and a MediaBrowser.xcodeproj and MediaBrowser.xcworkspace for the project itself. The CocoaPods badge and the Swift Package Manager badge are both in the header, so both paths are actively published.
The last push was on 2026-09-13, a little over a month after the 3.0.0 release, so the modernization was followed by some settling work. The repository is not archived. The CI configuration is a .travis.yml, which dates from an earlier era of the project and is a small sign of its history, and the project keeps a CHANGELOG.md, a Tests/ directory and a docs/ folder for the CocoaDocs output.
Where MediaBrowser is the wrong tool
The first case is a SwiftUI app. The entire API surface is UIKit and delegate based. You push a UIViewController, you implement ObjC style protocol methods, and you configure it by setting properties on a view controller. A SwiftUI team would have to wrap it, and the wrapping is friction the component does nothing to reduce.
The second case is a full asset management flow. MediaBrowser is a browser. It displays, it zooms, it pages, it selects. It does not edit, it does not crop, it does not tag, it does not upload, and it does not persist a selection set for you. The selection callbacks give you isMediaSelected and mediaDid so you can track state, but the state is yours to hold. A product whose screen is a photo editor needs more than this, and a product whose screen is a picker with crop, filter and permission handling needs considerably more.
The third case is a very large gallery with heavy customisation needs. The configuration is broad, and a team that ends up setting thirty properties and fighting between them has outgrown the component.
The alternative worth comparing is not another gallery library, it is the platform's own picker. PHPhotoViewController, or the UIImagePickerController, gives you a system media browser with the platform's own look, its own permissions handling and its own selection model, for free and with no dependency. The difference in approach is ownership of the experience. MediaBrowser gives you a browser you can restyle completely, with your own navigation bar, your own captions and your own grid behaviour, at the cost of maintaining that surface and of the fact that a system picker will always feel more native. For a screen that needs to look like your app, MediaBrowser earns its place. For a screen that should look like iOS, the system picker is the better answer and needs no third party code.
Editorial conclusion
MediaBrowser fits an iOS team that needs a gallery or media viewer screen quickly, whether the media comes from the photo library, from disk or from the web, because it takes a plain delegate protocol and a Media array rather than a view model. It is the wrong choice for a SwiftUI codebase, since the API is entirely UIKit and delegate based, and for an app that needs a full asset management flow with editing, cropping and upload. Before adopting it, check the deployment target, since the project requires iOS 13.0 or later, and read the CocoaDocs page rather than the README tables, because a long list of public properties means the configuration surface is wide and the demo project is the better source for how a real integration looks.
Frequently asked questions
What can MediaBrowser display?
It can display one or more images or videos by providing UIImage objects, PHAsset objects, or URLs to library assets, web images and videos, or local files, and it handles the downloading and caching of web photos. Photos can be zoomed and panned, and optional customisable captions can be shown.
How do I use MediaBrowser in my app?
Create a MediaBrowser with your delegate and push it onto a navigation controller, then implement the two required delegate methods, numberOfMedia returning the count and media returning the item for an index. Optional delegate methods cover thumbnails, captions, selection state and the action button.
Which iOS and Swift versions does MediaBrowser need?
The requirements state that MediaBrowser is written in Swift 6 and requires iOS 13.0 or later. It supports both Swift Package Manager and CocoaPods.
Does MediaBrowser support selecting multiple photos?
Yes. The introduction says it can be used to let the user select one or more photos using either the grid or the main image view, and the delegate has isMediaSelected and mediaDid callbacks for that, with displaySelectionButtons controlling whether the selection controls appear.
What licence is MediaBrowser released under?
MediaBrowser is MIT licensed, with the licence file at the repository root. The latest release is 3.0.0 from 2026-07-08, titled Swift 6 modernization, and the last push was on 2026-09-13.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/younatics-mediabrowser)Was this useful?