How to remove divider in lastmost List item in SwiftUI? - swiftui

This is my code where I attempt to use NavigationLinks as some sort of menu:
struct ContentView: View {
init() {
UITableView.appearance().tableFooterView = UIView()
}
var body: some View {
NavigationView {
List {
NavigationLink(destination: Text("Test")) {
Text("Link A")
}
NavigationLink(destination: Text("Test")) {
Text("Link B")
}
NavigationLink(destination: Text("Test")) {
Text("Link C")
}
Text("Footer content here")
}
}
}
}
This is what it looks like:
Is there a way I can remove the Divider that appears on the bottom-most item, i.e. the line just below where it says "Footer content here"?

Here is possible solution. Tested with Xcode 11.4 / iOS 13.4
extension View {
func listRowUpperSeparator() -> some View {
self.listRowBackground(
VStack {
Divider().padding(.leading)
Spacer()
})
}
}
struct ContentView: View {
init() {
UITableView.appearance().separatorStyle = .none
}
var body: some View {
NavigationView {
List {
NavigationLink(destination: Text("Test")) {
Text("Link A")
}.listRowUpperSeparator()
NavigationLink(destination: Text("Test")) {
Text("Link B")
}.listRowUpperSeparator()
NavigationLink(destination: Text("Test")) {
Text("Link C")
}.listRowUpperSeparator()
Text("Footer content here")
.listRowUpperSeparator()
}
}
}
}

Related

SwiftUI - WatchOS - NavigationView - Layout issues

I am writing a simple watchOS app using SwiftUI but I am having issues to proper layout it.
This is the code:
struct ContentView: View {
var body: some View {
VStack(spacing: 0) {
HeaderView()
NavigationView {
ScrollView {
NavigationLink(destination: View1()) {
Text("View 1")
}
NavigationLink(destination: View2()) {
Text("View 2")
}
NavigationLink(destination: View2()) {
Text("View 3")
}
NavigationLink(destination: View2()) {
Text("View 4")
}
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct HeaderView: View {
var body: some View {
VStack() {
Text("Header")
}
.frame(maxWidth: .infinity)
.background(Color.red)
}
}
struct View2: View {
var body: some View {
VStack {
Text("View 2 Content")
}
.navigationTitle("View 2")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("+") {
}
}
}
}
}
Issues:
I cannot rid of this unused space
No matter what the "+" button shows up below the toolbar. I would like to have it in the shown position

Hiding the chevron for navigation link [duplicate]

How to delete Navigation Link > symbol in SwiftUI?
Navigation code
NavigationView{
List{
ForEach(messages) { item in
NavigationLink(destination: MessageDetailView()){
ChatRowView(chat: item)
.padding(.vertical,3)
}
}
}
Chevron image of link is injected by List for automatically detected NavigationLink. This is default behavior of List.
The possible solution is to replace NavigationLink inside List with Button and activate NavigationLink programmatically.
Here is a demo of approach. Tested with Xcode 12.4 / iOS 14.4
struct Message: Identifiable, Hashable {
let id: String
}
struct ContentView: View {
let messages = [Message(id: "1"), Message(id: "2"), Message(id: "3")]
#State private var tappedItem: Message?
var body: some View {
NavigationView{
List{
ForEach(messages) { item in
Button(action: { tappedItem = item }) { // << activate !!
Text("Message \(item.id)")
.padding(.vertical,3)
}
}
}
.background(
NavigationLink(destination: Text("MessageDetailView \(tappedItem?.id ?? "")"),
isActive: Binding(
get: { tappedItem != nil }, // << handle !!
set: { _,_ in tappedItem = nil }
)){
EmptyView()
}
)
}
}
}
You should not use List in this case:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
VStack {
ForEach(0..<10) { item in
NavigationLink(destination: Text("some text here!")){
HStack { Text("Link " + item.description); Spacer() }.padding(.horizontal)
}
Divider()
}
Spacer()
}
.navigationTitle("Hello World!")
}
}
}

How to close Half sheet in this situation?

I want to close the half sheet and back to the root view In swiftUI when with navigationLink we go to the another view. dismiss() doesn't work and turn us back to the previous view not the root view.
struct ContentView: View {
#State var showFirstSheetView = false
var body: some View {
NavigationView {
VStack {
Text("half sheet")
.onTapGesture {
showFirstSheetView.toggle()
}
}
.navigationTitle("Root view")
.sheet(isPresented: $showFirstSheetView) {
halfSheet()
}
}
}
}
struct halfSheet : View {
#State var showSecondView = false
var body: some View {
NavigationView {
VStack {
}
.background(
NavigationLink(isActive: $showSecondView, destination: {
SecondView()
}, label: {
EmptyView()
})
)
.navigationTitle("First view")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems( trailing: Button(action: {
showSecondView.toggle()
}) {
Text("Next")
}
)
}
}
}
struct SecondView : View {
#Environment(\.dismiss) var dismiss
var body: some View {
VStack {
}
.navigationTitle("Second view")
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action: {
dismiss()
}) {
HStack(spacing : 5) {
Image(systemName: "chevron.backward")
Text("Back")
}
}, trailing: Button(action: {
// How back to the root View?
// below code works but not compatible with ios 15 and gives a warning
// UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true)
}) {
Text("Done")
})
}
}

