Image slide-show viewer with multiple predefined transition styles
ATGMediaBrowser
ATGMediaBrowser is an image slide-show viewer that supports multiple predefined transition styles, and also allows the client to define new transition styles. It supports both horizontal and vertical gestures to control transitions, and adding new transitions is fun and easy. Checkout some of the transition styles below;
Vertical Zoom In Out
Horizontal Slide In
Horizontal Slide Out
Horizontal Move In Out
It supports commonly expected features like pinch-zoom, double-tap-to-zoom, interactive dismiss transition, dismiss zoomed-out to target frame etc. You can choose between linear and carousel styles for the media browser.
Installation
Carthage
ATGMediaBrowser
can be installed using Carthage. To do so, simply add the following line to your Cartfile
;
github "altayer-digital/ATGMediaBrowser" ~> 1.0.0
Cocoapods
To use ATGMediaBrowser
with cocoapods, add the following line to the Podfile
;
pod 'ATGMediaBrowser', '~> 1.0'
Usage
The media browser can be used as shown below;
let mediaBrowser = MediaBrowserViewController(dataSource: self)
present(mediaBrowser, animated: true, completion: nil)
self
needs to conform to MediaBrowserViewControllerDataSource
protocol and implement following methods;
func numberOfItems(in mediaBrowser: MediaBrowserViewController) -> Int {
// return number of images to be shown
}
func mediaBrowser(_ mediaBrowser: MediaBrowserViewController, imageAt index: Int, completion: @escaping MediaBrowserViewControllerDataSource.CompletionBlock) {
// Fetch the required image here. Pass it to the completion
// block along with the index, zoom scale, and error if any.
completion(index, image, ZoomScale.default, nil)
}
Why this approach?
You might be asking, why didn't we accept images as an array of images or as an array of URLs? Well, the reason is we wanted to stick to single responsibility principle.
Holding an array of images in memory could be trivial for a small number of images, but if it becomes large enough to cause large memory footprints, we will need to start using caching inside media browser, which is not what media browser is supposed to do.
Accepting URLs will require media browser to do network requests, download and cache the images, and then display them. That's gonna break the single responsibility.
So, media browser will stay clear of all the roles it is not supposed to do, and will stick to what it does well. On the bright side, you can decide how you will download and cache the images, which library to use for the same and in fact, have total control over the contents.
Transitions
There are 8 built-in transitions available with ATGMediaBrowser. On top of this anyone can easily create new transitions using the ContentTransformer
closure. More on that can be found in the custom transitions section.
In-built transitions
There are 8 built-in transitions available with ATGMediaBrowser. They are;
- horizontalMoveInOut
- verticalMoveInOut
- horizontalSlideOut
- verticalSlideOut
- horizontalSlideIn
- verticalSlideIn
- horizontalZoomInOut
- verticalZoomInOut
For transition to work the way it is expected, each transition type has to have it's related property set on the media browser. For example, horizontalZoomInOut
requires horizontal
gesture direction and previousToNext
draw order.
mediaBrowser.contentTransformer = DefaultContentTransformers.horizontalZoomInOut
mediaBrowser.gestureDirection = .horizontal
mediaBrowser.drawOrder = .previousToNext
TADA!, With just 3 lines of code, you have changed your media browser's transition style.
Custom transitions
Creating custom transition is super easy and fun with ATGMediaBrowser. The basic concept of ATGMediaBrowser transition is that there will be 3 views always in memory, and this will not change even if the total number of images to be shown is 1 or 10,000. The transition closure will be updating these three views' transform using the position passed into it. All the crazy stuff like transition progress, view reuse, zoom-in, zoom-out etc are already handled by ATGMediaBrowser. So, in order to create a custom transition, your responsibility will be to update the appearance of each content view corresponding to it's position.
There are 3 things to be done for creating a custom transition;
1. Choosing the gesture direction
It can be either horizontal
or vertical
. It does not necessarily needs to be a horizontal gesture for a horizontal transition. The direction simply defines the axis in which gesture will be detected, the transition can be in the opposite axis, if need be.
2. Choosing the draw order
The draw order can be either previousToNext
or nextToPrevious
.
previousToNext
means left/top content view is drawn first, then middle view and then the right/bottom view.
nextToPrevious
means right/bottom content view is drawn first, then middle view and then the left/top view.
3. Implementing the ContentTransformer
The ContentTransformer
is a closure which should update the appearance of content views based on the position. When a content view is at position0
the view should be visible to the user full screen. When the position is -1
, the view should have the position and appearance of previous(top/left normally) item. When the position is 1
, the view must be at next(bottom/right) item's position.
Below given is an example for the transition horizontalMoveInOut
;
public static let horizontalMoveInOut: ContentTransformer = { contentView, position in
let widthIncludingGap = contentView.bounds.size.width + MediaContentView.interItemSpacing
contentView.transform = CGAffineTransform(translationX: widthIncludingGap * position, y: 0.0)
}
It is as simple as that.!
More Usage
Browser style
You can set the browser style to either linear or carousel. Carousel means that you can scroll the media items in circular fashion, and linear means, you can't. carousel
is the default one.
mediaBrowser.browserStyle = .linear
Gap between media views
By default the gap will be 50.0 points.
mediaBrowser.gapBetweenMediaViews = 64.0
If you are implementing custom content transformer, you can access this value using MediaContentView.interItemSpacing
Show page control
Default is true. You can hide page control using the following code;
mediaBrowser.shouldShowPageControl = false
Hiding controls
By controls, we mean both page control and close button. You can set it to auto-hide using autoHideControls
or show/hide them manually using hideControls
.
mediaBrowser.autoHideControls = false
mediaBrowser.hideControls = false
Interactive dismissal
By default interactive dismissal is enabled. Interactive dismissal uses the gesture in direction orthogonal to the gestureDirection
. You can disable the interaction as shown below;
mediaBrowser.enableInteractiveDismissal = false
Item Index
You can access the current index of the item in media browser using currentItemIndex
variable.
Customizing close button
You can customize the close button using the data source call back func mediaBrowser(_ mediaBrowser: MediaBrowserViewController, updateCloseButton button: UIButton)
. You will be getting a UIButton
instance in there, and you can customize the appearance, use auto-layout constraints to position it, even add target to get touch events on them.
If you do not add any constraints to the button, media browser will automatically add required constraints to keep it on top right of the screen.
Dismissing the media browser into target frame
If you implement the func targetFrameForDismissal(_ mediaBrowser: MediaBrowserViewController) -> CGRect?
method, the media browser will transition the currently shown image to the supplied target frame on dismissal.