How to remove top space of `Form` in SwiftUI?

SwiftUI Form is actually a grouped style UITableView under the hood and that is default tableHeaderView. So you can remove it like this:

iOS 13

struct ContentView: View {
    
    init() { // this can be done in `onAppear` modifier if you need to restore the appereance later on `onDisappear`
        UITableView.appearance().tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: Double.leastNonzeroMagnitude))
    }
    
    var body: some View {
        ,,,
    }
}

iOS 14

Apple is limiting the appearance hack and setting the tableHeaderView's frame is one of them so we need to dig more around by adding these steps:

1. Use constraint instead of frame:

init() {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.heightAnchor.constraint(equalToConstant: 0).isActive = true
    UITableView.appearance().tableHeaderView = view
}

2. Force reload the table:

The constraint is added but it needs a reload. SwiftUI does not allow you to reload a list manually. So we need to add a dummy view and a dummy state to trick it:

@State var loaded = false // need for stay update
,,,

    List {

        if loaded {
            Text("Actual content of the list")

        } else {
            Text("Dummy content. Not important")
                .onAppear {
                    loaded = true // need for instatnt force update 
                }
        }

    }

Note1:

Try never hard-code any numbers (like -35 for inset!). Numbers vary on different devices and platforms and states and etc.

Note2:

.zero frame is not working. You should pass at least a minimum height if you use frame for iOS 13 or UIKit.

Note3:

All methods are hacks and Apple is trying to restrict access to underlying types like UITableView. So stay up to date and try to help the community.

Note4:

Contribute to the community by upvoting and commenting. So people will be more motivated to dig in unknown places and bring us cool stuff.

Note5:

Since we are using the Appearance proxy, any change will be applied to all TableViews in the app. You can store the state of the original Appearance -> apply the new style onAppear and restore the original onDisaper if needed.


if you use Introspect add this to your NavigationView

.introspectTableView {
    $0.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: Double.leastNonzeroMagnitude))
}

Setting the table header view is no longer working for me in Xcode 12. Instead, setting a negative top content inset seems to achieve the same thing.

UITableView.appearance().contentInset.top = -35

Tags:

Swiftui