SwiftUI - Navigation View title is overlapping list - ios

I have a simple list with a navigation view, where the navigation view is overlapping the list while scrolling.
Here is the look I want.
Here is what I am getting with the overlap
Here is the code
struct MedicalDashboard: View {
let menuItemData = MenuItemList()
var body: some View {
NavigationView {
List(menuItemData.items, id: \.id) { item in
MenuItemRow(menuItem: item)
}
.listStyle(.insetGrouped)
.navigationTitle("Dashboard")
.navigationBarItems(trailing:
Button(action: {
// TODO: - Pop up a sheet for the settings page.
print("User icon pressed...")
}) {
Image(systemName: "person.crop.circle").imageScale(.large)
}
)
.padding(.top)
}
}
}
when I add padding(.top) the overlap stops but I get a different color background on the navigation

On Xcode 13.4, except a missing }, without the .padding(.top) and with a custom List everything works like a charm for me.
The problem might come from MenuItemList().
I have still updated your code by replacing .navigationBarItems and by adding the sheet for you:
struct MedicalDashboard: View {
#State private var showSheet = false
var body: some View {
NavigationView {
List { // Custom list
Text("Hello")
Text("Salut")
}
.listStyle(.insetGrouped)
.navigationTitle("Dashboard")
.toolbar() { // .navigationBarItems will be deprecated
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showSheet.toggle()
print("User icon pressed.")
}, label: {
Image(systemName: "person.crop.circle")
})
.sheet(isPresented: $showSheet) { /* SettingsView() */ }
}
}
}
} // New
}
Edit your post and show us MenuItemList().

Try this:
Swift
struct MedicalDashboard: View {
init() {
if #available(iOS 15, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
}
}
...
}

Related

SwiftUI - Navigation Link pops out on iPhone, but not in Simulator

