Skip to content

Instantly share code, notes, and snippets.

@ericlewis
Last active February 11, 2022 00:13
Show Gist options
  • Save ericlewis/5898a13bab5c80f65bce92cb89109ca7 to your computer and use it in GitHub Desktop.
Save ericlewis/5898a13bab5c80f65bce92cb89109ca7 to your computer and use it in GitHub Desktop.
Customize the separator insets on lists in SwiftUI. Only tested on iOS 15. Does not work on SidebarListStyles or interface idioms that aren't pad / phone.
struct ContentView: View {
var body: some View {
List {
ForEach(0..<100) { index in
Text("Index: \(index)")
.listRowSeparatorInsets(
.init(
top: 0,
left: CGFloat(index) * 5,
bottom: 0,
right: 0
)
)
}
}
.listStyle(.insetGrouped)
}
}
extension View {
/// The top and bottom insets are ignored
public func listRowSeparatorInsets(_ insets: UIEdgeInsets) -> some View {
self.modifier(ListRowSeparatorInsetsModifier(insets: insets))
}
}
struct ListRowSeparatorInsetsModifier: ViewModifier {
let insets: UIEdgeInsets
func body(content: Content) -> some View {
content.background {
Representable(insets: insets)
.hidden()
}
}
struct Representable: UIViewRepresentable {
// Forces layoutMargins to take effect.
private static let paddingInset = UIEdgeInsets(top: 0, left: 1, bottom: 0, right: 0)
let insets: UIEdgeInsets
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIView(context: Context) -> some UIView { .init() }
func updateUIView(_ uiView: UIViewType, context: Context) {
guard let cell = context.coordinator.cellCache else {
guard let superview = uiView.superview,
// TODO: a better reflection solution, would be nice to grab the host directly
let host = Mirror(reflecting: superview).descendant("host", "some") as? UIView,
let cell = host.superview?.superview as? UITableViewCell else {
return
}
updateInsets(cell)
context.coordinator.cellCache = cell
return
}
updateInsets(cell)
context.coordinator.cellCache = cell
}
func updateInsets(_ cell: UITableViewCell) {
guard insets != cell.layoutMargins else { return }
if cell.separatorInset != Self.paddingInset {
cell.separatorInset = Self.paddingInset
}
cell.layoutMargins = insets
}
class Coordinator {
var cellCache: UITableViewCell?
}
}
}
@ericlewis
Copy link
Author

Oh, I bet the reason the insets need to be some non-zero amount is because they’re being applied to the table view. So when we set it to something non-zero we are taking control from the table view.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment