Swift UI Remove Extra List Empty Cells Without Navigation Controller

Swift UI Remove Extra List Empty Cells without Navigation Controller

They are not empty cells. The List just fills all available space and draws empty rows placeholders where there is no content. In your scenario is needed to limit height of List to its content.

Here is a solution. Tested with Xcode 11.4 / iOS 13.4

For better visibility let's separate your list into own view named CovidDataList, then here is in your provided body:

  //List to Initialize TableView Data
CovidDataList(covid: self.covid)

}//END List

Note1: The height of list updated in run-time so should be tested in running application (not in Preview)

Note2: I tested with replicated cover data model so some adapting might be needed to your code

struct CovidDataList: View {
@Environment(\.defaultMinListRowHeight) var minRowHeight

let covid: [CovidData]
@State private var listHeight = CGFloat.infinity

var body: some View {
List {
ForEach(covid, id: \.self) { covidData in
HStack {
Image(systemName: "photo")
.padding()
HStack {
Text("Title").frame(maxWidth: .infinity, alignment: .leading)

Text("Value")
.font(.subheadline)
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
.listRowInsets(EdgeInsets()).padding(.horizontal)
}
.anchorPreference(key: BoundsPreferenceKey.self, value: .bounds) { [$0] }
}
.backgroundPreferenceValue(BoundsPreferenceKey.self) { rects in
GeometryReader { gp in
self.updateListFrame(gp: gp, anchors: rects)
}
}.frame(maxHeight: listHeight)
}

private func updateListFrame(gp: GeometryProxy, anchors: [Anchor<CGRect>]) -> some View {
let contentHeight = anchors.reduce(CGFloat.zero) { $0 + gp[$1].size.height }
DispatchQueue.main.async { // << required !!
self.listHeight = max(contentHeight, self.minRowHeight * CGFloat(self.covid.count))
}
return Color.clear
}
}

Note: BoundsPreferenceKey is taken from this my answer

Remove empty rows in List Swift UI Xcode 11

struct LandMarkList: View {
var body: some View {
NavigationView {
List(LandMarkListData) { landmark in
LandMarkRow(landmark: landmark)
}

.navigationBarTitle("List")
.listStyle(GroupedListStyle())
}
}
}

Finally i have found the solution by adding the listStyle.

How to remove the line separators from a List in SwiftUI without using ForEach?

iOS 15:

This year Apple introduced a new modifier .listRowSeparator that can be used to style the separators. you can pass .hidden to hide it:

List {
ForEach(items, id:\.self) {
Text("Row \($0)")
.listRowSeparator(.hidden)
}
}


iOS 14:

you may consider using a LazyVStack inside a ScrollView instead (because iOS is NOT supporting UIAppearance for SwiftUI lists anymore).

LazyVStack Preview



iOS 13:

⚠️ This method is deprecated and it's not working from iOS 14

There is a UITableView behind SwiftUI's List for iOS 13. So to remove

Extra separators (below the list):

you need a tableFooterView and to remove

All separators (including the actual ones):

you need separatorStyle to be .none

Example of usage

init() {
if #available(iOS 14.0, *) {
// iOS 14 doesn't have extra separators below the list by default.
} else {
// To remove only extra separators below the list:
UITableView.appearance().tableFooterView = UIView()
}

// To remove all separators including the actual ones:
UITableView.appearance().separatorStyle = .none
}

var body: some View {
List {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}

Note that a static list doesn't show extra separators below the list by default

Eliminate extra separators below UITableView

Interface builder (iOS 9+)

Just drag a UIView to the table. In storyboard, it will sit at the top below your custom cells. You may prefer to name it "footer".

Here it is shown in green for clarity, you'd probably want clear color.

Note that by adjusting the height, you can affect how the "bottom bounce" of the table is handled, as you prefer. (Height zero is usually fine).

enter image description here


To do it programmatically:

Swift

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.tableFooterView = UIView()
}

Objective-C

iOS 6.1+

- (void)viewDidLoad 
{
[super viewDidLoad];

// This will remove extra separators from tableview
self.tableView.tableFooterView = [UIView new];
}

or if you prefer,

    self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];

Historically in iOS:

Add to the table view controller...

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
// This will create a "invisible" footer
return CGFLOAT_MIN;
}

and if necessary...

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
return [UIView new];

// If you are not using ARC:
// return [[UIView new] autorelease];
}

Remove empty space in modified text

try something like this:

func textWithHashtags(_ text: String, color: Color) -> Text {
let words = text.split(separator: " ")
var output: Text = Text("")
var firstWord = true // <-- here

for word in words {
let spacer = Text(firstWord ? "" : " ") // <-- here
if word.hasPrefix("@") { // Pick out hash in words
output = output + spacer + Text(String(word))
.foregroundColor(color) // Add custom styling here
} else {
output = output + spacer + Text(String(word))
}
firstWord = false
}
return output
}

Remove top padding from `List` in SwiftUI

Firstly, I would say that GroupedListStyle is working as intended.

On iOS, the grouped list style displays a larger header and footer
than the plain style, which visually distances the members of
different sections.

You say you have tried this, but it does work for me (Xcode 12.5.1):

    List { ... }    
.onAppear(perform: {
UITableView.appearance().contentInset.top = -35
})

You could also hide the list header, by using a ZStack with the List at the bottom of the stack and the Picker over the top. The Picker does have transparency, so you would also have to add an opaque view to act as background for the Picker.

var body: some View {
NavigationView {
ZStack(alignment: .top) {
List { ... }
.listStyle(.grouped)
.padding(.top, 30)

Color.white
.frame(height: 65)

Picker { ... }
.pickerStyle(.segmented)
.padding()
}
.navigationBarTitle("Add Task", displayMode: .inline)
}
}

enter image description here

As far as I can see this just appears the same as PlainListStyle would do, but I assume you have a particular reason for wanting to use GroupedListStyle.



Related Topics



Leave a reply



Submit