Skip to content

Commit e93a6ca

Browse files
committed
Add transition
1 parent 548e7af commit e93a6ca

File tree

5 files changed

+312
-80
lines changed

5 files changed

+312
-80
lines changed

WorkflowUI/Sources/Screen/AnyContentContainerScreen/AnyContentContainerScreen.swift

-49
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2021 Square Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#if canImport(UIKit)
18+
19+
import Foundation
20+
21+
///
22+
///
23+
public struct AnyContentScreen: Screen {
24+
public var transition: ViewTransition
25+
public let content: AnyScreen
26+
27+
public init<ScreenType: Screen>(
28+
transition: ViewTransition = .fade(),
29+
content: () -> ScreenType
30+
) {
31+
let content = content()
32+
33+
if let content = content as? Self {
34+
self = content
35+
} else {
36+
self.content = content.asAnyScreen()
37+
}
38+
39+
self.transition = transition
40+
}
41+
42+
// MARK: Screen
43+
44+
public func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription {
45+
let description = content.viewControllerDescription(environment: environment)
46+
47+
return ViewControllerDescription(
48+
/// The inner `DescribedViewController` will respect `performInitialUpdate` from
49+
/// the nested screen – so our value should always be false.
50+
performInitialUpdate: false,
51+
transition: transition,
52+
type: DescribedViewController.self,
53+
build: {
54+
DescribedViewController(description: description)
55+
},
56+
update: { vc in
57+
vc.update(description: description, animated: true)
58+
}
59+
)
60+
}
61+
}
62+
63+
#endif

WorkflowUI/Sources/ViewControllerDescription/DescribedViewController.swift

