sub nav manager

This commit is contained in:
Scott Pfeil 2021-12-17 14:48:42 -05:00
parent ad483e42c5
commit ba16e241cb
7 changed files with 633 additions and 6 deletions

View File

@ -15,7 +15,6 @@ import UIKit
@objcMembers open class Tabs: View, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
public var tabsModel: TabsModel? {
get { return model as? TabsModel }
}
@ -77,7 +76,7 @@ import UIKit
func setupCollectionView () {
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = cellSpacing
layout.minimumLineSpacing = 0
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
@ -182,6 +181,12 @@ extension Tabs: UICollectionViewDataSource {
extension Tabs: UICollectionViewDelegateFlowLayout {
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
guard self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) != 2 else {
// If two tabs, take up the screen
let insets = self.collectionView(collectionView, layout: collectionViewLayout, insetForSectionAt: indexPath.section)
let width = (collectionView.bounds.width / 2.0) - insets.left - insets.right
return CGSize(width: width, height: cellHeight)
}
guard let labelModel = tabsModel?.tabs[indexPath.row].label else {
return .zero
}
@ -209,6 +214,10 @@ extension Tabs: UICollectionViewDelegateFlowLayout {
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
// If two tabs, take up the screen, no space between items
guard self.collectionView(collectionView, numberOfItemsInSection: section) != 2 else {
return 0
}
return sectionPadding
}
@ -237,7 +246,7 @@ extension Tabs: UICollectionViewDelegateFlowLayout {
guard let collect = collectionView, tabsModel?.tabs.count ?? 0 > 0 else { return }
collect.selectItem(at: indexPath, animated: animated, scrollPosition: .centeredHorizontally)
guard let tabCell = collect.cellForItem(at: indexPath) as? TabItemCell, let tabsModel = self.tabsModel else { return }
guard let tabCell = collect.cellForItem(at: indexPath) as? TabItemCell, let tabsModel = tabsModel else { return }
moveSelectionLine(toIndex: indexPath, animated: animated, cell: tabCell)
tabCell.label.textColor = tabsModel.selectedColor.uiColor
tabCell.updateAccessibility(indexPath: indexPath, selected: true, tabsModel: tabsModel)
@ -270,10 +279,10 @@ extension Tabs: UIScrollViewDelegate {
//-------------------------------------------------
extension Tabs {
func moveSelectionLine(toIndex indexPath: IndexPath, animated: Bool, cell: TabItemCell) {
guard let collect = self.collectionView else {return}
guard let collect = collectionView else {return}
let size = collectionView(collect, layout: layout, sizeForItemAt: indexPath)
let barWidth = max(size.width, selectionLineWidth)
let barWidth = getLineWidth(for: indexPath)
let animationBlock = {
[weak self] in
let x = cell.frame.origin.x
@ -287,6 +296,39 @@ extension Tabs {
animationBlock()
}
}
func getLineWidth(for indexPath: IndexPath) -> CGFloat {
guard let collection = collectionView else { return 0 }
let width = collectionView(collection, layout: layout, sizeForItemAt: indexPath).width
guard collectionView(collection, numberOfItemsInSection: indexPath.section) != 2 else {
return width
}
return max(width, selectionLineWidth)
}
func progress(from index: Int, toIndex: Int, percentage: CGFloat) {
let fromIndexPath = IndexPath(row: index, section: 0)
let toIndexPath = IndexPath(row: toIndex, section: 0)
guard let collection = collectionView,
let fromCell = collection.cellForItem(at: fromIndexPath),
let toCell = collection.cellForItem(at: toIndexPath) else { return }
let fromLineWidth = getLineWidth(for: fromIndexPath)
let toLineWidth = getLineWidth(for: toIndexPath)
let finalLineWidth = (toLineWidth - fromLineWidth) * percentage + fromLineWidth
selectionLineWidthConstraint?.constant = finalLineWidth
// setting the x for percentage
let fromCellWidth = collectionView(collection, layout: layout, sizeForItemAt: fromIndexPath).width
let toCellWidth = collectionView(collection, layout: layout, sizeForItemAt: toIndexPath).width
let originalX = fromCell.frame.origin.x + ((fromCellWidth - fromLineWidth) / 2.0)
let toX = toCell.frame.origin.x + ((toCellWidth - toLineWidth) / 2.0)
let xDifference = toX - originalX
let finalX = (xDifference * percentage) + originalX
selectionLineLeftConstraint?.constant = finalX
bottomContentView.layoutIfNeeded()
}
}
@objcMembers public class TabItemCell: CollectionViewCell {

View File

@ -66,11 +66,23 @@ public class TabItemModel: Codable {
case action
}
func setDefaults() {
if label.textAlignment == nil {
label.textAlignment = .center
}
}
public init(with label: LabelModel, action: ActionModelProtocol?) {
self.label = label
self.action = action
setDefaults()
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
label = try typeContainer.decode(LabelModel.self, forKey: .label)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
setDefaults()
}
public func encode(to encoder: Encoder) throws {

View File

@ -0,0 +1,144 @@
//
// SubNavInteractor.swift
// MobileFirstFramework
//
// Created by Matt Bruce on 10/22/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
@objc public protocol SubNavSwipeNavigationProtocol: AnyObject {
func swipeLeft()
func swipeRight()
func update(percentage: CGFloat)
}
fileprivate enum SubNavPanningDirection : Int {
case left
case right
case none
}
@objcMembers public class SubNavInteractor: UIPercentDrivenInteractiveTransition {
//// true while panning
public var panning: Bool = false
////Pan gesture instance
public var panGesture: UIPanGestureRecognizer = .init()
////set pannable percentage 0 to 1
public var pannablePercentage: CGFloat = 0.0
//// can be used to keep track of if we are ..
public var interactive: Bool = false
//private
private var shouldCompleteTransition: Bool = false
private weak var delegate: SubNavSwipeNavigationProtocol?
private var panningDirection: SubNavPanningDirection = .none
////constructor
public convenience init(viewController: (UIViewController & SubNavSwipeNavigationProtocol)) {
self.init(viewController: viewController, delegate: viewController)
}
public init(viewController: UIViewController, delegate: SubNavSwipeNavigationProtocol) {
self.pannablePercentage = 0.15
self.delegate = delegate
super.init()
self.panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(handlePanGesture));
viewController.view.addGestureRecognizer(self.panGesture)
}
//overrides
public override func cancel() {
if interactive {
delegate?.update(percentage: 0)
super.cancel()
}
interactive = false
panning = false
shouldCompleteTransition = false
}
public override func finish() {
if interactive {
delegate?.update(percentage: 1)
super.finish()
}
interactive = false
panning = false
shouldCompleteTransition = false
}
//private
@objc private func handlePanGesture(_ pan: UIPanGestureRecognizer) {
guard let panView = pan.view else { return }
let velocityX = pan.velocity(in: panView).x
let translation = pan.translation(in: panView)
var percentage = translation.x / panView.bounds.width
let locationInView = pan.location(in: panView)
// Simulates an edge gesture by only accepting pans at the edge of the screen. Needed because edge gesture doesn't work nicely with extended menus such as on ipad.
let frame = panView.frame
let pannableFrameLeft = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width * pannablePercentage, height: frame.size.height)
let pannableFrameRight = CGRect(x: frame.origin.x + frame.size.width * (1 - pannablePercentage), y: frame.origin.y, width: frame.size.width * pannablePercentage, height: frame.size.height)
switch (pan.state) {
case .began:
// Begin the transition to the next page.
self.panning = true
if velocityX < 0 && pannableFrameRight.contains(locationInView) {
self.delegate?.swipeLeft()
self.panningDirection = .left
} else if velocityX > 0 && pannableFrameLeft.contains(locationInView){
self.delegate?.swipeRight()
self.panningDirection = .right
}
case .changed:
guard self.interactive else { return }
if self.panningDirection == .right && translation.x < 0 ||
self.panningDirection == .left && translation.x > 0 {
percentage = 0
}
if percentage < 0 {
percentage = -percentage;
}
if percentage > 1.00 {
percentage = 1.00;
}
self.shouldCompleteTransition = percentage > 0.65;
self.update(percentage)
self.delegate?.update(percentage: percentage)
case .cancelled:
self.cancel()
case .ended:
if ((percentage < 0) != (velocityX < 0)) && abs(velocityX) > 50 {
// If we are moving back toward the previous view we want to cancel. (the speed threshold is for shaky hands)
self.cancel()
} else if abs(velocityX) > 300.0 || shouldCompleteTransition {
// Need to cancel the transition if we pass the threshold, but from a opposite direction(e.g. Start panning from left edge to right, then move back, pass the start x location, end with a high velocityX)
if ((panningDirection == .right) && translation.x < 0) || ((panningDirection == .left) && translation.x > 0) {
self.cancel()
} else {
// If we pass the speed or screen percentage thresholds, move on to the next screen.
self.finish()
}
} else {
self.cancel()
}
default:
return
}
}
}

View File

@ -0,0 +1,331 @@
//
// SubNavManagerController.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 10/22/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
import MVMCore
open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, TabsDelegate, MVMCorePresentationDelegateProtocol, SubNavSwipeNavigationProtocol {
/// The current managed view controller
private var viewController: UIViewController
/// A list of cached controllers.
private var viewControllers: [UIViewController?]
private var tabsModel: TabsModel
public lazy var tabs: Tabs = {
let tabs = Tabs(model: tabsModel, delegateObjectIVar, nil)
tabs.delegate = self
return tabs
}()
public lazy var line: Line = {
return Line(model: LineModel(type: .standard), delegateObjectIVar, nil)
}()
public lazy var subNavigationController: UINavigationController = {
let subNavigationController = SubNavManagerNavigationController(rootViewController: viewController)
subNavigationController.view.translatesAutoresizingMaskIntoConstraints = false
return subNavigationController
}()
/// Interactor for swiping.
public var customInteractor: SubNavInteractor?
/// The index to go to.
private var index: Int?
/// Flag for if we need to call a track action.
private var needToTrackTabSelect = false
public init(viewController: UIViewController, tabsModel: TabsModel, loadObject: MVMCoreLoadObject, shouldEnableSwipeGestures: Bool) {
self.viewController = viewController
self.tabsModel = tabsModel
self.viewControllers = [UIViewController?](repeating: nil, count: tabsModel.tabs.count)
self.viewControllers[tabsModel.selectedIndex] = viewController
super.init(nibName: nil, bundle: nil)
setup(with: loadObject, shouldEnableSwipeGestures: shouldEnableSwipeGestures)
}
public init(viewControllers: [UIViewController], tabsModel: TabsModel, loadObject: MVMCoreLoadObject, shouldEnableSwipeGestures: Bool) {
self.tabsModel = tabsModel
self.viewControllers = viewControllers
self.viewController = viewControllers[tabsModel.selectedIndex]
super.init(nibName: nil, bundle: nil)
setup(with: loadObject, shouldEnableSwipeGestures: shouldEnableSwipeGestures)
}
func setup(with loadObject: MVMCoreLoadObject, shouldEnableSwipeGestures: Bool) {
self.loadObject = loadObject
pageType = loadObject.pageType
if shouldEnableSwipeGestures {
customInteractor = SubNavInteractor(viewController: self)
}
if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller)
}
hideNavigationBarLine(for: viewController)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
open override func loadView() {
let view = View()
view.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(tabs)
view.addSubview(line)
addChild(subNavigationController)
view.addSubview(subNavigationController.view)
subNavigationController.didMove(toParent: self)
NSLayoutConstraint.activate([
tabs.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: tabs.trailingAnchor),
line.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: line.trailingAnchor),
subNavigationController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: subNavigationController.view.trailingAnchor),
tabs.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
line.topAnchor.constraint(equalTo: tabs.bottomAnchor),
subNavigationController.view.topAnchor.constraint(equalTo: line.bottomAnchor),
view.bottomAnchor.constraint(equalTo: subNavigationController.view.bottomAnchor)
])
self.view = view
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Notify the view controller it is showing.
if manager == nil {
(viewController as? MVMCoreViewManagerViewControllerProtocol)?.viewControllerReady?(inManager: self)
}
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Notify showing view we will disappear.
(viewController as? MVMCoreViewManagerViewControllerProtocol)?.managerWillDisappear?(self)
}
/// Hides the navigation bar for the page.
open func hideNavigationBarLine(for viewController: UIViewController) {
(viewController as? PageProtocol)?.pageModel?.navigationBar?.line?.type = .none
}
open override func updateViews() {
super.updateViews()
if screenSizeChanged() {
tabs.updateView(view.bounds.size.width)
}
}
// MARK: - MVMCoreLoadDelegateProtocol
open func shouldContinue(toErrorPage loadObject: MVMCoreLoadObject, error: MVMCoreErrorObject?) -> Bool {
// Push error screens so they do not replace the tab page.
loadObject.requestParameters?.navigationController = self.navigationController
loadObject.requestParameters?.loadStyle = .push
return true
}
// MARK: - Action Related
/// Logs the action for the selected tab.
open func trackSelectTab() {
guard let action = tabs.tabsModel?.tabs[tabs.selectedIndex].action,
let json = MVMCoreUIActionHandler.shared()?.convertActionToJSON(action, delegateObject: delegateObjectIVar) else { return }
MVMCoreUIActionHandler.shared()?.logAction(json, additionalData: getAdditionalDataForNewTabLoad(indexPath: IndexPath(row: tabs.selectedIndex, section: 0)), delegateObject: delegateObjectIVar)
}
/// Allow override of additioonal data for tab press action
open func getAdditionalDataForNewTabLoad(indexPath: IndexPath) -> [AnyHashable: Any]? {
return ["tabBarPressed": true, KeySourceModel: tabsModel]
}
/// Allows modification of requestParameters object for openPage request
open func getRequestParametersForNewTabLoad(requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> MVMCoreRequestParameters {
requestParameters.navigationController = subNavigationController
requestParameters.loadStyle = .replaceCurrent
requestParameters.tabWasPressed = true
return requestParameters
}
open override func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) {
var requestParameters = requestParameters
guard let additionalData = additionalData,
additionalData.boolForKey("tabBarPressed") else {
// Pass to delegate
if (viewController as? MVMCoreActionDelegateProtocol)?.handleOpenPage?(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData) == nil {
super.handleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
}
return
}
// Allow opportunity to modify parameters.
requestParameters = getRequestParametersForNewTabLoad(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
super.handleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
}
// MARK: - MVMCorePresentationDelegateProtocol
public func navigationController(_ navigationController: UINavigationController, interactiveTransitionWasCanceled canceled: Bool) {
needToTrackTabSelect = false
index = nil
}
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard navigationController == subNavigationController,
let index = index else { return nil }
// Tab bars animate left and right navigation accordingly.
if tabs.selectedIndex < index {
return SubNavSwipeAnimator(swipingDirection: .left, controller: self)
} else if tabs.selectedIndex > index {
return SubNavSwipeAnimator(swipingDirection: .right, controller: self)
} else {
return nil
}
}
public func navigationController(_ navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
// Only percent interact if we've already loaded the view controller
guard let customInteractor = customInteractor,
let index = index,
customInteractor.panning,
viewControllers[index] != nil else { return nil }
customInteractor.interactive = true
return customInteractor
}
/// Handles when the controller has committed to be changed.
private func commitTo(controller: UIViewController) {
guard let index = index,
index != tabs.selectedIndex else { return }
self.viewController = controller
pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType
hideNavigationBarLine(for: controller)
if let viewController = getCurrentViewController() {
manager?.willDisplay?(viewController)
}
tabs.selectIndex(index, animated: true)
self.index = nil
}
public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) {
guard navigationController == subNavigationController else { return }
if let viewController = viewController as? UIViewController & MVMCoreViewManagerViewControllerProtocol & MVMCoreViewControllerProtocol {
MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: viewController)
// Cache the controller.
if viewController.shouldCacheInManager?() ?? true,
let index = index {
viewControllers[index] = viewController
}
}
// Wait to consider page loaded until after transition if we are custom animating swipe, otherwise do it now.
guard customInteractor?.interactive != true else { return }
commitTo(controller: viewController)
}
public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) {
guard navigationController == subNavigationController else { return }
// Need to track swipe action.
if needToTrackTabSelect {
needToTrackTabSelect = false
trackSelectTab()
}
// Consider the page loaded if we have not already
commitTo(controller: viewController)
(viewController as? MVMCoreViewManagerViewControllerProtocol)?.viewControllerReady?(inManager: self)
if let viewController = getCurrentViewController() {
manager?.displayedViewController?(viewController)
}
}
// MARK: - TabsDelegate
// We will manually manage tab selection due to possible swipe interactions.
open func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool {
guard tabs.selectedIndex != indexPath.row else { return false }
index = indexPath.row
if let controller = viewControllers[indexPath.row] {
// Load controller from the cache
needToTrackTabSelect = true
MVMCoreNavigationHandler.shared()?.replaceTopViewController(with: controller, navigationController: subNavigationController, animated: true, delegate: self, replaceInStack: false, completionHandler: nil)
} else if let tabsModel = tabs.tabsModel,
let action = tabsModel.tabs[indexPath.row].action {
// Perform the tab action
MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: getAdditionalDataForNewTabLoad(indexPath: indexPath), delegateObject: delegateObjectIVar)
}
return false
}
// Not currently used.
open func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) {}
// MARK: - MVMCoreViewManagerViewControllerProtocol
open override func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) {
// Pass on down
(viewController as? MVMCoreViewManagerViewControllerProtocol)?.viewControllerReady?(inManager: self)
}
// MARK: - MVMCoreViewManagerProtocol
open func getCurrentViewController() -> UIViewController? {
MVMCoreUIUtility.getViewControllerTraversingManagers(viewController)
}
open func containsPage(withPageType pageType: String?) -> Bool {
guard let pageType = pageType else { return false }
return viewControllers.contains { controller in
return (controller as? MVMCoreViewControllerProtocol)?.pageType == pageType
}
}
open func newDataReceived(in viewController: UIViewController) {
if viewController == self.viewController {
hideNavigationBarLine(for: viewController)
}
manager?.newDataReceived?(in: viewController)
}
public func willDisplay(_ viewController: UIViewController) {
hideNavigationBarLine(for: viewController)
manager?.willDisplay?(viewController)
}
public func displayedViewController(_ viewController: UIViewController) {
manager?.displayedViewController?(viewController)
}
// MARK: - MVMCoreUISwipeNavigationProtocol
public func swipeLeft() {
guard tabs.selectedIndex < (tabs.tabsModel?.tabs.count ?? 0) - 1 else { return }
_ = shouldSelectItem(IndexPath(row: tabs.selectedIndex + 1, section: 0), tabs: tabs)
}
public func swipeRight() {
guard tabs.selectedIndex != 0 else { return }
_ = shouldSelectItem(IndexPath(row: tabs.selectedIndex - 1, section: 0), tabs: tabs)
}
public func update(percentage: CGFloat) {
guard customInteractor?.interactive == true,
let index = index else { return }
tabs.progress(from: tabs.selectedIndex, toIndex: index, percentage: percentage)
}
}

