Skip to content

Commit

Permalink
💫 Update: Exp - AdaptiveModal
Browse files Browse the repository at this point in the history
Summary: Update experiment/test - `swift-programmatic-modal/AdaptiveModal`.
  • Loading branch information
dominicstop committed May 28, 2023
1 parent ff6cad9 commit ec5a7e1
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// AdaptiveModalClampingConfig.swift
// swift-programmatic-modal
//
// Created by Dominic Go on 5/28/23.
//

import Foundation

struct AdaptiveModalClampingConfig {
static let `default`: Self = .init();

let shouldClampModalInitHeight: Bool;
let shouldClampModalLastHeight: Bool;

let shouldClampModalInitWidth: Bool;
let shouldClampModalLastWidth: Bool;

let shouldClampModalInitX: Bool;
let shouldClampModalLastX: Bool;

let shouldClampModalInitY: Bool;
let shouldClampModalLastY: Bool;

init(
shouldClampModalInitHeight: Bool = false,
shouldClampModalLastHeight: Bool = false,
shouldClampModalInitWidth: Bool = false,
shouldClampModalLastWidth: Bool = false,
shouldClampModalInitX: Bool = false,
shouldClampModalLastX: Bool = false,
shouldClampModalInitY: Bool = false,
shouldClampModalLastY: Bool = false
) {
self.shouldClampModalInitHeight = shouldClampModalInitHeight;
self.shouldClampModalLastHeight = shouldClampModalLastHeight;
self.shouldClampModalInitWidth = shouldClampModalInitWidth;
self.shouldClampModalLastWidth = shouldClampModalLastWidth;
self.shouldClampModalInitX = shouldClampModalInitX;
self.shouldClampModalLastX = shouldClampModalLastX;
self.shouldClampModalInitY = shouldClampModalInitY;
self.shouldClampModalLastY = shouldClampModalLastY;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import UIKit


struct AdaptiveModalConfig {
enum Direction {
case horizontal;
Expand All @@ -17,32 +16,30 @@ struct AdaptiveModalConfig {
// MARK: - Properties
// ------------------


let snapPoints: [AdaptiveModalSnapPointConfig];
let snapDirection: Direction;

private let _snapAnimationConfig: AdaptiveModalSnapAnimationConfig?;
// let snapPointInitial:

let snapAnimationConfig: AdaptiveModalSnapAnimationConfig;
let interpolationClampingConfig: AdaptiveModalClampingConfig;

// let entranceConfig: AdaptiveModalEntranceConfig;
// let snapSwipeVelocityThreshold: CGFloat = 0;

// MARK: - Computed Properties
// ---------------------------

var snapAnimationConfig: AdaptiveModalSnapAnimationConfig {
self._snapAnimationConfig ?? .default;
};

// MARK: - Init
// ------------

init(
snapPoints: [AdaptiveModalSnapPointConfig],
snapDirection: Direction,
snapAnimationConfig: AdaptiveModalSnapAnimationConfig? = nil
snapAnimationConfig: AdaptiveModalSnapAnimationConfig = .default,
interpolationClampingConfig: AdaptiveModalClampingConfig = .default
) {
self.snapPoints = snapPoints;
self.snapDirection = snapDirection;
self._snapAnimationConfig = snapAnimationConfig;

self.snapAnimationConfig = snapAnimationConfig;
self.interpolationClampingConfig = interpolationClampingConfig;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

import Foundation

struct AdaptiveModalEntranceConfig {
let initialSnapPoint: AdaptiveModalSnapPointPreset;
let initialAnimationKeyFrame: AdaptiveModalAnimationConfig;
struct AdaptiveModalSnapPointPresetConfig {

let snapPoint: AdaptiveModalSnapPointPreset;

// MARK: - Functions - Helpers
// ---------------------------


};
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension AdaptiveModalManager {
return rangeOutput.first!;
};

if shouldClampMax, inputValue >= rangeInput.last! {
if shouldClampMax, inputValue <= rangeInput.last! {
return rangeOutput.last!;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import UIKit

class AdaptiveModalManager {

// MARK: - Properties - Config
// ----------------------------
// MARK: - Properties - Config-Related
// ------------------------------------

var modalConfig: AdaptiveModalConfig;

Expand All @@ -20,8 +20,7 @@ class AdaptiveModalManager {

// MARK: - Properties - Refs/Providers
// ------------------------------------

var targetRectProvider: () -> CGRect;

var currentSizeProvider: () -> CGSize;

weak var targetView: UIView?;
Expand All @@ -32,6 +31,12 @@ class AdaptiveModalManager {
var gestureInitialPoint: CGPoint?;
var gesturePoint: CGPoint?;

// MARK: - Properties
// -------------------

/// The computed frames of the modal based on the snap points
var computedSnapRects: [CGRect]?;

// MARK: - Computed Properties
// ---------------------------

Expand All @@ -49,19 +54,6 @@ class AdaptiveModalManager {
};
};

/// The computed frames of the modal based on the snap points
var computedSnapRects: [CGRect] {
let targetSize = self.targetRectProvider();
let currentSize = self.currentSizeProvider();

return self.modalConfig.snapPoints.map {
$0.snapPoint.computeRect(
withTargetRect: targetSize,
currentSize: currentSize
);
};
};

var currentSnapPointConfig: AdaptiveModalSnapPointConfig {
self.modalConfig.snapPoints[self.currentSnapPointIndex];
};
Expand Down Expand Up @@ -129,15 +121,12 @@ class AdaptiveModalManager {
modalConfig: AdaptiveModalConfig,
modalView: UIView,
targetView: UIView,
targetRectProvider: @escaping () -> CGRect,
currentSizeProvider: @escaping () -> CGSize
){
self.modalConfig = modalConfig;

self.modalView = modalView;
self.targetView = targetView;

self.targetRectProvider = targetRectProvider;
self.currentSizeProvider = currentSizeProvider;
};

Expand Down Expand Up @@ -201,8 +190,8 @@ class AdaptiveModalManager {
snapPointIndex: Int,
snapPointConfig: AdaptiveModalSnapPointConfig,
computedRect: CGRect
) {
let snapRects = self.computedSnapRects;
)? {
guard let snapRects = self.computedSnapRects else { return nil };

let gestureOffset = self.gestureOffset ?? 0;
let gestureCoordAdj = gestureCoord - gestureOffset;
Expand Down Expand Up @@ -241,8 +230,8 @@ class AdaptiveModalManager {
snapPointConfig: AdaptiveModalSnapPointConfig,
computedRect: CGRect,
snapDistance: CGFloat
) {
let snapRects = self.computedSnapRects;
)? {
guard let snapRects = self.computedSnapRects else { return nil };

let delta = snapRects.map {
CGRect(
Expand Down Expand Up @@ -276,13 +265,16 @@ class AdaptiveModalManager {
forGesturePoint gesturePoint: CGPoint
) -> CGRect {

guard let modalView = self.modalView else { return .zero };
guard let modalView = self.modalView,
let targetView = self.targetView,
let computedSnapRects = self.computedSnapRects
else { return .zero };

let targetRect = self.targetRectProvider();
let modalRect = modalView.frame;
let targetRect = targetView.frame;
let modalRect = modalView.frame;

let gestureCoord = gesturePoint[keyPath: self.inputAxisKey];
let snapRects = self.computedSnapRects.reversed();
let snapRects = computedSnapRects.reversed();

let gestureOffset = self.gestureOffset ?? {
let modalCoord = modalRect.origin[keyPath: self.inputAxisKey];
Expand All @@ -296,6 +288,8 @@ class AdaptiveModalManager {
let gestureInput = gestureCoord - gestureOffset;
let rangeInputGesture = snapRects.map { $0.minY };

let clampConfig = modalConfig.interpolationClampingConfig;

print(
"gesturePoint: \(gesturePoint)"
+ "\n" + " - targetRect: \(targetRect)"
Expand All @@ -308,31 +302,39 @@ class AdaptiveModalManager {
let nextHeight = Self.interpolate(
inputValue: gestureInput,
rangeInput: rangeInputGesture,
rangeOutput: snapRects.map { $0.height }
rangeOutput: snapRects.map { $0.height },
shouldClampMin: clampConfig.shouldClampModalLastHeight,
shouldClampMax: clampConfig.shouldClampModalInitHeight
);

print(" - nextHeight: \(nextHeight!)");

let nextWidth = Self.interpolate(
inputValue: gestureInput,
rangeInput: rangeInputGesture,
rangeOutput: snapRects.map { $0.width }
rangeOutput: snapRects.map { $0.width },
shouldClampMin: clampConfig.shouldClampModalLastWidth,
shouldClampMax: clampConfig.shouldClampModalInitWidth
);

print(" - nextWidth: \(nextWidth!)");

let nextX = Self.interpolate(
inputValue: gestureInput,
rangeInput: rangeInputGesture,
rangeOutput: snapRects.map { $0.minX }
rangeOutput: snapRects.map { $0.minX },
shouldClampMin: clampConfig.shouldClampModalLastX,
shouldClampMax: clampConfig.shouldClampModalInitX
);

print(" - nextX: \(nextX!)");

let nextY = Self.interpolate(
inputValue: gestureInput,
rangeInput: rangeInputGesture,
rangeOutput: snapRects.map { $0.minY }
rangeOutput: snapRects.map { $0.minY },
shouldClampMin: clampConfig.shouldClampModalLastY,
shouldClampMax: clampConfig.shouldClampModalInitY
)!;

print(" - nextY: \(nextY)");
Expand All @@ -355,6 +357,17 @@ class AdaptiveModalManager {

// MARK: - User-Invoked Functions
// ------------------------------
func computeSnapPoints(){
guard let targetView = self.targetView else { return };
let currentSize = self.currentSizeProvider();

self.computedSnapRects = self.modalConfig.snapPoints.map {
$0.snapPoint.computeRect(
withTargetRect: targetView.frame,
currentSize : currentSize
);
};
};

func notifyOnDragPanGesture(_ gesture: UIPanGestureRecognizer){
guard let modalView = self.modalView else { return };
Expand All @@ -376,13 +389,17 @@ class AdaptiveModalManager {
};

let gestureFinalPoint = self.gestureFinalPoint ?? gesturePoint;
let gestureFinalCoord = gestureFinalPoint[keyPath: self.inputAxisKey]

let closestSnapPoint = self.getClosestSnapPoint(
forGestureCoord: gestureFinalPoint[keyPath: self.inputAxisKey]
);
guard let closestSnapPoint =
self.getClosestSnapPoint(forGestureCoord: gestureFinalCoord)
else {
self.clearGestureValues();
return;
};

print(
"closestSnapPoint: \(closestSnapPoint.computedRect)"
"closestSnapPoint: \(closestSnapPoint.computedRect)"
+ "\n - gesturePoint: \(gesturePoint)"
+ "\n - gestureFinalPoint: \(gestureFinalPoint)"
);
Expand All @@ -406,7 +423,9 @@ class AdaptiveModalManager {
};

func setFrameForModal(){
guard let modalView = self.modalView else { return };
guard let modalView = self.modalView,
let targetView = self.targetView
else { return };

let computedRect: CGRect = {
if let gesturePoint = self.gesturePoint {
Expand All @@ -418,7 +437,7 @@ class AdaptiveModalManager {
let currentSnapPoint = self.currentSnapPointConfig.snapPoint;

return currentSnapPoint.computeRect(
withTargetRect: self.targetRectProvider(),
withTargetRect: targetView.frame,
currentSize: self.currentSizeProvider()
);
}();
Expand All @@ -427,14 +446,15 @@ class AdaptiveModalManager {
};

func snapToClosestSnapPoint(){
guard let modalView = self.modalView else { return };
let targetRect = self.targetRectProvider();

let closestSnapPoint = self.getClosestSnapPoint(forRect: modalView.frame);
guard let modalView = self.modalView,
let targetView = self.targetView,
let closestSnapPoint =
self.getClosestSnapPoint(forRect: modalView.frame)
else { return };

let interpolatedDuration = Self.interpolate(
inputValue: closestSnapPoint.snapDistance,
rangeInput: [0, targetRect.height],
rangeInput: [0, targetView.frame.height],
rangeOutput: [0.2, 0.7]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct AdaptiveModalSnapAnimationConfig {
static let `default`: Self = .init(
springDampingRatio: 0.9,
springAnimationSettlingTime: 0.4,
maxGestureVelocity: 20
maxGestureVelocity: 15
);

let springDampingRatio: CGFloat;
Expand Down
Loading

0 comments on commit ec5a7e1

Please sign in to comment.