SwiftUI EditButton in HStack not activating edit mode - swiftui

As part of a bigger Form I'd like to have a Section with a List whose entries can be reordered.
In the whole view it should be only possible to edit that particular list, nothing else. Therefore I'd like to have the EditButton near the List.
If I just set the EditButton as the Section header, reordering List items works but I can't display a section title:
struct ContentView: View {
private let items = Range(1...4).map { "Item " + String($0) }
var body: some View {
Form {
Section(header: EditButton()) {
ForEach(items, id: \.self) { item in
Text(item)
}
.onMove(perform: reorderItems)
.onDelete(perform: deleteItems)
}
}
}
func reorderItems(from sourceIndices: IndexSet, to destinationIndex: Int) { /* ... */ }
func deleteItems(at offsets: IndexSet) { /* ... */ }
}
But if I wrap the EditButton in a HStack to display the button on the right, a tap on "Edit" changes the button's title to "Done" but doesn't start List's edit mode anymore:
struct ContentView: View {
#Environment(\.editMode) var editMode
private let items = Range(1...4).map { "Item " + String($0) }
var body: some View {
Form {
Section(header: HStack {
Text("Section title")
Spacer()
EditButton()
}.environment(\.editMode, self.editMode)) {
ForEach(items, id: \.self) { item in
Text(item)
}
.onMove(perform: reorderItems)
.onDelete(perform: deleteItems)
}
}
}
func reorderItems(from sourceIndices: IndexSet, to destinationIndex: Int) { /* ... */ }
func deleteItems(at offsets: IndexSet) { /* ... */ }
}
I also tried, as the code shows, passing the editMode environment variable to the HStack, but nothing helped.
Is there any way to get the EditButton inside an HStack working?
(Remark: As the List is part of a bigger Form, placing the EditButton out of the Section as suggested here is not an option in my case.)

Here is working solution - looks like they require that EditButton was a root view of section, so we can construct everything else above it. (tested with Xcode 11.4 / iOS 13.4)
Note: #Environment(\.editMode) var editMode is not needed
Section(header:
EditButton().frame(maxWidth: .infinity, alignment: .trailing)
.overlay(Text("Header"), alignment: .leading)
)
{
ForEach(items, id: \.self) { item in
Text(item)
}
.onMove(perform: reorderItems)
.onDelete(perform: deleteItems)
}

Related

How to add the Navigation Bar space in SwiftUI NavigationView

