Back button skipping a view in View1 -> View2 -> View3 NavigationLink - swiftui-navigationlink

I'm fairly new to Swift and it may be I'm going about this all wrong, but I have a bug I can't work out - within a NavigationView, a NavigationLink in View1 opens to View 2, then that view has a NavigationLink to View3. View3 shows the title from View1, which is obviously wrong, and when the back button is pressed it also goes back to View1 rather than View2. Am I using these links wrong or is it a SwiftUI issue? Simplified example of what I'm doing below.
struct FirstView: View {
var body: some View {
Text("Hello, World #1!")
.navigationBarItems(trailing:
NavigationLink(destination: SecondView()){
Image(systemName: "folder.badge.plus")
}
)
}
}
struct SecondView: View {
var body: some View {
Text("Hello, World #2!")
.navigationBarItems(trailing:
NavigationLink(destination: ThirdView()){
Image(systemName: "folder.badge.plus")
}
)
}
}
struct ThirdView: View {
var body: some View {
Text("Hello, World #3!")
}
}

Funny enough I had the same problem with the navigation skipping one view. Your problem is different though. The first view only will have the NavigationView declared. After that you just use navigation links.
Refactoring:
import SwiftUI
struct FirstView: View {
var body: some View {
NavigationView {
VStack {
Text("Hello, World #1!")
.padding()
NavigationLink(destination: SecondView()){
Image(systemName: "folder.badge.plus")
}
}
// a navigationBarTitle modifier inside!
.navigationBarTitle(Text("Hello"))
}
}
}
struct SecondView: View {
var body: some View {
VStack {
Text("Hello, World #2!")
.padding()
NavigationLink(destination: ThirdView()){
Image(systemName: "folder.badge.plus")
}
}
}
}
struct ThirdView: View {
var body: some View {
Text("Hello, World #3!")
}
}
struct FirstView_Previews: PreviewProvider {
static var previews: some View {
FirstView()
}
}

Related

.isFavorite results in errors

I'm trying to place an inFavorite on a Detailed View but get the following errors:
-Cannot convert value of type 'Binding' to expected condition type 'Bool'
-Value of type 'ObservedObject.Wrapper' has no dynamic member 'isFavorite' using key path from root type 'TitleModel'
import SwiftUI
struct DetailView: View {
#EnvironmentObject var model: TitleModel
var detail: Title
var body: some View {
ScrollView {
VStack (alignment: .leading) {
//MARK: Detail Image
Image(detail.image1)
.resizable()
.scaledToFill()
//MARK: Divider
Divider()
if $model.isFavorite {
Image(systemName: "star.fill")
.imageScale(.medium)
.foregroundColor(.yellow)
}
Divider()
//MARK: Remark
VStack(alignment: .leading) {
Text("Remark:")
.font(.headline)
.padding([.bottom, .top], 5)
ForEach(detail.remark, id:\.self) {item in
Text("• " + item)
}
}
.padding(.horizontal)
//MARK: Reference(s)
VStack(alignment: .leading) {
Text("Reference(s):")
.font(.headline)
.padding([.bottom, .top], 5)
}
.padding(.horizontal)
}
}
.navigationBarTitle(detail.title)
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
//create a dummy title and pass it into the detail view so we can see a preview
let model = TitleModel()
DetailView(detail: model.titles[0])
}
}
The view works perfect until I throw in the .isFavorite code. 'var model' references my View Model for my JSON data 'titles'. I am attempting to allow the person to tap the .star to highlight the view. I will then have a Scroll View with only the favorites. Thanks for helping me out.

In SwiftUI, supporting iOS 14 and above, how do I get a ScrollView child view to ignore the safe area

