Comments (11)
@YuantongL I found a way without too much hacking.
First, you need to hide the UITabBar globally.
UITabBar.appearance().isHidden = true
And then you need to create an environment variable to pass active tab value to inside through subviews
struct ActiveTab: EnvironmentKey {
static let defaultValue: Int = 0
}
we need to use the customize
method of the coordinator to be able to place our custom tab bar and then we need to pass activeTab
value to be able to make our custom tab bar fully functional
@ViewBuilder func customize(_ view: AnyView) -> some View {
ZStack(alignment: .bottom) {
view
CustomTabBarView() // <-- This is our custom tab bar
}
.environment(\.activeTab, self.child.activeTab)
}
Every time user changed the tab, we need to pass the new active tab index value. Therefore, we'll be using tab item creating @ViewBuilder
methods
@ViewBuilder func makeHomeTab(isActive: Bool) -> some View {
EmptyView()
.environment(\.activeTab, self.child.activeTab)
}
Almost everything is done, the only thing that we need to do is, handling the navigation when user click a specific custom tab button
Button {
_ = router <-- Injecting coordinator router
.focusFirst(\.home)
.child
} label: {
// activeTab == 0 <-- You can change the UI with checking the active tab index value
// Tab Item UI omitted
}
Finally, I managed to create a custom tab bar using this structure without hacking the library. I hope I would be helpful to you as well
from stinsen.
@Goktug Thanks, that's a nice approach, definitely better then the hack!
I made similar change to my project, the only difference is, instead of pass in environment variable, I made it through a binding.
struct CustomTabBarView: View {
@Binding
var activeTabIndex: Int
var body: some View {
HStack {
Button {
activeTabIndex = 0
} label: {
Text("Tab 0")
}
Button {
activeTabIndex = 1
} label: {
Text("Tab 1")
}
}
}
Then in the coordinator, do the following
private var activeTabIndex = 0 {
didSet {
child.activeTab = activeTabIndex
}
}
@ViewBuilder func customize(_ view: AnyView) -> some View {
ZStack(alignment: .bottom) {
view
CustomTabBarView(activeTabIndex: .init(get: {
self.activeTabIndex
}, set: { newValue in
self.activeTabIndex = newValue
}))
}
}
from stinsen.
@Goktug my approch doesn't have to depend on tab clicks if I simply remove the private activeTabIndex
and use
@ViewBuilder func customize(_ view: AnyView) -> some View {
ZStack(alignment: .bottom) {
view
CustomTabBarView(activeTabIndex: .init(get: {
self.child.activeTab
}, set: { newValue in
self.child.activeTab = newValue
}))
}
}
Anything that has a TabCoordinator.Router
can change tab via focusFirst
and our custom tab bar changes accordingly (since we are getting the index value from child.activeTab
).
I think both approach works, it is just personally I prefer pass around a Binding in this case instead of environment object.
from stinsen.
+1
@Goktug I personally have a hack to achieve this, Overriding func view() -> AnyView
function in TabCoordinatable
gives you opportunity to use your own custom tab bar, you can create one using UIKit or use a pure SwiftUI implementation as you like.
To make it all working, this custom tab bar should mimic the TabCoordinatableView
, basically follow its init, this is where the hack comes in, I have to use a @testable import Stinsen
to access things like coordinator.child.allItems
, and its presentable
s.
Expose these internal variables to public will do it, but a nicer way is - we can pass in our own ViewBuilder for tab bar and their associated views.
from stinsen.
Thanks for the advice @YuantongL, I was also trying to hack but couldn't reach the active tab index which is reactive. At least, if the library could provide this value we don't need to use @testable
hack
from stinsen.
@Goktug I think #43 this is all we need in order to create a customized tab bar and tab view.
from stinsen.
Your approach only relies on tab clicks, if you want to navigate through tabs via the router, your approach will fail. E.g. deep link. WDYT?
from stinsen.
Hi! Instead of a TabCoordinatable, a NavigationCoordinatable can also be used with your previous workaround @Goktug. Then you don't need to hide the tabbar globally - and instead of switching the tab you can use the setRoot
-function. I could whip up an example if things are still unclear later...
from stinsen.
How
@Goktug my approch doesn't have to depend on tab clicks if I simply remove the private
activeTabIndex
and use@ViewBuilder func customize(_ view: AnyView) -> some View { ZStack(alignment: .bottom) { view CustomTabBarView(activeTabIndex: .init(get: { self.child.activeTab }, set: { newValue in self.child.activeTab = newValue })) } }
Anything that has a
TabCoordinator.Router
can change tab viafocusFirst
and our custom tab bar changes accordingly (since we are getting the index value fromchild.activeTab
). I think both approach works, it is just personally I prefer pass around a Binding in this case instead of environment object.
Hi! How you resolve problem if u need to hide tabView? Custom tab view always showing if u use this method )
from stinsen.
How
@Goktug my approch doesn't have to depend on tab clicks if I simply remove the private
activeTabIndex
and use@ViewBuilder func customize(_ view: AnyView) -> some View { ZStack(alignment: .bottom) { view CustomTabBarView(activeTabIndex: .init(get: { self.child.activeTab }, set: { newValue in self.child.activeTab = newValue })) } }
Anything that has a
TabCoordinator.Router
can change tab viafocusFirst
and our custom tab bar changes accordingly (since we are getting the index value fromchild.activeTab
). I think both approach works, it is just personally I prefer pass around a Binding in this case instead of environment object.Hi! How you resolve problem if u need to hide tabView? Custom tab view always showing if u use this method )
Did you manage to solve this problem later on? If so, how did you do it?
from stinsen.
Did you manage to solve this problem later on? If so, how did you do it?
Hi @alvin-7 ! Yes, remove everything and create TabBar with UIKit :-)
from stinsen.
Related Issues (20)
- PopToRoot shows second view while transitioning from third to first view HOT 6
- How can I get access to a stack during popToRoot
- Problem with .push transition. HOT 2
- Can't I use onDismiss in route method? HOT 1
- Dismissing item issue HOT 2
- Exclude screens during deeplink HOT 1
- Issues caused by use of SwiftUI's NavigationStack HOT 2
- Can't get NavigationRouter via protocol
- Tabbar items customisation HOT 2
- dismissCoordinator animation
- dismissChild fatal optional
- How to correctly use focusFirst HOT 1
- Example just don't work
- Tabbar Item Accent Color Not working
- Presentation detents for sheets
- Memory leaks in iOS17 HOT 5
- How do I know which route I am in?
- Custom back button modifier HOT 1
- Poplast didn't work as expected
- Pops back when updating @ObservableObject
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from stinsen.