Toolbar does not appear properly SwiftUI - swiftui

In my GeneralView I have a NavigationView And a Tab View.
Inside each tabItem I navigate with some ZStack (using zIndex, hiding and showing items)
Randomly leading and trailing items are not shown properly and can't be clicked.
See below, on top of screen back button is not full. But I select same button to go on the "Coureur1View"
Info : I do not have any other problem with this navigation.
In My generalView :
.toolbar {
ToolbarItemGroup(placement: .principal) {
TitleBarView().environmentObject(objCourse)
}}
.navigationBarItems(leading: TitleBarLeadingView(),
trailing: TitleBarTrailingView())
I don't have problem with TitleBarView (principale) but with leading and trailing
In my TitleBarLeadingView :
struct TitleBarLeadingView: View {
#EnvironmentObject var objGroupe : GroupeActuel
#EnvironmentObject var objCourse : CourseActuelle
#EnvironmentObject var zindex : Zindex
var body: some View {
HStack {
if zindex.selectedTab > 0 {
if zindex.detailCoureurVisible {
Button{
zindex.detailCoureurVisible = false
} label : {
Image(systemName: "chevron.backward")
Text("Back")
}.foregroundColor(.orange)
}else{
EmptyView()
}else{
EmptyView()
}
}
}

Provided snapshots are not testable, so just idea - try to recreate navigation bar items forcefully. It can be on some known changed value (I see titled changed on gif), but also can be just by UUID():
.navigationBarItems(leading: TitleBarLeadingView().id(UUID()),
trailing: TitleBarTrailingView().id(UUID()))
Note: make .id(param) is preferable because by UUID it will be recreated by each refresh.

Related

Disable or ignore taps on TabView in swiftui

I have a pretty usual app with a TabView. However, when a particular process is happening in one of the content views, I would like to prevent the user from switching tabs until that process is complete.
If I use the disabled property on the TabView itself (using a #State binding to drive it), then the entire content view seems disabled - taps don't appear to be getting through to buttons on the main view.
Example:
struct FooView: View {
var body: some View {
TabView {
View1().tabItem(...)
View2().tabItem(...)
}
.disabled(someStateVal)
}
}
Obviously, I want the View1 to still allow the user to, you know, do things. When someStateVal is true, the entire View1 doesn't respond.
Is there a way to prevent changing tabs based on someStateVal?
Thanks!
I could not find a way to individually disable a tabItem, so here is
an example idea until someone comes up with more principled solution.
The trick is to cover the tab bar with a clear rectangle to capture the taps.
struct ContentView: View {
#State var isBusy = false
var body: some View {
ZStack {
TabView {
TestView(isBusy: $isBusy)
.tabItem {Image(systemName: "globe")}
Text("textview 2")
.tabItem {Image(systemName: "info.circle")}
Text("textview 3")
.tabItem {Image(systemName: "gearshape")}
}
VStack {
Spacer()
if isBusy {
Rectangle()
.fill(Color.white.opacity(0.001))
.frame(width: .infinity, height: 50)
}
}
}
}
}
struct TestView: View {
#Binding var isBusy: Bool
var body: some View {
VStack {
Text("TestView")
Button(action: {
isBusy.toggle()
}) {
Text("Busy \(String(isBusy))").frame(width: 170, height: 70)
}
}
}
}
I use another trick. Just hide the tab image.
struct FooView: View {
var body: some View {
TabView {
View1().tabItem{Image(systemName: someStateVal ? "": "globe")}
View2().tabItem{Image(systemName: someStateVal ? "": "gearshape")}
}
}
}

Navigation Link Returning Back Two Levels on Exit

I am having trouble with a return from a navigation view within a tabbed view. My project has a Settings tab where the user may select via navigation link "View Entries". And from there another navigation link to "Add New Entry". Returning from Add New Entry should bring you to View Entries but instead is return another level to the Setting Menu.
I am seeing a warning on the console stating "trying to pop to a missing destination at /Library/Caches/com.apple...". Using the tabbed view sample code at SwiftUI NavigationView trying to pop to missing destination (Monoceros?) I no longer get the "pop-to-missing-destination" warning but I still have the same problem with the navigation return.
The sample code below is ready to run and test in Xcode 12.
In the sample code below, tap settings and select the navigation view "View Entries". This would be a screen where entries are displayed in a list. Tapping the plus button is where new entries could be added. The textfield on the "Add New Entry" screen doesn't do anything. Clicking the Save or Back buttons should return you to "View Entries" screen but instead returns you to the Setting Menu. The Save button uses presentationMode.wrappedValue.dismiss to dismiss the view.
The fact that two different version of the tab view logic didn't have any impact on my navigation view return logic leads me to believe that I just have some kind on plain old bug in my navigation view logic but I sure don't see one. The sample code below is using the standard tab view logic.
struct ContentView: View {
#State private var selection = 0
var body: some View {
NavigationView {
TabView (selection: $selection) {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}.tag(1)
AView()
.tabItem {
Label("A", systemImage: "a.circle")
}.tag(2)
BView()
.tabItem {
Label("B", systemImage: "b.circle")
}.tag(3)
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape")
}.tag(4)
}
}
}
}
struct HomeView: View {
var body: some View {
Text("Home Screen")
}
}
struct AView: View {
var body: some View {
Text("A Screen")
}
}
struct BView: View {
var body: some View {
Text("B Screen")
}
}
struct SettingsView: View {
var body: some View {
VStack (alignment: .leading) {
List {
Text("Settings")
.font(.title)
.fontWeight(.bold)
.padding(.leading, 15)
NavigationLink(destination: SetAView()) {Text("View Entries")}
}
}
.font(.body)
}
}
struct SetAView: View {
var body: some View {
List {
Text("View Entries")
.padding(.vertical, 10)
Text("Normally entires would be displayed here")
Text("Should return here upon adding new entry")
.padding(.vertical, 10)
Text("Click the + button to add new entry")
}
.navigationBarItems(trailing: NavigationLink (destination: AddTestView()) {
Image(systemName: "plus")
.resizable()
.foregroundColor(Color(.systemBlue))
.frame(width: 18, height: 18)
} // body
)
}
}
struct AddTestView: View {
#Environment(\.presentationMode) var presentationMode
#State private var catSelect: String = ""
var body: some View {
NavigationView {
VStack {
Form {
Section {
TextField("Enter Entry Name", text: $catSelect)
.padding(.horizontal, 20)
.keyboardType(.default)
}
}
}
.navigationBarTitle(Text("Add new Entry"), displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text ("Save")
})
}
}
}
After considerable analysis I discovered in my actual code that I had two copies of NavigationView––nothing wrong with the TabView code. Removing the one NavigationView not in contentView then caused several functions from working so rebuilt them from scratch. Now have everything working with TabView and NaigationView including the back buttons.

