Implement dialpad view controller

This commit is contained in:
ismailgulek
2021-01-13 21:51:47 +03:00
parent 313ff48feb
commit aa6f3e8e60
7 changed files with 853 additions and 155 deletions
@@ -0,0 +1,233 @@
//
// Copyright 2020 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
/// Controller for custom sized presentations. By default, presented view controller will be sized as both half of the screen in width and height, and will be centered to the screen. Implement `CustomSizedPresentable` in presented view controller to change that if needed. This class can also be set as `transitioningDelegate` as presented view controller, as it's conforming `UIViewControllerTransitioningDelegate`.
@objcMembers
class CustomSizedPresentationController: UIPresentationController {
// MARK: - Public Properties
/// Corner radius for presented view controller's view. Default value is `8.0`.
var cornerRadius: CGFloat = 8.0
/// Background color of dimming view, which is located behind the presented view controller's view. Default value is `white with 0.5 alpha`.
var dimColor: UIColor = UIColor(white: 0.0, alpha: 0.5)
/// Dismiss view controller when background tapped. Default value is `true`.
var dismissOnBackgroundTap: Bool = true
// MARK: - Private Properties
/// Dim view
private var dimmingView: UIView!
/// Wrapper view for presentation. It's introduced to handle corner radius on presented view controller's view and it's superview of all other views.
private var presentationWrappingView: UIView!
// MARK: - Initializer
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
presentedViewController.modalPresentationStyle = .custom
}
// MARK: - Actions
@objc private func dimmingViewTapped(_ sender: UITapGestureRecognizer) {
if dismissOnBackgroundTap {
presentedViewController.dismiss(animated: true, completion: nil)
}
}
// MARK: - Presentation
override func presentationTransitionWillBegin() {
guard let presentedViewControllerView = super.presentedView else { return }
// Wrap the presented view controller's view in an intermediate hierarchy
// that applies a shadow and rounded corners to the top-left and top-right
// edges. The final effect is built using three intermediate views.
//
// presentationWrapperView <- shadow
// |- presentationRoundedCornerView <- rounded corners (masksToBounds)
// |- presentedViewControllerWrapperView
// |- presentedViewControllerView (presentedViewController.view)
//
// SEE ALSO: The note in AAPLCustomPresentationSecondViewController.m.
do {
let presentationWrapperView = UIView(frame: frameOfPresentedViewInContainerView)
presentationWrapperView.layer.shadowOffset = CGSize(width: 0, height: -2)
presentationWrapperView.layer.shadowRadius = 10
presentationWrapperView.layer.shadowColor = UIColor(white: 0, alpha: 0.5).cgColor
presentationWrappingView = presentationWrapperView
// presentationRoundedCornerView is CORNER_RADIUS points taller than the
// height of the presented view controller's view. This is because
// the cornerRadius is applied to all corners of the view. Since the
// effect calls for only the top two corners to be rounded we size
// the view such that the bottom CORNER_RADIUS points lie below
// the bottom edge of the screen.
let cornerViewRect = presentationWrapperView.bounds//.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: -cornerRadius, right: 0))
let presentationRoundedCornerView = UIView(frame: cornerViewRect)
presentationRoundedCornerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
presentationRoundedCornerView.layer.cornerRadius = cornerRadius
presentationRoundedCornerView.layer.masksToBounds = true
// To undo the extra height added to presentationRoundedCornerView,
// presentedViewControllerWrapperView is inset by CORNER_RADIUS points.
// This also matches the size of presentedViewControllerWrapperView's
// bounds to the size of -frameOfPresentedViewInContainerView.
let wrapperRect = presentationRoundedCornerView.bounds
let presentedViewControllerWrapperView = UIView(frame: wrapperRect)
presentedViewControllerWrapperView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Add presentedViewControllerView -> presentedViewControllerWrapperView.
presentedViewControllerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
presentedViewControllerView.frame = presentedViewControllerWrapperView.bounds
presentedViewControllerWrapperView.addSubview(presentedViewControllerView)
// Add presentedViewControllerWrapperView -> presentationRoundedCornerView.
presentationRoundedCornerView.addSubview(presentedViewControllerWrapperView)
// Add presentationRoundedCornerView -> presentationWrapperView.
presentationWrapperView.addSubview(presentationRoundedCornerView)
}
// Add a dimming view behind presentationWrapperView. self.presentedView
// is added later (by the animator) so any views added here will be
// appear behind the -presentedView.
do {
let dimmingView = UIView(frame: containerView?.bounds ?? .zero)
dimmingView.backgroundColor = dimColor
dimmingView.isOpaque = false
dimmingView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dimmingViewTapped(_:)))
dimmingView.addGestureRecognizer(tapGestureRecognizer)
self.dimmingView = dimmingView
containerView?.addSubview(dimmingView)
// Get the transition coordinator for the presentation so we can
// fade in the dimmingView alongside the presentation animation.
let transitionCoordinator = self.presentingViewController.transitionCoordinator
dimmingView.alpha = 0.0
transitionCoordinator?.animate(alongsideTransition: { _ in
self.dimmingView?.alpha = 1.0
}, completion: nil)
}
}
override func presentationTransitionDidEnd(_ completed: Bool) {
if !completed {
presentationWrappingView = nil
dimmingView = nil
}
}
// MARK: - Dismissal
override func dismissalTransitionWillBegin() {
guard let coordinator = presentingViewController.transitionCoordinator else {
dimmingView.alpha = 0.0
return
}
coordinator.animate(alongsideTransition: { _ in
self.dimmingView.alpha = 0.0
})
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
if completed {
presentationWrappingView = nil
dimmingView = nil
}
}
// MARK: - Overrides
override var presentedView: UIView? {
return presentationWrappingView
}
override func size(forChildContentContainer container: UIContentContainer,
withParentContainerSize parentSize: CGSize) -> CGSize {
guard container === presentedViewController else {
return super.size(forChildContentContainer: container, withParentContainerSize: parentSize)
}
// return value from presentable if implemented
if let presentable = presentedViewController as? CustomSizedPresentable, let customSize = presentable.customSize?(withParentContainerSize: parentSize) {
return customSize
}
// half of the width/height by default
return CGSize(width: parentSize.width/2.0, height: parentSize.height/2.0)
}
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else {
return super.frameOfPresentedViewInContainerView
}
let size = self.size(forChildContentContainer: presentedViewController,
withParentContainerSize: containerView.bounds.size)
// use origin value from presentable if implemented
if let presentable = presentedViewController as? CustomSizedPresentable, let origin = presentable.position?(withParentContainerSize: containerView.bounds.size) {
return CGRect(origin: origin, size: size)
}
// center presented view by default
let origin = CGPoint(x: (containerView.bounds.width - size.width)/2,
y: (containerView.bounds.height - size.height)/2)
return CGRect(origin: origin, size: size)
}
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
self.dimmingView?.frame = containerView?.bounds ?? .zero
self.presentationWrappingView?.frame = frameOfPresentedViewInContainerView
}
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
super.preferredContentSizeDidChange(forChildContentContainer: container)
if container === presentedViewController {
self.containerView?.setNeedsLayout()
}
}
}
// MARK: - UIViewControllerTransitioningDelegate
extension CustomSizedPresentationController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return CustomSizedPresentationController(presentedViewController: presented, presenting: presenting)
}
}