Path: blob/main/Signal/src/ViewControllers/MediaGallery/Transitions/MediaPresentationContext.swift
1 views
//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import UIKit
enum Media {
case gallery(MediaGalleryItem)
case image(UIImage)
var image: UIImage? {
switch self {
case let .gallery(item):
return try? item.attachmentStream.attachmentStream.decryptedImage()
case let .image(image):
return image
}
}
}
struct RoundedCorners: Equatable {
var topLeft: CGFloat
var topRight: CGFloat
var bottomRight: CGFloat
var bottomLeft: CGFloat
static func all(_ radius: CGFloat) -> RoundedCorners {
return RoundedCorners(topLeft: radius, topRight: radius, bottomRight: radius, bottomLeft: radius)
}
var isAllCornerRadiiEqual: Bool {
topLeft == topRight && topLeft == bottomLeft && topLeft == bottomRight
}
}
enum MediaViewShape {
case circle
case rectangle(CGFloat)
case variableRoundedCorners(RoundedCorners)
}
struct MediaPresentationContext {
static let animationDuration: TimeInterval = 0.25
let mediaView: UIView
let presentationFrame: CGRect
let backgroundColor: UIColor
let mediaViewShape: MediaViewShape
let clippingAreaInsets: UIEdgeInsets?
init(
mediaView: UIView,
presentationFrame: CGRect,
backgroundColor: UIColor = .clear,
mediaViewShape: MediaViewShape = .rectangle(0),
clippingAreaInsets: UIEdgeInsets? = nil,
) {
self.mediaView = mediaView
self.presentationFrame = presentationFrame
self.backgroundColor = backgroundColor
self.mediaViewShape = mediaViewShape
self.clippingAreaInsets = clippingAreaInsets
}
}
// There are two kinds of AnimationControllers that interact with the media detail view. Both
// appear to transition the media view from one VC to it's corresponding location in the
// destination VC.
//
// MediaPresentationContextProvider is either a target or destination VC which can provide the
// details necessary to facilite this animation.
//
// First, the MediaZoomAnimationController is non-interactive. We use it whenever we're going to
// show the Media detail pager.
//
// We can get there several ways:
// From conversation settings, this can be a push or a pop from the tileView.
// From conversationView/MessageDetails this can be a modal present or a pop from the tile view.
//
// The other animation controller, the MediaDismissAnimationController is used when we're going to
// stop showing the media pager. This can be a pop to the tile view, or a modal dismiss.
protocol MediaPresentationContextProvider {
func mediaPresentationContext(item: Media, in coordinateSpace: UICoordinateSpace) -> MediaPresentationContext?
func mediaWillPresent(fromContext: MediaPresentationContext)
func mediaWillPresent(toContext: MediaPresentationContext)
func mediaDidPresent(fromContext: MediaPresentationContext)
func mediaDidPresent(toContext: MediaPresentationContext)
func mediaWillDismiss(fromContext: MediaPresentationContext)
func mediaWillDismiss(toContext: MediaPresentationContext)
func mediaDidDismiss(fromContext: MediaPresentationContext)
func mediaDidDismiss(toContext: MediaPresentationContext)
}
extension MediaPresentationContextProvider {
func mediaWillPresent(fromContext: MediaPresentationContext) { }
func mediaWillPresent(toContext: MediaPresentationContext) { }
func mediaDidPresent(fromContext: MediaPresentationContext) { }
func mediaDidPresent(toContext: MediaPresentationContext) { }
func mediaWillDismiss(fromContext: MediaPresentationContext) { }
func mediaWillDismiss(toContext: MediaPresentationContext) { }
func mediaDidDismiss(fromContext: MediaPresentationContext) { }
func mediaDidDismiss(toContext: MediaPresentationContext) { }
}