Is there a way to create BottomBar using SwiftUI? - ios

I want to create a bottom bar which should be common for all my page. Here I am posting the image:
I know how to do it using storyboard. But how can I do it in SwiftUI?

This kind of view is called tab bar in iOS and in SwiftUI it is called TabView. It is declared like this:
var body: some View {
TabView {
Text("Favourites Screen")
.tabItem {
Image(systemName: "heart.fill")
Text("Favourites")
}
Text("Friends Screen")
.tabItem {
Image(systemName: "person.fill")
Text("Friends")
}
Text("Nearby Screen")
.tabItem {
Image(systemName: "mappin.circle.fill")
Text("Nearby")
}
}
}

Alternatively, you can use Label view for giving name & icon of the tab:
struct BottomTabBar: View {
var body: some View {
TabView{
Text("Profile Contents")
.tabItem{
Label("Profile",systemImage: "person.fill")
}
Text("Tv Contents")
.tabItem{
Label("Tv",systemImage: "tv.fill")
}
}
}
}

Related

SwiftUI TabView inside a NavigationView

So I know it's not really encouraged to put a TabView inside a NavigationView and that you're supposed to do it the other way around. But the way I want my app I don't really see how I can have it another way...
So I have a login screen in which a user inputs their username, then only once I verify that everything is okay with username I wanna bring them over to a TabView(the search button is a navlink) I don't really see any other way to implement this but the problem is with my implementation is once I switch tabs in the tab view, the navigation title doesn't seem to change, and there also doesn't seem to be a navigation bar because when I scroll the old NavigationTitle gets drawn over by a Text View I have.
I'm not sure if adding code would help in this case because it seems this is just kind of a problem with TabViews inside NavigationViews but if someone wants me to show some code I can add an edit with it. I was just wondering if anyone had any ideas for how I could fix something like this or some other way to implement this?
Edit:
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink{
TabView{
ScrollView{
Text("Some view")
}
.tabItem{
Text("New View")
}
ScrollView{
Text("Another view")
}
.tabItem{
Text("Another view")
}
}
} label: {
Text("Go to new view!")
}
}
}
}
It is perfectly fine to have TabView() inside a NavigationView. Every time we switch between pages in any app, the navigating is mostly expected, so almost every view is inside the NavigtionView for this reason.
You can achieve this design with something like this (see the bottom images also):
struct LoginDemo: View {
#State var username = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter your user name", text: $username)
.font(.headline)
.multilineTextAlignment(.center)
.frame(width: 300)
.border(.black)
NavigationLink {
GotoTabView()
} label: {
Text("search")
}
.disabled(username == "Admin" ? false : true)
}
}
.navigationTitle("Hey It's Nav View")
}
}
struct GotoTabView: View {
#State var temp = "status"
#State var selection = "view1"
var body: some View {
TabView(selection: $selection) {
Image("Swift")
.resizable()
.frame(width: 300, height: 300)
.tabItem {
Text("view 1")
}
.tag("view1")
Image("Swift")
.resizable()
.frame(width: 500, height: 500)
.tabItem {
Text("view 2")
}
.tag("view2")
}
.onChange(of: selection){ _ in
if selection == "view1" {
temp = "status"
}
else {
temp = "hero"
}
}
.toolbar{
ToolbarItem(placement: .principal) {
Text(temp)
}
}
}
}
NavigationView:
TabView:

Make NavigationView searchable but only for one page in the TabView

I have a NaviagtionView with a TabView and 4 TabItems inside. One of the TabItems should display a searchbar. I can make the NavigationView .searchable but I only want that inside the on TabItem where I want to do the search. How can i do that?
Here is my code:
var body: some View {
NavigationView {
TabView{
HomeScreen()
.background(Color("BackgroundColor"))
.tabItem{
Image(systemName: "house")
}
PostScreen()
.background(Color("BackgroundColor"))
.tabItem{
Image(systemName: "plus")
}
SearchScreen()
.background(Color("BackgroundColor"))
.tabItem{
Image(systemName: "magnifyingglass")
}
ProfileScreen()
.background(Color("BackgroundColor"))
.tabItem{
Image(systemName: "person")
}
}
.navigationTitle("MyApp")
.navigationBarTitleDisplayMode(.inline)
.searchable(text: $text)
}
}
searchable work with single child view after NavigationView. So my modification is to remove the parent NavigationView and add NavigationView in the individual View.
Check this answer too
https://stackoverflow.com/a/73180190/4549220
Found a way, add an extension:
extension View {
#ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content)
-> some View
{
if condition {
transform(self)
} else {
self
}
}
}
and then use it like this:
.if(selectedTab == "search") { view in
view.searchable(text: $search)
}

