mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
5.8 KiB
Swift
166 lines
5.8 KiB
Swift
3 years ago
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||
|
|
||
3 years ago
|
import UIKit
|
||
3 years ago
|
import SessionUtilitiesKit
|
||
5 years ago
|
|
||
3 years ago
|
open class Modal: UIViewController, UIGestureRecognizerDelegate {
|
||
3 years ago
|
private static let cornerRadius: CGFloat = 11
|
||
|
|
||
3 years ago
|
private let afterClosed: (() -> ())?
|
||
|
|
||
3 years ago
|
// MARK: - Components
|
||
|
|
||
3 years ago
|
private lazy var dimmingView: UIView = {
|
||
3 years ago
|
let result = UIVisualEffectView()
|
||
|
|
||
|
ThemeManager.onThemeChange(observer: result) { [weak result] theme, _ in
|
||
|
result?.effect = UIBlurEffect(
|
||
|
style: (theme.interfaceStyle == .light ?
|
||
|
UIBlurEffect.Style.systemUltraThinMaterialLight :
|
||
|
UIBlurEffect.Style.systemUltraThinMaterial
|
||
|
)
|
||
|
)
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}()
|
||
|
|
||
|
lazy var containerView: UIView = {
|
||
|
let result: UIView = UIView()
|
||
|
result.clipsToBounds = false
|
||
|
result.themeBackgroundColor = .alert_background
|
||
|
result.themeShadowColor = .black
|
||
|
result.layer.cornerRadius = Modal.cornerRadius
|
||
|
result.layer.shadowRadius = 10
|
||
|
result.layer.shadowOpacity = 0.4
|
||
|
|
||
|
return result
|
||
|
}()
|
||
5 years ago
|
|
||
3 years ago
|
public lazy var contentView: UIView = {
|
||
3 years ago
|
let result: UIView = UIView()
|
||
|
result.clipsToBounds = true
|
||
4 years ago
|
result.layer.cornerRadius = Modal.cornerRadius
|
||
3 years ago
|
|
||
5 years ago
|
return result
|
||
|
}()
|
||
|
|
||
3 years ago
|
public lazy var cancelButton: UIButton = {
|
||
3 years ago
|
let result: UIButton = Modal.createButton(title: "cancel".localized(), titleColor: .textPrimary)
|
||
|
result.addTarget(self, action: #selector(close), for: .touchUpInside)
|
||
|
|
||
5 years ago
|
return result
|
||
|
}()
|
||
|
|
||
3 years ago
|
// MARK: - Lifecycle
|
||
4 years ago
|
|
||
3 years ago
|
public init(targetView: UIView? = nil, afterClosed: (() -> ())? = nil) {
|
||
3 years ago
|
self.afterClosed = afterClosed
|
||
|
|
||
|
super.init(nibName: nil, bundle: nil)
|
||
3 years ago
|
|
||
|
// Ensure the modal doesn't crash on iPad when being presented
|
||
3 years ago
|
Modal.setupForIPadIfNeeded(self, targetView: (targetView ?? self.view))
|
||
3 years ago
|
}
|
||
|
|
||
3 years ago
|
required public init?(coder: NSCoder) {
|
||
|
fatalError("Use init(targetView:afterClosed:) instead")
|
||
3 years ago
|
}
|
||
|
|
||
3 years ago
|
public override func viewDidLoad() {
|
||
5 years ago
|
super.viewDidLoad()
|
||
3 years ago
|
|
||
3 years ago
|
navigationItem.backButtonTitle = ""
|
||
3 years ago
|
view.themeBackgroundColor = .clear
|
||
3 years ago
|
ThemeManager.applyNavigationStylingIfNeeded(to: self)
|
||
|
|
||
|
setNeedsStatusBarAppearanceUpdate()
|
||
3 years ago
|
|
||
|
view.addSubview(dimmingView)
|
||
|
view.addSubview(containerView)
|
||
|
|
||
|
containerView.addSubview(contentView)
|
||
|
|
||
|
dimmingView.pin(to: view)
|
||
|
contentView.pin(to: containerView)
|
||
|
|
||
|
if UIDevice.current.isIPad {
|
||
|
containerView.set(.width, to: Values.iPadModalWidth)
|
||
|
containerView.center(in: view)
|
||
|
}
|
||
|
else {
|
||
|
containerView.leadingAnchor
|
||
|
.constraint(equalTo: view.leadingAnchor, constant: Values.veryLargeSpacing)
|
||
|
.isActive = true
|
||
|
view.trailingAnchor
|
||
|
.constraint(equalTo: containerView.trailingAnchor, constant: Values.veryLargeSpacing)
|
||
|
.isActive = true
|
||
|
containerView.center(.vertical, in: view)
|
||
|
}
|
||
|
|
||
|
// Gestures
|
||
5 years ago
|
let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(close))
|
||
|
swipeGestureRecognizer.direction = .down
|
||
3 years ago
|
dimmingView.addGestureRecognizer(swipeGestureRecognizer)
|
||
3 years ago
|
|
||
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(close))
|
||
|
tapGestureRecognizer.delegate = self
|
||
3 years ago
|
dimmingView.addGestureRecognizer(tapGestureRecognizer)
|
||
3 years ago
|
|
||
5 years ago
|
populateContentView()
|
||
|
}
|
||
|
|
||
|
/// To be overridden by subclasses.
|
||
3 years ago
|
open func populateContentView() {
|
||
5 years ago
|
preconditionFailure("populateContentView() is abstract and must be overridden.")
|
||
|
}
|
||
|
|
||
3 years ago
|
public static func createButton(title: String, titleColor: ThemeValue) -> UIButton {
|
||
|
let result: UIButton = UIButton() // TODO: NEED to fix the font (looks bad)
|
||
3 years ago
|
result.titleLabel?.font = .systemFont(ofSize: Values.mediumFontSize, weight: UIFont.Weight(600))
|
||
|
result.setTitle(title, for: .normal)
|
||
|
result.setThemeTitleColor(titleColor, for: .normal)
|
||
|
result.setThemeBackgroundColor(.alert_buttonBackground, for: .normal)
|
||
|
result.setThemeBackgroundColor(.alert_buttonHighlight, for: .highlighted)
|
||
|
result.set(.height, to: Values.alertButtonHeight)
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
3 years ago
|
// MARK: - Interaction
|
||
5 years ago
|
|
||
5 years ago
|
@objc func close() {
|
||
3 years ago
|
// Recursively dismiss all modals (ie. find the first modal presented by a non-modal
|
||
|
// and get that to dismiss it's presented view controller)
|
||
|
var targetViewController: UIViewController? = self
|
||
|
|
||
|
while targetViewController?.presentingViewController is Modal {
|
||
|
targetViewController = targetViewController?.presentingViewController
|
||
|
}
|
||
|
|
||
3 years ago
|
targetViewController?.presentingViewController?.dismiss(animated: true) { [weak self] in
|
||
|
self?.afterClosed?()
|
||
|
}
|
||
5 years ago
|
}
|
||
3 years ago
|
|
||
|
// MARK: - UIGestureRecognizerDelegate
|
||
|
|
||
3 years ago
|
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
||
3 years ago
|
let location: CGPoint = touch.location(in: contentView)
|
||
|
|
||
|
return !contentView.point(inside: location, with: nil)
|
||
|
}
|
||
5 years ago
|
}
|
||
3 years ago
|
|
||
|
// MARK: - Convenience
|
||
|
|
||
|
public extension Modal {
|
||
|
static func setupForIPadIfNeeded(_ viewController: UIViewController, targetView: UIView) {
|
||
|
if UIDevice.current.isIPad {
|
||
|
viewController.popoverPresentationController?.permittedArrowDirections = []
|
||
|
viewController.popoverPresentationController?.sourceView = targetView
|
||
|
viewController.popoverPresentationController?.sourceRect = targetView.bounds
|
||
|
}
|
||
|
}
|
||
|
}
|