I hide the navigation bar and provide a customized navigation bar.
But my list appears below the navigation bar.
I want the list to appear below the navigation bar when scrolling.
My desired effect:
Actual effect: Blocked 0
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
FirstView()
.tabItem {
Image(systemName: "folder.fill")
Text("Home")
}
SecondView()
.tabItem {
Image(systemName: "folder.fill")
Text("SecondView")
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct FirstView: View {
var body: some View {
ZStack(alignment: .top) {
List {
ForEach(0..<40, id: \.self) { index in
Text("cell view \(index)")
}
}
.listStyle(.inset)
HStack {
Text("首页")
}
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(.bar)
}
.navigationBarTitleDisplayMode(.inline)
//.ignoresSafeArea(edges: .)
}
}
struct SecondView: View {
var body: some View {
List {
ForEach(0..<40, id: \.self) { index in
Text("FirstView2")
}
}
.listStyle(.inset)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Please do not add padding to the List. This is a security zone problem.
Edit: scrolling under the header
For a glass effect under the header:
You can add a "dummy" element that will push the list one row down. So the the header will cover the dummy element and the whole list will be visible.
struct FirstView: View {
var body: some View {
ZStack(alignment: .top) {
List {
// Dummy Text
Text("")
ForEach(0..<40, id: \.self) { index in
Text("cell view \(index)")
}
}
.listStyle(.inset)
HStack {
Text("首页")
}
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(.bar)
.opacity(0.7)
}
.navigationBarTitleDisplayMode(.inline)
//.ignoresSafeArea(edges: .)
}
}
Original answer
Your FirstView is using a ZStack, which tells exactly the compiler to show one view on top of (covering) the other. Use a VStack, that should solve your issue.
struct FirstView: View {
var body: some View {
VStack {
HStack {
Text("首页")
}
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(.bar)
List {
ForEach(0..<40, id: \.self) { index in
Text("cell view \(index)")
}
}
.listStyle(.inset)
}
.navigationBarTitleDisplayMode(.inline)
//.ignoresSafeArea(edges: .)
}
}

onTapGesture To Dismiss Keyboard Makes View Inoperable

I am working on a SwiftUI app. In the app I have a custom coded List that acts as a Form. The reason it is custom coded is because I am using a custom color. I have TextField rows among other rows that act as Navigation Links. My issue is that when I add a onTapGesture to dismiss the keyboard all other row functions stop working. For example the NavigationLinks.
NavigationLink Work Here
ZStack(alignment: .leading, content: {
Color.pacificBlue
.edgesIgnoringSafeArea(.all)
List {
Section(header: Text("Header") {
NavigationLink(
destination: SecondaryView(),
label: {
Text("Secondary View")
})
TextField("MyField", text: self.$myField)
}
}
}
NavigationLink Does Not Work Here
ZStack(alignment: .leading, content: {
Color.pacificBlue
.edgesIgnoringSafeArea(.all)
List {
Section(header: Text("Header") {
NavigationLink(
destination: SecondaryView(),
label: {
Text("Secondary View")
})
TextField("MyField", text: self.$myField)
}
}
}
.onTapGesture {
self.dismissKeyboard()
}
Dismiss Keyboard
extension View {
func dismissKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil)
}
}
Is there a way to work around this without having to move to a secondary view to add text. Any help would be appreciated.
Taking the answer from: SwiftUI NavigationLink in list
the following works for me for NavigationLink. If you have
a Button for example, this will not work.
struct ContentView: View {
#State var myField = ""
#State private var showIt: Int? = 0 // <-- here
var body: some View {
NavigationView {
ZStack(alignment: .leading) {
Color.blue.edgesIgnoringSafeArea(.all)
List {
Section(header: Text("Header")) {
// -- here --
NavigationLink(destination: Text("destination view"), tag: 1, selection: $showIt) {
Text("Secondary View")
}.disabled(true) // <-- here
.onTapGesture { showIt = 1 } // <-- here
TextField("MyField", text: $myField)
}
}.listStyle(.plain)
}
.onTapGesture {
self.dismissKeyboard()
}
}.navigationViewStyle(.stack)
}
}

ScrollViewReader not scrolling programmatically

I have the following view and I'm attempting to have it scroll to the bottom on button click as elements are added to the list. I've searched and found that ScrollViewReader is the option to use however my implementation doesn't appear to be working.
My attempts at fixing have included explicitly setting the id of the cell on both the inner views as well as the outer HStack{} I even attempted to set the id to a reference of itself, kind of knowing that's a bad idea, but for brevity. I also removed any extra views inside of the list such as HStack{}, Spacer(), etc.. and just left my ColorsChosenView().id(i) thinking that extra views might cause it, but I digress the issue still persists.
var body: some View {
VStack {
ScrollViewReader { reader in
List {
ForEach(0..<vm.guesses.count, id: \.self) { i in
HStack{
Spacer()
ColorsChosenView(locationCorrect: 1,
locationIncorrect: 3,
color1: vm.guesses[i][0],
color2: vm.guesses[i][1],
color3: vm.guesses[i][2],
color4: vm.guesses[i][3])
Spacer()
}.id(i)
}
}.listStyle(InsetListStyle())
Divider()
.frame(maxWidth: 250)
ColorChoicePicker(vm: vm)
Divider()
.frame(maxWidth: 250)
HStack {
Spacer()
FABButton(text: "SUBMIT")
.onTapGesture {
vm.submit()
reader.scrollTo(vm.guesses.count - 1)
}
}.padding()
}
}
.navigationBarHidden(true)
.navigationBarHidden(true)
.onAppear(perform: {
vm.resetGame()
})
}
To simplify things, I found that this works just fine. Yet my implementation doesn't feel much different.
var body: some View {
ScrollViewReader { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50)
}
List(0..<100, id: \.self) { i in
Text("Example \(i)")
.id(i)
}
}
}
}
Since you're modifying the array, this should work:
1: call the function in the main thread (DispatchQueue.main.async)
-> this will "kinda" work, it will scroll but not to the current but the previous last item
2: (Workaround) handle scrolling in a change-handler (you could also remove the shouldScroll variable if all changes should make it scroll to the bottom)
class NumbersContainer: ObservableObject {
#Published var numbers: [Int] = Array(0..<25)
func submit() {
self.numbers.append(self.numbers.count)
}
}
struct ContentView: View {
#StateObject var nc = NumbersContainer()
#State var shouldScroll: Bool = false
var body: some View {
VStack {
ScrollViewReader { reader in
Button("Submit", action: {
DispatchQueue.main.async {
nc.submit()
}
self.shouldScroll = true
})
List {
ForEach(0..<nc.numbers.count, id: \.self) { i in
HStack {
Spacer()
Text("Row \(i)")
Spacer()
}.id(i)
}
}
.onChange(of: nc.numbers) { newValue in
if shouldScroll {
reader.scrollTo(newValue.count - 1)
shouldScroll = false
}
}
}
}
}
}
Another Possibility would be to use the ScrollReaderProxy as a parameter of the submit function:
class NumbersContainer: ObservableObject {
#Published var numbers: [Int] = Array(0..<25)
func submit(reader: ScrollViewProxy) {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter() // All leaves must have an enter
DispatchQueue.main.async {
self.numbers.append(self.numbers.count)
dispatchGroup.leave() // Notifies the DispatchGroup
}
dispatchGroup.notify(queue: .main) {
reader.scrollTo(self.numbers.count - 1)
}
}
}
struct ContentView: View {
#StateObject var nc = NumbersContainer()
var body: some View {
VStack {
ScrollViewReader { reader in
Button("Submit", action: {
nc.submit(reader: reader)
})
List {
ForEach(0..<nc.numbers.count, id: \.self) { i in
HStack {
Spacer()
Text("Row \(i)")
Spacer()
}.id(i)
}
}
}
}
}
}

SwiftUI NavigationLink Hide Arrow

Is there a way to hide the arrow to the right of the navigation link view that is automatically added?
I want to show an image grid using NavigationView -> List -> HStack -> NavigationLink_1 - NavigationLink_2
The NavigationLinks have arrows and it looks weird
The way it worked for me:
List {
ForEach(elements) { element in
ZStack {
CustomView(element: element)
NavigationLink(destination: DestinationView()) {
EmptyView()
}.buttonStyle(PlainButtonStyle())
}
}
}
The easiest way I've found is to place the navigation in the .background modifier with the opacity of zero:
List {
Text("The cell")
.background( NavigationLink("", destination: Text("The detail view")).opacity(0) )
}
And with this solution you don't loose the dynamic height functionality of the cells.
I got it done with this
NavigationLink(destination: DestinationView()) {
EmptyView()
}
.frame(width: 0, height: 0)
.hidden()
#State var selection: Int? = nil
var body: some View {
let navigation = NavigationLink(destination: Text("View"), tag: 1, selection: $selection) { EmptyView() }
return
VStack {
navigation
Text("Tap").onTapGesture { self.selection = 1 }
}
}
The only thing that helped me is to add .opacity(0) to NavigationLink like so:
List {
ForEach(elements) { element in
ZStack {
CustomView(element: element)
NavigationLink(destination: DestinationView()),
label: {}).opacity(0)
}
}
}
List {
ForEach(elements) { element in
ZStack {
CustomView(element: element)
NavigationLink(destination: DestinationView()) {
EmptyView()
}.opacity(0.0)
}
}
}
Setting .opacity(0) on the NavigationLink seems to be the most reliable solution for me because I noticed that it might show the indicators again when messing with the .listStyle property. You will also not lose the highlighted effect.
var body: some View {
NavigationView {
List {
ForEach(items) { item in
ZStack(alignment: .leading) {
NavigationLink(destination: EmptyView()) {
EmptyView()
}
.opacity(0)
Text(item.value)
}
}
}
}
}
This is what worked for me, just adding an empty NavigationLink in a ZStack
List(viewModel.items, id: \.id) { item in
ZStack {
NavigationLink(destination: Destination()) {}
CustomView(item: item)
}
}
Only this worked for me, when I tried to implement button tap inside row in List:
ZStack {
NavigationLink(destination: FlightBoardInformation(flight: flight), tag: FlightBoardNavigation.toFlightDetailed, selection: $navigation) {
EmptyView()
}
.frame(width: 0, height: 0)
.hidden()
.disabled(true)
Button(action: {
self.navigation = .toFlightDetailed
}) {
Text("\(self.flight.airline) \(self.flight.number)")
}.buttonStyle(PlainButtonStyle())
}
Although .background(...).opacity(0) works, in a more complex view it expands itself through all the view and conflicts with other elements like buttons.
If you need it inside a List, what worked for me is also marking the NavigationLink as .disabled(true):
Text(...)
.background( NavigationLink(...).opacity(0).disabled(true) )
Use .background modifier.
ForEach(elements) { e in
AnyViewYouWantToShow(e)
.background(
NavigationLink("", destination: DestinationView()))
.opacity(0)
)
}
Finally found out a way how to avoid the the chevron without doing some tricky ZStacks and other solutions. The only downside is that this is only tested on iOS 16 with the new NavigationPath + NavigationStack.
Instead of using a regular NavigationLink where you apply the hashable object, you'll just use a regular Button and append the object to the NavigationPath.
Example:
#State private var path = NavigationPath()
var body: some View {
List {
ForEach(viewModel.customers) { customer in
Button {
path.append(customer)
} label: {
CustomerCell(customer: customer)
}
}
}
.navigationDestination(for: Customer.self) { customer in
CustomerView(customer: customer)
}
}
For projects using the NavigationBackport (for preparing the new navigation), it might work as well. As you can use NBNavigationPath and append the object to the path with a Button just like the example above.
The best workaround for me is using background:
NavigationLink(...) {}
.opacity(0)
.background(
HStack {
Text("Your custom view without arrow")
}
)
Or if you need dynamic height as #turingtested posted use NavigationLink as background
Text("Your custom view without arrow")
.background(NavigationLink( ... ) {}.opacity(0))
though there is lots of solution. I'm posting my one.
var body: some View {
VStack{
List{
ForEach (items){item in
switch item.onClick {
//For SettingsOverviewView
case .Settings:
ZStack{
NavigationLink (destination: SettingsMenuView(item: item)){
EmptyView()
}
.opacity(0.0)
.buttonStyle(PlainButtonStyle())
//some views that you will show as your listItem
HStack {
Text(item.name)
.font(.body)
Spacer()
}
}
}
}
.listStyle(GroupedListStyle())
}
}
}
A lot of examples playing around with ZStack and .opacity but for my opinion SwiftUI can offer more elegant solution using NavigationLink with isActive parameter that works perfect with .listRowSeparator or .listStyle modificators:
struct HidingNavArrowInList: View {
let planets = ["Mars", "Sun", "Mercury", "Venus", "Jupiter", "Uranus", "Saturn", "Earth"]
#State var selectedPlanet: String?
#State var showDetailView = false
var body: some View {
NavigationView {
List {
ForEach(planets, id: \.self) { planet in
Text(planet)
.onTapGesture {
segue(planet: planet)
}
}
}
.background(
NavigationLink(isActive: $showDetailView, destination: {
if let unwrappedPlanet = selectedPlanet {
VStack {
Text("This is detail view of \(unwrappedPlanet)")
}
}
}, label: {
EmptyView()
})
)
}
}
private func segue(planet: String) {
selectedPlanet = planet
showDetailView.toggle()
}
}
I've also struggled with this recently and I think I've found a solution by using a custom view for the navigation link (it works for me):
struct CustomNavigationLink<D: View, L: View>: View {
#ViewBuilder var destination: () -> D
#ViewBuilder var label: () -> L
#State private var isActive = false
var body: some View {
Button {
withAnimation {
isActive = true
}
} label: {
label()
}
.onAppear {
isActive = false
}
.overlay {
NavigationLink(isActive: $isActive) {
destination()
} label: {
EmptyView()
}
.opacity(0)
}
}
}
And you use like this:
CustomNavigationLink {
SomeViewHere()
} label: {
Text("hello world")
}
2023 Update
This simple solution works for me:
ZStack {
CustomCell()
NavigationLink(destination: DetailView()) {
EmptyView()
}
.opacity(0)
}
You can also do like:
This worked for me,
#State var boolValue: Bool = false
HStack {
Text("Your text")
Toggle(isOn: $boolValue){
Text("")
}
if boolValue {
NavigationLink(destination: DestinationView()) {
EmptyView()
}.frame(width: 0)
}
}
It also works with any View (not only Text)
ZStack {
Text("Some text")
NavigationLink(destination: Text("Hello")) {
EmptyView()
}.frame(width: 0)
}
I set the opacity of the navigationLink to zero and it work like a charm
NavigationLink(
destination: Text("Destination"),
label: {}
).opacity(0)

