NavigationLink in Detail Page - swiftui

hey folk I have a problem I wanna make a NavationLink in Detail Page to show other Fruits. but when I click it it will make a page(with NavigationView) inside a Parent’s Page actually I want to replace it, not push a new one. this is my code
var body: some View {
NavigationView {
ScrollView(showsIndicators:false) {
VStack(alignment: .center, spacing: 20) {
// HEADER
FruitHeaderView(Fruit: fruit)
VStack(alignment:.leading,spacing:20) {
// TITLE
Text("\(fruit.title)")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(fruit.gradientColors[1])
// HEADLINE
Text("\(fruit.headline)")
.font(.headline)
.fontWeight(.bold)
// SUBHEADING
Text("Learn more about \(fruit.title)".uppercased())
.fontWeight(.bold)
.foregroundColor(fruit.gradientColors[1])
// DESCRIPTION
Text("\(fruit.description)")
.multilineTextAlignment(.leading)
} //: VSTACK
.padding(.horizontal,20)
.frame(maxWidth:640,alignment: .center)
} //: VSTACK
VStack(alignment:.leading) {
Text("Other Fruit like \(fruit.title)")
.fontWeight(.bold)
.foregroundColor(fruit.gradientColors[1])
ScrollView(.horizontal, showsIndicators:false) {
HStack(spacing:30) {
ForEach(5...10,id:\.self) { item in
NavigationLink(destination: FruitDetailView(fruit: fruitsData[item])) {
AlternativeFruitView(Fruit: fruitsData[item])
}
}
}
}
}
.padding(.horizontal,20)
.frame(maxWidth:640,alignment: .center)
} //: SCROLL
.navigationBarTitle("\(fruit.title)",displayMode: .inline)
.navigationBarHidden(true)
.edgesIgnoringSafeArea(.top)
} //: NAVIGATION
.navigationViewStyle(StackNavigationViewStyle()) // iPad OS
}
and this is the result
current Build
My Problem
I know there will be calling my struct and making a new NavigationView I just wanna know is is there an API or function that I can fix my problem or not
thanks.

Related

iOS15 Navigation title Not Showing Properly in Some Devices SwiftUI

I'm trying to figure out why the navigation title is not working with some devices, but I'm not able to figure out this issue. Can any one please help me to find out this issue, why the navigation title shows only the first 3 letters and after showing?
I have attached the Screenshot also please check.
iPhone 11 Device
iPhone 11 Simulator
Code:-
var body: some View {
ZStack {
Color.init(ColorConstantsName.MainThemeBgColour)
.ignoresSafeArea()
GeometryReader { geo in
ScrollView(.vertical) {
Text("Testing")
}
}
}
.navigationBarBackButtonHidden(true)
.navigationTitle(CommonAllString.BlankStr)
.navigationViewStyle(.stack)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading:AnyView(leadingButton),trailing:AnyView(self.trailingButton))
.foregroundColor(Color.white)
}
var trailingButton: some View {
HStack {
Image(systemName: ImageConstantsNameForChatScreen.PersonImg)
.padding(.trailing)
Image(ImageConstantsName.DTRShareIconImg)
.resizable().frame(width: 20, height: 20)
}
}
Below Code Working:-
struct PSScreen: View {
var body: some View {
ZStack {
Color.init("Colour")
.ignoresSafeArea()
VStack{
Text("PSScreen")
}
}
.navigationBarBackButtonHidden(true)
.navigationBarTitle("", displayMode: .inline)
.navigationViewStyle(.stack)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
leadingButton.frame(width: 50, alignment: .leading)
}
ToolbarItem(placement: .navigationBarTrailing) {
trailingButton
}
}.foregroundColor(Color.white)
}
var leadingButton: some View {
HStack{
Text("Profile")
}
}
var trailingButton: some View {
HStack {
Image(systemName: "Person")
.padding(.trailing)
Image("Share")
.resizable().frame(width: 20, height: 20)
}
}
}

NavigationLink touch area

