import SwiftUI struct SplitView: View { let direction: Direction let left: L let right: R private let splitterVisibleSize: CGFloat = 5 private let splitterInvisibleSize: CGFloat = 5 @State var split: CGFloat = 0.5 var body: some View { GeometryReader { geo in let leftRect = self.leftRect(for: geo.size) let rightRect = self.rightRect(for: geo.size, leftRect: leftRect) let splitterPoint = self.splitterPoint(for: geo.size, leftRect: leftRect) ZStack(alignment: .topLeading) { left .frame(width: leftRect.size.width, height: leftRect.size.height) .offset(x: leftRect.origin.x, y: leftRect.origin.y) right .frame(width: rightRect.size.width, height: rightRect.size.height) .offset(x: rightRect.origin.x, y: rightRect.origin.y) Splitter(direction: direction, visibleSize: splitterVisibleSize, invisibleSize: splitterInvisibleSize) .position(splitterPoint) } } } func leftRect(for size: CGSize) -> CGRect { // Initially the rect is the full size var result = CGRect(x: 0, y: 0, width: size.width, height: size.height) switch (direction) { case .horizontal: result.size.width = result.size.width * split result.size.width -= splitterVisibleSize / 2 case .vertical: result.size.height = result.size.height * split result.size.height -= splitterVisibleSize / 2 } return result } func rightRect(for size: CGSize, leftRect: CGRect) -> CGRect { // Initially the rect is the full size var result = CGRect(x: 0, y: 0, width: size.width, height: size.height) switch (direction) { case .horizontal: // For horizontal layouts we offset the starting X by the left rect // and make the width fit the remaining space. result.origin.x += leftRect.size.width result.origin.x += splitterVisibleSize / 2 result.size.width -= result.origin.x case .vertical: assert(false) } return result } func splitterPoint(for size: CGSize, leftRect: CGRect) -> CGPoint { switch (direction) { case .horizontal: return CGPoint(x: leftRect.size.width, y: size.height / 2) case .vertical: return CGPoint(x: size.width / 2, y: leftRect.size.height) } } init(_ direction: Direction, @ViewBuilder left: (() -> L), @ViewBuilder right: (() -> R)) { self.direction = direction self.left = left() self.right = right() } } extension SplitView { enum Direction { case horizontal, vertical } }