add an exit button to .navigationBarItems

I'm a beginner at swiftui. I need to add an exit button to .navigationBarItems. How can I add this button in the parent NavigationView to show this button on all children's views?
// a simple example on my question
struct FirstView: View {
var body: some View {
NavigationView {
ZStack{
TabView{
SubExampleViewOne()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
SubExampleViewTwo()
.tabItem {
Image(systemName: "bookmark.circle.fill")
Text("Bookmark")
}
}
}
//here I have added a toolbar and it is perfectly visible in tabitem
//this is what I am trying to achieve, the visibility of the button on all pages
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ButtonExitView()
}
}
}
}
}
something strange - if I add NavigationLink in this way, Image and Text("Home") are visible twice
and the ToolbarItem is no longer on the new page
struct SubExampleViewOne: View {
var body: some View {
Text("This is hime page!")
.padding()
NavigationLink(destination: SubExampleViewThree()){
Text("Navigation link")
}
}
}
struct SubExampleViewTwo: View {
var body: some View {
Text("Hello, world!")
.padding()
}
}
struct SubExampleViewThree: View {
var body: some View {
Text("This is Navigation link")
.padding()
}
}
struct ButtonExitView: View {
var body: some View {
Button(action: {}, label: {Image(systemName: "arrowshape.turn.up.right.circle")})
}
}
after learning about TabView, I thought that there should be a similar solution for the top of the page
You have to add the button to each child view separately.
And you should use .toolbar and .toolBarItem because .navigationBarItems is deprecated.

NavigationTitle visual glitches - transparent and not changing state from .large to .inline on scroll

The .navigationTitle on some views seem to be having some problems. On some views (and only some of the time), the .navigationTitle will not change from .large to .inline as would be expected. Instead, the title stays in place when scrolling up, and the navigation bar is completely invisible (as outlined in the video below). This is all reproducible every time.
Video of reproducible .navigationTitle bugs
I haven't found any people on stack overflow or the Apple Developer forums who have run into this exact issue. There have some people who have produced similar results as this, but those were all fixed by removing some stylizing code to the .navigationbar, of which I am not making any modifications to it anywhere in my code.
Below are some snippets of my code:
import SwiftUI
struct WelcomeUI: View {
var body: some View {
NavigationView {
VStack {
//NavigationLink(destination: SignupUI(), label: {
//Text("Sign Up")
//}
NavigationLink(destination: LoginUI(), label: {
Text("Log In")
})
}
}
}
}
struct LoginUI: View {
var body: some View {
VStack {
NavigationLink(destination: MainUI(), label: { Text("Log In") })
//Button(action: { ... }
}
.navigationBarHidden(false)
}
}
struct MainUI: View {
#State var selectedTab: Views = .add
var body: some View {
TabView(selection: $selectedTab) {
SpendingView()
.tabItem {
Image(systemName: "bag.circle")
Text("Spending")
}.tag(Views.spending)
Text("Adding View")
.tabItem {
Image(systemName: "plus")
Text("Add")
}.tag(Views.add)
Text("Edit View")
.tabItem {
Image(systemName: "pencil")
Text("Edit")
}.tag(Views.edit)
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}.tag(Views.settings)
}
.navigationBarTitle(Text(selectedTab.rawValue))
.navigationBarBackButtonHidden(true)
}
}
enum Views: String {
case spending = "Spending"
case add = "Add"
case edit = "Edit"
case settings = "Settings"
}
struct SettingsView: View {
var body: some View {
VStack{
ZStack {
Form {
Section(header: Text("Section Header")) {
NavigationLink(destination: WelcomeUI()) {
Text("Setting Option")
}
}
Section {
//Button("Log Out") {
//self.logout()
//}
Text("Log Out")
}
}
Button("say-high", action: {print("Hi")})
}
}
}
}
struct SpendingView: View {
var body: some View {
ScrollView{
Text("SpendingView")
NavigationLink("subSpending", destination: SubSpendingView())
}.padding()
}
}
struct SubSpendingView: View {
var body: some View {
ScrollView{
Text("SubSpendingView")
}.navigationBarTitle("SubSpending")
}
}
It almost seems like a bug in SwiftUI itself just because the fact that bringing down the control centre makes it kind of work, but with no animation (as seen in the video). Also, changing which view is selected first in #State var selectedTab: Views seems to let the view selected to work as expected, but lets the rest of the tabs mess up.
When I build and run the app on my iPad, it behaves as expected with no bugs, it's only when run on my iPhone and the iOS simulator on Mac that it does this, any way to fix this?
For this to work flawlessly the ScrollView needs to be the direct child of the NavigationView. I ran into a similar issue with wanting to dismiss the TabView when I navigating but SwiftUI won't let that happen. Each tab needs to be a NavigationView and you need to dismiss the TabView creatively if that is what you want.
TabView {
NavigationView {
ScrollView {
// your view here
}
}.tabItem {
// tab label
}
// etc
}
Essentially the navigation view needs to be a child (in the brackets) of the tab view and the scrollview needs to be the direct child of the navigation view.
Use navigationBarTitle("Title") and navigationBarBackButtonHidden(true) on the TabView's sub-view, not on itself.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
}
.navigationBarTitle("Title")
.navigationBarBackButtonHidden(true)
}
}
}