I'm trying to use NavigationLink inside List. For a specific reason, it is in .background with EmptyView().
var body: some View {
List {
Section {
HStack {
Image(systemName: "checkmark")
.frame(width: 30, height: 30, alignment: .center)
.opacity(selected ? 1 : 0)
Text("TEST")
Spacer()
}
.onTapGesture {
selected.toggle()
}
.background(
NavigationLink(destination: WifiView()) { EmptyView() }
.disabled(isPad)
)
}
}
}
The problem is the touch area for the NavigationLink is not as expected.
.background's area is as above.
but NavigationLink and EmptyView's area is as above. I tried to force the frame with .frame but it won't change.
What am I missing?
try something like this, works well for me:
var body: some View {
List {
Section {
HStack {
Image(systemName: "checkmark")
.frame(width: 30, height: 30, alignment: .center)
.opacity(selected ? 1 : 0)
Text("TEST")
Spacer()
}
.contentShape(Rectangle()) // <--- here
.onTapGesture {
selected.toggle()
}
.background(
NavigationLink(destination: WifiView()) { EmptyView() }
.disabled(isPad)
)
}
}
}
To distill the accepted answer: all you need to do is to add .contentShape(Rectangle()) to the same element which has .onTapGesture. This solution works for any container, not just List.
E.g. if you have:
VStack {
// ...
}
.onTapGesture {
// ...
}
Add contentShape to the same element:
VStack {
// ...
}
.contentShape(Rectangle())
.onTapGesture {
// ...
}
Rationale:
If you add a tap gesture to a container SwiftUI view, such as VStack or HStack, then SwiftUI only adds the gesture to the parts of the container that have something inside – large parts of the stack are likely to be untappable.
If this is what you want then the default behavior is fine. However, if you want to change the shape of hit tests – the area that responds to taps – then you should use the contentShape() modifier with the shape you want.
(source)

How to position text and a button in this manner?

This is an example of what I am trying to do.Link to image of design to implement. I am unsure how to position a button and text at the top of the screen in this manner coding in swiftui. Alternatively I thought I could use the navigation bar inline and customise that but I am unsure.
var body: some View {
VStack (alignment: .trailing) {
HStack(spacing:10) {
Button(action: {
}) {
Image(systemName: "line.horizontal.3")
.font(.headline)
}
Text("WeCollab")
.foregroundColor(.white)
.font(.title)
//padding(.leading,40)
Spacer()
}
.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top)
.background(customPurpleColour)
Spacer()
}
.edgesIgnoringSafeArea(.top)
}
}
Seems like you're on the right track. In order to keep the title centered, it seemed easier to make a ZStack. The menu button gets it's own .leading-aligned VStack and then the title goes on top of that.
The edgesIgnoringSafeArea and padding can be simplified so that you don't have to use the screen size safe areas.
struct ContentView: View {
var body: some View {
VStack {
ZStack {
VStack {
Button(action: { }) {
Image(systemName: "line.horizontal.3")
.font(.headline)
}.padding(.leading)
}.frame(maxWidth: .infinity, alignment: .leading)
Text("WeCollab")
.foregroundColor(.white)
.font(.title)
}
.background(Color.purple.edgesIgnoringSafeArea(.top))
Spacer() //other content goes here
}
}
}

Adding a Subtitle under NavigationTitle

Let's say I have the following code:
struct SwiftUIView: View {
var body: some View {
NavigationView {
VStack {
Text("Hello")
Text("World")
}
.navigationTitle("SwiftUI")
}
}
}
I'd like to add a smaller subtitle right under SwiftUI. I tried adding something like .navigationSubtitle("") but it doesn't exist. I also tried reading the documentation, and it does mention func navigationSubtitle(_ subtitle: Text) -> some View, but I'm just not sure how to add that to my code. Thanks in advance!
You can add a ToolbarItem with the principal placement:
struct SwiftUIView: View {
var body: some View {
NavigationView {
VStack {
Text("Hello")
Text("World")
}
// .navigationTitle("SwiftUI") this won't make any changes now
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("title")
Text("subtitle")
}
}
}
}
}
}
The downside is that it overrides the navigation title, so any changes made with navigationTitle won't visible.
You can do something like:
.navigationBarItems(leading:
VStack(alignment: .leading, spacing: 5) {
Text("SwiftUI")
.font(.system(size: 35, weight: .semibold, design: .default))
Text("Subtitle")
}
)
Using a VStack in a toolbar causes the child view to display < Back for the the back navigation button rather than the title of the parent view. What I ended up doing is:
.navigationTitle("Title") // Will not be shown, but will be used for the back button of the child view
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("Real Title").font(.headline)
Text("Subtitle").font(.subheadline)
}
}
}
I ended up doing something different: instead of making "SwiftUI" a navigation title, I just put it inside a VStack with the rest of the body, like so:
struct SwiftUIView: View {
var body: some View {
NavigationView {
VStack {
//Header
VStack(alignment: .leading, spacing: 5) {
Text("SwiftUI")
.font(.system(size: 35, weight: .semibold, design: .default))
Text("Subtitle")
}
.padding()
.padding(.leading, -110) //I'm still not sure how to give it a leading alignment without hardcoding it
Divider()
Spacer()
//Body
VStack {
Text("Hello")
Text("World")
}
Spacer()
//Navbar title
}
}
}}
Thank you all for the help regardless!

