PageTabViewStyle breaks NavigationView - swiftui

I am working on a SwiftUI view. When using the NavigationView inside the TabView the app works as expected.
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
Text("A")
.navigationBarTitle("Nav A")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
NavigationView {
Text("B")
.navigationBarTitle("Nav B")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
//.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) // this line breaks the view
}
}
As soon as I uncomment .tabViewStyle(...) the view does not work as expected. Any ideas why? The text is not visible anymore. The navigation does not seem to be correct either.

Try moving the NavigationView outside the TabView:
struct ContentView: View {
#State var selection = 1
var body: some View {
NavigationView {
TabView(selection: $selection) {
Text("A").tag(1)
Text("B").tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.navigationTitle(selection == 1 ? "A" : "B")
}
}
}

It seems like the NavigationView defaults to presenting as a primary/detail display.
You can force it to work like a normal navigation stack with the modifier .navigationViewStyle(StackNavigationViewStyle())
Here it is in your code:
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
Text("A")
.navigationBarTitle("Nav A")
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: .infinity, maxHeight: .infinity)
NavigationView {
Text("B")
.navigationBarTitle("Nav B")
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}

Related

How do you set autosaveName for SwiftUI VSplitView

I have a VSplitView in my app and I would like the position to be saved and restored automatically by the user defaults. Normally I would do this by providing an autosaveName for the split view either programmatically or in the XIB.
How do I accomplish that in SwiftUI using a VSplitView?
struct ContentView: View {
var body: some View {
VSplitView {
Text("Top View")
.padding()
.frame(maxWidth:.infinity, maxHeight: .infinity)
Text("Bottom View")
.padding()
.frame(maxWidth:.infinity, maxHeight: .infinity)
}
// I expected something like:
//.autosaveName("MainSplitView")
}
}

iOS 15 SwiftUI Conditionals on a view with Navigation View makes NavigationBar config to be ignore if navigationViewStyle stack

been searching for this everywhere and can't find anything around this, I believe is a bug, maybe is not.
I need NavigationView with .navigationViewStyle(.stack) to have it stacked on the iPad and make it look the same as the iphone, now suppose you have this view:
import SwiftUI
struct ContentView: View {
#State var isShowingProfile = false
#State var isNavigationViewShowing = true
var body: some View {
if isNavigationViewShowing {
NavigationView {
VStack {
Button("Simple view") {
isNavigationViewShowing = false
}
.padding()
Button("Profile navigation") {
isShowingProfile = true
}
.padding()
NavigationLink(
destination: ProfileView(),
isActive: $isShowingProfile
) {
EmptyView()
}
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
)
.background(Color.gray)
.navigationBarHidden(true)
}
.navigationViewStyle(.stack)
} else {
VStack {
Button("Show NavigationView"){
isNavigationViewShowing = true
}
.padding()
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
).background(Color.yellow)
}
}
}
struct ProfileView: View {
var body: some View {
Text("This is a profile")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Well this just show this 3 simple views:
The navigationView when you start
The Profile view if you tap on "Profile navigation"
Finally the Simple view which is trigger by the conditional state pressing "Simple view"
Up to here is all fine and good.
The problem is when navigate to the "Simple view" and then tap "Show NavigationView" to navigate back to the NavigtionView.
The app opens the first view (NavigationView), but the NavigationView ignores the .navigationBarHidden(true) and just show a big empty space on the top. In fact, it would ignore things like .navigationBarTitleDisplayMode(.inline) and just show the large version of the navigationBar
This is working correctly in all iOS 14.x, but on iOS 15.0 seems broken. The behaviour continues to be the same on iOS 15.1 beta.
Any idea whats going on? I'm not really interested in changing the conditionals on the view, because real life app is more complex.
Also, I tried ViewBuilder without any success. And if I take out .navigationViewStyle(.stack) it works all fine on iOS 15, but then the view on the iPad is with the side menu.
Thanks a lot for any tip or help, you should be able to reproduce in simulator and real device.
Video of the explained above
I think the better solution all around is to not have the NavigationView be conditional. There is no reason your conditional can't just live in the NavigationView. You just don't ever want the bar to show. Therefore, this code would seem to meet the requirements:
struct ContentView: View {
#State var isShowingProfile = false
#State var isNavigationViewShowing = true
var body: some View {
NavigationView {
Group {
if isNavigationViewShowing {
VStack {
Button("Simple view") {
isNavigationViewShowing = false
}
.padding()
Button("Profile navigation") {
isShowingProfile = true
}
.padding()
NavigationLink(
destination: ProfileView(),
isActive: $isShowingProfile
) {
EmptyView()
}
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
)
.background(Color(UIColor.systemGray6))
} else {
VStack {
Button("Show NavigationView"){
isNavigationViewShowing = true
}
.padding()
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
).background(Color.yellow)
}
}
.navigationBarHidden(true)
}
.navigationViewStyle(.stack)
}
}
I used Group simply to put the .navigationBarHidden(true) in the correct place so the code would compile.
Is this the behavior you are looking for?
import SwiftUI
struct ContentView: View {
#State private var isShowingProfile = false
#State private var showSimple = false
var body: some View {
NavigationView {
VStack {
Button("Simple view") {
showSimple = true
}
.padding()
Button("Profile navigation") {
isShowingProfile = true
}
.padding()
NavigationLink(destination: ProfileView(), isActive: $isShowingProfile) {
EmptyView()
}
}
.fullScreenCover(isPresented: $showSimple, onDismiss: {
print("Dismissed")
}, content: {
SimpleView()
})
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
)
.background(Color.gray)
.navigationBarHidden(true)
}
.navigationViewStyle(.stack)
}
}
struct ProfileView: View {
var body: some View {
Text("This is a profile")
}
}
struct SimpleView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
Button("Show NavigationView") {
presentationMode.wrappedValue.dismiss()
}
.padding()
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
).background(Color.yellow)
}
}

