SwiftUI: Tabview Repeating itself - swift

I'm trying to create a tabview for a macOS 10.15 app.
TabView {
BookmarksView()
.tabItem {
Text("Bookmark Settings")
}
DisplaySettings()
.tabItem {
Text("Display Settings")
}
}
And in any of my views included in the tab that has one element in the body it renders properly in the tab view.
struct BookmarksView: View {
var body: some View {
Text("Bookmarks View")
.font(.title)
.font(Font.body.bold())
}
}
But if i add any other element in the view, the tab repeats and shows the added element in its own tab.
struct BookmarksView: View {
var body: some View {
Text("Bookmarks View")
.font(.title)
.font(Font.body.bold())
Text("Testing")
.font(.system(size: 15))
}
}

Try to wrap them in container (stack or something) explicitly, like
struct BookmarksView: View {
var body: some View {
VStack { // << this !!
Text("Bookmarks View")
.font(.title)
.font(Font.body.bold())
Text("Testing")
.font(.system(size: 15))
}
}
}

Related

Dismissing a SwiftUI sheet with a lot of navigation views

I have a button that opens up a Profile & Settings view in a sheet that has additional navigation views in it.
I am aware how to dismiss the sheet, however this method seems to not work with additional navigation views, as when I'm deeper into the navigation and I tap "Done" to dismiss the sheet, it only returns me back to the previous navigation view until I go back to the main Profile & Settings view.
The view with the button:
import SwiftUI
struct TodayView: View {
#State private var showSheet = false
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading) {
TodayTabDateComponent()
.padding(.top, -10)
ForEach(0 ..< 32) { item in
VStack(alignment: .leading) {
Text("Title")
Text("Description")
}
.padding(.vertical)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
}
.navigationTitle("Today")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showSheet = true
}, label: {
Image(systemName: "person.circle.fill")
.foregroundColor(.primary)
})
.sheet(isPresented: $showSheet) {
ProfileAndSettingsView()
}
}
}
}
}
}
struct TodayView_Previews: PreviewProvider {
static var previews: some View {
TodayView()
}
}
The Profile & Settings view:
import SwiftUI
struct ProfileAndSettingsView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
VStack {
List {
Section {
NavigationLink {
UserProfileView()
} label: {
HStack(alignment: .center) {
Image("avatar")
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.frame(width: 60, height: 60)
VStack(alignment: .leading) {
Text("Name Surname")
.font(.title2)
.fontWeight(.bold)
Text("Profile Settings, Feed Preferences\n& Linked Accounts")
.font(.caption)
}
}
}
.padding(.vertical, 6)
} }
.listStyle(.insetGrouped)
.navigationTitle("Profile & Settings")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Text("Done")
}
}
}
}
}
}
}
struct ProfileAndSettingsView_Previews: PreviewProvider {
static var previews: some View {
ProfileAndSettingsView()
}
}
I have looked into the issue but couldn't find any working solutions.
Is your issue here that you're applying the .sheet to the Button inside the Toolbar? I think you need to apply it to the NavigationView itself?
import SwiftUI
struct TodayView: View {
#State private var showSheet = false
var body: some View {
NavigationView {
....
}
.navigationTitle("Today")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showSheet = true
}, label: {
Image(systemName: "person.circle.fill")
.foregroundColor(.primary)
})
}
}
.sheet(isPresented: $showSheet) {
ProfileAndSettingsView()
}
}
}
}
If you're targeting iOS15 or higher don't use presentationMode use #Environment(\.isPresented) private var isPresented instead this will perform the action that you want.
presentationMode was deprecated and replaced by isPresented and dismiss
I believe that presentationMode performs a similar action as dismiss does which according to Apple Docs (on the dismiss)
If you do this, the sheet fails to dismiss because the action applies to the environment where you declared it, which is that of the detail view, rather than the sheet. In fact, if you’ve presented the detail view in a NavigationView, the dismissal pops the detail view the navigation stack.
The dismiss action has no effect on a view that isn’t currently presented. If you need to query whether SwiftUI is currently presenting a view, read the isPresented environment value.
If you're targeting a lower iOS version you can create your own key like so
struct SheetOpen: EnvironmentKey {
static var defaultValue: Binding<Bool> = .constant(false)
}
extension EnvironmentValues {
var sheetOpen: Binding<Bool> {
get { self[SheetOpen.self] }
set { self[SheetOpen.self] = newValue }
}
}
Where you have your sheet defined you do this
.sheet(isPresented: $showSheet) {
ProfileAndSettingsView()
.environment(\.sheetOpen, $showSheet)
}
Then you can use it like any other environment variable
#Environment(\.sheetOpen) var sheetOpen
To dismiss it you simply do this sheetOpen.wrappedValue.toggle()

