mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
Implement dialpad view controller
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user