Skip to content

Commit

Permalink
Merge pull request #732 from HeroTransitions/pullrequests/nn1900/1.6.…
Browse files Browse the repository at this point in the history
…1-hotfix

Pullrequests/nn1900/1.6.1 hotfix
  • Loading branch information
JoeMatt authored Apr 11, 2022
2 parents 15e78a2 + 1a9ce72 commit dd260a3
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 2 deletions.
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ The changelog for `Hero`. Also see the [releases](https://github.com/HeroTransit
- #595 - Add Accio supported badge
- #619 - XCode 11/12 support in example
- CI/CD improvements

### Changed

- #648 - Updated iOS version support
Expand Down
24 changes: 23 additions & 1 deletion Sources/Transition/HeroTransition+Complete.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,32 @@ extension HeroTransition {
if isPresenting != finished, !inContainerController, transitionContext != nil {
// only happens when present a .overFullScreen VC
// bug: http://openradar.appspot.com/radar?id=5320103646199808
container.window?.addSubview(isPresenting ? fromView : toView)

// $workaround(eric): try to restore the view of the prensenting view controller to
// where it was otherwise. Simply putting the view back under window will leak the view in
// some edge cases, for example, when the presenting view was deeply nested under some
// exotic view hierarchy (e.g., react native views). as stated where the transition starts,
// `originalSuperview` remembers the original super view when the `presenting` transition
// animation starts, now it's safe to restore it where it was if possible.
if let superview = originalSuperview, superview.window != nil {
let view = isPresenting ? fromView : toView
superview.addSubview(view)
if let frame = originalFrame {
view.frame = frame
}
} else {
container.window?.addSubview(isPresenting ? fromView : toView)
}
}
}

// clear temporary states only when dismissing finishes.
if !isPresenting && finished {
originalSuperview = nil
originalFrame = nil
originalFrameInContainer = nil
}

if container.superview == transitionContainer {
container.removeFromSuperview()
}
Expand Down
32 changes: 32 additions & 0 deletions Sources/Transition/HeroTransition+Start.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ extension HeroTransition {
state = .starting

if let toView = toView, let fromView = fromView {
// remember the superview of the view of the `fromViewController` which is
// presenting the `toViewController` with `overFullscreen` `modalPresentationStyle`,
// so that we can restore the presenting view controller's view later on dismiss
if isPresenting && !inContainerController {
originalSuperview = fromView.superview
originalFrame = fromView.frame
}
if let toViewController = toViewController, let transitionContext = transitionContext {
toView.frame = transitionContext.finalFrame(for: toViewController)
} else {
Expand Down Expand Up @@ -111,11 +118,36 @@ extension HeroTransition {
}

if let toView = toView, let fromView = fromView {
// if we're presenting a view controller, remember the position & dimension
// of the view relative to the transition container so that we can:
// - correctly place the view in the transition container when presenting
// - correctly place the view back to where it was when dismissing
if isPresenting && !inContainerController {
originalFrameInContainer = fromView.superview?.convert(
fromView.frame, to: container
)
}

// when dismiss and before animating, place the `toView` to be animated
// with the correct position and dimension in the transition container.
// otherwise, there will be an apparent visual jagging when the animation begins.
if !isPresenting, let frame = originalFrameInContainer {
toView.frame = frame
}

context.loadViewAlpha(rootView: toView)
context.loadViewAlpha(rootView: fromView)
container.addSubview(toView)
container.addSubview(fromView)

// when present and before animating, place the `fromView` to be animated
// with the correct position and dimension in the transition container to
// prevent any possible visual jagging when animation starts, even though not
// that apparent in some cases.
if isPresenting, let frame = originalFrameInContainer {
fromView.frame = frame
}

toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
Expand Down
4 changes: 4 additions & 0 deletions Sources/Transition/HeroTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ open class HeroTransition: NSObject {
internal var plugins: [HeroPlugin] = []
internal var animatingFromViews: [UIView] = []
internal var animatingToViews: [UIView] = []
internal var originalSuperview: UIView?
internal var originalFrame: CGRect?
internal var originalFrameInContainer: CGRect?

internal static var enabledPlugins: [HeroPlugin.Type] = []

/// destination view controller
Expand Down
60 changes: 60 additions & 0 deletions docs/UsageGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Usage

## Storyboard

1. In the Identity Inspector, for every pair of source/destination views, give each one the same `HeroID` attribute.
2. For any other views that you would like to animate, specify animation effects in the `Hero Modifier String` attribute.
3. Also in the Identity Inspector, enable Hero Transition on your destination view controller.

## In Code

1. Before doing a transition, set the desired `heroID` and `heroModifiers` to both your source and destination views.
2. Enable Hero for the destination view controller

```swift
viewController.hero.isEnabled = true
```

### UINavigationController & UITabBarController

Hero also supports transitions within a navigation controller or a tab bar controller—just set the 'hero.isEnabled' attribute to true on the UINavigationController/UITabBarController instance.

## Attributes

There are two important attributes to understand: `heroID` and `heroModifiers`. These are implemented as extensions (using associated objects) for `UIView`. Therefore, after the Hero library is imported, every `UIView` will have these two attributes.

| Attribute Name | Description |
| --- | --- |
| `heroID` | Identifier for the view. Hero will automatically transition between views with the same `heroID` |
| `hero.modifiers` | Specifies the extra animations performed alongside the main transition. |

## HeroID

`heroID` is the identifier for the view. When doing a transition between two view controllers, Hero will search through all subviews for both controllers, and match any views with the same `heroID`. Whenever a pair is discovered, Hero will automatically transit the views from source state to destination state.

## HeroModifiers

Use `hero.modifiers` to specify animations alongside the main transition. Checkout [HeroModifier.swift](https://github.com/lkzhao/Hero/blob/master/Sources/HeroModifier.swift) for available modifiers.

#### For example, to achieve the following effect, set the `hero.modifiers` to be

```swift
view.hero.modifiers = [.fade, .translate(x:0, y:-250), .rotate(x:-1.6), .scale(1.5)]
```

<!--- TODO: Fix broken image below --->
<img src="https://github.com/lkzhao/Hero/blob/master/Resources/heroTransition.gif?raw=true" width="300">

Note: For matched views, the target view's heroModifier will be used. The source view's heroModifier will be ignored. When dismissing, the target view is the presentingViewController's view and the source view is the presentedViewController's view.

## HeroModifierString

This is a string value. It provides another way to set `hero.modifiers`. It can be accessed through the storyboard.

It must be in the following syntax:

```swift
modifier1() modifier2(parameter1) modifier3(parameter1, parameter2) ...
```

Parameters must be between a pair of parentheses, separated by a comma, and each modifier must be separated by a space. Not all modifiers are settable this way.

0 comments on commit dd260a3

Please sign in to comment.