SwiftUI - TabView with nested NavigationViews resets Navigationflow

I'm having a TabView with Navigation Views in it.
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
NavigationLink("Link to my first Navigation Level", destination: MyFirstView())
}
.tabItem {
Image(systemName: "house")
Text("Home")
}.tag(0)
NavigationView {
Text("Second Nav Page")
}
.tabItem {
Image(systemName: "gear")
Text("Settings")
}.tag(1)
}
}
}
struct MyFirstView: View {
#State var selectedTag: String?
var body: some View {
VStack {
Text("My First View")
NavigationLink("(Working) Link to my second Navigation Level", destination: MySecondView())
Text("But the Button in the Navigation Bar doesn't work")
}
.navigationBarTitle("My First View", displayMode: .inline)
.navigationBarItems(
leading: HStack {
NavigationLink(destination: MySecondView(), tag: "xx", selection: $selectedTag ){
Button(action: {
print("Settings button pressed...")
self.selectedTag = "xx"
}) {
Image(systemName: "gearshape.2.fill").imageScale(.large)
}
}
}
)
}
}
struct MySecondView: View {
var body: some View {
Text("My Second View")
}
}
Now I got a super weired behavior. If I click on "Link to my first Navigation Level" and then on "(Working) Link to my second Naviation Level" the journey works. If I click on "Back" while being in the second Navigation Level it goes back to the first Navigation Level.
Issue:
When I click on the gear symbol in the Navbar on the first Navigation Level it kind of escapes from the nested Navigation and sets it to the top level. This has the consequence that when I click on "Back" it brings me from the Second Navigation Level back to the very root screen but my expected behavior is that it should go back to the first Navigation Level.
Any ideas what I'm doing wrong? I'm using Xcode 12.2 beta 3 and iOS 14.2 (not sure if it's a beta bug).
Many Thanks!
This did the trick (thanks #Asperi). Has to be outside tabBarItems
NavigationLink(destination: AddDetailView(existingItem: nil),
isActive: $addMode) { EmptyView() }

SwiftUI Navigation bar items disappear on iOS 14

Discovered in my app that navigation bar items in some views disappear when orientation of the device changes. This seems to occur only in a view that is opened using NavigationLink, on main view navigation bar items work as expected. It appears that something has changed between iOS 13.7 and iOS 14.2 related to this. Also, it does not seem to matter whether using leading or trailing items, both disappear.
Example snippet where this occurs:
struct ContentView: View {
var detailView: some View {
Text("This is detail view")
.navigationBarTitle("Detail view title", displayMode: .inline)
.navigationBarItems(trailing: Button(action: {}, label: {
Image(systemName: "pencil.circle.fill")
}))
}
var body: some View {
NavigationView {
NavigationLink(
destination: detailView,
label: {
Text("Open detail view")
})
.navigationBarTitle("Main view")
}.navigationViewStyle(StackNavigationViewStyle())
}
}
The issue occurs only when running on a real device. (iPhone 11 in my case) On simulator everything works as expected.
Anyone else seen similar issues? Workarounds/fixes?
.navigationBarTitle and .navigationBarItems are being deprecated. I think that the best "fix" is to switch to .toolbar
It's a weird issue but I guess there is a hack to make it work.
SwiftUI doesn't call body property when rotations happen. So you can add #State UIDeviceOrientation property to your view and update it every time orientation changes. One tricky thing is to use that property somewhere in the body of the view since SwiftUI smart enough to ignore #State that is not used in the body.
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(
destination: DetailsView(),
label: {
Text("Open detail view")
}).navigationBarTitle("Main view")
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct DetailsView: View {
#State var orientation: UIDeviceOrientation = UIDevice.current.orientation
var body: some View {
Text("This is detail view")
.navigationBarTitle("Detail view title")
.navigationBarItems(trailing: button)
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
orientation = UIDevice.current.orientation
}.background(Text("\(orientation.rawValue)"))
}
var button: some View {
Button(action: { print("123") }, label: {
Image(systemName: "pencil.circle.fill")
}).id("123")
}
}
In my experience when I change a Button in the toolbar from disabled to enabled, they disappear. But if I scroll to the bottom of the View, they re-appear. If I am already at the end of the View when the button is enabled, it acts normally, until I then scroll away from the bottom, the button again disappears.
Try scrolling to the bottom of your view, if you use landscape.

SwiftUI .popover dismiss won't work after List row being removed

I have a SwiftUI List with rows that contain a thumb. When clicked, this thumb opens a popover with a larger version of the image.
The problem I'm facing is that when a row, with its popover open, is removed from the list, the popover is left open and with no way of closing it, ending up with an unusable UI.
My goal would be to have the popover closed automatically when the row is removed from the list.
Following is a stripped version of the row's body. I'm using a Button because is more reliable than the onTapGesture event.
#State private var showPopover: Bool = false
var body: some View {
Button(action: { self.showPopover = true }) {
Image(systemName: "photo")
.onDisappear { self.showPopover = false}
}
.popover(isPresented: $showPopover, arrowEdge: .leading) {
Image(systemName: "photo")
}
}
You use this:
for dismiss, swipe down or tap on Back button! (I guarantee it is going dismiss)100%
import SwiftUI
struct ContentView: View {
#State private var showModalView: Bool = false
var body: some View {
VStack
{
Button("showModalView") {showModalView.toggle()}
}
.sheet(isPresented: $showModalView, content: {
ZStack
{
Color.red
.ignoresSafeArea()
Button("← Back"){showModalView.toggle()}.foregroundColor(Color.black)
}
})
}
}