Swiftui invisible bar on the top of the screen

I have the following in problem.
If I try to give a view a color (Color.red for example)
I get the following output:
I can just add .edgesignoressafearea(.top) and the top also gets red. But when I want to add an clickable item, the user won't be able to click it since there still is this invisible bar on the top of the screen. Does Anyone know what my problem is? The problem is in all the tabable views(Timeline, Favorits, Discover, Account). So It must be in either the first code or in tabview (second code) that I send in this post.
When the user clicks on the app first they get this view sending the user to the signin view or the app itself:
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: tabView().navigationBarHidden(true), isActive: $tabview, label: { EmptyView() })
NavigationLink(destination: loginView().navigationBarHidden(true), isActive: $login, label: { EmptyView() })
if tabview == false && login == false {
Text("loading")
.onAppear(perform: checklogin)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Then the apps sends them to tabview:
var body: some View {
TabView(selection: $selection) {
Timeline()
.tabItem {
Label("timeline", systemImage: "house")
}
.tag(0)
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
.tag(1)
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
.tag(2)
Account()
.tabItem {
Label("account", systemImage: "person")
}
.tag(3)
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
The problem happens on all of this views.
This is the view where I made the screenshot of:
var body: some View {
ZStack {
Color.red
Text("Hello fav!")
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
add .edgesIgnoringSafeArea(.top) and remove your navigation bar if you don't want to be able to get back to your login view anytime.
That's the full code with a login page, now it depends what you want on that login page :
import SwiftUI
struct ContentView: View {
#State var showLoginView: Bool = false
var body: some View {
VStack {
if showLoginView {
MainView()
} else {
Button("Login") {
self.showLoginView = true
}
}
}
}
}
struct MainView: View {
var body: some View {
TabView {
TimeLine()
.tabItem {
Label("timeline", systemImage: "house")
}
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
Account()
.tabItem {
Label("account", systemImage: "person")
}
}
}
}
struct TimeLine: View {
var body: some View {
Color.blue
.edgesIgnoringSafeArea(.top)
}
}
struct Favorits: View {
var body: some View {
ZStack {
Color.red
.edgesIgnoringSafeArea(.top)
Text("Hello fav!")
}
}
}
struct Discover: View {
var body: some View {
Color.yellow
.edgesIgnoringSafeArea(.top)
}
}
struct Account: View {
var body: some View {
Color.purple
.edgesIgnoringSafeArea(.top)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You need to set the navigation Title to "" because your View is embedded in NavigationView
Create a ViewModifier like this and apply it to your VStack
struct HiddenNavBarModifier: ViewModifier {
func body(content: Content) -> some View {
content
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}

SwiftUI Sheet is only working once when opened from navigation bar

I have the problem that my sheet is not working as it should. When using a second NavigationView inside the SecondView then the sheet works but it looks crazy (first screenshot). When I comment out the NavigationView in SecondView the app looks correct (second screenshot), but the sheet is only working once when I open it from the Add button in navigation bar. When I use the "open sheet"-button in SecondView then the sheet works correct.
Did I do something wrong with this:
.navigationBarItems(trailing: Button("Add") {
showSheet.toggle()
})
SecondView with NavigationView:
SecondView without NavigationView (how it should look like):
This is the code I'm using:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("Start View")
.font(.title)
NavigationLink(destination: SecondView()) {
CustomButton()
}
}
}
}
}
struct CustomButton: View {
var body: some View {
HStack {
Spacer()
Text("Go To Second View")
.font(.headline)
.foregroundColor(.red)
.padding()
Spacer()
}
.background(Color.white)
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.red, lineWidth: 2)
)
.padding()
}
}
struct SecondView: View {
#State private var showSheet = false
var body: some View {
//NavigationView{
Text("Second View")
Button("open sheet", action: {
showSheet.toggle()
})
.navigationBarBackButtonHidden(true)
.navigationBarItems(trailing: Button("Add") {
showSheet.toggle()
})
.sheet(isPresented: $showSheet, content: {
SecondViewsSheet()
})
//}
}
}
struct SecondViewsSheet: View {
var body: some View {
Text("Sheet")
.navigationBarItems(trailing: Button("Test"){
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I use Xcode 12.2 with an iPhone 12 Pro simulator.

SwiftUI navigationBarItems disappear in TabView

I have a view that has navigation bar items and I embed that view in a TabView. But when doing that, the bar items no longer appear. If I call the view outside of a TabView everything works as expected.
Below a small sample project to illustrate my issue, note that the TabView is not called on the initial ContentView but later down:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink(destination: WarehouseOrderTabView()){
Text("Click me")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct WarehouseOrderTabView: View {
var body: some View {
TabView{
TabView1().navigationBarTitle("Dashboard")
.tabItem {
Image(systemName: "gauge")
Text("Dashboard")
}
TabView2().navigationBarTitle("Orders")
.tabItem {
Image(systemName: "list.dash")
Text("Orders")
}
}
}
}
struct TabView1: View {
var body: some View {
Text("TabView 1")
//I would expect to see those bar items when displaying tab 1
.navigationBarItems(trailing: (
HStack{
Button(action: {
}, label: {
Image(systemName: "arrow.clockwise")
.font(.title)
})
.padding(.init(top: 0, leading: 0, bottom: 0, trailing: 20))
Button(action: {
}, label: {
Image(systemName: "slider.horizontal.3")
.font(.title)
})
}
))
}
}
struct TabView2: View {
var body: some View {
Text("TabView 2")
}
}
What am I missing here?
A NavigationView can be embedded in a TabView and not vice-versa.
TabView contains different tabItem() (at most 5) that can contain your views.
This is how you can use it.
TabView1.swift
struct TabView1: View {
var body: some View {
NavigationView {
Text("TabView 1")
.navigationBarTitle("Dashboard")
.navigationBarItems(trailing:
HStack {
Button(action: {
// more code here
}) {
Image(systemName: "arrow.clockwise")
.font(.title)
}
Button(action: {
// more code here
}) {
Image(systemName: "slider.horizontal.3")
.font(.title)
}
}
)
}
}
}
TabView2.swift
struct TabView2: View {
var body: some View {
NavigationView {
NavigationLink(destination: YourNewView()) {
Text("TabView 1")
}
.navigationBarTitle("Orders")
}
}
}
ContentView.Swift
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
TabView1()
.tabItem {
Image(systemName: "gauge")
Text("Dashboard")
}
TabView2()
.tabItem {
Image(systemName: "list.dash")
Text("Orders")
}
}
}
}
Hope it helps :)

NavigationView doesn't display correctly when using TabView in SwiftUI

Hello everyone. I'm developing a simple SwiftUI application that displays some tweets. It has a tab view with two views: the main page that will display the tweets and a secondary view.
The problem is that the main page has a NavigationView. If I choose to display only the main page, everything seems correct, but when I display it from the TabView and I scroll down, the NavigationView feels a bit weird.
As I'm not good at explaining, here you have some images:
It should be like this
But it is like this
I thought of adding .edgesIgnoringSafeArea(.top), but the NavigationView is now hidden by the notch and it doesn't make the effect.
Is there any way I can make the NavigationView display like in the first image?
Any help is appreciated. Thanks in advance.
My code
HomePageView:
struct HomePageView: View {
var body: some View {
NavigationView {
List {
//tweet code
}
.navigationBarTitle("Your feed")
}
}
}
TabView:
struct TabController: View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
HomePageView()
.tabItem {
VStack {
Image(systemName: "house.fill")
.font(.title)
}
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
VStack {
Image(systemName: "bell.fill")
.font(.title)
}
}
.tag(1)
}
}
}
Try adding .edgesIgnoringSafeArea(.top) to your tabview.
struct ContentView: View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
HomePageView()
.tabItem {
VStack {
Image(systemName: "house.fill")
.font(.title)
}
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
VStack {
Image(systemName: "bell.fill")
.font(.title)
}
}
.tag(1)
}.edgesIgnoringSafeArea(.top)
}
}