Last active
February 11, 2022 00:13
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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? | |
| } | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.