Why is there a huge big gap between the back button and the starting element of the view? - ios

This is how my view looks like on simulator
I don't understnad how this huge gap comes. This is how my view starts
NavigationView {
VStack {
Form {
Section(header: Text("Mandatory Fields")){
TextField("First name", text: $identifyingData.firstName)
.autocapitalization(.words)
.disableAutocorrection(true)
....
This is the code that calls the above view
.toolbar {
ToolbarItem(placement: .confirmationAction){
NavigationLink(destination: MyView(myData: .constant(MyData.empty))){
Image(systemName: "plus")
}
}
}

I was enable to reproduce your problem after realizing that your pushed view is also embedded in the NavigationView. Just extract your pushed view outside of NavigationView and problem will be solved. Works for me.
Root view:
struct FirstTestView: View {
var body: some View {
NavigationView {
NavigationLink(destination: FormsView()) {
Text("Push me")
}
}
}
}
Pushed view:
struct FormsView: View {
#State var txt = ""
var body: some View {
VStack {
Form {
Section(header: Text("Mandatory Fields")){
TextField("First name", text: $txt)
.autocapitalization(.words)
.disableAutocorrection(true)
}
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
Result:

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:

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.

Presenting a modal view sheet from a Sub view

I am trying to present a sheet from a sub view selected from the menu item on the navigation bar but the modal Sheet does does not display. I spent a few days trying to debug but could not pin point the problem.
I am sorry, this is a little confusing and will show a simplified version of the code to reproduce. But in a nutshell, the problem seems to be a sheet view that I have as part of the main view. Removing the sheet code from the main view displays the sheet from the sub view. Unfortunately, I don't have the freedom to change the Mainview.swift
Let me show some code to make it easy to understand....
First, before showing the code, the steps to repeat the problem:
click on the circle with 3 dots in the navigation bar
select the second item (Subview)
click on the "Edit Parameters" button and the EditParameters() view will not display
ContentView.swift (just calls the Mainview()). Included code to copy for reproducing issue :-)
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Mainview()
}
}
}
}
Mainview.swift. This is a simplified version of the actual App which is quite complex and I don't have leeway to change much here unfortunately!
fileprivate enum CurrentView {
case summary, sub
}
enum OptionSheet: Identifiable {
var id: Self {self}
case add
}
struct Mainview: View {
#State private var currentView: CurrentView? = .summary
#State private var showSheet: OptionSheet? = nil
var body: some View {
GeometryReader { g in
content.frame(width: g.size.width, height: g.size.height)
.navigationBarTitle("Main", displayMode: .inline)
}
//Removing the below sheet view will display the sheet from the subview but with this sheet here, it the sheet from subview does not work. This is required as these action items are accessed from the second menu item (circle and arrow) navigation baritem
.sheet(item: $showSheet, content: { mode in
sheetContent(for: mode)
})
.toolbar {
HStack {
trailingBarItems
actionItems
}
}
}
var actionItems: some View {
Menu {
Button(action: {
showSheet = .add
}) {
Label("Add Elements", systemImage: "plus")
}
} label: {
Image(systemName: "cursorarrow.click").resizable()
}
}
var trailingBarItems: some View {
Menu {
Button(action: {currentView = .summary}) {
Label("Summary", systemImage: "list.bullet.rectangle")
}
Button(action: {currentView = .sub}) {
Label("Subview", systemImage: "circle")
}
} label: {
Image(systemName: "ellipsis.circle").resizable()
}
}
#ViewBuilder
func sheetContent(for mode: OptionSheet) -> some View {
switch mode {
case .add:
AddElements()
}
}
#ViewBuilder
var content: some View {
if let currentView = currentView {
switch currentView {
case .summary:
SummaryView()
case .sub:
SubView()
}
}
}
}
Subview.swift. This is the file that contains the button "Edit Parameters" which does not display the sheet. I am trying to display the sheet from this view.
struct SubView: View {
#State private var editParameters: Bool = false
var body: some View {
VStack {
Button(action: {
editParameters.toggle()
}, label: {
HStack {
Image(systemName: "square.and.pencil")
.font(.headline)
Text("Edit Parameters")
.fontWeight(.semibold)
.font(.headline)
}
})
.padding(10)
.foregroundColor(Color.white)
.background(Color(.systemBlue))
.cornerRadius(20)
.sheet(isPresented: $editParameters, content: {
EditParameterView()
})
.padding()
Text("Subview....")
}
.padding()
}
}
EditParameters.swift. This is the view it should display when the Edit Parameters button is pressed
struct EditParameterView: View {
var body: some View {
Text("Edit Parameters...")
}
}
Summaryview.swift. Nothing special here. just including for completeness
struct SummaryView: View {
var body: some View {
Text("Summary View")
}
}
In SwiftUI, you can't have 2 .sheet() modifiers on the same hierarchy. Here, the first .sheet() modifier is on one of the parent views to the second .sheet(). The easy solution is to move one of the .sheets() so it's own hierarchy.
You could either use ZStacks:
var body: some View {
ZStack {
GeometryReader { g in
content.frame(width: g.size.width, height: g.size.height)
.navigationBarTitle("Main", displayMode: .inline)
}
ZStack{ }
.sheet(item: $showSheet, content: { mode in
sheetContent(for: mode)
})
}
.toolbar {
HStack {
trailingBarItems
actionItems
}
}
}
or more elegantly:
var body: some View {
GeometryReader { g in
content.frame(width: g.size.width, height: g.size.height)
.navigationBarTitle("Main", displayMode: .inline)
}
.background(
ZStack{ }
.sheet(item: $showSheet, content: { mode in
sheetContent(for: mode)
})
)
.toolbar {
HStack {
trailingBarItems
actionItems
}
}
}

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)
}
}
}

NavigationView containing VStack embedded with HStack and List causes error when swiping backwards

I currently have a navigationView embedded with a navigationLink that leads to a VStack that contains an HStack and a List. For some reason, when I drag back to the previous navigation in the stack and leave it halfway, the app glitches and needs to be restarted. This only occurs if I include the navigationbar title.
Here is the code for the view that is embedded in the navigationView. Removing the HStack causes the code bug to stop. The navigation bar items section is not large enough for my desired behavior.
Desired Behavior: Static HStack with a list below it and navigation bar title.
Any help would be appreciated. Thanks.
struct Sell: View {
var body: some View {
NavigationView {
List(self.stores) { store in
NavigationLink(destination: StoreHome2(store: store)) {
VStack(alignment: .center) {
Text(store.storeName)
}
}
}.navigationBarTitle(Text("Sell").foregroundColor(Color.black))
}.onAppear(perform: getStores)
}
}
import SwiftUI
struct StoreHome2: View {
var categoryItem: [String: [Item]]
var store: Store
#State var isPresentingFirst = false
var body: some View {
VStack {
HStack {
Button(action: {
self.isPresentingFirst = true
}) {
Image(systemName: "plus")
}.sheet(isPresented: $isPresentingFirst) {
EmptyView()
}
}.padding()
List {
ForEach(categoryItem.keys.sorted(), id: \.self) { key in
Text("Hello World")
}.listRowInsets(EdgeInsets())
}
}.navigationBarTitle(Text("Hello World"))
}
}

Resources