Skip to content

Commit

Permalink
Add transition
Browse files Browse the repository at this point in the history
  • Loading branch information
kyleve committed Oct 7, 2022
1 parent 548e7af commit 0fe89cc
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 80 deletions.

This file was deleted.

63 changes: 63 additions & 0 deletions WorkflowUI/Sources/Screen/AnyContentScreen/AnyContentScreen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2021 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#if canImport(UIKit)

import Foundation

///
///
public struct AnyContentScreen: Screen {
public var transition: ViewTransition
public let content: AnyScreen

public init<ScreenType: Screen>(
transition: ViewTransition = .fade(),
content: () -> ScreenType
) {
let content = content()

if let content = content as? Self {
self = content
} else {
self.content = content.asAnyScreen()
}

self.transition = transition
}

// MARK: Screen

public func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription {
let description = content.viewControllerDescription(environment: environment)

return ViewControllerDescription(
/// The inner `DescribedViewController` will respect `performInitialUpdate` from
/// the nested screen – so our value should always be false.
performInitialUpdate: false,
transition: transition,
type: DescribedViewController.self,
build: {
DescribedViewController(description: description)
},
update: { vc in
vc.update(description: description, animated: true)
}
)
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
/// Displays the backing `ViewControllerDescription` for a given `Screen`.
///
public final class DescribedViewController: UIViewController {
var currentViewController: UIViewController
var content: UIViewController

public init(description: ViewControllerDescription) {
self.currentViewController = description.buildViewController()
self.content = description.buildViewController()
super.init(nibName: nil, bundle: nil)

addChild(currentViewController)
currentViewController.didMove(toParent: self)
addChild(content)
content.didMove(toParent: self)
}

public convenience init<S: Screen>(screen: S, environment: ViewEnvironment) {
Expand All @@ -40,32 +40,53 @@
fatalError("init(coder:) is unavailable")
}

public func update(description: ViewControllerDescription) {
if description.canUpdate(viewController: currentViewController) {
description.update(viewController: currentViewController)
public func update(description: ViewControllerDescription, animated: Bool = false) {
if description.canUpdate(viewController: content) {
description.update(viewController: content)
} else {
currentViewController.willMove(toParent: nil)
currentViewController.viewIfLoaded?.removeFromSuperview()
currentViewController.removeFromParent()
let old = content
let new = description.buildViewController()

currentViewController = description.buildViewController()

addChild(currentViewController)
content = new

if isViewLoaded {
currentViewController.view.frame = view.bounds
view.addSubview(currentViewController.view)
updatePreferredContentSizeIfNeeded()
let animated == animated && view.window != nil

addChild(new)
old.willMove(toParent: nil)

description.transition.transition(
from: old.view,
to: new.view,
in: view,
animated: animated,
setup: {
self.view.addSubview(new.view)
},
completion: {
new.didMove(toParent: self)

old.view.removeFromSuperview()
old.removeFromParent()

self.currentViewControllerChanged()
}
)

} else {
addChild(new)
new.didMove(toParent: self)

old.willMove(toParent: nil)
old.removeFromParent()
}

currentViewController.didMove(toParent: self)

updatePreferredContentSizeIfNeeded()
}
}

public func update<S: Screen>(screen: S, environment: ViewEnvironment) {
if let screen = screen as? AnyContentContainerScreen {
if let screen = screen as? AnyContentScreen {
update(description: screen.content.viewControllerDescription(environment: environment))
} else {
update(description: screen.viewControllerDescription(environment: environment))
Expand All @@ -75,62 +96,76 @@
override public func viewDidLoad() {
super.viewDidLoad()

currentViewController.view.frame = view.bounds
view.addSubview(currentViewController.view)
content.view.frame = view.bounds
view.addSubview(content.view)

updatePreferredContentSizeIfNeeded()
}

override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
currentViewController.view.frame = view.bounds
content.view.frame = view.bounds
}

override public var childForStatusBarStyle: UIViewController? {
return currentViewController
return content
}

override public var childForStatusBarHidden: UIViewController? {
return currentViewController
return content
}

override public var childForHomeIndicatorAutoHidden: UIViewController? {
return currentViewController
return content
}

override public var childForScreenEdgesDeferringSystemGestures: UIViewController? {
return currentViewController
return content
}

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return currentViewController.supportedInterfaceOrientations
return content.supportedInterfaceOrientations
}

override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return currentViewController.preferredStatusBarUpdateAnimation
return content.preferredStatusBarUpdateAnimation
}

@available(iOS 14.0, *)
override public var childViewControllerForPointerLock: UIViewController? {
return currentViewController
return content
}

override public func preferredContentSizeDidChange(
forChildContentContainer container: UIContentContainer
) {
super.preferredContentSizeDidChange(forChildContentContainer: container)

guard container === currentViewController else { return }
guard container === content else { return }

updatePreferredContentSizeIfNeeded()
}

private func updatePreferredContentSizeIfNeeded() {
let newPreferredContentSize = currentViewController.preferredContentSize
let newPreferredContentSize = content.preferredContentSize

guard newPreferredContentSize != preferredContentSize else { return }

preferredContentSize = newPreferredContentSize
}

private func currentViewControllerChanged() {
setNeedsFocusUpdate()
setNeedsUpdateOfHomeIndicatorAutoHidden()

if #available(iOS 14.0, *) {
self.setNeedsUpdateOfPrefersPointerLocked()
}

setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
setNeedsStatusBarAppearanceUpdate()

UIAccessibility.post(notification: .screenChanged, argument: nil)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
/// duplicate updates to your children if they are created in `init`.
public var performInitialUpdate: Bool

public var transition: ViewTransition

/// Describes the `UIViewController` type that backs the `ViewControllerDescription`
/// in a way that is `Equatable` and `Hashable`. When implementing view controller
/// updating and diffing, you can use this type to identify if the backing view controller
Expand All @@ -69,11 +71,13 @@
/// - update: Closure that updates the given view controller
public init<VC: UIViewController>(
performInitialUpdate: Bool = true,
transition: ViewTransition = .none,
type: VC.Type = VC.self,
build: @escaping () -> VC,
update: @escaping (VC) -> Void
) {
self.performInitialUpdate = performInitialUpdate
self.transition = transition

self.kind = .init(VC.self)

Expand Down
Loading

0 comments on commit 0fe89cc

Please sign in to comment.