How to delete Navigation Link > symbol in SwiftUI?

How to delete Navigation Link > symbol in SwiftUI?
Navigation code
NavigationView{
List{
ForEach(messages) { item in
NavigationLink(destination: MessageDetailView()){
ChatRowView(chat: item)
.padding(.vertical,3)
}
}
}
Chevron image of link is injected by List for automatically detected NavigationLink. This is default behavior of List.
The possible solution is to replace NavigationLink inside List with Button and activate NavigationLink programmatically.
Here is a demo of approach. Tested with Xcode 12.4 / iOS 14.4
struct Message: Identifiable, Hashable {
let id: String
}
struct ContentView: View {
let messages = [Message(id: "1"), Message(id: "2"), Message(id: "3")]
#State private var tappedItem: Message?
var body: some View {
NavigationView{
List{
ForEach(messages) { item in
Button(action: { tappedItem = item }) { // << activate !!
Text("Message \(item.id)")
.padding(.vertical,3)
}
}
}
.background(
NavigationLink(destination: Text("MessageDetailView \(tappedItem?.id ?? "")"),
isActive: Binding(
get: { tappedItem != nil }, // << handle !!
set: { _,_ in tappedItem = nil }
)){
EmptyView()
}
)
}
}
}
You should not use List in this case:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
VStack {
ForEach(0..<10) { item in
NavigationLink(destination: Text("some text here!")){
HStack { Text("Link " + item.description); Spacer() }.padding(.horizontal)
}
Divider()
}
Spacer()
}
.navigationTitle("Hello World!")
}
}
}

SwiftUI Nested NavigationView navigationBar disappears

I have a three views which are lists. struct MainMenuView: View {
#EnvironmentObject var dataModel: DM
var body: some View {
return NavigationView{
List {
Matchup()
GameSettings()
EnteringGame()
}
}
}
Inside Matchup()
struct Matchup: View {
#EnvironmentObject var dataModel: DM
var body: some View {
Section(header: Text("MATCH-UP")
.fontWeight(.heavy)
.foregroundColor(Color("TPLightGrey"))
) {
NavigationLink(destination: TrendSingleSelect(
title: .constant("TEAM"),
col: .constant(self.dataModel.queryColumnTeam1),
items: .constant(self.dataModel.team1Values) ,
selection: self.$dataModel.team1ListValue
)) {
HStack {
Text("TEAM")
Spacer()
if dataModel.team1ListValue.count == 0 {
Text("IS ANY").foregroundColor(Color("TPLightGrey"))
} else {
Text( self.dataModel.team1ListValue.joined(separator: ", ")).foregroundColor(Color("TPOrange"))
}
}
}
}
.listRowBackground(Color("TPDarkGrey"))
.font(.system(size: 14))
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}
Notice that I hide theNavBar. I want to push in a nav when the user tabs a row.: Here is the final view:
var body: some View {
return VStack {
List {
ForEach(self.items, id: \.self) { item in
SingleSelectionRow(title: item, isSelected: self.selection.contains(item)) {
if self.selection.contains(item) {
self.selection = []
}
else {
self.selection = [item]
}
self.queryCallback()
}
.listRowBackground(Color("TPDarkGrey"))
}//ForEach
}//list
.font(.system(size: 14))
}
.navigationBarHidden(false)
.navigationBarTitle(title)
.navigationBarItems(trailing:
Button(action: {
// Actions
self.reset()
}, label: {
Text("Clear")
}
)
)
}
What happens is: That when I tap the sell, I push in that section. However, when it pushes in, I see the navBar, then it gets collapsed. However,when I then tap anything in the view to trigger the view reload, it shows up.
What is causing the navbar collapse?
try this code in MatchupView:
struct Matchup: View {
#EnvironmentObject var dataModel: DM
var body: some View {
NavigationView { // attention hear************
Section(header: Text("MATCH-UP")
.fontWeight(.heavy)
.foregroundColor(Color("TPLightGrey"))
) {
NavigationLink(destination: TrendSingleSelect(
title: .constant("TEAM"),
col: .constant(self.dataModel.queryColumnTeam1),
items: .constant(self.dataModel.team1Values) ,
selection: self.$dataModel.team1ListValue
)) {
HStack {
Text("TEAM")
Spacer()
if dataModel.team1ListValue.count == 0 {
Text("IS ANY").foregroundColor(Color("TPLightGrey"))
} else {
Text( self.dataModel.team1ListValue.joined(separator: ", ")).foregroundColor(Color("TPOrange"))
}
}
}
}
.listRowBackground(Color("TPDarkGrey"))
.font(.system(size: 14))
} // attention hear************
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
I couldn't compile your project, so I assume the following solution:
You can bind navigationBarHidden to variable, so that you can change the value under certain conditions. Like this: .navigationBarHidden($onOff)
struct ContentView: View {
#State var onOff = false
var body: some View {
NavigationView {
Button("Button") {
self.onOff.toggle()
}
.navigationBarTitle(Text("Events"), displayMode: .inline)
.navigationBarHidden($onOff.wrappedValue)
}
// that means only show one view at a time no matter what device I'm working
.navigationViewStyle(StackNavigationViewStyle())
}
}
Arrrgh... this was unnecessary: .navigationBarHidden(true) in MatchupView