SwiftUI NavigationLink pops out by itself
Looks like a bug when there is exactly 2 NavigationLinks.
If you add another empty link it goes away:
NavigationLink(destination: EmptyView(), label: {})
More details: https://forums.swift.org/t/14-5-beta3-navigationlink-unexpected-pop/45279
SwiftUI NavigationView pops by itself
You can replace \.self
with \.id
:
ForEach(model.someRObj, id: \.id) { obj in
Heh(obj: obj.obj, pr: obj.prop)
}
Then every object will be identified by id
and the ForEach
loop will only refresh when the id
is changed.
SwiftUI NavigationView pop itself when a datasource is finished loading
This happens because you're specifying id
as item
itself, and when list updated there's no original item anymore, so it closes
If you just wanna modify items without adding/removing/reordering, you can make index your item id:
NavigationView {
List(viewModel.dataSource.indices, id: \.self) { i in
let item = viewModel.dataSource[i]
NavigationLink(destination: Text("\(item)")) {
Text("\(item)")
.padding()
}
}
}
But with more complex data you need to have your items Identifiable
with unique ids, and you won't have such problem. Check out this example:
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
NavigationView {
List(viewModel.dataSource) { item in
NavigationLink(destination: Text("\(item.value)")) {
Text("\(item.value)")
.padding()
}
}
}
}
}
class ViewModel: ObservableObject {
@Published private(set) var dataSource: [Item] = [1, 2, 3, 4, 5]
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [self] in // simulate calling webservice
// you're modifying value but id stays the same
self.dataSource[0].value = 99
}
}
}
struct Item: Identifiable, ExpressibleByIntegerLiteral {
let id = UUID()
var value: Int
init(integerLiteral value: IntegerLiteralType) {
self.value = value
}
}
SwiftUI pushed View through Navigationview pops back after dismissing sheet
Currently placing a NavigationLink
in the navigationBarItems
may cause some issues.
Try removing the NavigationLink
from navigationBarItems
and put it the background
:
struct homeView: View {
@State var showSearch: Bool = false
var body: some View {
NavigationView {
Text("home")
// move `NavigationLink` outside `navigationBarItems`
.background(NavigationLink("", destination: SearchContentView(), isActive: $showSearch))
.navigationBarTitle("", displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: HStack {
Button("search", action: {
showSearch.toggle()
})
})
}
}
}
SwiftUI: NavigationLink pops back when variable is updated
I had the same problem and managed to solve it today. Try adding the option .navigationViewStyle(.stack)
to your NavigationView
struct ContentView: View {
var body: some View {
NavigationView {
//All your content and navigation links
}.navigationViewStyle(.stack)
}
}
SwiftUI: How can I determine if a view is presented in a NavigationView, Sheet, or is the root?
You can use SwiftUI-Introspect
, used to "Introspect underlying UIKit components from SwiftUI".
Here is a working example of what you are looking for. It is an interactive example, so you can click through the different modes.
import Introspect
import SwiftUI
/* ... */
struct ContentView: View {
@State private var testing = 1
private let thingsToTest = 3
var body: some View {
VStack {
Picker("Testing", selection: $testing) {
ForEach(1 ... thingsToTest, id: \.self) { index in
Text("\(index)")
.tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
Divider()
Spacer()
switch testing {
case 1:
PresentationReader { kind in
Text("Hello! Kind: \(kind.rawValue)")
}
case 2:
NavigationView {
PresentationReader { kind in
Text("Hello! Kind: \(kind.rawValue)")
}
}
case 3:
Text("Parent")
.sheet(isPresented: .constant(true)) {
PresentationReader { kind in
Text("Hello! Kind: \(kind.rawValue)")
}
}
default:
fatalError("Unavailable")
}
Spacer()
}
}
}
enum Kind: String {
case navigationView
case root
case sheet
}
struct PresentationReader<Content: View>: View {
typealias PresentedContent = (Kind) -> Content
@State private var kind: Kind = .root
private let content: PresentedContent
init(@ViewBuilder content: @escaping PresentedContent) {
self.content = content
}
var body: some View {
content(kind)
.presentationReader(kind: $kind)
}
}
extension View {
func presentationReader(kind: Binding<Kind>) -> some View {
self
.introspectViewController { vc in
let rootVC = UIApplication.shared.windows.first?.rootViewController
let isRoot = vc === rootVC
var isHosted: Bool { Introspect.findHostingView(from: vc.view) != nil }
if isRoot {
kind.wrappedValue = .root
} else if isHosted {
kind.wrappedValue = .navigationView
} else {
kind.wrappedValue = .sheet
}
}
}
}
It works by getting the current view controller the view is in.
- If the class reference of the root view controller is the same as the current root view controller, this is the root view (meaning it isn't embedded in a
NavigationView
or.sheet(...)
). - If this is not the root, we then check if this view is embedded in a hosting view. If it is, it is in a
NavigationView
. - If the view is neither the root or in a
NavigationView
, it is therefore in a.sheet(...)
.
This is now what your CustomNavigationBar
will look like with these 3 changes:
struct CustomNavigationBar<Content>: View where Content: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State private var kind: Kind = .root // <--- CHANGE #1
private let title: LocalizedStringKey
private let content: (() -> Content)?
private var backButton: AnyView? {
let button = Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
// custom image extension, just resolves to a back icon
Image.Icons.arrowBack
}
if kind == .navigationView { // <--- CHANGE #2
return AnyView(button)
} else {
return nil
}
}
public init(_ title: LocalizedStringKey, content: (() -> Content)? = nil) {
self.title = title
self.content = content
}
var body: some View {
VStack {
content?()
.presentationReader(kind: $kind) // <--- CHANGE #3
Divider().foregroundColor(.gray)
}.navigationBarTitle(title, displayMode: .large)
.frame(minHeight: 96)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: backButton)
}
}
Related Topics
Issues While Lightweight Core Data Migration
Uipickerviewdelegate Xcode 8 Swift 3
Implement a Custom Staggeregrid in UIview Like Etsy App in Swift
How to Cut a Hole in a Sprite Image or Texture to Show What Is Behind It Using Spritekit in Swift
Customize Mglpolyline Using Mapbox
Decrypting Des with Commoncrypto in Swift 3
Spritekit Skscene Not Resizing Correctly to Fit iPhone 12
Why Extensions Cannot Add Stored Properties
Create Tab Bar Controller and Navigation Controller
iOS - Could Not Open Obj File When Convert Mdlasset to Mdlmesh
How to Write a Flip Method in Swift
How to Have a Searchbar Which Shows Suggestions with Different UItableview
How to Get Window Reference (Cgwindow, Nswindow or Windowref) from Cgwindowid in Swift