How to avoid spacer in NavigationView with Forms? - swiftui

I have an NavigationView with an NavigationLink, when I get to the second View I have a strange spacer between the head and the beginning of my form:
This is my code:
struct SampleView: View {
var body: some View {
NavigationView {
VStack {
Form {
Section(header: Text("Hallo")){
Text("abc")
}
Section(header: Text("abc")){
Text("lorem")
Text("ispsum")
Button(action: {
//UIApplication.shared.open(eintrag.eintrag.url as URL)
}) {
Text("starten")
}
}
}
}.navigationBarTitle("Hello")
}
}
}
Is this a bug? How to avoid this? Its only when navigation in. If I set the view as my start view it looks like it should:
What do I need to do to get in on the top?

Related

Navigation + Tabview + Sheet broken in iOS 15

It looks like Navigation + TabView + Sheet is broken in iOS 15.
When I do this:
ContentView -> DetailView -> Bottom Sheet
When the bottom sheet comes up, the Detail view is automatically popped off the stack:
https://www.youtube.com/watch?v=gguLptAx0l4
I expect the Detail view to stay there even when the bottom sheet appears. Does anyone have any idea on why this happens and how to fix it?
Here is my sample code:
import Combine
import SwiftUI
import RealmSwift
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
TabItemView(num: 1)
.tabItem {
Text("One")
}
TabItemView(num: 2)
.tabItem {
Text("Two")
}
}
}
}
}
struct TabItemView: View {
private let num: Int
init(num: Int) {
self.num = num
}
var body: some View {
NavigationLink(destination: DetailView(text: "Detail View \(num)")) {
Text("Go to Detail View")
}
}
}
struct DetailView: View {
#State private var showingSheet = false
private let text: String
init(text: String) {
self.text = text
}
var body: some View {
Button("Open Sheet") {
showingSheet.toggle()
}.sheet(isPresented: $showingSheet) {
Text("Sheet Text")
}
}
}
This works on iOS 14 btw
UPDATE 1:
Tried #Sebastian's suggestion of putting NavigationView inside of TabView. While this fixed the nav bug, it fundamentally changed the behavior (I don't want to show the tabs in DetailView).
Also tried his suggestion of using Introspect to set navigationController.hidesBottomBarWhenPushed = true on the NavigationLink destination, but that didn't do anything:
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
TabItemView(num: 1)
}.tabItem {
Text("One")
}
NavigationView {
TabItemView(num: 2)
}.tabItem {
Text("Two")
}
}
}
}
struct TabItemView: View {
private let num: Int
init(num: Int) {
self.num = num
}
var body: some View {
NavigationLink(destination: DetailView(text: "Detail View \(num)").introspectNavigationController { navigationController in
navigationController.hidesBottomBarWhenPushed = true
}) {
Text("Go to Detail View")
}
}
}
struct DetailView: View {
#State private var showingSheet = false
private let text: String
init(text: String) {
self.text = text
}
var body: some View {
Button("Open Sheet") {
showingSheet.toggle()
}.sheet(isPresented: $showingSheet) {
Text("Sheet Text")
}
}
}
You need to flip how you nest TabView & NavigationView. Instead of nesting several TabView views inside a NavigationView, use the TabView as the parent component, with a NavigationView for each tab.
This is how the updated ContentView would look like:
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
TabItemView(num: 1)
}
.tabItem {
Text("One")
}
NavigationView {
TabItemView(num: 2)
}
.tabItem {
Text("Two")
}
}
}
}
This makes sense and is more correct: The tabs should always be visible, but you want to show a different navigation stack with different content in each tab.
That it worked previously doesn't make it more correct - SwiftUI probably just changed its mind on dealing with unexpected situations. That, and the lack of error messages in these situations, is the downside of using a framework that tries to render anything you throw at it!
If the goal is specifically to hide the tabs when pushing a new view on a NavigationView (e.g., when tapping on a conversation in a messaging app), you have to use a different solution. Apple added the UIViewController.hidesBottomBarWhenPushed property to UIKit to support this specific use case.
This property is set on the UIViewController that, when presented, should not show a toolbar. In other words: Not the UINavigationController or the UITabBarController, but the child UIViewController that you push onto the UINavigationController.
This property is not supported in SwiftUI natively. You could set it using SwiftUI-Introspect, or simply write the navigation structure of your application using UIKit and write the views inside in SwiftUI, linking them using UIHostingViewController.