SwiftUI NavigationButton without the disclosure indicator?

When making a List with a row that pushes to a new view, SwiftUI adds a disclosure indicator ">" automatically? How do I remove it if I don't want it?
NavigationView {
List {
NavigationButton(destination: DetailView()) {
ListItem()
}
}
.navigationBarTitle(Text("Some title"))
}
On a UITableViewCell you set Accessory to None but how do I do that in SwiftUI?
Setting the NavigationLink width and hiding it did the trick for me
List {
ForEach(pages) { page in
HStack(spacing: 0) {
Text("Something")
NavigationLink(destination: Text("Somewhere")) {
EmptyView()
}
.frame(width: 0)
.opacity(0)
}
}
}
Swift 5, Xcode 11. ZStack works perfect.
var body: some View {
NavigationView {
List {
ForEach(viewModel.currenciesViewModel) { cellViewModel in
ZStack {
cellViewModel.makeView()
NavigationLink(destination: ChooseCurrencyListView()) {
EmptyView()
}
.buttonStyle(PlainButtonStyle())
}
}
}
.navigationBarHidden(true)
.navigationBarTitle("", displayMode: .inline)
}
}
The easiest one. The content for each item in the list.
ZStack {
NavigationLink(destination: DetailView()) {
EmptyView()
}.hidden()
RowView()
}
As workaround I can suggest to add .padding modifier like this:
NavigationView {
List {
NavigationButton(destination: DetailView()) {
ListItem()
}
}
.navigationBarTitle(Text("Some title"))
}
.padding(.trailing, -32.0)
So you will get rows without visible disclosure:
You can also put it in the .background modifier:
List {
Text("Go to...")
.background(NavigationLink("", destination: Text("Detail View")))
}
If you already have the background modifier on the Text, you can wrap the Text in a HStack and apply background to the HStack.
What you can do, if you are using list, is setting the navigationlink to hidden and its frame width to zero.
HStack{
Button(action: {self.statusShow = 1}, label: {
Image(systemName: "info.circle")
})
NavigationLink(destination: StimulatorSettingView(),
tag: 1,
selection: self.$statusShow){
EmptyView()
}.hidden().frame(width: 0)
}
This worked for me.
As of beta 6, this works well:
struct SwiftUIView: View {
var body: some View {
NavigationView {
List {
HStack {
Text("My Cell Content")
NavigationLink(destination: Text("destination"), label: {
EmptyView()
})
}
}
}
}
}
You don't have to use NavigationLink to wrap your Label directly. It will work as long as the link is anywhere in your view hierarchy.
Here I've wrapped it in a button, which allows you to trigger an action prior to pushing the view. Since the NavigationLink has an EmptyView for the label the disclosure indicator is not visible. You can also style this with ButtonStyle.
struct NavigationButton<Destination: View, Label: View>: View {
var action: () -> Void = { }
var destination: () -> Destination
var label: () -> Label
#State private var isActive: Bool = false
var body: some View {
Button(action: {
self.action()
self.isActive.toggle()
}) {
self.label()
.background(NavigationLink(destination: self.destination(), isActive: self.$isActive) {
EmptyView()
})
}
}
}
And to use it:
NavigationButton(
action: { print("tapped!") },
destination: { Text("Pushed View") },
label: { Text("Tap me") }
)
NavigationLink is what we should define in a scope enclosed inside a NavigationView.
But when we use NavigationLink it is attached to the enclosing view, so to reuse the same NavigationLink with other views, we use tag which differentiates between different Destinations.
struct SwiftUIView: View {
#State private var viewState: Int? = 0
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("View 1"), tag: 1, selection: $viewState) {
EmptyView()
}
NavigationLink(destination: Text("View 2"), tag: 2, selection: $viewState) {
EmptyView()
}
Text("First View")
.onTapGesture {
self.viewState = 1
}
Text("Second View")
.onTapGesture {
self.viewState = 2
}
}
}
}
}
Here we bind a Hashable property with all the NavigationLinks present in our VStack so that when a particular View is tapped we can notify which Destination should be opened by setting the value of Bindable property.
If we don't notify the correct Destination by setting the value of tag, always the View defined inside the Closure of NavigationLink will be clickable and nothing else.
Using this approach you don't need to wrap all your clickable views inside NavigationView, any action on any view can use any NavigationLink just by setting the tag.
Thanks, hope this helps.
Works well for me!
import SwiftUI
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
EmptyView()
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
ForEach(["iPhone SE", "iPhone 11 Pro Max"], id: \.self) { deviceName in
LandmarkList()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName)
}
}
}
Use .frame(width: 0).opacity(0.0):
NavigationView {
List {
ForEach(options) {
option in
ZStack {
YourView(option: option)
NavigationLink(destination: ProductListView(),
label: {
EmptyView()
}).frame(width: 0).opacity(0.0)
}.listRowInsets(EdgeInsets())
}
}.navigationBarHidden(true)
}
My version of this solution is to make a view modifier. I think it's the cleanest way, as it doesn't use AnyView.
Note that this solution runs the init() for the destination when it draws the element the .navigationLink() is attached to.
Usage
Text("Link")
.navigationLink({
// put your destination here
})
How To
import SwiftUI
extension View {
func navigationLink<Destination: View>(_ destination: #escaping () -> Destination) -> some View {
modifier(NavigationLinkModifier(destination: destination))
}
}
fileprivate struct NavigationLinkModifier<Destination: View>: ViewModifier {
#ViewBuilder var destination: () -> Destination
func body(content: Content) -> some View {
content
.background(
NavigationLink(destination: self.destination) { EmptyView() }.opacity(0)
)
}
}
This helps to push and pass the model to the next navigation view controller.
struct ContentView : View {
#State var model = PostListViewModel()
var body: some View {
NavigationView {
List(model.post) { post in
ListCell(listData: post)
}.navigationBarTitle(Text("My Post"))
}
}
}
struct ListCell: View {
var listData: Post
var body: some View {
return NavigationButton(destination: DetailContentView(post: listData)) {
HStack {
ImageRow(model: listData) // Get image
VStack(alignment: .leading) {
Text(listData.login).font(.headline).lineLimit(nil)
Text(listData.url).font(.subheadline).lineLimit(nil)
}.padding(.leading, 10)
}.padding(.init(top: 5, leading: 0, bottom: 5, trailing: 0))
}
}
}
Here's a reusable "plain" navigation link view (i.e. without the chevron disclosure indicator) that can be a drop-in replacement for NavigationLink:
struct PlainNavigationLink<Label, Destination>: View where Label: View, Destination: View {
#ViewBuilder var destination: () -> Destination
#ViewBuilder var label: () -> Label
var body: some View {
label()
.background(
NavigationLink(destination: destination, label: {})
.opacity(0)
)
}
}
To use it, simply replace NavigationLink with PlainNavigationLink:
NavigationView { // or NavigationStack in iOS 16
List {
ForEach(1...30, id: \.self) { _ in
PlainNavigationLink {
Text("Hello, world!")
} label: {
Text("Hello, world!")
}
}
}
}
We can also extend it with convenience initializers for LocalizedStringKey and String, just like NavigationLink does.
just came here looking for the answer to this question, but none of the proposed solutions worked for me (can't have an empty view, because i want to put something in the list row; i'm already messing with the padding (and increasing trailing padding didn't seem to work) ... i was about to give up, and then something occurred to me: what if you crank up the z-index of the list row itself? seemed somewhat unlikely, but i gave it a try and, i'll be damned, it worked! i was so pleasantly surprised, i felt like sharing ...
e.g.:
// in body of your list row view
HStack(alignment: .top, spacing: 0.0) {
// stuff ...
}
.zIndex(9999999999)
If you need children behaviour for List and NavigationLink, without additional discloser in the same time, I want to promote this tricky solution, main point at HStack
var body: some View {
NavigationView {
List(items, children: \.items) { item in
ZStack {
NavigationLink(destination: DetailsView()) {
EmptyView()
}.hidden()
HStack {
RowView(item: item)
Spacer()
}
}
}
}
}
Once you put your button in a scrollview, the disclosure button will be hidden. Just make sure to disable your scroll indicator.
there is no documentation yet, so you can use ScrollView for now
NavigationView {
ScrollView {
ForEach(0...100){ x in
NavigationButton(destination: Text("ss")) {
HStack {
Text(String(x))
Spacer()
}
.padding()
.background(Color.white)
.shadow(radius:1,y:1)
}
}
.frame(width: UIScreen.main.bounds.width - 32)
.padding()
}
}
Removing List and just using ForEach works fine with navigation link. You just have to create your own list row. This works for me
NavigationView {
ForEach(pages) {
page in
NavigationLink(destination: DetailView()) {
ListItem()
}
}
}