swiftui-hosting-transitions

0.1.0

A library to perform matched geometry effect transitions between SwiftUI's UIHostingControllers.
pbalduz/swiftui-hosting-transitions

What's New

0.1.0

2024-02-26T09:40:32Z

Initial release

This release includes basic transition animations:

  • Animated transition for push navigation.
  • Position and frame animation.

swiftui-hosting-transitions

SwiftUI enables to perform smooth transitions between views using matchedGeometryEffect view modifier but what happens when the views are contained in UIHostingControllers?

When transitioning UIKit projects to SwiftUI, many developers initially wrap their views in UIHostingControllers while keeping navigation in UIKit. However, this approach limits the ability to implement custom transitions between the SwiftUI views. This library addresses this challenge by providing a straightforward solution for executing custom matched geometry transitions specifically designed for UIHostingControllers.

During the development of this repository was inspired by and article about view controller transitions in SwiftUI.

A Hero View Controller Transition in SwiftUI

Installation

If you want to use Matched Transitions in an SPM project, just add it to the dependencies in your Package.swift:

dependencies: [
  .package(url: https://github.com/pbalduz/swiftui-hosting-transitions, from: "0.1.0")
]

Then, you can include in any target as a dependency:

targets: [
  .target(
    name: "MyTarget",
    dependencies: [
      .product(name: "MatchedTransitions", package, "swiftui-hosting-transitions")
    ]
]

You can also add it to and Xcode project by adding it as a package dependency.

Usage

The API to perform custom transitions is pretty simple and requires of two steps, identifying the SwiftUI views that should animate (source and destination) and wrapping the views' in a MatchedHostingController.

This is how the views to animate are identified:

struct FirstView: View {
  var body: some View {
    Color.red
      .frame(width: 100, height: 100)
      .matchedGeometry(id: "id", isSource: true)
  }
}

struct FirstView: View {
  var body: some View {
    Color.red
      .frame(maxWidth: .infinity)
      .frame(height: 100)
      .matchedGeometry(id: "id", isSource: false)
  }
}

Then we would just need to wrap the views in MatchedHostingControllers instead of UIHostingController:

class FirstViewController: MatchedHostingController<FirstView> {
  let state = MatchedGeometryState()
  
  init() {
    super.init(
      rootView: FirstView(),
      state: state
    )
  }
}

class SecondViewController: MatchedHostingController<SecondView> {
  init(state: MatchedGeometryState) {
    super.init(
      rootView: SecondView(),
      state: state
    )
  }
}

MatchedHostingController inherits from UIHostingController and retains a similar initialization API, with the addition of a state property crucial for the proper execution of transitions. It is essential to retain an instance of this property, as it must be passed to the destination view controller to enable the sharing of information about matched views.

Finally, in order to navigate between view controllers the regular navigation controller API can be used:

// from FirstViewController
let vc = SecondViewController(state: state)
navigationController?.pushViewController(vc, animated: true)

Demo

matched-transition-demo

What's next

  • Currently, only push navigation is supported and modal presentation is still to be implemented.
  • Interactive transitions?
  • MacOS navigation support?
  • Background transition for non matched views has a small weird opacity change when background is not white that should be addressed.

Description

  • Swift Tools 5.9.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun May 05 2024 14:36:34 GMT-0900 (Hawaii-Aleutian Daylight Time)