I need change color of tabItem badge in swiftUI.
I founded this pod
https://github.com/jogendra/BadgeHub
Is it possible to use with swiftUI?
I have tabItems:
TabView(selection: $selection) {
FeaturedView(users: filteredUsers, usersCount: filteredUsersCount)
.tabItem {
Image(selection == 0 ? "ic_searchFill" : "ic_search").renderingMode(.template)
}
.tag(0)
LikesView(userViews: self.userViews, userLikes: self.userLikes)
.tabItem {
Image(selection == 1 ? "ic_heartFill" : "ic_heart").renderingMode(.template)
}
.badge(userLikesCount)
.tag(1)
ConversationsView(conversations: conversations)
.tabItem {
Image("ic_chat").renderingMode(.template)
}
.badge(conversationsCount)
.tag(2)
SettingsView(user: user)
.tabItem {
Image(selection == 3 ? "ic_profileFill" : "ic_profile").renderingMode(.template)
}
.tag(3)
}
Actually we can configure SwiftUI badge color as well, via appearance.
Tested with Xcode 13.4 / iOS 15.5
init() {
UITabBarItem.appearance().badgeColor = .purple // << here !!
}
var body: some View {
TabView {
Color.green.overlay(Text("One"))
.tabItem { Image(systemName: "car") }
.badge(4)
// ...
Related
I've been experimenting with TabView and tabViewStyle and I've run into a problem with my code I can't figure out.
In the code below, when the app opens up on my device I start on the HomeScreen() (as expected) but if I tap on Profile in the top bar, the tab navigation doesn't happen. The Profile text turns red (indicating that pageIndex has been updated), but for reasons I can't figure out, the TabView isn't updating accordingly.
BUT, if I open the app and tap on Settings in the top bar, the tab navigation happens as expected.
Swiping works as expected, no issues there.
Have I missed something obvious?
Steps to reproduce:
Copy code into xcode
Run on simulator / canvas / device
Tap Profile (don't swipe or tap anything else)
Profile will turn red, but the page won't be animated left to the Profile screen.
If you tap Settings or swipe any direction, tapping Profile will work as expected.
import SwiftUI
struct SwipeNavigation2: View {
#State var pageIndex = 1
var body: some View {
NavigationView {
TabView(selection: self.$pageIndex) {
// The screen to the "left" of the Home screen
ProfileScreen()
.tag(0)
// The screen we want the app to load on
HomeScreen()
.tag(1)
// The screen to the "right" of the Home screen
SettingsScreen()
.tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation(.spring()) {
pageIndex = 0
}
} label: {
Text("Profile")
.foregroundColor(pageIndex == 0 ? .red : .primary)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
withAnimation(.spring()) {
pageIndex = 2
}
} label: {
Text("Settings")
.foregroundColor(pageIndex == 2 ? .red : .primary)
}
}
}
}
}
}
private struct ProfileScreen: View {
var body: some View {
Text("Profile screen")
}
}
private struct HomeScreen: View {
var body: some View {
Text("Home screen")
}
}
private struct SettingsScreen: View {
var body: some View {
Text("Settings screen")
}
}
Edit:
I've taken some of the suggestions and amended the code as such:
struct SwipeNavigation2: View {
#State var pageIndex = 0
var body: some View {
NavigationView {
TabView(selection: self.$pageIndex) {
ProfileScreen()
.tag(0)
HomeScreen()
.tag(1)
SettingsScreen()
.tag(2)
}
.onAppear {
pageIndex = 1
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation(.spring()) {
pageIndex = 0
}
} label: {
Text("Profile")
.foregroundColor(pageIndex == 0 ? .red : .primary)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
withAnimation(.spring()) {
pageIndex = 2
}
} label: {
Text("Settings")
.foregroundColor(pageIndex == 2 ? .red : .primary)
}
}
}
}
}
}
Edit 1:
Here's a recording from my simulator (Xcode14.1), on an iPhone 14. You'll see once the recording starts, I tap on Profile (which turns it red), but the TabView isn't moving me to the correct page.
https://imgur.com/a/B9QiYDM
Edit 2:
It gets weirder. I've tested the following devices in XCode simulator:
iPhone 13 (doesn't work)
iPhone 13 Mini (doesn't work)
iPhone 14 (doesn't work)
iPhone 14 Pro (works)
iPhone 14 Pro Max (works)
Move the onAppear modifier on the tab with the index set at init (in your case the Profile View.
import SwiftUI
struct AdamView: View {
#State var pageIndex: Int = 0
var body: some View {
NavigationView {
TabView(selection: self.$pageIndex) {
Text("Profile")
.onAppear {
pageIndex = 1
}
.tag(0)
Text("Home")
.tag(1)
Text("Settings")
.tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation(.spring()) {
pageIndex = 0
}
} label: {
Text("Profile")
.foregroundColor(pageIndex == 0 ? .red : .primary)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
withAnimation(.spring()) {
pageIndex = 2
}
} label: {
Text("Settings")
.foregroundColor(pageIndex == 2 ? .red : .primary)
}
}
}
}
}
}
Another solution is to use the task modifier instead. With the task, you can keep the onAppear on the TabView but I noticed we see the Profile View for a very short time before showing the Home tab.
I have a TabView with three views (triangle, square and circle) nested inside a navigation view and link. TabView works fine. I'd like to have toolbar buttons for only a specific tabview; say circle. The toolbar modifier adds the buttons on all the tabviews.
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink() {
TabView {
Text("triangle")
.tabItem {
Label("triangle", systemImage: "triangle")
}
Text("square")
.tabItem {
Label("square", systemImage: "square")
}
Text("circle")
.tabItem {
Label("circle", systemImage: "circle")
}
}
.navigationTitle("Tabs")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button("About") {
print("About tapped!")
}
Button("Help") {
print("Help tapped!")
}
}
}
} label: {
Text("Hello!")
}
.navigationTitle("Title")
}
}
}
How can I set this up to only show toolbar buttons on one tabview only?
I suppose a secondary option (way less preferred) may be to disable the buttons on tabviews where they are not needed (if possible).
It can be done with a selection state for TabView and making visibility of specific toolbar buttons depending on that state.
Here is a demo. Tested with Xcode 13.4 / iOS 15.5
struct ContentView: View {
#State private var selection = 0 // << here !!
var body: some View {
NavigationView {
NavigationLink() {
TabView(selection: $selection) { // << here !!
Text("triangle")
.tabItem {
Label("triangle", systemImage: "triangle")
}.tag(0)
Text("square")
.tabItem {
Label("square", systemImage: "square")
}.tag(1)
Text("circle")
.tabItem {
Label("circle", systemImage: "circle")
}.tag(2)
}
.navigationTitle("Tabs")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
if selection == 2 { // << here !!
Button("About") {
print("About tapped!")
}
Button("Help") {
print("Help tapped!")
}
}
}
}
} label: {
Text("Hello!")
}
.navigationTitle("Title")
}
}
}
Test code in GitHub
I've tried this to try to change the color of the tab icons individually, but for some reason, the color will modify it correctly and then after tapping back to the icon, it will not display the customized color.
How would I go about changing the tab items icons for each individual tab (different colors for each)?
Here's the code for the view holding the TabView that I'm trying to modify.
struct MainView: View {
#AppStorage("PendingOnboarding") var pendingOnboarding = true
init() {
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.recyclepediaGreen)
}
var body: some View {
NavigationView {
ZStack {
TabView {
CurbsideView()
.tabItem {
Label("Curbside", systemImage: "car.fill")
}
ItemsView()
.tabItem {
Label("Items", systemImage: "archivebox.fill")
}
LearnView()
.tabItem {
Label("Learn", systemImage: "info.circle.fill")
}
ContactUsView()
.tabItem {
Label("Contact Us", systemImage: "phone.fill.connection")
}
}
.accentColor(Color.recyclepediaBlue)
.toolbar {
ToolbarItem(placement: .principal) {
Image("Recyclepedia")
.resizable()
.scaledToFit()
.padding(.top, 5)
.padding(.bottom, 5)
}
}
}
.popup(isPresented: $pendingOnboarding, dragToDismiss: false, closeOnTap: false, backgroundColor: Color.white) {
OnboardingView(pendingOnboarding: $pendingOnboarding)
}
}
}
}
The easiest solution is to use an enum that returns a Color. Like this:
struct TestTabviewIconColor: View {
#State var pageIndex: TabColor = .red
var body: some View {
VStack {
Text("Current page is \(pageIndex.rawValue)")
TabView(selection: $pageIndex) { // Use the Tabview(selection:) init
Text("The First Tab: \(pageIndex.color().description)")
.tabItem {
Image(systemName: "1.square.fill")
Text("First")
}
.tag(TabColor.red)
Text("Another Tab: \(pageIndex.color().description)")
.tabItem {
Image(systemName: "2.square.fill")
Text("Second")
}
.tag(TabColor.green)
Text("The Last Tab: \(pageIndex.color().description)")
.tabItem {
Image(systemName: "3.square.fill")
Text("Third")
}
.tag(TabColor.teal)
}
.accentColor(pageIndex.color())
.font(.headline)
}
}
}
// Define your enum here
enum TabColor: Int {
case red = 0
case green = 1
case teal = 2
func color() -> Color {
switch self {
case .red:
return .red
case .green:
return .green
case .teal:
return .teal
}
}
}
As a bonus, you can select your tab by using it's color simply by calling:
pageIndex = .green
I'm trying to use filled image when it is selected and outlined image when it is deselected. I tried to render the images but still filled
So I thought this would work, but it doesn't:
struct ListTabView: View {
#State private var selectedTab = 0
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
Text("Tab 1")
.onTapGesture {
self.selectedTab += 1
}
.tabItem {
selectedTab == 0 ? Image(systemName: "star.fill") : Image(systemName: "star")
Text("First")
}
.tag(0)
Text("Tab 2")
.onTapGesture {
self.selectedTab -= 1
}
.tabItem {
selectedTab == 1 ? Image(systemName: "moon.stars.fill") : Image(systemName: "moon.stars")
Text("Second")
}
.tag(1)
}
.accentColor(.pink)
.onAppear {
UITabBar.appearance().barTintColor = .white
}
}
}
}
struct ListTabView_Previews: PreviewProvider {
static var previews: some View {
ListTabView()
}
}
Your code actually works. The issue is something else not documented that I can find. If you use a non .fill variant of an SF Font, the .fill variant will be substituted. Use the following code to test it:
TabView(selection: $selectedTab) {
VStack {
Text("Tab 1")
Text(Image(systemName: "star"))
}
.tabItem {
selectedTab == 0 ? Image(systemName: "star") : Image(systemName: "sun.max")
Text("First")
}
.tag(0)
VStack {
Text("Tab 2")
Text(Image(systemName: "moon.stars"))
}
.tabItem {
selectedTab == 1 ? Image(systemName: "moon.stars") : Image(systemName: "sun.max")
Text("Second")
}
.tag(1)
}
You will note I used plain variants, and yet the filled variant was used. Also, you don't need the .onTap(), but I suspect you added it when the images didn't seem to switch.
You can add this to Image / Label:
Image(systemName: selectedTab == 0 ? "star.fill" : "star")
.environment(\.symbolVariants, selectedTabItemIndex == 0 ? .fill : .none)
It will allow you to set the symbol variants as you would expect it.
How can you dynamically change SwiftUI Navigation Bar Items?
I have a TabView within a NavigationView, and I would like the Navigation Bar Items to change depending on tab that is selected. However, I am having a hard time determining how to change this with .onAppear(), assuming that is even what you are suppose to do.
My code is currently laid out as follows:
var body: some View {
NavigationView {
TabView {
contentWithNavigationButtons()
.tabItem {
Image(systemName: "house")
Text("Buttons")
}
contentWithoutNavigationButtons()
.tabItem {
Image(systemName: "person")
Text("No Buttons")
}
.onAppear {
//Navigation Bar Items should be removed
}
}
.navigationBarItems(trailing: someButton)
}
Here is a demo of possible solution - add tracking selection for tabs and make button depending of tab selection. Tested with Xcode 12 / iOS 14
struct DemoView: View {
#State private var selection = 0 // << here !!
var body: some View {
NavigationView {
TabView(selection: $selection) {
contentWithNavigationButtons()
.tabItem {
Image(systemName: "house")
Text("Buttons")
}.tag(0) // << here !!
contentWithoutNavigationButtons()
.tabItem {
Image(systemName: "person")
Text("No Buttons")
}.tag(1)
}
.navigationBarItems(trailing: Group {
if selection == 0 { // << here !!
Button("Some"){}
}
})
}
}
}