Support standalone modal controllers

This commit is contained in:
Peter 2019-10-11 16:10:46 +04:00
parent b01c3d9b09
commit 37f28503aa
5 changed files with 92 additions and 23 deletions

View File

@ -371,7 +371,11 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
}
switch transitionType {
case .push:
strongSelf.syncKeyboard(leftEdge: topFrame.minX - bottomFrame.width, transition: transition)
if let _ = strongSelf.state.transition, let top = strongSelf.state.top, viewTreeContainsFirstResponder(view: top.value.view) {
strongSelf.syncKeyboard(leftEdge: topFrame.minX, transition: transition)
} else {
strongSelf.syncKeyboard(leftEdge: topFrame.minX - bottomFrame.width, transition: transition)
}
case .pop:
strongSelf.syncKeyboard(leftEdge: topFrame.minX, transition: transition)
}

View File

@ -537,6 +537,11 @@ open class NavigationController: UINavigationController, ContainableController,
for i in (0 ..< navigationLayout.modal.count).reversed() {
let modalContainer = self.modalContainers[i]
var isStandaloneModal = false
if case .standaloneModal = modalContainer.container.controllers.first?.navigationPresentation {
isStandaloneModal = true
}
let containerTransition: ContainedViewLayoutTransition
if modalContainer.supernode == nil {
containerTransition = .immediate
@ -572,7 +577,9 @@ open class NavigationController: UINavigationController, ContainableController,
}
if modalContainer.supernode != nil {
visibleModalCount += 1
if !isStandaloneModal || visibleModalCount != 0 {
visibleModalCount += 1
}
if previousModalContainer == nil {
topModalDismissProgress = modalContainer.dismissProgress
if case .compact = layout.metrics.widthClass {
@ -587,6 +594,14 @@ open class NavigationController: UINavigationController, ContainableController,
modalContainer.canHaveKeyboardFocus = false
}
previousModalContainer = modalContainer
if isStandaloneModal {
switch modalContainer.container.statusBarStyle {
case .Hide:
statusBarHidden = true
default:
break
}
}
}
}

View File

@ -10,6 +10,7 @@ enum RootNavigationLayout {
struct ModalContainerLayout {
var controllers: [ViewController]
var isStandalone: Bool
}
struct NavigationLayout {
@ -23,6 +24,7 @@ func makeNavigationLayout(mode: NavigationControllerMode, layout: ContainerViewL
for controller in controllers {
let requiresModal: Bool
var beginsModal: Bool = false
var isStandalone: Bool = false
switch controller.navigationPresentation {
case .default:
requiresModal = false
@ -31,6 +33,10 @@ func makeNavigationLayout(mode: NavigationControllerMode, layout: ContainerViewL
case .modal:
requiresModal = true
beginsModal = true
case .standaloneModal:
requiresModal = true
beginsModal = true
isStandalone = true
case .modalInLargeLayout:
switch layout.metrics.widthClass {
case .compact:
@ -40,13 +46,17 @@ func makeNavigationLayout(mode: NavigationControllerMode, layout: ContainerViewL
}
}
if requiresModal {
if beginsModal || modalStack.isEmpty {
modalStack.append(ModalContainerLayout(controllers: [controller]))
if beginsModal || modalStack.isEmpty || modalStack[modalStack.count - 1].isStandalone {
modalStack.append(ModalContainerLayout(controllers: [controller], isStandalone: isStandalone))
} else {
modalStack[modalStack.count - 1].controllers.append(controller)
}
} else if !modalStack.isEmpty {
modalStack[modalStack.count - 1].controllers.append(controller)
if modalStack[modalStack.count - 1].isStandalone {
modalStack.append(ModalContainerLayout(controllers: [controller], isStandalone: isStandalone))
} else {
modalStack[modalStack.count - 1].controllers.append(controller)
}
} else {
rootControllers.append(controller)
}

View File

@ -289,6 +289,11 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
self.validLayout = layout
var isStandaloneModal = false
if case .standaloneModal = controllers.first?.navigationPresentation {
isStandaloneModal = true
}
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
self.ignoreScrolling = true
self.scrollNode.view.isScrollEnabled = (layout.inputHeight == nil || layout.inputHeight == 0.0) && self.isInteractiveDimissEnabled
@ -302,6 +307,8 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
}
self.ignoreScrolling = false
self.scrollNode.view.isScrollEnabled = !isStandaloneModal
let containerLayout: ContainerViewLayout
let containerFrame: CGRect
let containerScale: CGFloat
@ -310,14 +317,28 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
self.panRecognizer?.isEnabled = true
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
self.container.clipsToBounds = true
self.container.cornerRadius = 10.0
if #available(iOS 11.0, *) {
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
if isStandaloneModal {
self.container.cornerRadius = 0.0
} else {
self.container.cornerRadius = 10.0
}
var topInset: CGFloat = 10.0
if let statusBarHeight = layout.statusBarHeight {
topInset += statusBarHeight
if #available(iOS 11.0, *) {
if layout.safeInsets.bottom.isZero {
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
}
}
var topInset: CGFloat
if isStandaloneModal {
topInset = 0.0
} else {
topInset = 10.0
if let statusBarHeight = layout.statusBarHeight {
topInset += statusBarHeight
}
}
containerLayout = ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), safeInsets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.safeInsets.bottom, right: layout.safeInsets.right), statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
@ -358,8 +379,11 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
}
func animateIn(transition: ContainedViewLayoutTransition) {
transition.updateAlpha(node: self.dim, alpha: 1.0)
transition.animatePositionAdditive(node: self.container, offset: CGPoint(x: 0.0, y: self.bounds.height + self.container.bounds.height / 2.0 - (self.container.position.y - self.bounds.height)))
if case .standaloneModal = self.container.controllers.first?.navigationPresentation {
} else {
transition.updateAlpha(node: self.dim, alpha: 1.0)
transition.animatePositionAdditive(node: self.container, offset: CGPoint(x: 0.0, y: self.bounds.height + self.container.bounds.height / 2.0 - (self.container.position.y - self.bounds.height)))
}
}
func dismiss(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) -> ContainedViewLayoutTransition {
@ -367,15 +391,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
controller.viewWillDisappear(transition.isAnimated)
}
if transition.isAnimated {
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0, beginWithCurrentState: true)
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
completion()
})
return positionTransition
} else {
if case .standaloneModal = self.container.controllers.first?.navigationPresentation {
for controller in self.container.controllers {
controller.setIgnoreAppearanceMethodInvocations(true)
controller.displayNode.removeFromSupernode()
@ -384,6 +400,25 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
}
completion()
return transition
} else {
if transition.isAnimated {
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0, beginWithCurrentState: true)
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
completion()
})
return positionTransition
} else {
for controller in self.container.controllers {
controller.setIgnoreAppearanceMethodInvocations(true)
controller.displayNode.removeFromSupernode()
controller.setIgnoreAppearanceMethodInvocations(false)
controller.viewDidDisappear(transition.isAnimated)
}
completion()
return transition
}
}
}

View File

@ -62,6 +62,7 @@ public enum ViewControllerNavigationPresentation {
case `default`
case master
case modal
case standaloneModal
case modalInLargeLayout
}
@ -439,7 +440,11 @@ public enum ViewControllerNavigationPresentation {
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
if let navigationController = self.navigationController as? NavigationController {
navigationController.filterController(self, animated: flag)
var animated = flag
if case .standaloneModal = self.navigationPresentation {
animated = false
}
navigationController.filterController(self, animated: animated)
} else {
assertionFailure()
}