SwiftUI NavigationView detail view not shown - swiftui

The following NavigationView contains a primary master and secondary detail view:
struct ContentView: View {
var body: some View {
NavigationView {
Text("Primary View")
.navigationTitle("Primary")
Text("Secondary View")
.navigationTitle("Secondary")
}
}
}
When used with an iPad in any orientation everything works as expected. The primary view is shown in the sidebar and the secondary view is shown in the main content area:
When used on an iPhone the primary view is displayed and there seems to be no way to access the secondary view:
What is the correct way to set up a master/detail NavigationView on the iPhone?

If all you want to show is the secondary view when horizontal size is compact, why not just show it? The master view is there to provide selections for the user for navigating.
Otherwise, you don't have a NavigationLink and that is what will push the secondary view when horizontal size is compact.
NavigationView {
VStack {
NavigationLink("Choice 1", Text("Choice 1"))
...
}
Text("Secondary View").navigationTitle("Secondary")

Related

Swiftui: How to Close one Tab in a TabView?

I'm learning SwiftUI and having trouble with closing Each Tab View Element.
My App shows photos from user's album by TabView with pageViewStyle one by one.
And What I want to make is user can click save button in each view, and when button is clicked, save that photo and close only that view while other photos are still displayed. So unless all photos are saved or discarded, if user clicks save button, TabView should automatically move to another one.
However, I don't know how to close only one Tab Element. I've tried to use dismiss() and dynamically changing vm.images element. Latter one actually works, but it displays awkward movement and it also requires quite messy code. How could I solve this issue?
Here is my code.
TabView {
ForEach(vm.images, id: \.self) { image in
TestView(image: image)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
struct TestView: View {
#ObservedObject var vm: TestviewModel
...
var body: some View {
VStack(spacing: 10) {
Image(...)
Spacer()
Button {
...
} label: {
Text("Save")
}
}
You need actually to remove saved image from the viewModel container, and UI will be updated automatically
literally
Button {
vm.images.removeAll { $0.id == image.id } // << here !!
} label: {
Text("Save")
}
You need to use the selection initializer of TabView in order to control what it displays. So replace TabView with:
TabView(selection: $selection)
Than add a new property: #State var selection: YourIdType = someDefaultValue, and in the Button action you set selection to whatever you want to display.
Also add .tag(TheIdTheViewWillUse) remember that whatever Id you use must be the same as your selection variable. I recommend you use Int for the simple use.

Animating SwiftUI NavigationLinks that use tags

I've added onboarding steps to my app using SwiftUI instead of a UIPageViewController. The root-level view has several steps:
struct OnboardingView: View {
#EnvironmentObject private var onboardingModel: OnboardingModel
var body: some View {
NavigationView {
VStack {
FirstStep()
NavigationLink(destination: SecondStep(),
tag: "second-step",
selection: $onboardingModel.selectedStep) {
EmptyView()
}
NavigationLink(destination: ThirdStep(),
tag: "third-step",
selection: $onboardingModel.selectedStep) {
EmptyView()
}
// etc.
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
The OnboardingModel keeps track of the selectedStep, so that changes to it will trigger the corresponding NavigationLink's tag.
When the onboarding view starts up, the FirstStep animates in from the right, just like an ordinary push navigation. The remaining steps, however, simply replace the previous step without any animation. Adding an .animation(.easeIn) to each step does nothing. How can I make this look and feel like a UIPageViewController?
I should note that each step hides the navigation bar and title:
.navigationTitle(Text(""))
.navigationBarHidden(true)
but removing these lines doesn't restore any built-in animation that the NavigationView provides.
Click the screencap to see it in action. Note that only the first scene is animated in; the rest just appear, even though that is obscured a bit by the permissions dialogs.

Swift UI show modal sheet with tab bar visible

Is there anyway to keep the tab bar showing while presenting a modal / sheet view?
Here is a minimal failing example.
import SwiftUI
struct SheetView: View {
#Environment(\.dismiss) var dismiss
var body: some View {
Button("Press to dismiss") {
dismiss()
}
.padding()
}
}
struct Tab1: View {
#State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
SheetView()
}
}
}
struct MainView: View {
var body: some View {
TabView {
Tab1()
.tabItem {
Label("Tab 1", systemImage: "heart")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}
Thanks for answering my question in the comments.
Unfortunately the standard means of presenting views in SwiftUI is that they are truly modal – they capture the whole interaction context for the current scene, and you can’t interact with anything else until the modal is dismissed.
This is also the case for iPadOS. Even though a modal presented with .sheet on an iPad allows much more of the underlying view to be visible, you can’t interact with it until the sheet disappears. You can interact with different parts of the app by running two scenes side-by-side in split screen mode, but each half is a separate scene and any presented sheets are modal for that scene.
If you want one tab to optionally present a view over its usual content but still allow access to the tab view and its other tabs, that’s not a modal context and SwiftUI’s built-in sheet won’t work. You will have to implement something yourself - but I think that’s doable.
Rather than using .sheet, you could optionally add an overlay to your Tab1 view, using the same boolean state variable showingSheet. In this approach, the default dismiss environment variable won’t be available, so passing in the state variable as a binding value would be an alternative:
var body: some View
<main display>
.overlay(showingSheet ? Sheet1(presented: $showingSheet) : EmptyView())
You might also find that a ZStack works better than .overlay depending on what the contents of the tab view actually are.
You’ll definitely have a lot more structural work to do to make this work, but I hope you can see that it’s possible.

How to change master view width in SwiftUI split view on iPad

Is it possible to increase the default width of the master view in SwiftUI split view implemented with NavigationView on iPad?
NavigationView {
masterView
detailView
}
To change the default width of the master view you may install SwiftUI-Introspect and add this to your NavigationView:
.introspectNavigationController { navigationController in
navigationController.splitViewController?.preferredPrimaryColumnWidthFraction = 1
navigationController.splitViewController?.maximumPrimaryColumnWidth = *preferred width*
}

How do I create a simple app with a side bar in SwiftUI?

I've had my app inside a NavigationView using the StackNavigationViewStyle style for some time with no problems. Recently I wanted to add a sidebar to it though, so I thought I should try using the DoubleColumnNavigationViewStyle style for this. At the moment I can kind of make it work but it has some quirks:
If I am in a subview, and I try to slide back into its parent view, sliding back always brings the side bar into view instead of taking me back into the parent view which is what I would expect. Now matter how deep into my view hierarchy I am. (If you use the default Notes app and select View as a Gallery, that is exactly the way I expect my app to work like).
Much less important but annoying nonetheless is that if I press the back button, the nice animations of the sliding < back buttons into/out of view I got when I used StackNavigationViewStyle no longer happen. The buttons work fine but the animations are much worse now.
Here is a sample minimum app and a video to show what I mean:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
Text("Sidebar")
MainView()
}
}
}
struct MainView: View {
var body: some View {
Text("Main View")
NavigationLink(destination: Text("Sub View")) {
Text("Go to Sub View")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Thanks!