I am trying to create a simple list view in SwiftUI however, it shows up as blank (pictured below) no matter how many objects are in the array. If I get rid of the list, it will show the elements of the array. I know for a fact that the array is not empty. Any recommendations on how to fix it.
This is my code"
var body: some View {
NavigationView {
ScrollView {
if(leaderboard.isEmpty) {
VStack {
Text("No Entries Yet :(")
.foregroundColor(.gray)
.font(.largeTitle)
}
} else {
ScrollView {
List {
ForEach(leaderboard.asArray(), id: \.self) { score in
Text("\(score)")
}
}
}
}
}
.navigationBarTitle("Leaderboard")
}
}
Here is what the view currently shows:
Also, bonus points if you can help me make it so the Text("No Entries Yet :( ") is centered vertically within the view.
You don't need either of those ScrollViews - If the list is empty you only have the text view. If the list isn't empty, List provides the necessary scroll view.
To vertically centre the text when the list is empty, simply add Spacer views above and below:
struct ContentView: View {
var body: some View {
let leaderboard:[Int] = [10,20,30,40,100,1999,1393,444]
NavigationView {
if(leaderboard.isEmpty) {
VStack {
Spacer()
Text("No Entries Yet :(")
.foregroundColor(.gray)
.font(.largeTitle)
Spacer()
}
} else {
List {
ForEach(leaderboard, id: \.self) { score in
Text("\(score)")
}
}
}
}
.navigationBarTitle("Leaderboard")
}
}
If your list is empty, just add another row without background. Stylish it as you want.
if rows.isEmpty {
Text("No records")
.listRowBackground(EmptyView())
}
Related
I have this simple view:
struct ContentView: View {
let items = [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
var body: some View {
List {
ForEach(items, id:\.self) { i in
Text("item \(i)")
.frame(height:200)
.onAppear {
print("A: \(i)")
}
.onDisappear {
print("D: \(i)")
}
}
}
}
}
I would like to know when the cell's rect is fully visible. As is, onAppear triggers as soon as the cell comes in the view as I scroll. However, I'd like to get a hook that tells me when the cell is fully visible.
How could I do that in SwiftUI?
Screen shot of white space
I want to remove the empty space below the <Back button in the second navigation view. I know that this question has been asked several times before, but I have not been able to find a good solution that does this.
I have tried
.navigationBarTitle("")
.navigationBarHidden(true)
and
.navigationBarTitle("", displayMode: .inline)
without the desired result.
Any hints that could help me?
struct SecondNavView: View {
let item: String
var body: some View {
ZStack {
Color.red
Text(item)
}
}
}
struct FirstNavView: View {
let listItems = ["One", "Two", "Three"]
var body: some View {
NavigationView {
List(listItems, id: \.self) { item in
NavigationLink(destination: SecondNavView(item: item)) {
Text(item).font(.headline)
}
}
}
}
}
I assume it is do to place of applied modifiers.
The following works (tested with Xcode 13.4 / iOS 15.5)
struct SecondNavView: View {
let item: String
var body: some View {
ZStack {
Color.red
Text(item)
}
.navigationBarTitleDisplayMode(.inline) // << here !!
}
}
It seens like your parent View hasn't a title, to solve this you need to set .navigationTitle inside NavigationView on parent View like this:
NavigationView {
VStack {
//....
}
.navigationTitle(Text("Awesome View"))
.toolbar {
ToolbarItem(placement: .principal){
// Put any view (Text, Image, Stack...) you want here
}
}
}
I want to hide my NavigationBar while scrolling, actually It must hide automatically but when I tried with multiple views It doesn't work. Also, It works when I remove custom views and capsulate List with NavigationView. But I need SearchBar and StatusView view. Is there any suggestion?
By the way, I run it on the device, I use canvas here for demonstration purposes.
Thank you.
var body: some View {
NavigationView {
VStack(spacing: 0) {
SearchBar(searchText: $viewModel.searchText)
StatusView(status: $viewModel.status)
Divider()
List(0...viewModel.characters.results.count, id: \.self) { index in
if index == self.viewModel.characters.results.count {
LastCell(vm: self.viewModel)
} else {
ZStack {
NavigationLink(destination: DetailView(detail: self.viewModel.characters.results[index])) {
EmptyView()
}.hidden()
CharacterCell(character: self.viewModel.characters.results[index])
}
}
}
.navigationBarTitle("Characters", displayMode: .large)
}
}
.onAppear {
self.viewModel.getCharacters()
}
}
Just idea, scratchy... try to put your custom views inside List as below (I know it will work, but I'm not sure if autohiding will work)
NavigationView {
List {
SearchBar(searchText: $viewModel.searchText)
StatusView(status: $viewModel.status)
Divider()
ForEach (0...viewModel.characters.results.count, id: \.self) { index in
...
Based on Asperi's solution, I wanted to have the SearchBar and StatusView always visible, i.e. it should stop scrolling after the title has disappeard. You can achieve this with a section header like shown below (just a rough sketch):
NavigationView {
List {
Section(header: {
VStack {
SearchBar...
StatusView....
}
}) {
ForEach...
}
}
}
I want to make my List inside a ScrollView so that I can scroll List rows and headers together.
But I found that List inside ScrollView isn't working. It shows nothing.
I should use both of them.
I should use ScrollView so that I can make my header(image or text) also scrolled when I scroll the rows.
I should use List to use .ondelete() method.
my sample code is below.
#State private var numbers = [1,2,3,4,5,6,7,8,9]
var body: some View {
ScrollView {
Text("header")
List {
ForEach(numbers, id: \.self) {
Text("\($0)")
}
.onDelete { index in
// delete item
}
}
}
}
Anyone know why this happens and(or) how to fix?
It is possible but not when the List is using the full screen.
In the code example I used GeometryReader to make the list as big as possible. But you can also remove the GeometryReader and just insert a fixed dimension into .frame()
struct ContentView: View {
#State private var numbers = [1,2,3,4,5,6,7,8,9]
var body: some View {
GeometryReader { g in
ScrollView {
Text("header")
List {
ForEach(self.numbers, id: \.self) {
Text("\($0)")
}
.onDelete { index in
// delete item
}
}.frame(width: g.size.width - 5, height: g.size.height - 50, alignment: .center)
}
}
}
}
There is no need for two scrolling objects. You can also use section for this:
#State private var numbers = [1,2,3,4,5,6,7,8,9]
var body: some View {
List {
Section.init {
Text("Header")
}
ForEach(numbers, id: \.self) {
Text("\($0)")
}
.onDelete { index in
// delete item
}
}
}
Just put header inside the List, like
var body: some View {
List {
Text("Header").font(.title)
ForEach(numbers, id: \.self) {
Text("\($0)")
}
.onDelete { index in
// delete item
}
}
}
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
}
I want to display a message at the centre of the view when the list view (table view) is empty. What is the best way to achieve this in SwiftUI. Is checking for the data source count in "onAppear" and setting some sort of Bool value the correct approach ?
struct LandmarkList: View {
var body: some View {
NavigationView {
if landmarkData.count == 0 {
VStack {
Text("is empty")
} else {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
}
}
I would simply try an if else statement. If landmarkdata is nil or count = 0, show a text. Else show the list.
If you want your message to have the same behavior as a list item, use ScrollView and GeometryReader.
Embed the condition state in the Group, so that the NavigationView is displayed in any case.
Don't forget to change the background color and ignore the safe area, for more resemblance to a list.
struct LandmarkList: View {
var body: some View {
NavigationView {
Group {
if landmarkData.isEmpty {
GeometryReader { geometry in
ScrollView {
Text("Your message!")
.multilineTextAlignment(.center)
.padding()
.position(x: geometry.size.width/2, y: geometry.size.height/2)
}
}
.background(Color(UIColor.systemGroupedBackground))
} else {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
.ignoresSafeArea()
.navigationTitle("Title")
}
}
}
You can also use #ViewBuilder to provide an empty state view in the list, as suggested in this answer.