Why does the SwiftUI "Form" cause my button to go to the bottom of the screen?

I want to create a view that has a form with a button below it. If I include a form and a button, the button goes to the bottom of the screen.
Without Form Element
With Form Element
Is this just a SwiftUI bug? Or am I doing something wrong?
//
// TestFile.swift
// searchparty
//
// Created by Me
//
import SwiftUI
struct TestFile: View {
var body: some View {
VStack {
Form{
Text("Hello, World!")
}
Button("Button") {
print("Button tapped!")
}
}
}
}
struct TestFile_Previews: PreviewProvider {
static var previews: some View {
TestFile()
}
}
An empty Section with a footer will do the job, although you'll want to explicitly set the font, or else the footer will change it based on the form footer environment:
struct ContentView: View {
var body: some View {
Form {
Text("Entry")
Section(footer:
HStack {
Spacer()
Button(action: {}) {
Text("My button")
.font(.system(.body))
}
Spacer()
}
) {
EmptyView()
}
}
}
}

How to prevent DoubleColumnNavigationView from being collapsed under ipad

struct NavigationViewPadTest: View {
var body: some View {
NavigationView{
List(0..<30){ i in
Text("id:\(i)")
}
Text("abc")
}
.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
Use the above code to create a navigationView with a collapsible button at the top left of the screen. I don't want the navigationView to be collapsed and keep the DoubleColumn state. Is there a way to disable this button.
thanks
The possible solution is to remove bar
var body: some View {
NavigationView{
List(0..<30){ i in
Text("id:\(i)")
}
.navigationBarHidden(true) // << here !!
Text("abc")
}
.navigationViewStyle(DoubleColumnNavigationViewStyle())
}

SwiftUI Navigation Multiple back button

When I push more than one view, multiple back buttons are visible in the navigation bar.
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination:SecView()) {
Text("Primo")
}
}
}
}
struct SecView: View {
var body: some View {
NavigationView {
NavigationLink(destination:TerView()) {
Text("Secondo")
}
}
}
}
struct TerView: View {
var body: some View {
Text("Hello World!")
}
}
I would like to have only one back button per view.
Here is a screenshot of the problem.
There should only be a single NavigationView at the root of your navigation stack.
Remove the NavigationView block from SecView and you will then have a single navigation bar owned by ContentView.
as Gene said, there should only be a single NavigationView at the root of you navigation stack. This means that the next time you need to navigate to a page in a different View, you will add a NavigationLink but not wrap it in a NavigationView. So in the code that you initially posted, you need to remove the NavigationView from your SecView View but still keep the NavigationLink. See the code below:
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination:SecView()) {
Text("Primo")
}
}
}
}
struct SecView: View {
var body: some View {
NavigationLink(destination:TerView()) {
Text("Secondo")
}
}
}
struct TerView: View {
var body: some View {
Text("Hello World!")
}
}

Multi Level Navigation SwiftUI

I have a master detail application I'm working on in SwiftUI but once on the 1st DetailView, NavigationLink in the NavBar no longer works. I wrote this as a simple demonstration:
struct NavView: View {
var body: some View {
NavigationView {
NavigationLink(destination: Layer()) {Text("Go to Layer 1")}
}
}
}
struct Layer: View {
var body: some View {
Text("Welcome to Layer 1")
.navigationBarItems(trailing: NavigationLink(destination: AnotherLayer()) { Text("Go to Layer 2") })
}
}
struct AnotherLayer: View {
var body: some View {
Text("Welcome to Layer 2")
}
}
Everything renders and you can tap the navigationBarItem in Layer but nothing happens.
What's going on here? How can I access AnotherLayer?
A NavigationLink used as a navigationBarItem will not work no matter if you use it in the first or second level but a NavigationDestinationLink would solve the problem.
import SwiftUI
struct TestSwift: View {
var body: some View {
NavigationView {
NavigationLink(destination: Layer()) {Text("Go to Level")}
}
}
}
struct Layer: View {
let detailView = NavigationDestinationLink(AnotherLayer())
var body: some View {
VStack {
Text("Text")
}
.navigationBarItems(trailing:
Button(action: {
self.detailView.presented?.value = true
}, label: {
Text("Present Now!")
})
)
}
}
struct AnotherLayer: View {
var body: some View {
Text("Hello")
}
}