Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
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) { }
}