-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[swift] [rfc] Add TabBarScreen Sample #1098
Conversation
485ddad
to
5947904
Compare
5947904
to
a68dde4
Compare
view.addSubview(tabBar) | ||
|
||
addChild(contentViewController) | ||
containerView.contentView.addSubview(contentViewController.view) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why containerView instead of adding the contentViewController’s view directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I received feedback on my SplitScreen PR from @AquaGeek to use a container view to codify a view who sets it's subviews bounds to its frame on every layout pass. Decided to keep the pattern the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can’t find that discussion from that PR. But I feel like this adds an unnecessary layer to the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Darn it was in person: #925 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For context, IIRC that discussion was around adding static container subviews in the split screen so that you don't have to redo all the sizing math when swapping out contents — you just hand the new content view to the appropriate container and it adds it as a subview and does a view.frame = bounds
.
internal override func viewDidLayoutSubviews() { | ||
super.viewDidLayoutSubviews() | ||
|
||
let tabBarHeight: CGFloat = 83 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we ask the tab bar its height? And how does this work across iPhone X-ish sized phones with bottom safe area insets and iPhone 8-ish ones without them?
FBSnapshotVerifyView( | ||
viewController.view, | ||
identifier: name, | ||
suffixes: ["_64"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What’s the _64
for?
|
||
extension FooWorkflow { | ||
|
||
enum Action: WorkflowAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this action?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope removing both in Bar and Foo.
swift/Samples/TabBarContainer/DemoApp/Bar/BarBadgeWorkflow.swift
Outdated
Show resolved
Hide resolved
7b1c413
to
1e74402
Compare
override internal func viewDidLoad() { | ||
super.viewDidLoad() | ||
|
||
view.addSubview(containerView) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason we shouldn't use UITabBarController
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've seen two internal implementations that use BluePrint to render the tab bar and only accept the currently displayed screen.
This allows greater control of what the UI rendering / view behavior looks like. For this sample, I didn't want to use Blueprint, so decided to use a simple UITabBar.
The question of holding onto an array of multiple screens is a good one and should be discussed in more detail. I'd like a few more folks to weigh their opinions on this PR before we circle up as a group to decide.
swift/Samples/TabBarContainer/Sources/TabBarScreenViewController.swift
Outdated
Show resolved
Hide resolved
7ca7ed8
to
5a12fc5
Compare
5a12fc5
to
aed5b3a
Compare
aed5b3a
to
782c43b
Compare
|
||
extension BazBadgeWorkflow { | ||
|
||
struct BarBadgeWorker: Worker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intentionally called Bar
instead of Baz
?
These names are confusing. I don't have better suggestions outside of building more "real" UI.
} | ||
|
||
return TabBarContainerScreen( | ||
screens: [bazScreen, fooScreen], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very much of the mind that all the tabs should be rendered in each pass.
extension BarItem { | ||
public enum Badge { | ||
case none | ||
case value(Int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a separate case for this? Why not force consumers to do the text conversion?
Can we use a map function to convert the
This way, if we reparent these workflows into a different container (e.g. hamburger menu side bar) we don't have to change their content at all. |
I’d say badge value should be part of rendering of children, not outputs (i.e. it’s their state, not events). |
public struct TabScreen { | ||
public let barItem: BarItem | ||
public let content: AnyScreen | ||
public let onSelect: (() -> ()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Snip this, and handle the selection at the parent.
// MARK: Rendering | ||
|
||
extension BazWorkflow { | ||
typealias Rendering = TabScreen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be just BazScreen
|
||
typealias State = Void | ||
enum Output { | ||
case tabSelected |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create an worker output for the badgeCountUpdate.
@bencochran could you clarify this a little more. Do you mean that you'd rather see the rendering be a tuple of screen and badge count? Instead of using the child workflow's output when the badge count is updated? |
Closing for now - will re-open post repo move and when I have time to wrap up. |
Adds a TabBarScreen sample with snapshot tests
Demonstrates a way to separate BarItem renderings as a separate Workflow to isolate contained tab screen from bar badge value fetch / calculations.
Note: Compose Screen concept is out of scope for this PR.
TODO & Open Questions:
AnyScreen
s and use selected index to control display?Single Screen vs. Multiple Screens:
Pros for Single Screen:
Cons for Single Screen:
How do we want to demonstrate fetching / rendering the badge value for the Baz tab? A separate Workflow or a simple Worker that is awaited in
DemoWorkflow
?Determine the right height for the tab bar on all the devices, should this come in on the screen or is it an implementation detail?
Should we snip the ContainerView?
Any way to tighten up the API (I assume less verbosity)?