EdgeZoomKit is an iOS Swift Package that displays elastic visual feedback
along the left or right edge during a zoom gesture. Its event and presenter
types are independent from a specific board engine, and it supports UIKit and
SwiftUI without intercepting touches.
- iOS 15 or later
- Swift 6.2 or later
Both examples include edge detection, feedback lifecycle forwarding, visual customization, and an actual scale transform.
Add this repository through Xcode's File > Add Package Dependencies, then import the library:
import EdgeZoomKitKeep a controller alongside the object that handles the zoom gesture:
@MainActor
final class BoardEngine: ObservableObject {
let edgeZoomFeedback = EdgeZoom.Controller()
func zoomBegan(at point: CGPoint, from side: EdgeZoom.Side) {
edgeZoomFeedback.begin(side: side, at: point)
}
func zoomChanged(at point: CGPoint, from side: EdgeZoom.Side) {
edgeZoomFeedback.update(at: point, side: side)
}
func zoomEnded() {
edgeZoomFeedback.end()
}
}Place the feedback view over the zoomable content:
struct BoardView: View {
@StateObject private var engine = BoardEngine()
var body: some View {
ZoomableBoard(engine: engine)
.edgeZoomFeedback(engine.edgeZoomFeedback)
}
}Gesture points must use the overlay's local coordinate system.
Shape and animation are configured separately from appearance. Use a built-in shape preset:
ZoomableBoard(engine: engine)
.edgeZoomFeedback(
engine.edgeZoomFeedback,
configuration: .playful,
appearance: .accent
)Or provide a complete custom configuration:
let configuration = EdgeZoom.Configuration(
curveSize: 180,
maximumWidth: 100,
minimumVisibleWidth: 12,
distanceMultiplier: 1.2,
controlPointRatio: 0.6,
minimumRadiusRatio: 0.4,
springStiffness: 380,
springDamping: 26,
settlingDistance: 0.15,
settlingVelocity: 0.8
)
let appearance = EdgeZoom.Appearance(
fillColor: Color.purple,
opacity: 0.85,
shadowColor: Color.purple,
shadowOpacity: 0.3,
shadowRadius: 10,
shadowOffset: CGSize(width: 2, height: 0)
)
ZoomableBoard(engine: engine)
.edgeZoomFeedback(
engine.edgeZoomFeedback,
configuration: configuration,
appearance: appearance
)Available configuration presets are .default, .subtle, .responsive, and
.playful. Appearance accepts both UIColor and SwiftUI Color.
An individual gesture can override the configured color:
controller.begin(
side: .right,
at: point,
color: .systemOrange
)Omit color to use the overlay's configured appearance.
Add EdgeZoomFeedbackView over the zoomable view and forward gesture events:
let feedbackView = EdgeZoomFeedbackView(
configuration: .responsive,
appearance: EdgeZoom.Appearance(
fillColor: .systemIndigo,
opacity: 0.9
)
)
feedbackView.frame = container.bounds
feedbackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
container.addSubview(feedbackView)
feedbackView.begin(side: .left, at: point)
feedbackView.update(at: nextPoint, side: .left)
feedbackView.end()Gesture logic can depend on EdgeZoomFeedbackPresenting instead of UIKit or
SwiftUI:
@MainActor
final class ZoomGestureEngine {
private let feedback: any EdgeZoomFeedbackPresenting
init(feedback: any EdgeZoomFeedbackPresenting) {
self.feedback = feedback
}
func handle(_ event: EdgeZoom.Event) {
feedback.handle(event)
}
}Events sent before a SwiftUI overlay is mounted are retained by the controller and displayed as soon as the feedback view is attached.
The package renders feedback only. Your gesture or board engine remains responsible for applying the actual zoom transform.