I have an app that contains several views with NavigationLinks inside.
The main view looks like this, calling a Toolbar view I have created.
struct CountListView: View {
#StateObject var vm = CountListViewModel()
let navigationBar = HomePageNavigationBar()
var body: some View {
NavigationView {
List {
ForEach(vm.count, id: \.uid) { item in
NavigationLink(destination: CountView(count: item)) {
CountListItemView(name: item.name)
}
}
}
.toolbar {
navigationBar.rightSideOfBar()
navigationBar.leftSideOfBar()
}
.navigationBarTitle("Count")
The navigation bar function that is playing up looks like this
func leftSideOfBar() -> some ToolbarContent {
ToolbarItemGroup(placement: .navigationBarLeading) {
NavigationLink(destination: SettingsView()) {
Label("Settings", systemImage: "gear")
}
}
}
And the SettingsView is as follows:
struct SettingsView: View {
var body: some View {
List {
NavigationLink(destination: NameSettingView()) {
Text("Name")
}
.buttonStyle(PlainButtonStyle())
NavigationLink(destination: PrivacyPolicyView()) {
Text("Privacy Policy")
}
.buttonStyle(PlainButtonStyle())
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
When I open the Privacy Policy View on device, the returns to the SettingsView without any user intervention. But this problem doesn't exist in the simulator.

SwiftUI how to show large navigation title without using a navigation view?

I'm using the below PullToRefreshHack from this SO Answer which works well, but the problem is when I wrap it inside a NavigationView (which I need to be able to show this view's large navigation title) I loose the functionality of the pull to refresh. I'm not sure why though? 🤔 How can I fix this, or remove the NavigationView but still show a large title at the top?
//Usage
var body: some View {
NavigationView {
ScrollView {
PullToRefreshHack(coordinateSpaceName: "pullToRefreshInTrendsView") {
print("user pulled to refresh")
generator.impactOccurred()
self.loadDataForTrendsView()
}
struct PullToRefreshHack: View {
var coordinateSpaceName: String
var onRefresh: ()->Void
#State var needRefresh: Bool = false
var body: some View {
GeometryReader { geo in
if (geo.frame(in: .named(coordinateSpaceName)).midY > 50) {
Spacer()
.onAppear {
needRefresh = true
}
} else if (geo.frame(in: .named(coordinateSpaceName)).maxY < 10) {
Spacer()
.onAppear {
if needRefresh {
needRefresh = false
onRefresh()
}
}
}
HStack {
Spacer()
if needRefresh {
ProgressView()
} else {
Text("")
}
Spacer()
}
.onAppear {
//print("PullToRefreshHack VIEW .onAppear is called")
}
}.padding(.top, -50)
}
}

Hide chevron/arrow on NavigationLink when displaying a view, SwiftUI

I am trying to remove the chevron that appears on the right of the screen with a navigationLink that contains a view. This is my code below:
NavigationView {
List {
NavigationLink(destination: DynamicList()) {
ResultCard()
}
...
}
Other answers on Stack Overflow have recommended using something like the below:
NavigationLink(...)
.opacity(0)
However, this doesn't work in my case since bringing the opacity down to 0 also removes the view that I am trying to display. This is also the case with '.hidden'. I've searched everywhere and the only somewhat working solution I could find was to alter the padding in order to 'push' the chevron off of the side, but this is a poor solution since the 'ResultCard' view will appear wonky/off-centre on different display sizes.
Perhaps it isn't possible to remove the chevron - and if this is the case, is there any other way I can allow the user to tap the 'ResultCard' view and be taken to a new page, that isn't through a navigation link?
I'm banging my head on the wall so any ideas are very much appreciated.
You can use an .overlay on your label view with a NavigationLink with an EmptyView() set as its label:
struct ContentView : View {
var body: some View {
NavigationView {
List {
NavigationLink("Link 1", destination: Text("Hi"))
Text("Test")
.overlay(NavigationLink(destination: Text("Test"), label: {
EmptyView()
}))
}
}
}
}
Update:
Another solution, which seems to work with other types of Views besides Text:
struct ContentView : View {
#State private var linkActive = false
var body: some View {
NavigationView {
List {
NavigationLink("Link 1", destination: Text("Hi"))
Button(action: { linkActive = true }) {
Image(systemName: "pencil")
}.overlay(VStack {
if linkActive {
NavigationLink(destination: Text("Test"), isActive: $linkActive) {
EmptyView()
}.opacity(0)
}
})
}
}
}
}
The Update solution from jnpdx almost worked for me, but it messed up the animation to the next view. Here's what worked for me (is actually simpler than jnpdx's answer):
struct ContentView : View {
#State private var linkActive = false
var body: some View {
NavigationView {
List {
Button(action: { linkActive = true }) {
Image(systemName: "pencil")
}.overlay(
NavigationLink(
isActive: $linkActive,
destination: { Text("Test") },
label: { EmptyView() }
).opacity(0)
)
}
}
}
}
Here are two alternative variations using .background():
// Replicate the iPhone Favorites tab with the info button
// - Compose a button to link from a NavigationView to a next view
// - Use this when you want to hide the navigation chevron decoration
// - and/or to have a button trigger the link
struct NavigationLinkButton<Destination: View, Label: View>: View {
#Binding var selectedID: String?
#State var rowID: String
#ViewBuilder var destination: () -> Destination
#ViewBuilder var label: () -> Label
var body: some View {
HStack {
Spacer()
Button {
selectedID = rowID
} label: {
label()
}
.buttonStyle(.plain) // prevent List from processing *all* buttons in the row
.background {
NavigationLink("", tag: rowID, selection: $selectedID) {
destination()
}
.hidden()
}
}
}
}
// Replicate the iOS Spotlight search for contacts with action buttons
// - Compose a list row to link from a NavigationView to a next view
// - Use this when you want to hide the navigation chevron decoration
// - and add action buttons
struct NavigationLinkRowHidingChevron<Destination: View, Label: View>: View {
#Binding var selectedID: String?
#State var rowID: String
#ViewBuilder var destination: () -> Destination
#ViewBuilder var label: () -> Label
var body: some View {
ZStack {
// Transparent button to capture taps across the entire row
Button("") {
selectedID = rowID
}
label()
}
.background {
NavigationLink("", tag: rowID, selection: $selectedID) {
destination()
}
.hidden()
}
}
}
// Example Usages
//
// #State private var selectedID: String?
// #State private var editMode: EditMode = .inactive
//
// ... and then in the body:
// List {
// ForEach(items) { item in
// row(for: item)
// .swipeActions(edge: .leading, allowsFullSwipe: true) {
// ...
// }
// .contextMenu {
// ...
// }
// .onDrag({
// ...
// })
// // Overlay so that a tap on the entire row will work except for these buttons
// .overlay {
// // Hide info button just as with Phone Favorites edit button
// if editMode == .inactive {
// NavigationLinkHidingChevron(selectedID: $selectedID, rowID: item.id) {
// // Destination view such as Detail(for: item)
// } label: {
// // Button to activate nav link such as an ô€…´ button
// }
// }
// }
// }
// .onDelete(perform: deleteItems)
// .onMove(perform: moveItems)
// }
//
// ... or this in the body:
// NavigationLinkHidingChevron(selectedID: $selectedID, rowID: contact.id) {
// // Destination view such as Detail(for: item)
// } label: {
// // Content for the list row
// }
// .contextMenu {
// ...
// }
// .overlay {
// HStack(spacing: 15) {
// // Right-justify the buttons
// Spacer()
// // Buttons that replace the chevron and take precedence over the row link
// }
// }
This worked for me, building on #wristbands's solution and targeting iOS 16 using Xcode 14.1:
struct ContentView : View {
var body: some View {
NavigationStack {
List {
Text("View") // your view here
.overlay {
NavigationLink(destination: { Text("Test") },
label: { EmptyView() })
.opacity(0)
}
}
}
}
}

Segmented style picker alignment in NavigationBarItems

I'm now working on putting segmented style picker inside NavigationBarItems.
Here's the code I'm using:
struct ContentView: View {
let modes = ["temperature", "distance"]
var body: some View {
NavigationView {
ZStack {
...
}
}
.navigationBarItems (leading:
Picker ("Select mode:", selection: $currentMode) {
ForEach (0..<mods.count) {
Text(self.mods[$0])
}
}
.pickerStyle(SegmentedPickerStyle())
)
}
}
}
If I use leading: the picker is shown on the left, if I use trailing: then the picker is shown on the right. How can I put it in the centre?
Use .toolbar instead, like
ZStack {
Text("Demo")
}
.toolbar {
ToolbarItem(placement: .principal) {
Picker ("Select mode:", selection: $currentMode) {
ForEach (0..<modes.count) {
Text(self.modes[$0])
}
}
.pickerStyle(SegmentedPickerStyle())
}
}

Why background color of List is different while presenting view in SwiftUI?

I am implementing List in Presented view (AddItemView). I want background color same as List in any view.
struct HomeView: View {
#State private var showAddItemView: Bool = false
var body: some View {
NavigationView {
List(0..<9, id: \.self) { i in
Text("Row \(i)")
}
.navigationTitle("Home")
.navigationBarItems(trailing:
Button("Add") {
showAddItemView.toggle()
})
.sheet(isPresented: $showAddItemView) {
AddItemView()
}
}
}
}
struct AddItemView: View {
init(){
UITableView.appearance().backgroundColor = .clear
}
var body: some View {
NavigationView {
List(0..<9, id: \.self) { i in
Text("Row \(i)")
}.background(Color(UIColor.systemGroupedBackground))
.listStyle(InsetGroupedListStyle())
.navigationBarTitle("Add Item View", displayMode: .inline)
}
}
}
Above code is creating simple List with InsetGroupedListStyle. But background colour is different while Presenting view (AddItemView in my case).
I have already tried https://stackoverflow.com/a/58427518/7084910
How to set background color of List in presented view as in any normal list. Red/Yellow/Green can set to List, "BUT" I want same as normal list in HomeView that will work in light & dark mode.
Use this:
var body: some View {
NavigationView {
List(0..<9, id: \.self) { i in
Text("Row \(i)")
}
.colorMultiply(Color.red)
}
}
They think it is better visual representation for .sheet (probably to make it more determinable)...
SwiftUI 2.0
The .fullScreenCover gives what you want. Alternate is to present AddItemView manually using some transition.
.navigationBarItems(trailing:
Button("Add") {
showAddItemView.toggle()
})
.fullScreenCover(isPresented: $showAddItemView) {
AddItemView()
}

Resources