+66-31
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
/// Displays the backing `ViewControllerDescription` for a given `Screen`.
2222
///
2323
public final class DescribedViewController: UIViewController {
24-
var currentViewController: UIViewController
24+
var content: UIViewController
2525

2626
public init(description: ViewControllerDescription) {
27-
self.currentViewController = description.buildViewController()
27+
self.content = description.buildViewController()
2828
super.init(nibName: nil, bundle: nil)
2929

30-
addChild(currentViewController)
31-
currentViewController.didMove(toParent: self)
30+
addChild(content)
31+
content.didMove(toParent: self)
3232
}
3333

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

43-
public func update(description: ViewControllerDescription) {
44-
if description.canUpdate(viewController: currentViewController) {
45-
description.update(viewController: currentViewController)
43+
public func update(description: ViewControllerDescription, animated: Bool = false) {
44+
if description.canUpdate(viewController: content) {
45+
description.update(viewController: content)
4646
} else {
47-
currentViewController.willMove(toParent: nil)
48-
currentViewController.viewIfLoaded?.removeFromSuperview()
49-
currentViewController.removeFromParent()
47+
let old = content
48+
let new = description.buildViewController()
5049

51-
currentViewController = description.buildViewController()
52-
53-
addChild(currentViewController)
50+
content = new
5451

5552
if isViewLoaded {
56-
currentViewController.view.frame = view.bounds
57-
view.addSubview(currentViewController.view)
58-
updatePreferredContentSizeIfNeeded()
53+
let animated = animated && view.window != nil
54+
55+
addChild(new)
56+
old.willMove(toParent: nil)
57+
58+
description.transition.transition(
59+
from: old.view,
60+
to: new.view,
61+
in: view,
62+
animated: animated,
63+
setup: {
64+
self.view.addSubview(new.view)
65+
},
66+
completion: {
67+
new.didMove(toParent: self)
68+
69+
old.view.removeFromSuperview()
70+
old.removeFromParent()
71+
72+
self.currentViewControllerChanged()
73+
}
74+
)
75+
76+
} else {
77+
addChild(new)
78+
new.didMove(toParent: self)
79+
80+
old.willMove(toParent: nil)
81+
old.removeFromParent()
5982
}
6083

61-
currentViewController.didMove(toParent: self)
62-
6384
updatePreferredContentSizeIfNeeded()
6485
}
6586
}
6687

6788
public func update<S: Screen>(screen: S, environment: ViewEnvironment) {
68-
if let screen = screen as? AnyContentContainerScreen {
89+
if let screen = screen as? AnyContentScreen {
6990
update(description: screen.content.viewControllerDescription(environment: environment))
7091
} else {
7192
update(description: screen.viewControllerDescription(environment: environment))
@@ -75,62 +96,76 @@
7596
override public func viewDidLoad() {
7697
super.viewDidLoad()
7798

78-
currentViewController.view.frame = view.bounds
79-
view.addSubview(currentViewController.view)
99+
content.view.frame = view.bounds
100+
view.addSubview(content.view)
80101

81102
updatePreferredContentSizeIfNeeded()
82103
}
83104

84105
override public func viewDidLayoutSubviews() {
85106
super.viewDidLayoutSubviews()
86-
currentViewController.view.frame = view.bounds
107+
content.view.frame = view.bounds
87108
}
88109

89110
override public var childForStatusBarStyle: UIViewController? {
90-
return currentViewController
111+
return content
91112
}
92113

93114
override public var childForStatusBarHidden: UIViewController? {
94-
return currentViewController
115+
return content
95116
}
96117

97118
override public var childForHomeIndicatorAutoHidden: UIViewController? {
98-
return currentViewController
119+
return content
99120
}
100121

101122
override public var childForScreenEdgesDeferringSystemGestures: UIViewController? {
102-
return currentViewController
123+
return content
103124
}
104125

105126
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
106-
return currentViewController.supportedInterfaceOrientations
127+
return content.supportedInterfaceOrientations
107128
}
108129

109130
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
110-
return currentViewController.preferredStatusBarUpdateAnimation
131+
return content.preferredStatusBarUpdateAnimation
111132
}
112133

113134
@available(iOS 14.0, *)
114135
override public var childViewControllerForPointerLock: UIViewController? {
115-
return currentViewController
136+
return content
116137
}
117138

118139
override public func preferredContentSizeDidChange(
119140
forChildContentContainer container: UIContentContainer
120141
) {
121142
super.preferredContentSizeDidChange(forChildContentContainer: container)
122143

123-
guard container === currentViewController else { return }
144+
guard container === content else { return }
124145

125146
updatePreferredContentSizeIfNeeded()
126147
}
127148

128149
private func updatePreferredContentSizeIfNeeded() {
129-
let newPreferredContentSize = currentViewController.preferredContentSize
150+
let newPreferredContentSize = content.preferredContentSize
130151

131152
guard newPreferredContentSize != preferredContentSize else { return }
132153

133154
preferredContentSize = newPreferredContentSize
134155
}
156+
157+
private func currentViewControllerChanged() {
158+
setNeedsFocusUpdate()
159+
setNeedsUpdateOfHomeIndicatorAutoHidden()
160+
161+
if #available(iOS 14.0, *) {
162+
self.setNeedsUpdateOfPrefersPointerLocked()
163+
}
164+
165+
setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
166+
setNeedsStatusBarAppearanceUpdate()
167+
168+
UIAccessibility.post(notification: .screenChanged, argument: nil)
169+
}
135170
}
136171
#endif

WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
/// duplicate updates to your children if they are created in `init`.
4444
public var performInitialUpdate: Bool
4545

46+
public var transition: ViewTransition
47+
4648
/// Describes the `UIViewController` type that backs the `ViewControllerDescription`
4749
/// in a way that is `Equatable` and `Hashable`. When implementing view controller
4850
/// updating and diffing, you can use this type to identify if the backing view controller
@@ -69,11 +71,13 @@
6971
/// - update: Closure that updates the given view controller
7072
public init<VC: UIViewController>(
7173
performInitialUpdate: Bool = true,
74+
transition: ViewTransition = .none,
7275
type: VC.Type = VC.self,
7376
build: @escaping () -> VC,
7477
update: @escaping (VC) -> Void
7578
) {
7679
self.performInitialUpdate = performInitialUpdate
80+
self.transition = transition
7781

7882
self.kind = .init(VC.self)
7983

0 commit comments

Comments
 (0)