Skip to content

Commit

Permalink
Merge pull request #32 from erikdrobne/feature/modal-transitions
Browse files Browse the repository at this point in the history
Add modal transition delegate associated value to the present action.
  • Loading branch information
erikdrobne authored Nov 13, 2023
2 parents 43373e3 + c5ab7cc commit 48afedf
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
17AABAF12A6D5F2100AFE8A7 /* ShapesAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17AABAF02A6D5F2100AFE8A7 /* ShapesAction.swift */; };
17C379712ACEDD7800CA4105 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C379702ACEDD7800CA4105 /* AppCoordinator.swift */; };
17C379742ACEE1EA00CA4105 /* CoordinatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C379732ACEE1EA00CA4105 /* CoordinatorFactory.swift */; };
17DF24AF2AFD14C600578CD9 /* SlideTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF24AE2AFD14C600578CD9 /* SlideTransition.swift */; };
17F1183529CC63B1004755DB /* SimpleShapesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17F1183429CC63B1004755DB /* SimpleShapesView.swift */; };
17F1183729CC63C0004755DB /* CustomShapesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17F1183629CC63C0004755DB /* CustomShapesView.swift */; };
17F1183B29CC6678004755DB /* SimpleShapesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17F1183A29CC6678004755DB /* SimpleShapesCoordinator.swift */; };
Expand Down Expand Up @@ -50,6 +51,7 @@
17AABAF02A6D5F2100AFE8A7 /* ShapesAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShapesAction.swift; sourceTree = "<group>"; };
17C379702ACEDD7800CA4105 /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
17C379732ACEE1EA00CA4105 /* CoordinatorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatorFactory.swift; sourceTree = "<group>"; };
17DF24AE2AFD14C600578CD9 /* SlideTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideTransition.swift; sourceTree = "<group>"; };
17E1C1FF2A1BD86D00542AB9 /* SwiftUICoordinator.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SwiftUICoordinator.xctestplan; sourceTree = "<group>"; };
17F1183429CC63B1004755DB /* SimpleShapesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleShapesView.swift; sourceTree = "<group>"; };
17F1183629CC63C0004755DB /* CustomShapesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomShapesView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -122,6 +124,7 @@
isa = PBXGroup;
children = (
17360A402A1275D600DB2296 /* FadeTransition.swift */,
17DF24AE2AFD14C600578CD9 /* SlideTransition.swift */,
);
path = Transitions;
sourceTree = "<group>";
Expand Down Expand Up @@ -297,6 +300,7 @@
17C379742ACEE1EA00CA4105 /* CoordinatorFactory.swift in Sources */,
17AABAF12A6D5F2100AFE8A7 /* ShapesAction.swift in Sources */,
176F3CB829B8C05B009C4987 /* ShapesCoordinator.swift in Sources */,
17DF24AF2AFD14C600578CD9 /* SlideTransition.swift in Sources */,
176F3CB529B8BF71009C4987 /* ShapeListView.swift in Sources */,
17C379712ACEDD7800CA4105 /* AppCoordinator.swift in Sources */,
17360A412A1275D600DB2296 /* FadeTransition.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ enum SimpleShapesRoute: NavigationRoute {
}

var action: TransitionAction? {
return .push(animated: true)
switch self {
case .rect:
return .present(delegate: SlideTransitionDelegate())
default:
return .push(animated: true)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// SlideTransition.swift
// SwiftUICoordinatorExample
//
// Created by Erik Drobne on 9. 11. 23.
//

import UIKit

class SlideTransition: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting: Bool

init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
0.5
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let key = isPresenting ? UITransitionContextViewControllerKey.to : UITransitionContextViewControllerKey.from
guard let controller = transitionContext.viewController(forKey: key) else { return }

if isPresenting {
transitionContext.containerView.addSubview(controller.view)
}

let finalFrame = transitionContext.finalFrame(for: controller)
let startingFrame = isPresenting ? finalFrame.offsetBy(dx: 0, dy: -finalFrame.height) : finalFrame
let endingFrame = isPresenting ? finalFrame : finalFrame.offsetBy(dx: 0, dy: -finalFrame.height)

controller.view.frame = startingFrame

UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
controller.view.frame = endingFrame
}) { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}

final class SlideTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SlideTransition(isPresenting: true)
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SlideTransition(isPresenting: false)
}
}
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,39 @@ lazy var delegate = factory.makeNavigationDelegate([FadeTransition()])
lazy var navigationController = factory.makeNavigationController(delegate: delegate)
```

#### Modal transitions

Custom modal transitions can enhance the user experience by providing a unique way to `present` and `dismiss` view controllers.

First, define a transition delegate object that conforms to the `UIViewControllerTransitioningDelegate` protocol.

```Swift
final class SlideTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SlideTransition(isPresenting: true)
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SlideTransition(isPresenting: false)
}
}
```

In this example, `SlideTransition` is a custom class that conforms to the `UIViewControllerAnimatedTransitioning` protocol and handles the actual animation logic.

Pass the `SlideTransitionDelegate` instance to the specific action where you wish to apply your modal transition.

```Swift
var action: TransitionAction? {
switch self {
case .rect:
return .present(delegate: SlideTransitionDelegate())
default:
return .push(animated: true)
}
}
```

### Handling deep links

In your application, you can handle deep links by creating a `DeepLinkHandler` that conforms to the `DeepLinkHandling` protocol. This handler will specify the URL scheme and the supported deep links that your app can recognize.
Expand Down
12 changes: 10 additions & 2 deletions Sources/SwiftUICoordinator/Navigator/Navigator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ public extension Navigator where Self: RouterViewFactory {
switch route.action {
case .push(let animated):
navigationController.pushViewController(viewController, animated: animated)
case .present(let animated, let modalPresentationStyle, let completion):
case .present(let animated, let modalPresentationStyle, let delegate, let completion):
present(
viewController: viewController,
animated: animated,
modalPresentationStyle: modalPresentationStyle,
delegate: delegate,
completion: completion
)
case .none:
Expand Down Expand Up @@ -129,9 +130,16 @@ public extension Navigator where Self: RouterViewFactory {
viewController: UIViewController,
animated: Bool,
modalPresentationStyle: UIModalPresentationStyle,
delegate: UIViewControllerTransitioningDelegate?,
completion: (() -> Void)?
) {
viewController.modalPresentationStyle = modalPresentationStyle
if let delegate {
viewController.modalPresentationStyle = .custom
viewController.transitioningDelegate = delegate
} else {
viewController.modalPresentationStyle = modalPresentationStyle
}

navigationController.present(viewController, animated: animated, completion: completion)
}
}
3 changes: 2 additions & 1 deletion Sources/SwiftUICoordinator/Transition/TransitionAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ public enum TransitionAction {
case push(animated: Bool)
/// Represents a present action for presenting a view controller modally.
case present(
animated: Bool,
animated: Bool = true,
modalPresentationStyle: UIModalPresentationStyle = .automatic,
delegate: UIViewControllerTransitioningDelegate? = nil,
completion: (() -> Void)? = nil
)
}

0 comments on commit 48afedf

Please sign in to comment.