element-ios/Riot/Modules/SlidingModal/SlidingModalPresentationAnimator.swift
2019-11-28 17:30:01 +01:00

135 lines
5.4 KiB
Swift

/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
/// `SlidingModalPresentationAnimator` handles the animations for a custom sliding view controller transition.
final class SlidingModalPresentationAnimator: NSObject {
// MARK: - Constants
private enum AnimationDuration {
static let presentation: TimeInterval = 0.2
static let dismissal: TimeInterval = 0.3
}
// MARK: - Properties
private let isPresenting: Bool
// MARK: - Setup
/// Instantiate a SlidingModalPresentationAnimator object.
///
/// - Parameter isPresenting: true to animate presentation or false to animate dismissal
required public init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}
// MARK: - Private
// Animate presented view controller presentation
private func animatePresentation(using transitionContext: UIViewControllerContextTransitioning) {
guard let presentedViewController = transitionContext.viewController(forKey: .to),
let sourceViewController = transitionContext.viewController(forKey: .from) else {
return
}
guard let presentedViewControllerView = presentedViewController.view else {
return
}
let containerView = transitionContext.containerView
let slidingModalContainerView = SlidingModalContainerView.instantiate()
slidingModalContainerView.alpha = 0
slidingModalContainerView.updateDimmingViewAlpha(0.0)
// Add presented view controller view to slidingModalContainerView content view
slidingModalContainerView.setContentView(presentedViewControllerView)
// Add slidingModalContainerView to container view
containerView.vc_addSubViewMatchingParent(slidingModalContainerView)
containerView.layoutIfNeeded()
// Adapt slidingModalContainerView content view height from presentedViewControllerView height
if let slidingModalPresentable = presentedViewController as? SlidingModalPresentable {
let slidingModalContainerViewContentViewWidth = slidingModalContainerView.contentViewFrame.width
let presentableHeight = slidingModalPresentable.layoutHeightFittingWidth(slidingModalContainerViewContentViewWidth)
slidingModalContainerView.updateContentViewMaxHeight(presentableHeight)
slidingModalContainerView.updateContentViewLayout()
}
// Hide slidingModalContainerView content view
slidingModalContainerView.prepareDismissAnimation()
containerView.layoutIfNeeded()
let animationDuration = self.transitionDuration(using: transitionContext)
slidingModalContainerView.preparePresentAnimation()
slidingModalContainerView.alpha = 1
UIView.animate(withDuration: animationDuration, animations: {
containerView.layoutIfNeeded()
slidingModalContainerView.updateDimmingViewAlpha(1.0)
}, completion: { completed in
transitionContext.completeTransition(completed)
})
}
// Animate presented view controller dismissal
private func animateDismissal(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let slidingModalContainerView = self.slidingModalContainerView(from: transitionContext)
let animationDuration = self.transitionDuration(using: transitionContext)
slidingModalContainerView?.prepareDismissAnimation()
UIView.animate(withDuration: animationDuration, animations: {
containerView.layoutIfNeeded()
slidingModalContainerView?.updateDimmingViewAlpha(0.0)
}, completion: { completed in
transitionContext.completeTransition(completed)
})
}
private func slidingModalContainerView(from transitionContext: UIViewControllerContextTransitioning) -> SlidingModalContainerView? {
let modalContentView = transitionContext.containerView.subviews.first(where: { view -> Bool in
view is SlidingModalContainerView
}) as? SlidingModalContainerView
return modalContentView
}
}
// MARK: - UIViewControllerAnimatedTransitioning
extension SlidingModalPresentationAnimator: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return self.isPresenting ? AnimationDuration.presentation : AnimationDuration.dismissal
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if self.isPresenting {
self.animatePresentation(using: transitionContext)
} else {
self.animateDismissal(using: transitionContext)
}
}
}