View File

@ -0,0 +1,38 @@
//
// SubNavManagerNavigationController.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 11/1/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
/// The navigation controller that the tabbarpagecontrol uses for the children controllers. It always has the navigation bar hidden.
@objc public class SubNavManagerNavigationController: UINavigationController {
public override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
setNavigationBarHidden(true, animated: false)
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setNavigationBarHidden(true, animated: false)
}
public override init(navigationBarClass: AnyClass?, toolbarClass: AnyClass?) {
super.init(navigationBarClass: navigationBarClass, toolbarClass: toolbarClass)
setNavigationBarHidden(true, animated: false)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setNavigationBarHidden(true, animated: false)
}
public override func setNavigationBarHidden(_ hidden: Bool, animated: Bool) {
guard hidden else { return }
super.setNavigationBarHidden(hidden, animated: animated)
}
}

View File

@ -0,0 +1,60 @@
//
// SubNavSwipeAnimator.swift
// MobileFirstFramework
//
// Created by Matt Bruce on 10/22/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
import MVMCore
@objcMembers public class SubNavSwipeAnimator: NSObject, MVMCoreViewControllerAnimatedTransitioning {
@objc dynamic public var interactiveTransitionCanceled: Bool = false
private var animationTime: TimeInterval = 0.0
private var direction: UISwipeGestureRecognizer.Direction
private weak var controller: UIViewController?
public init(swipingDirection direction: UISwipeGestureRecognizer.Direction, controller: UIViewController) {
self.direction = direction
self.controller = controller
super.init()
}
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
self.animationTime = 0.5
return self.animationTime
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
self.controller?.view.isUserInteractionEnabled = false
let containerView = transitionContext.containerView
guard let originalViewController = transitionContext.viewController(forKey: .from),
let destinationViewController = transitionContext.viewController(forKey: .to) else { return }
var destStartFrame = containerView.bounds
destStartFrame.origin.x = self.direction == .right ? -destStartFrame.size.width : destStartFrame.size.width
containerView.addSubview(destinationViewController.view)
destinationViewController.view.frame = destStartFrame
let destEndFrame = containerView.bounds
var origEndFrame = containerView.bounds
origEndFrame.origin.x = self.direction == .right ? origEndFrame.size.width : -origEndFrame.size.width
UIView.animate(withDuration: animationTime, animations: {
originalViewController.view.frame = origEndFrame
destinationViewController.view.frame = destEndFrame
}) { finished in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
public func animationEnded(_ transitionCompleted: Bool) {
if !transitionCompleted {
self.interactiveTransitionCanceled = true
}
self.controller?.view.isUserInteractionEnabled = true
}
}

View File

@ -16,7 +16,7 @@ import UIKit
loadHandler = MVMCoreLoadHandler()
cache = MVMCoreCache()
sessionHandler = MVMCoreSessionTimeHandler()
actionHandler = MVMCoreActionHandler()
actionHandler = MVMCoreUIActionHandler()
session = MVMCoreUISession()
viewControllerMapping = MVMCoreUIViewControllerMappingObject()
loggingDelegate = MVMCoreUILoggingHandler()