I have a VStack that contains a Rectangle. The Rectangle has an edgesIgnoresSafeArea(.top) view modifier on it to extend it through the top safe area.
import SwiftUI
struct ScrollViewSafeArea: View {
var body: some View {
ScrollView {
VStack {
Rectangle()
.fill(Color.orange)
.frame(height: 200)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
.background(Color.green.ignoresSafeArea())
}
}
Nice!
However, when I embed this inside a ScrollView, the Rectangle no longer extends through the safe area.
import SwiftUI
struct ScrollViewSafeArea: View {
var body: some View {
ScrollView {
VStack {
Rectangle()
.fill(Color.orange)
.frame(height: 200)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
.background(Color.green.ignoresSafeArea())
}
}
I can add a negative top padding to the Rectangle to extend it through the safe area, but this feels hacky to me.
Does anyone have a better way?
So I figured it out. I feel kind-of stupid actually. I wasn't ignoring the safe areas properly. Here is the code that does what I want it to do.
import SwiftUI
struct ScrollViewSafeArea: View {
var body: some View {
ScrollView {
VStack {
Rectangle()
.fill(Color.orange)
.frame(height: 200)
Spacer()
}
}
.ignoresSafeArea()
// Need to ignoreSafeArea again on the background color so
// the color extends to the horizontal edges in landscape.
.background(Color.green.ignoresSafeArea())
}
}
struct ScrollViewSafeArea_Previews: PreviewProvider {
static var previews: some View {
ScrollViewSafeArea()
}
}

SwiftUI NavigationView, going back if NavigationLink is inside a NavigationBarItem

As there are some problems with iOS 13.4 and Xcode 11.4 with presentationMode.wrappedValue.dismiss() I am looking for an alternative approach to go back programmatically. I found this solution from MScottWaller:
iOS SwiftUI: pop or dismiss view programmatically
Unfortunately, in my case it does not work:
struct MasterView: View {
#State private var showDetail = false
var body: some View {
VStack {
Text("MasterView")
.navigationBarItems(trailing: HStack {
NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
Image(systemName: "tag")
.padding(.leading, 4)
}
})
}
}
}
struct DetailView: View {
#Binding var showSelf: Bool
var body: some View {
Button(action: {
self.showSelf = false
}) {
Text("Pop")
}
}
}
If the NavigationLink is inside a navigationBarItem, I can't go back from my DetailView. I don't know if it is a bug or there are other reasons why NavigationLink does not work in the same way inside a navigationBarItem.
As a workaround I use this variant with a empty NavigationLink inside the view. It works, but I don't like this:
struct MasterView: View {
#State private var showDetail = false
var body: some View {
VStack {
Text("MasterView")
NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
EmptyView()
}
.navigationBarItems(trailing: HStack {
Button(action: { self.showDetail.toggle() }) {
Image(systemName: "tag")
.padding(.leading, 4)
}
})
}
}
}
Any ideas why the NavigationLink does not correct work inside a navigationBarItem?
It's an iOS bug.
https://forums.developer.apple.com/thread/125937
The work around is to toggle a NavigationLink hidden outside of nav bar:
struct Parent: View {
#State private var showingChildView = false
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: Child(),
isActive: self.$showingChildView)
{ Text("HiddenLink").hidden() }
}
.navigationBarItems(trailing: Button(action:{ self.showingChildView = true }) { Text("Next") })
}
}
}

ScrollView SwiftUI

I'am developing an app with swift ui. It's a single View App. But I have a problem with the scrollView
I have tried to delete the scrollview but it didn't change anything. I always have this big white problem on the top of my view.
https://imgur.com/a/BiWy0fe
import SwiftUI
struct Redactor {
var id: Int
let name, imageURL, since, bio: String
}
struct Article {
var id: Int
let name, image, content, redactor: String
}
struct MainScreenView: View {
let redactors:[Redactor] = [...]
let articles:[Article] = [...]
var body: some View {
NavigationView {
ScrollView(.vertical, showsIndicators: true) {
VStack(alignment: .leading) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 20) {
ForEach(redactors, id: \.id) { Redactor in
NavigationLink(destination: MainRedactorView(redactor: Redactor)) {
RedactorView(redactor: Redactor)
}
}
}.padding(.leading, 15)
}.padding(.top, 10)
VStack(alignment: .leading) {
Text("Les Dernières nouvelles :")
.font(.title)
.fontWeight(.bold)
.padding(.leading, 15)
.padding(.top, 20)
ForEach(articles, id: \.id) { Article in
ArticleView(article: Article)
}
}
}
}
.navigationBarTitle(Text("The News Place"))
}
}
}
struct RedactorView: View {
let redactor : Redactor
var body : some View {
VStack {
Image(redactor.imageURL)
.resizable()
.frame(width: 150, height: 150)
.aspectRatio(contentMode: .fill)
.cornerRadius(75)
Text(redactor.name)
.font(.subheadline)
.fontWeight(.bold)
}
}
}
struct ArticleView: View {
var article: Article
var body: some View {
VStack (alignment: .leading) {
Image(article.image)
.renderingMode(.original)
.resizable()
.cornerRadius(10)
.aspectRatio(contentMode: .fit)
Text(article.name)
.font(.title)
.fontWeight(.bold)
Text("Le Rondeau Mag'")
.font(.subheadline)
.foregroundColor(.red)
Text(article.content)
.font(.body)
.lineLimit(5)
.foregroundColor(.secondary)
}.padding()
}
}
I would like to delete this white blank on the top of my scroll view. Thank you
Did you try deleting the VStack inside ScrollView? ScrollView already defines an implicit stack.
A good way to know which element causes the blank space is applying .border(Color.red) to different views.
Hope this helps.
Is the NavigationView that's creating the white space on top.
Try with
.navigationBarTitle(Text("The News Place"), displayMode: .inline)
and see if the white space disappears.

SwiftUI with ObservableObject - List having NavigationLink to details downloading it on scroll NOT on appear

I have list of cities
struct CityListView: View {
#ObservedObject private(set) var citiesViewModel: CitiesViewModel
var body: some View {
LoadingView(isShowing: .constant(citiesViewModel.cities?.isEmpty ?? false)) {
NavigationView {
List(self.citiesViewModel.cities ?? []) { city in
NavigationLink(destination: DetailView(cityName: city.name,
detailCityModel: DetailCityModel(cityId: city.id))) {
Text(city.name)
}
}
.navigationBarTitle(Text("Cities"), displayMode: .large)
}
}
}
}
and when I'm scrolling the list, the DetailCityModel inits and download data from API. How to downloading (or init DetailCityModel) on DetailView's appearance, not for showing item with NAvigationLink to DetailView?
You have to kick off the API call in onAppear() not in the initialiser of DetailView.