ScrollView in VStack align to bottom in swiftui

I can't get this scrollview to align to the bottom in SwiftUI!
I've three items in the scrollview, but I want them to be aligned to the bottom of the screen (the red arrow shows where I WANT the items to be!)
Here is my code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Color.blue.edgesIgnoringSafeArea(.all)
VStack {
ScrollView {
Spacer(minLength: 80)
Text("a")
Text("b")
Text("c")
}.frame(maxHeight: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/)
Text("Button")
.padding()
}.frame(alignment: .bottom)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'm trying spacers, and rotating content by 180 degrees? What should I do?
GeometryReader to the rescue! First, wrap the ScrollView in a GeometryReader so you can get the scrolling area’s height. Then set that as the minimum height of the ScrollView’s content and use bottom alignment:
var body: some View {
ZStack {
Color.blue.edgesIgnoringSafeArea(.all)
VStack {
GeometryReader { geometry in
ScrollView {
VStack {
Text("a")
Text("b")
Text("c")
}
.frame(maxWidth: .infinity,
minHeight: geometry.size.height,
alignment: .bottom)
}
}
Text("Button")
.padding()
}
}
}

Scrollable content not working as expected in SwiftUI

I'm trying to create a scroll view with my custom view, but when I add scroll to view it's not working as expected, without scroll view working fine.
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// But the below code is working fine.
struct ContentView: View {
var body: some View {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
// List Item
struct ListItem: View {
var body: some View {
VStack {
HStack {
Image("steve")
.resizable()
.clipShape(Circle())
.aspectRatio(contentMode: .fit)
.frame(maxWidth:44, maxHeight: 44)
VStack {
Text("Steve Jobs")
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text("1 hour ago")
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
}
Spacer()
}
ZStack(alignment:.top) {
GeometryReader { geometry in
VStack {
ZStack {
Image("poster_1")
.resizable()
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
.shadow(color: Color.black.opacity(0.12),
radius: 4, x: 1, y: 1)
.frame(width: geometry.size.width - 64,
height: geometry.size.height * 0.35)
.padding([.horizontal], 32)
.clipped()
ZStack {
Rectangle()
.fill(Color.black.opacity(0.75))
.frame(maxWidth:84 , maxHeight: 84)
.cornerRadius(12)
Image(systemName: "play.fill")
.font(.system(size: 44, weight: .bold))
.foregroundColor(.white)
}
}
VStack {
Text("Game of Thrones")
.accentColor(Color.gray.opacity(0.25))
.font(Font.subheadline.weight(.bold))
.padding([.horizontal], 32)
.padding([.bottom], 2)
.frame(maxWidth: .infinity,
alignment: .leading)
VStack {
Text("Game of Thrones is an American fantasy drama television series created by David Benioff and D. B. Weiss for HBO. ")
.accentColor(Color.gray.opacity(0.25))
.font(.footnote)
.frame(maxWidth: .infinity,
alignment: .leading)
.padding([.horizontal], 32)
Text("Show more...")
.accentColor(Color.gray.opacity(0.01))
.font(Font.footnote.weight(.bold))
.frame(maxWidth: .infinity,
alignment: .trailing)
.padding([.trailing], 32).onTapGesture {
print("okay")
}
}
}
}
}
}
}
}
}
ListItem contains multiple views which creates publisher info and movie information as shown below image.
Scrollview is scrolling but images are not shown in view as first image.
It is the geometry reader that you have in ListItem. Because neither a GeometryReader nor a Scrollview have their own size. Since neither no what size to render, they collapse. This is what you are seeing in your view. See this answer. The solution is to put the GeometryReader into ContentView outside the Scrollview and send the GeometryProxy that you called geometry into ListItem something like this:
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem(geometry: geometry)
}
}
} // Scrollview
} // GeometryReader
}
}
struct ListItem: View {
let geometry: GeometryProxy
var body: some View {
...
}
This seems to fix it in Preview, though you may have to change your multipliers in the .frame() that uses geometry to size it how you want.

How position HStack in bottom of my screen no use the spacer()

I want move the HStack in bottom of my screen no use the spacer() because when use the spacer move the logo for top of my screen.
Here is a solution without using Spacer:
struct ContentView: View {
var body: some View {
VStack {
VStack {
Text("Image")
Text("Some text")
}.frame(minHeight: 0, maxHeight: .infinity)
HStack {
Text("Bottom")
}
}
}
}
I know you asked for no spacer, but this code shows you can use them without your logo going to the top of the screen. Alternatively you can use ".position(CGPoint(...))"
struct ContentView: View {
var body: some View {
VStack {
Spacer()
VStack (alignment: .center) {
Image("your-image").resizable().frame(width: 90, height: 95)
Text("TCheck time").font(.title).foregroundColor(.gray)
}.padding()
Spacer()
HStack (alignment: .bottom) {
Text("2020 SplitWay").font(.subheadline).foregroundColor(.gray)
}
}
}
}