SwiftUI 2.0: Close button is not dismissing the view - how do I get the Close button to return to the previous view?

I have tried to use Buttons and Navigation Links from various examples when researched on this channel and on the net. The NavigationLink would be ok, except that the NavigationView is pushing everything down in my view.
I have a view that contains an image and a text like this: ( x Close) but when I use the code below, the Close button is not doing anything.
In ContentView() I have a (?) button that takes me from WalkthroughView(), then to the PageTabView, then to this view, TabDetailsView:
ContentView():
ZStack {
NavigationView {
VStack {
Text("Hello World")
.padding()
.font(.title)
.background(Color.red)
.foregroundColor(.white)
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
showOnBoarding = true
}
} label: {
Image(systemName: "questionmark.circle.fill")
}
}
}
}
.accentColor(.red)
.disabled(showOnBoarding)
.blur(radius: showOnBoarding ? 3.0 : 0)
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
}
}
.onAppear {
if !isWalkthroughViewShowing {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
showOnBoarding.toggle()
isWalkthroughViewShowing = true
}
}
}
}
WalkthroughView():
var body: some View {
ZStack {
GradientView()
VStack {
PageTabView(selection: $selection)
// shows Previous/Next buttons only
ButtonsView(selection: $selection)
}
}
.transition(.move(edge: .bottom))
}
PageTabView():
var body: some View {
TabView(selection: $selection) {
ForEach(tabs.indices, id: \.self) { index in
TabDetailsView(index: index)
}
}
.tabViewStyle(PageTabViewStyle())
}
below, is the TabDetailsView():
At the top of the view is this Close button, when pressed, should send me back to ContentView, but nothing is happening.
struct TabDetailsView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
let index: Int
then, inside the body:
VStack(alignment: .leading) {
Spacer()
VStack(alignment: .leading) {
// Button to close each walkthrough page...
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "xmark.circle.fill")
Text("Close")
}
.padding(.leading)
.font(.title2)
.accentColor(.orange)
Spacer()
VStack {
Spacer()
Image(tabs[index].image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 415)
.padding(.leading, 10)
Text(tabs[index].title)
.font(.title)
.bold()
Text(tabs[index].text)
.padding()
Spacer()
}
.foregroundColor(.white)
}
}
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
}
Inserting view like above is not a presentation in standard meaning, that's why provided code does not work.
As this view is shown via showOnBoarding it should be hidden also via showOnBoarding, thus the solution is to pass binding to this state into view where it will be toggled back.
Due to deep hierarchy the most appropriate way is to use custom environment value. For simplicity let's use ResetDefault from https://stackoverflow.com/a/61847419/12299030 (you can rename it in your code)
So required modifications:
if showOnBoarding {
WalkthroughView(isWalkthroughViewShowing: $isWalkthroughViewShowing)
.environment(\.resetDefault, $showOnBoarding)
}
and in child view
struct TabDetailsView: View {
#Environment(\.resetDefault) var showOnBoarding
// .. other code
Button(action: {
self.showOnBoarding.wrappedValue.toggle()
}) {
Image(systemName: "xmark.circle.fill")
Text("Close")
}