Merge branch 'develop' into feature/accessibilityHandler
This commit is contained in:
commit
bb1ac5895b
@ -51,30 +51,33 @@ public class AlertOperation: MVMCoreOperation {
|
|||||||
// Observe for when it is removed.
|
// Observe for when it is removed.
|
||||||
observeForCurrentAlertViewDismissal()
|
observeForCurrentAlertViewDismissal()
|
||||||
|
|
||||||
// Adds the presentation to the animation queue.
|
Task(priority: .high) {
|
||||||
let blockingOperation = MVMCoreOperation()
|
guard let viewControllerToPresentOn = await NavigationHandler.shared().getViewControllerToPresentOn() else {
|
||||||
self.blockingOperation = blockingOperation
|
markAsFinished()
|
||||||
Task { @MainActor in
|
return
|
||||||
MVMCoreNavigationHandler.shared()?.present(alertController, animated: true, delegate: nil) { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
blockingOperation.markAsFinished()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Task {
|
|
||||||
// We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task
|
|
||||||
if await !self.properties.getIsDisplayed() {
|
|
||||||
self.markAsFinished()
|
|
||||||
} else {
|
|
||||||
(CoreUIObject.sharedInstance()?.loggingDelegate as? MVMCoreUILoggingDelegateProtocol)?.logAlert(with: self.alertObject)
|
|
||||||
if self.isCancelled {
|
|
||||||
await self.dismissAlertView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block navigations until this alert is removed.
|
// Presents the alert.
|
||||||
MVMCoreNavigationHandler.shared()?.addNavigationOperation(blockingOperation)
|
let presentationOperation = await NavigationOperation(with: .present(viewController: alertController, onController: viewControllerToPresentOn), tryToReplace: false)
|
||||||
|
|
||||||
|
let blockingOperation = MVMCoreOperation()
|
||||||
|
blockingOperation.addDependency(presentationOperation)
|
||||||
|
self.blockingOperation = blockingOperation
|
||||||
|
|
||||||
|
// Block other navigation until this alert is removed.
|
||||||
|
NavigationHandler.shared().navigationQueue.addOperation(blockingOperation)
|
||||||
|
|
||||||
|
await NavigationHandler.shared().navigate(with: presentationOperation)
|
||||||
|
|
||||||
|
// We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task
|
||||||
|
if await !self.properties.getIsDisplayed() {
|
||||||
|
self.markAsFinished()
|
||||||
|
} else {
|
||||||
|
(CoreUIObject.sharedInstance()?.loggingDelegate as? MVMCoreUILoggingDelegateProtocol)?.logAlert(with: self.alertObject)
|
||||||
|
if self.isCancelled {
|
||||||
|
await self.dismissAlertView()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,10 +92,13 @@ public class AlertOperation: MVMCoreOperation {
|
|||||||
private func dismissAlertView() async {
|
private func dismissAlertView() async {
|
||||||
guard await properties.getIsDisplayed() else { return }
|
guard await properties.getIsDisplayed() else { return }
|
||||||
await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
Task { @MainActor in
|
Task {
|
||||||
MVMCoreNavigationHandler.shared()?.dismiss(alertController, animated: true, delegate: nil) {
|
let dismissOperation = await NavigationOperation(with: .dismiss(viewController: alertController))
|
||||||
continuation.resume()
|
dismissOperation.queuePriority = .veryHigh
|
||||||
}
|
let task = Task(priority: .high) { await NavigationHandler.shared().navigate(with: dismissOperation) }
|
||||||
|
blockingOperation?.markAsFinished()
|
||||||
|
_ = await task.result
|
||||||
|
continuation.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,7 +144,9 @@ import MVMCore
|
|||||||
picker.displayedPropertyKeys = ["phoneNumbers"]
|
picker.displayedPropertyKeys = ["phoneNumbers"]
|
||||||
picker.predicateForEnablingContact = NSPredicate(format: "phoneNumbers.@count > 0")
|
picker.predicateForEnablingContact = NSPredicate(format: "phoneNumbers.@count > 0")
|
||||||
picker.predicateForSelectionOfProperty = NSPredicate(format: "key == 'phoneNumbers'")
|
picker.predicateForSelectionOfProperty = NSPredicate(format: "key == 'phoneNumbers'")
|
||||||
MVMCoreNavigationHandler.shared()?.present(picker, animated: true)
|
Task(priority: .userInitiated) {
|
||||||
|
await NavigationHandler.shared().present(viewController: picker, animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -114,7 +114,7 @@ import MVMCore
|
|||||||
|
|
||||||
open func performBlockOperation(with block: @escaping (MVMCoreBlockOperation) -> Void) {
|
open func performBlockOperation(with block: @escaping (MVMCoreBlockOperation) -> Void) {
|
||||||
let operation = MVMCoreBlockOperation(block: block)!
|
let operation = MVMCoreBlockOperation(block: block)!
|
||||||
MVMCoreNavigationHandler.shared()?.addNavigationOperation(operation)
|
NavigationHandler.shared().navigationQueue.addOperation(operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collapses after a delay
|
/// Collapses after a delay
|
||||||
|
|||||||
@ -7,9 +7,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MVMCore
|
||||||
|
import Combine
|
||||||
|
|
||||||
@objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol {
|
@objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol {
|
||||||
public weak var manager: (UIViewController & MVMCoreViewManagerProtocol)?
|
public weak var manager: (UIViewController & MVMCoreViewManagerProtocol)?
|
||||||
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
/// Getter for the main navigation controller
|
/// Getter for the main navigation controller
|
||||||
public static func navigationController() -> Self? {
|
public static func navigationController() -> Self? {
|
||||||
@ -20,9 +23,9 @@ import UIKit
|
|||||||
public static func setupNavigationController() -> Self? {
|
public static func setupNavigationController() -> Self? {
|
||||||
let navigationController = self.init()
|
let navigationController = self.init()
|
||||||
MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
|
MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
|
||||||
MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController
|
NavigationHandler.shared().viewControllerToPresentOn = navigationController
|
||||||
MVMCoreNavigationHandler.shared()?.navigationController = navigationController
|
NavigationHandler.shared().navigationController = navigationController
|
||||||
MVMCoreNavigationHandler.shared()?.addDelegate(navigationController)
|
navigationController.subscribe()
|
||||||
navigationController.setNavigationBarUI(with: NavigationItemModel())
|
navigationController.setNavigationBarUI(with: NavigationItemModel())
|
||||||
return navigationController
|
return navigationController
|
||||||
}
|
}
|
||||||
@ -34,15 +37,45 @@ import UIKit
|
|||||||
return navigationController
|
return navigationController
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function to return the navigation model of the lowest controller traversing managers if applicable.
|
/** Subscribes for events.
|
||||||
|
Updates the navigation item of the new view controller when one is pushed.
|
||||||
|
Based on ``NavigationItemModelProtocol`` of ``PageProtocol/pageModel``. Traverses the manager for the view controller if necessary.
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
|
public func subscribe() {
|
||||||
|
NavigationHandler.shared().onNavigation.sink { [weak self] (event, operation) in
|
||||||
|
guard let self = self,
|
||||||
|
self == operation.navigationType.getNavigationController(),
|
||||||
|
let viewController = NavigationHandler.shared().getViewControllers(for: self).last,
|
||||||
|
let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return }
|
||||||
|
switch event {
|
||||||
|
case .willNavigate:
|
||||||
|
if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
|
||||||
|
MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller)
|
||||||
|
}
|
||||||
|
if let model = getNavigationModel(from: newViewController) {
|
||||||
|
self.setNavigationItem(with: model, for: viewController)
|
||||||
|
self.setNavigationBarUI(with: model)
|
||||||
|
}
|
||||||
|
self.manager?.willDisplay?(newViewController)
|
||||||
|
case .didNavigate:
|
||||||
|
self.manager?.displayedViewController?(newViewController)
|
||||||
|
if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
|
||||||
|
controller.viewControllerReady?(inManager: self)
|
||||||
|
}
|
||||||
|
@unknown default: break
|
||||||
|
}
|
||||||
|
}.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function to return the navigation model of the view controller.
|
||||||
public func getNavigationModel(from viewController: UIViewController) -> NavigationItemModelProtocol? {
|
public func getNavigationModel(from viewController: UIViewController) -> NavigationItemModelProtocol? {
|
||||||
return (viewController as? PageProtocol)?.pageModel?.navigationBar
|
return (viewController as? PageProtocol)?.pageModel?.navigationBar
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies the controller is the currently displayed controller.
|
/// Verifies the controller is the currently displayed controller.
|
||||||
public func isDisplayed(viewController: UIViewController) -> Bool {
|
public func isDisplayed(viewController: UIViewController) -> Bool {
|
||||||
guard let topViewController = topViewController,
|
guard viewController == getViewController() else {
|
||||||
viewController == MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController) else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -84,8 +117,10 @@ extension NavigationController: MVMCoreViewManagerProtocol {
|
|||||||
manager?.willDisplay?(viewController)
|
manager?.willDisplay?(viewController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates the navigation item/bar of the current view controller based on the passed in model and view controller.
|
||||||
|
@MainActor
|
||||||
private func updateNavigationView(with model: NavigationItemModelProtocol, for viewController: UIViewController) {
|
private func updateNavigationView(with model: NavigationItemModelProtocol, for viewController: UIViewController) {
|
||||||
guard let topViewController = topViewController else { return }
|
guard let topViewController = NavigationHandler.shared().getViewControllers(for: self).last else { return }
|
||||||
|
|
||||||
setNavigationItem(with: model, for: topViewController, coordinatingWith: viewController as? PageBehaviorHandlerProtocol)
|
setNavigationItem(with: model, for: topViewController, coordinatingWith: viewController as? PageBehaviorHandlerProtocol)
|
||||||
setNavigationBarUI(with: model)
|
setNavigationBarUI(with: model)
|
||||||
@ -99,34 +134,6 @@ extension NavigationController: MVMCoreViewManagerProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NavigationController: MVMCorePresentationDelegateProtocol {
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, prepareDisplayFor viewController: UIViewController) {
|
|
||||||
if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
|
|
||||||
MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller)
|
|
||||||
}
|
|
||||||
guard self == navigationController,
|
|
||||||
let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController),
|
|
||||||
let model = getNavigationModel(from: newViewController) else { return }
|
|
||||||
setNavigationItem(with: model, for: viewController)
|
|
||||||
setNavigationBarUI(with: model)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) {
|
|
||||||
guard self == navigationController,
|
|
||||||
let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return }
|
|
||||||
manager?.willDisplay?(newViewController)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) {
|
|
||||||
guard self == navigationController,
|
|
||||||
let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return }
|
|
||||||
manager?.displayedViewController?(newViewController)
|
|
||||||
if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
|
|
||||||
controller.viewControllerReady?(inManager: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UIColor {
|
extension UIColor {
|
||||||
func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
|
func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
|
||||||
return UIGraphicsImageRenderer(size: size).image { rendererContext in
|
return UIGraphicsImageRenderer(size: size).image { rendererContext in
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import MVMCore
|
||||||
|
|
||||||
public extension UINavigationController {
|
public extension UINavigationController {
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ public extension UINavigationController {
|
|||||||
if model.hidesSystemBackButton,
|
if model.hidesSystemBackButton,
|
||||||
model.alwaysShowBackButton != false {
|
model.alwaysShowBackButton != false {
|
||||||
if let backButtonModel = model.backButton,
|
if let backButtonModel = model.backButton,
|
||||||
MVMCoreNavigationHandler.shared()?.getViewControllers(for: self)?.count ?? 0 > 1 || model.alwaysShowBackButton ?? false {
|
NavigationHandler.shared().getViewControllers(for: self).count > 1 || model.alwaysShowBackButton ?? false {
|
||||||
leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
|
leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
|
||||||
}
|
}
|
||||||
if let leftItemModels = model.additionalLeftButtons {
|
if let leftItemModels = model.additionalLeftButtons {
|
||||||
@ -113,4 +114,11 @@ public extension UINavigationController {
|
|||||||
|
|
||||||
setNavigationBarHidden(model.hidden, animated: true)
|
setNavigationBarHidden(model.hidden, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func getViewController() -> UIViewController? {
|
||||||
|
guard let topViewController = getViewControllers().last,
|
||||||
|
let viewController = MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController) else { return nil }
|
||||||
|
return viewController
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,8 @@ public protocol StatusBarUI {
|
|||||||
// Navigation bar update functions
|
// Navigation bar update functions
|
||||||
public extension MVMCoreUISplitViewController {
|
public extension MVMCoreUISplitViewController {
|
||||||
|
|
||||||
/// Updates the state for various controls (navigation, tab, progress) for the controller.
|
/// Updates the state for various controls (top navigation controller item, tab, progress) for the controller.
|
||||||
|
@MainActor
|
||||||
func updateState(with viewController: UIViewController) {
|
func updateState(with viewController: UIViewController) {
|
||||||
guard let navigationController = navigationController,
|
guard let navigationController = navigationController,
|
||||||
navigationController.isDisplayed(viewController: viewController) else { return }
|
navigationController.isDisplayed(viewController: viewController) else { return }
|
||||||
@ -30,7 +31,9 @@ public extension MVMCoreUISplitViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Progress Bar
|
// MARK: - Progress Bar
|
||||||
/// Updates the progress bar based on the page json for the view controller.
|
/** Updates the progress bar based on the page json for the view controller.
|
||||||
|
Uses a string value between 0 and 100 from key progressPercent in the MVMCoreViewControllerProtocol.loadObject.pageJSON.
|
||||||
|
*/
|
||||||
func updateProgressBar(for viewController: UIViewController) {
|
func updateProgressBar(for viewController: UIViewController) {
|
||||||
guard let viewController = viewController as? MVMCoreViewControllerProtocol else { return }
|
guard let viewController = viewController as? MVMCoreViewControllerProtocol else { return }
|
||||||
var progress: Float = 0.0
|
var progress: Float = 0.0
|
||||||
@ -42,12 +45,16 @@ public extension MVMCoreUISplitViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Tab Bar
|
// MARK: - Tab Bar
|
||||||
/// Updates the tab bar based on the page json for the view controller.
|
/** Updates the tab bar based on the page json for the view controller.
|
||||||
|
For the index: checks the view controller's pageModel (``PageProtocol``) property ``TabPageModelProtocol/tabBarIndex``, else tabBarIndex in action map that led to this page, else the previous tab bar index of this page.
|
||||||
|
For hidden: checks the view controller's pageModel (``PageProtocol``) property ``TabPageModelProtocol/tabBarHidden``, else tabBarHidden in action map that led to this page, else it is visibile.
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
func updateTabBar(for viewController: UIViewController) {
|
func updateTabBar(for viewController: UIViewController) {
|
||||||
let mvmViewController = viewController as? MVMCoreViewControllerProtocol
|
let mvmViewController = viewController as? MVMCoreViewControllerProtocol
|
||||||
tabBar?.delegateObject = mvmViewController?.delegateObject?() as? MVMCoreUIDelegateObject
|
tabBar?.delegateObject = mvmViewController?.delegateObject?() as? MVMCoreUIDelegateObject
|
||||||
|
|
||||||
let navigationIndex = (MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 1) - 1
|
let navigationIndex = (navigationController != nil ? NavigationHandler.shared().getViewControllers(for: navigationController!).count : 1) - 1
|
||||||
|
|
||||||
// Set the highlighted index. In terms of priority, Page > Action > Previous.
|
// Set the highlighted index. In terms of priority, Page > Action > Previous.
|
||||||
if let index = ((viewController as? PageProtocol)?.pageModel as? TabPageModelProtocol)?.tabBarIndex {
|
if let index = ((viewController as? PageProtocol)?.pageModel as? TabPageModelProtocol)?.tabBarIndex {
|
||||||
@ -80,10 +87,13 @@ public extension MVMCoreUISplitViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Navigation Bar
|
// MARK: - Navigation Bar
|
||||||
/// Convenience function. Sets the navigation and split view properties for the view controller. Panel access is determined if view controller is a detail view protocol.
|
/** Convenience function. Sets the navigation and split view properties for the view controller.
|
||||||
|
Panel access is determined if view controller is a ``MVMCoreUIDetailViewProtocol``
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
func setNavigationBar(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) {
|
func setNavigationBar(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) {
|
||||||
guard navigationController == self.navigationController,
|
guard navigationController == self.navigationController,
|
||||||
viewController == getCurrentDetailViewController() else {
|
self.navigationController?.isDisplayed(viewController: viewController) == true else {
|
||||||
/// Not the split view navigation controller, skip split functions.
|
/// Not the split view navigation controller, skip split functions.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -97,9 +107,13 @@ public extension MVMCoreUISplitViewController {
|
|||||||
setNavigationIconColor(navigationItemModel.tintColor.uiColor)
|
setNavigationIconColor(navigationItemModel.tintColor.uiColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the left navigation items for the view controller based on model and splitview.
|
/** Sets the left navigation items for the top view controller based on the model and viewController.
|
||||||
|
Panel access is determined if view controller is a ``MVMCoreUIDetailViewProtocol``
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
|
func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
|
||||||
guard let topViewController = navigationController.topViewController else { return }
|
let viewControllers = NavigationHandler.shared().getViewControllers(for: navigationController)
|
||||||
|
guard let topViewController = viewControllers.last else { return }
|
||||||
|
|
||||||
var leftItems: [UIBarButtonItem] = []
|
var leftItems: [UIBarButtonItem] = []
|
||||||
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
|
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
|
||||||
@ -110,7 +124,7 @@ public extension MVMCoreUISplitViewController {
|
|||||||
if let forceBackButton = navigationItemModel?.alwaysShowBackButton {
|
if let forceBackButton = navigationItemModel?.alwaysShowBackButton {
|
||||||
showBackButton = forceBackButton
|
showBackButton = forceBackButton
|
||||||
} else {
|
} else {
|
||||||
showBackButton = MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1
|
showBackButton = viewControllers.count > 1
|
||||||
}
|
}
|
||||||
if showBackButton {
|
if showBackButton {
|
||||||
if let backButtonModel = navigationItemModel?.backButton {
|
if let backButtonModel = navigationItemModel?.backButton {
|
||||||
@ -144,9 +158,12 @@ public extension MVMCoreUISplitViewController {
|
|||||||
topViewController.navigationItem.setLeftBarButtonItems(leftItems.count > 0 ? leftItems : nil, animated: !DisableAnimations.boolValue)
|
topViewController.navigationItem.setLeftBarButtonItems(leftItems.count > 0 ? leftItems : nil, animated: !DisableAnimations.boolValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the right navigation items for the view controller based on model and splitview.
|
/** Sets the right navigation items for the top view controller based on the model and viewController.
|
||||||
|
Panel access is determined if view controller is a ``MVMCoreUIDetailViewProtocol``
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
|
func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
|
||||||
guard let topViewController = navigationController.topViewController else { return }
|
guard let topViewController = NavigationHandler.shared().getViewControllers(for: navigationController).last else { return }
|
||||||
|
|
||||||
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
|
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
|
||||||
var rightItems: [UIBarButtonItem] = []
|
var rightItems: [UIBarButtonItem] = []
|
||||||
@ -173,6 +190,9 @@ public extension MVMCoreUISplitViewController {
|
|||||||
topViewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue)
|
topViewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** If the current detail view controller has a navigation model.
|
||||||
|
``NavigationController/getNavigationModel(from:)`` from ``getCurrentDetailViewController()``
|
||||||
|
*/
|
||||||
@objc func navigationBarModelExists() -> Bool {
|
@objc func navigationBarModelExists() -> Bool {
|
||||||
// Legacy Navigation
|
// Legacy Navigation
|
||||||
guard let currentViewController = getCurrentDetailViewController(),
|
guard let currentViewController = getCurrentDetailViewController(),
|
||||||
@ -180,8 +200,9 @@ public extension MVMCoreUISplitViewController {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function to update the navigation bar if the controller is the current lowest controller.
|
/// Convenience function to update the navigation bar if the controller is the current detail controller.
|
||||||
@objc func updateNavigationBarFor(viewController: UIViewController) {
|
@MainActor @objc
|
||||||
|
func updateNavigationBarFor(viewController: UIViewController) {
|
||||||
guard let navigationController = navigationController,
|
guard let navigationController = navigationController,
|
||||||
navigationController.isDisplayed(viewController: viewController),
|
navigationController.isDisplayed(viewController: viewController),
|
||||||
let model = navigationController.getNavigationModel(from: viewController) else { return }
|
let model = navigationController.getNavigationModel(from: viewController) else { return }
|
||||||
@ -201,9 +222,13 @@ public extension MVMCoreUISplitViewController {
|
|||||||
return .default
|
return .default
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the status bar background color and style.
|
/** Updates the status bar background color and style for the passed view controller.
|
||||||
@objc func setStatusBarForCurrentViewController() {
|
The background color is fetched from ``MVMCoreUIDetailViewProtocol/defaultStatusBarBackgroundColor()``, else the current navigation bar background, else the current status bar background color.
|
||||||
let viewController = getCurrentViewController() as? MVMCoreUIDetailViewProtocol
|
The backgroundStytle is fetched from ``MVMCoreUIDetailViewProtocol/defaultStatusBarStyle()`` else ``getStatusBarStyle(for:)``
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
|
func setStatusBar(for viewController: UIViewController?) {
|
||||||
|
let viewController = viewController as? MVMCoreUIDetailViewProtocol
|
||||||
let backgroundColor = viewController?.defaultStatusBarBackgroundColor?() ??
|
let backgroundColor = viewController?.defaultStatusBarBackgroundColor?() ??
|
||||||
navigationController?.navigationBar.standardAppearance.backgroundColor ??
|
navigationController?.navigationBar.standardAppearance.backgroundColor ??
|
||||||
statusBarView?.backgroundColor
|
statusBarView?.backgroundColor
|
||||||
@ -213,6 +238,14 @@ public extension MVMCoreUISplitViewController {
|
|||||||
|
|
||||||
setStatusBarBackgroundColor(backgroundColor, style: style)
|
setStatusBarBackgroundColor(backgroundColor, style: style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Updates the status bar background color and style for the current view controller.
|
||||||
|
See ``setStatusBar(for:)``
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
|
@objc func setStatusBarForCurrentViewController() {
|
||||||
|
setStatusBar(for: getCurrentViewController())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
||||||
@ -235,6 +268,17 @@ extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc public extension MVMCoreUISplitViewController {
|
@objc public extension MVMCoreUISplitViewController {
|
||||||
|
@objc func goBack() {
|
||||||
|
Task(priority: .userInitiated) { @MainActor in
|
||||||
|
if let viewController = getCurrentDetailViewController() as? MVMCoreUIDetailViewProtocol,
|
||||||
|
let backButtonPressed = viewController.backButtonPressed {
|
||||||
|
backButtonPressed()
|
||||||
|
} else {
|
||||||
|
await NavigationHandler.shared().popTopViewController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Subscribes for notification events.
|
/// Subscribes for notification events.
|
||||||
@objc func subscribeForNotifications() {
|
@objc func subscribeForNotifications() {
|
||||||
guard cancellables == nil else { return }
|
guard cancellables == nil else { return }
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "MVMCoreUISplitViewController.h"
|
#import "MVMCoreUISplitViewController.h"
|
||||||
@import MVMCore.MVMCoreNavigationHandler;
|
|
||||||
@import MVMCore.MVMCoreDispatchUtility;
|
@import MVMCore.MVMCoreDispatchUtility;
|
||||||
@import MVMCore.MVMCoreViewManagerProtocol;
|
@import MVMCore.MVMCoreViewManagerProtocol;
|
||||||
@import MVMCore.MVMCoreActionUtility;
|
@import MVMCore.MVMCoreActionUtility;
|
||||||
@ -192,14 +191,7 @@ CGFloat const PanelAnimationDuration = 0.2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)backButtonPressed:(id)sender {
|
- (IBAction)backButtonPressed:(id)sender {
|
||||||
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
[self goBack];
|
||||||
UIViewController *detailViewController = [self getCurrentDetailViewController];
|
|
||||||
if ([detailViewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] && [detailViewController respondsToSelector:@selector(backButtonPressed)]) {
|
|
||||||
[((UIViewController <MVMCoreUIDetailViewProtocol> *)detailViewController) backButtonPressed];
|
|
||||||
} else {
|
|
||||||
[[MVMCoreNavigationHandler sharedNavigationHandler] popTopViewControllerAnimated:YES];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)rightPanelButtonPressed:(id)sender {
|
- (IBAction)rightPanelButtonPressed:(id)sender {
|
||||||
@ -258,7 +250,7 @@ CGFloat const PanelAnimationDuration = 0.2;
|
|||||||
|
|
||||||
- (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended {
|
- (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended {
|
||||||
NSMutableArray *leftBarButtonItems = [NSMutableArray array];
|
NSMutableArray *leftBarButtonItems = [NSMutableArray array];
|
||||||
if (self.navigationController && [MVMCoreNavigationHandler.sharedNavigationHandler getViewControllersForNavigationController:self.navigationController].count > 1) {
|
if (self.navigationController && [self.navigationController getViewControllers].count > 1) {
|
||||||
[leftBarButtonItems addObject:self.backButton];
|
[leftBarButtonItems addObject:self.backButton];
|
||||||
}
|
}
|
||||||
if ((accessible && !extended) && self.leftPanelButton) {
|
if ((accessible && !extended) && self.leftPanelButton) {
|
||||||
@ -1074,7 +1066,7 @@ CGFloat const PanelAnimationDuration = 0.2;
|
|||||||
|
|
||||||
// Returns the desired view or falls back. Hot fix until we can get away from using these functions...
|
// Returns the desired view or falls back. Hot fix until we can get away from using these functions...
|
||||||
+ (CGRect)getBounds:(UIView *)desiredView {
|
+ (CGRect)getBounds:(UIView *)desiredView {
|
||||||
UIView *view = desiredView ?: [MVMCoreNavigationHandler sharedNavigationHandler].navigationController.view ?: [MVMCoreGetterUtility getKeyWindow].rootViewController.view;
|
UIView *view = desiredView ?: [self mainSplitViewController].navigationController.view ?: [MVMCoreGetterUtility getKeyWindow].rootViewController.view;
|
||||||
return view ? view.bounds : [UIScreen mainScreen].bounds;
|
return view ? view.bounds : [UIScreen mainScreen].bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1099,17 +1091,7 @@ CGFloat const PanelAnimationDuration = 0.2;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (UIViewController *)getCurrentVisibleController {
|
- (UIViewController *)getCurrentVisibleController {
|
||||||
UIViewController *baseViewController = [MVMCoreNavigationHandler sharedNavigationHandler].viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController;
|
return [MVMCoreUIUtility getCurrentVisibleController];
|
||||||
UIViewController *viewController = nil;
|
|
||||||
while (baseViewController.presentedViewController && !baseViewController.presentedViewController.isBeingDismissed) {
|
|
||||||
viewController = baseViewController.presentedViewController;
|
|
||||||
baseViewController = viewController;
|
|
||||||
}
|
|
||||||
// if it is not presented viewcontroller, existing BAU logic will be working
|
|
||||||
if (!viewController) {
|
|
||||||
viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController];
|
|
||||||
}
|
|
||||||
return viewController;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIViewController *)getCurrentDetailViewController {
|
- (UIViewController *)getCurrentDetailViewController {
|
||||||
|
|||||||
@ -205,7 +205,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||||
// Only percent interact if we've already loaded the view controller
|
// Only percent interact if we've already loaded the view controller
|
||||||
guard let customInteractor = customInteractor,
|
guard let customInteractor = customInteractor,
|
||||||
let index = index,
|
let index = index,
|
||||||
@ -229,7 +229,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
hideNavigationBarLine(true)
|
hideNavigationBarLine(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) {
|
public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
|
||||||
guard navigationController == subNavigationController else { return }
|
guard navigationController == subNavigationController else { return }
|
||||||
|
|
||||||
if let viewController = viewController as? UIViewController & MVMCoreViewManagerViewControllerProtocol & MVMCoreViewControllerProtocol {
|
if let viewController = viewController as? UIViewController & MVMCoreViewManagerViewControllerProtocol & MVMCoreViewControllerProtocol {
|
||||||
@ -247,7 +247,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
commitTo(controller: viewController)
|
commitTo(controller: viewController)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) {
|
public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
|
||||||
guard navigationController == subNavigationController else { return }
|
guard navigationController == subNavigationController else { return }
|
||||||
// Need to track swipe action.
|
// Need to track swipe action.
|
||||||
if needToTrackTabSelect {
|
if needToTrackTabSelect {
|
||||||
@ -272,7 +272,9 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
if let controller = viewControllers[indexPath.row] {
|
if let controller = viewControllers[indexPath.row] {
|
||||||
// Load controller from the cache
|
// Load controller from the cache
|
||||||
needToTrackTabSelect = true
|
needToTrackTabSelect = true
|
||||||
MVMCoreNavigationHandler.shared()?.replaceTopViewController(with: controller, navigationController: subNavigationController, animated: true, delegate: self, replaceInStack: false, completionHandler: nil)
|
Task(priority: .userInitiated) {
|
||||||
|
await NavigationHandler.shared().replace(viewController: controller, navigationController: subNavigationController, tryToReplace: false, animated: true)
|
||||||
|
}
|
||||||
} else if let tabsModel = tabs.tabsModel,
|
} else if let tabsModel = tabs.tabsModel,
|
||||||
let action = tabsModel.tabs[indexPath.row].action {
|
let action = tabsModel.tabs[indexPath.row].action {
|
||||||
// Perform the tab action
|
// Perform the tab action
|
||||||
|
|||||||
@ -268,7 +268,7 @@ public class NotificationOperation: MVMCoreOperation {
|
|||||||
}
|
}
|
||||||
})!
|
})!
|
||||||
transitionOperation.completionBlock = completionBlock
|
transitionOperation.completionBlock = completionBlock
|
||||||
MVMCoreNavigationHandler.shared()?.addNavigationOperation(transitionOperation)
|
NavigationHandler.shared().navigationQueue.addOperation(transitionOperation)
|
||||||
return transitionOperation
|
return transitionOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,6 +314,8 @@ open class NotificationHandler {
|
|||||||
|
|
||||||
private var transitionDelegate: NotificationTransitionDelegateProtocol
|
private var transitionDelegate: NotificationTransitionDelegateProtocol
|
||||||
|
|
||||||
|
private var cancellable: Cancellable?
|
||||||
|
|
||||||
// MARK: - Publishers
|
// MARK: - Publishers
|
||||||
|
|
||||||
/// Publishes when a notification will show.
|
/// Publishes when a notification will show.
|
||||||
@ -354,7 +356,27 @@ open class NotificationHandler {
|
|||||||
|
|
||||||
/// Registers to know when pages change.
|
/// Registers to know when pages change.
|
||||||
private func registerForPageChanges() {
|
private func registerForPageChanges() {
|
||||||
MVMCoreNavigationHandler.shared()?.addDelegate(self)
|
cancellable = NavigationHandler.shared().onNavigation
|
||||||
|
.filter({ $0.0 == .didNavigate })
|
||||||
|
.sink { [weak self] (event, operation) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
Task {
|
||||||
|
// Update displayable for each top alert operation when page type changes, in top queue priority order.
|
||||||
|
guard self.queue.operations.count > 0,
|
||||||
|
let navigationController = await operation.navigationType.getNavigationController(),
|
||||||
|
await navigationController == MVMCoreUISplitViewController.main()?.navigationController,
|
||||||
|
let viewController = await navigationController.getViewController() else { return }
|
||||||
|
let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType
|
||||||
|
self.queue.operations.compactMap {
|
||||||
|
$0 as? NotificationOperation
|
||||||
|
}.sorted {
|
||||||
|
$0.notificationModel.priority.rawValue > $1.notificationModel.priority.rawValue
|
||||||
|
}.forEach {
|
||||||
|
$0.updateDisplayable(by: pageType)
|
||||||
|
}
|
||||||
|
self.reevaluteQueue()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks for new top alert json
|
/// Checks for new top alert json
|
||||||
@ -534,26 +556,6 @@ open class NotificationHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationHandler: MVMCorePresentationDelegateProtocol {
|
|
||||||
// Update displayable for each top alert operation when page type changes, in top queue priority order.
|
|
||||||
public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) {
|
|
||||||
guard queue.operations.count > 0 else { return }
|
|
||||||
let viewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController)
|
|
||||||
guard viewController == MVMCoreUISplitViewController.main()?.getCurrentViewController() else { return }
|
|
||||||
let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType
|
|
||||||
Task {
|
|
||||||
queue.operations.compactMap {
|
|
||||||
$0 as? NotificationOperation
|
|
||||||
}.sorted {
|
|
||||||
$0.notificationModel.priority.rawValue > $1.notificationModel.priority.rawValue
|
|
||||||
}.forEach {
|
|
||||||
$0.updateDisplayable(by: pageType)
|
|
||||||
}
|
|
||||||
reevaluteQueue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NotificationOperation {
|
extension NotificationOperation {
|
||||||
/// Updates the operation and notification with the new model.
|
/// Updates the operation and notification with the new model.
|
||||||
public func update(with model: NotificationModel, delegateObject: MVMCoreUIDelegateObject?) {
|
public func update(with model: NotificationModel, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
|||||||
@ -57,6 +57,8 @@ import SafariServices
|
|||||||
@MainActor
|
@MainActor
|
||||||
open func openURL(inSafariWebView url: URL) {
|
open func openURL(inSafariWebView url: URL) {
|
||||||
let safariViewController = SFSafariViewController(url: url)
|
let safariViewController = SFSafariViewController(url: url)
|
||||||
MVMCoreNavigationHandler.shared()?.present(safariViewController, animated: true)
|
Task(priority: .high) {
|
||||||
|
await NavigationHandler.shared().present(viewController: safariViewController)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MVMCore
|
||||||
|
|
||||||
public extension MVMCoreUIUtility {
|
public extension MVMCoreUIUtility {
|
||||||
|
|
||||||
@ -66,3 +66,25 @@ public extension MVMCoreUIUtility {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public extension MVMCoreUIUtility {
|
||||||
|
@objc @MainActor
|
||||||
|
static func getVisibleViewController() -> UIViewController? {
|
||||||
|
var viewController = NavigationHandler.shared().getViewControllerToPresentOn()
|
||||||
|
while let presentedController = viewController?.presentedViewController,
|
||||||
|
!presentedController.isBeingDismissed {
|
||||||
|
viewController = presentedController
|
||||||
|
}
|
||||||
|
if let navigationController = viewController as? UINavigationController {
|
||||||
|
viewController = navigationController.topViewController
|
||||||
|
}
|
||||||
|
if let viewController = viewController {
|
||||||
|
return getViewControllerTraversingManagers(viewController)
|
||||||
|
} else if let viewController = MVMCoreUISession.sharedGlobal()?.navigationController?.topViewController {
|
||||||
|
return getViewControllerTraversingManagers(viewController)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
#import "MVMCoreUISession.h"
|
#import "MVMCoreUISession.h"
|
||||||
#import "MVMCoreUISplitViewController.h"
|
#import "MVMCoreUISplitViewController.h"
|
||||||
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
||||||
@import MVMCore.MVMCoreNavigationHandler;
|
|
||||||
@import MVMCore.MVMCoreGetterUtility;
|
@import MVMCore.MVMCoreGetterUtility;
|
||||||
|
|
||||||
@implementation MVMCoreUIUtility
|
@implementation MVMCoreUIUtility
|
||||||
@ -52,19 +51,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (UIViewController *)getCurrentVisibleController {
|
+ (UIViewController *)getCurrentVisibleController {
|
||||||
UIViewController *baseViewController = [MVMCoreNavigationHandler sharedNavigationHandler].viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController;
|
__block UIViewController *viewController = nil;
|
||||||
UIViewController *viewController = nil;
|
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
|
||||||
while (baseViewController.presentedViewController && !baseViewController.presentedViewController.isBeingDismissed) {
|
viewController = [self getVisibleViewController];
|
||||||
viewController = baseViewController.presentedViewController;
|
}];
|
||||||
baseViewController = viewController;
|
|
||||||
}
|
|
||||||
if ([viewController isKindOfClass:[UINavigationController class]]) {
|
|
||||||
viewController = ((UINavigationController *)viewController).topViewController;
|
|
||||||
}
|
|
||||||
// if it is not presented viewcontroller, existing BAU logic will be working
|
|
||||||
if (!viewController) {
|
|
||||||
viewController = [self getViewControllerTraversingManagers:[MVMCoreUISession sharedGlobal].navigationController.topViewController];
|
|
||||||
}
|
|
||||||
return viewController;
|
return viewController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user