replace Tabbar with toolbar in SwiiftUI 2.0

I'm trying replicate the behavior of iOS Photos app.
Till now the thing I can't figure how could be done is the select mode, where when I press the button select how I can change the bottom bar?
Graphically, what I intend is, in this view:
When I pressed the button, the bottom bar changes to:
In the real project the views are embed inside a NavigationView
The code of the main view is similar to
struct ContentView: View {
var body: some View {
NavigationView{
TabView{
data()
.tabItem {
Text("Data")
}
data2()
.tabItem {
Text("Data")
}
}
}
}
I'm using Xcode 12 and swiftUI 2.0
First we need Conditional modifier like that https://stackoverflow.com/a/61253769/2715636
struct conditionalModifier: ViewModifier {
var isShowing: Bool
func body(content: Content) -> some View {
Group {
if self.isShowing {
content
.toolbar {
ToolbarItem(placement: .bottomBar, content: {
Button(action: {
}){
Image(systemName: "square.and.arrow.up")
}
})
}
.toolbar {
ToolbarItem(placement: .status, content: {
Text("Toolbar")
.fontWeight(.bold)
})
}
}
}
}}
I don't need else statement cause I only want to see Toolbar
else { content }
And here is my Tabbar inside ZStack. We're gonna overlay it with Text using Conditional modifier applied to Text
struct ContentView: View {
#State private var showToolbar: Bool = false
var body: some View {
Button(action: {
showToolbar.toggle()
}, label: {
Text(showToolbar ? "Show Tabbar" : "Show Toolbar")
}).padding()
ZStack {
TabView {
someView()
.tabItem {
Image(systemName: "placeholdertext.fill")
Text("Tab 1")
}
someView()
.tabItem {
Image(systemName: "placeholdertext.fill")
Text("Tab ")
}
someView()
.tabItem {
Image(systemName: "placeholdertext.fill")
Text("Tab 3")
}
}
Text("")
.modifier(conditionalModifier(isShowing: showToolbar))
}
}}
Final result
tabbar to toolbar
There's a new view modifier in iOS 16 that let you switch the tab bar and the bottom bar.
https://developer.apple.com/documentation/swiftui/view/toolbar(_:for:)
For example,
ContentView()
.toolbar(isSelecting ? .visible : .hidden, for: .bottomBar)
.toolbar(isSelecting ? .hidden : .visible, for: .tabBar)

Resources