Skip to content

Repository files navigation

SplitPaneKit

A flexible UIKit component for creating draggable split-pane interfaces with smooth animations and customizable breakpoints. Perfect for building adaptive layouts with resizable content areas, like email clients, file browsers, or any master-detail interface.

SplitPaneKit Demo


🚀 Installation

Swift Package Manager

  1. In Xcode, choose File ▶ Add Packages…
  2. Enter this repository URL:
    https://github.com/jsmmth/SplitPaneKit.git
    
  3. Click Add Package, select your target(s), and finish.

Package.swift

If you manage dependencies via a manifest:

// swift-tools-version:5.9
import PackageDescription
letpackage=Package(
name:"MyApp",
platforms:[.iOS(.v13)],
dependencies:[.package(url:"https://github.com/jsmmth/SplitPaneKit.git", from:"0.0.1"),],
targets:[.target(
name:"MyApp",
dependencies:[.product(name:"SplitPaneView",package:"SplitPaneView")])])

Then in your code:

import SplitPaneKit

🎨 Core Concepts

SplitPaneKit

The main container that manages two content panes with a draggable divider between them. The bottom pane can be dragged to different predefined heights (breakpoints).

// Create with default configuration
letsplitPane=SplitPaneView()
// Or with custom configuration
letconfig=SplitPaneConfiguration(
cornerRadius:20,
handleColor:.systemGray3,
defaultBreakpoint:.half
)letsplitPane=SplitPaneView(configuration: config)

SplitPaneContentView

Base class for content views that can be placed in either pane:

classMyContentView:SplitPaneContentView{overrideinit(frame:CGRect){
super.init(frame: frame)
// Setup your UI
}
// Optional: Provide a scroll view for dismissal handling
overridevardismissalHandlingScrollView:UIScrollView?{return myTableView
}}

Breakpoints

Define the possible positions for the split divider:

// Use predefined breakpoints
splitPane.setBreakpoints([.quarter, // 25% height
.half, // 50% height
.threeQuarters // 75% height
])
// Or create custom breakpoints
letcustomBreakpoint=SplitPaneBreakpoint(ratio:0.4, name:"custom")
splitPane.setBreakpoints([.quarter, customBreakpoint,.threeQuarters])

📱 Basic Usage

Setting Up Content

classViewController:UIViewController{overridefunc viewDidLoad(){
super.viewDidLoad()
// Create split pane
letsplitPane=SplitPaneView()
view.addSubview(splitPane)
// Add constraints (full screen)
splitPane.translatesAutoresizingMaskIntoConstraints =falseNSLayoutConstraint.activate([
splitPane.topAnchor.constraint(equalTo: view.topAnchor),
splitPane.leadingAnchor.constraint(equalTo: view.leadingAnchor),
splitPane.trailingAnchor.constraint(equalTo: view.trailingAnchor),
splitPane.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
// Set content views
lettopContent=MyTopContentView()letbottomContent=MyBottomContentView()
splitPane.setTopPaneContent(topContent)
splitPane.setBottomPaneContent(bottomContent)
// Configure breakpoints
splitPane.setBreakpoints([.quarter,.half,.threeQuarters])}}

Programmatic Control

// Transition to a specific breakpoint
splitPane.transitionTo(breakpoint:.half, animated:true)
// Get current position
let(progress, current, next)= splitPane.progressBetweenBreakpoints()print("Progress: \(progress) between \(current?.name ??"nil") and \(next?.name ??"nil")")

Responding to Changes

Implement the delegate to respond to user interactions:

classViewController:UIViewController,SplitPaneViewDelegate{overridefunc viewDidLoad(){
super.viewDidLoad()
splitPane.delegate =self}func splitPaneView(_ splitPaneView:SplitPaneView, didTransitionTo breakpoint:SplitPaneBreakpoint){print("Transitioned to: \(breakpoint.name)")}func splitPaneView(_ splitPaneView:SplitPaneView, isDraggingWithTranslation translation:CGPoint, velocity:CGPoint){
// Respond to dragging if needed
}}

🎯 Advanced Features

Pan Effecting Views

Create views that respond to pane height changes for coordinated animations:

classAnimatedHeaderView:UIView,SplitPanePanEffectingView{func splitPaneDidUpdateHeight(_ splitPane:SplitPaneView, height:CGFloat, progress:CGFloat, currentBreakpoint:SplitPaneBreakpoint?, nextBreakpoint:SplitPaneBreakpoint?){
// Animate based on height changes
alpha = progress
transform =CGAffineTransform(scaleX:0.8+(0.2* progress), y:0.8+(0.2* progress))}func splitPaneDidTransitionToBreakpoint(_ splitPane:SplitPaneView, breakpoint:SplitPaneBreakpoint){
// Final state after transition
print("Settled at: \(breakpoint.name)")}}
// Register the view
splitPane.addPanEffectingView(animatedHeader)

Scroll View Integration

When using scroll views in the bottom pane, the split pane automatically handles the interaction between scrolling and dragging:

classBottomListView:SplitPaneContentView{privatelettableView=UITableView()overridevardismissalHandlingScrollView:UIScrollView?{return tableView // Enable drag-to-dismiss when at top of scroll
}}

⚙️ Configuration

Customize every aspect via SplitPaneConfiguration:

PropertyDescriptionDefault
handleSizeSize of the drag handleCGSize(width: 56, height: 4)
handleColorColor of the drag handle.label.withAlphaComponent(0.08)
handleSpacingSpace between panes20
cornerRadiusCorner radius of panes40
bottomPaneBackgroundColorBackground color of bottom pane.secondarySystemGroupedBackground
topPaneBackgroundColorBackground color of top pane.secondarySystemGroupedBackground
animationSettingsSpring animation configuration.default
topPaneShadowShadow configuration for top pane.default
bottomPaneShadowShadow configuration for bottom pane.default
dragThresholdMinimum drag distance to trigger transition70
velocityThresholdMinimum velocity to trigger transition300
defaultBreakpointInitial breakpoint.quarter
rubberBandingStrengthRubber banding effect strength (0-1)0.7
hapticFeedbackStyleHaptic feedback on transitions.light

Animation Settings

Fine-tune animations with SplitPaneAnimationSettings:

letfastAnimation=SplitPaneAnimationSettings(
duration:0.25,
damping:0.9,
initialVelocity:0.8)varconfig=SplitPaneConfiguration.default
config.animationSettings = fastAnimation

Shadow Configuration

Add depth with PaneShadowConfiguration:

letshadow=PaneShadowConfiguration(
color:.black,
opacity:0.15,
radius:12,
offset:CGSize(width:0, height:-2))varconfig=SplitPaneConfiguration.default
config.bottomPaneShadow = shadow
config.topPaneShadow =.none // No shadow

🛠 Example Project

Check out the Examples folder for complete working demos including:

  • Basic split pane setup
  • Custom animations with pan effecting views

About

Extendable split pane system

Resources

Stars

112 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages