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 Jun 5, 2023
1 parent 7cffd39 commit ec6ad37
Show file tree
Hide file tree
Showing 7 changed files with 404 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ struct AdaptiveModalClampingConfig {
let shouldClampModalInitY: Bool;
let shouldClampModalLastY: Bool;

let shouldClampModalInitRotation: Bool;
let shouldClampModalLastRotation: Bool;

let shouldClampModalInitScaleX: Bool;
let shouldClampModalLastScaleX: Bool;

let shouldClampModalInitScaleY: Bool;
let shouldClampModalLastScaleY: Bool;

let shouldClampModalInitTranslateX: Bool;
let shouldClampModalLastTranslateX: Bool;

let shouldClampModalInitTranslateY: Bool;
let shouldClampModalLastTranslateY: Bool;

init(
shouldClampModalInitHeight: Bool = false,
shouldClampModalLastHeight: Bool = false,
Expand All @@ -30,15 +45,43 @@ struct AdaptiveModalClampingConfig {
shouldClampModalInitX: Bool = false,
shouldClampModalLastX: Bool = false,
shouldClampModalInitY: Bool = false,
shouldClampModalLastY: Bool = false
shouldClampModalLastY: Bool = false,
shouldClampModalInitRotation: Bool = true,
shouldClampModalLastRotation: Bool = true,
shouldClampModalInitScaleX: Bool = true,
shouldClampModalLastScaleX: Bool = true,
shouldClampModalInitScaleY: Bool = true,
shouldClampModalLastScaleY: Bool = true,
shouldClampModalInitTranslateX: Bool = true,
shouldClampModalLastTranslateX: Bool = true,
shouldClampModalInitTranslateY: Bool = true,
shouldClampModalLastTranslateY: Bool = true
) {
self.shouldClampModalInitHeight = shouldClampModalInitHeight;
self.shouldClampModalLastHeight = shouldClampModalLastHeight;

self.shouldClampModalInitWidth = shouldClampModalInitWidth;
self.shouldClampModalLastWidth = shouldClampModalLastWidth;

self.shouldClampModalInitX = shouldClampModalInitX;
self.shouldClampModalLastX = shouldClampModalLastX;

self.shouldClampModalInitY = shouldClampModalInitY;
self.shouldClampModalLastY = shouldClampModalLastY;

self.shouldClampModalInitRotation = shouldClampModalInitRotation;
self.shouldClampModalLastRotation = shouldClampModalLastRotation;

self.shouldClampModalInitScaleX = shouldClampModalInitScaleX;
self.shouldClampModalLastScaleX = shouldClampModalLastScaleX;

self.shouldClampModalInitScaleY = shouldClampModalInitScaleY;
self.shouldClampModalLastScaleY = shouldClampModalLastScaleY;

self.shouldClampModalInitTranslateX = shouldClampModalInitTranslateX;
self.shouldClampModalLastTranslateX = shouldClampModalLastTranslateX;

self.shouldClampModalInitTranslateY = shouldClampModalInitTranslateY;
self.shouldClampModalLastTranslateY = shouldClampModalLastTranslateY;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,32 @@ import UIKit

struct AdaptiveModalInterpolationPoint: Equatable {

private static let DefaultMaskedCorners: CACornerMask = [
.layerMaxXMinYCorner,
.layerMinXMinYCorner,
.layerMaxXMaxYCorner,
.layerMinXMaxYCorner,
];

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

let percent: CGFloat;
let snapPointIndex: Int;

/// The computed frames of the modal based on the snap points
let computedRect: CGRect;

//let modalRotation: CGFloat;
// MARK: - Properties - Keyframes
// ------------------------------

let modalRotation: CGFloat;

//let modalScaleX: CGFloat;
//let modalScaleY: CGFloat;
let modalScaleX: CGFloat;
let modalScaleY: CGFloat;

//let modalTranslateX: CGFloat;
//let modalTranslateY: CGFloat;
let modalTranslateX: CGFloat;
let modalTranslateY: CGFloat;

//let modalBackgroundColor: UIColor;
let modalBackgroundOpacity: CGFloat;
Expand All @@ -37,6 +50,30 @@ struct AdaptiveModalInterpolationPoint: Equatable {

let backgroundVisualEffect: UIVisualEffect?;
let backgroundVisualEffectIntensity: CGFloat;

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

var transform: CGAffineTransform {
var transform: CGAffineTransform = .identity;

transform = transform.rotated(by: self.modalRotation);

transform = transform.scaledBy(
x: self.modalScaleX,
y: self.modalScaleY
);

transform = transform.translatedBy(
x: self.modalTranslateX,
y: self.modalTranslateY
);

return transform;
};

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

init(
usingModalConfig modalConfig: AdaptiveModalConfig,
Expand Down Expand Up @@ -81,13 +118,33 @@ struct AdaptiveModalInterpolationPoint: Equatable {

let keyframeCurrent = snapPointConfig.animationKeyframe;

self.modalRotation = keyframeCurrent?.modalRotation
?? keyframePrev?.modalRotation
?? 0;

self.modalScaleX = keyframeCurrent?.modalScaleX
?? keyframePrev?.modalScaleX
?? 1;

self.modalScaleY = keyframeCurrent?.modalScaleY
?? keyframePrev?.modalScaleY
?? 1;

self.modalTranslateX = keyframeCurrent?.modalTranslateX
?? keyframePrev?.modalTranslateX
?? 0;

self.modalTranslateY = keyframeCurrent?.modalTranslateY
?? keyframePrev?.modalTranslateY
?? 0;

self.modalBackgroundOpacity = keyframeCurrent?.modalBackgroundOpacity
?? keyframePrev?.modalBackgroundOpacity
?? 1;

self.modalCornerRadius = keyframeCurrent?.modalCornerRadius
?? keyframePrev?.modalCornerRadius
?? Self.DefaultCornerRadius;
?? 0;

self.modalMaskedCorners = keyframeCurrent?.modalMaskedCorners
?? keyframePrev?.modalMaskedCorners
Expand All @@ -112,8 +169,42 @@ struct AdaptiveModalInterpolationPoint: Equatable {
?? 1;
};

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

func getTransform(
shouldApplyRotation: Bool = true,
shouldApplyScale: Bool = true,
shouldApplyTranslate: Bool = true
) -> CGAffineTransform {

var transform: CGAffineTransform = .identity;

if shouldApplyRotation {
transform = transform.rotated(by: self.modalRotation);
};

if shouldApplyScale {
transform = transform.scaledBy(
x: self.modalScaleX,
y: self.modalScaleY
);
};

if shouldApplyTranslate {
transform = transform.translatedBy(
x: self.modalTranslateX,
y: self.modalTranslateY
);
};

return transform;
};

func apply(toModalView modalView: UIView){
modalView.frame = self.computedRect;
modalView.transform = self.transform;

modalView.layer.cornerRadius = self.modalCornerRadius;
modalView.layer.maskedCorners = self.modalMaskedCorners;
};
Expand All @@ -137,15 +228,6 @@ struct AdaptiveModalInterpolationPoint: Equatable {

extension AdaptiveModalInterpolationPoint {

private static let DefaultCornerRadius: CGFloat = 0;

private static let DefaultMaskedCorners: CACornerMask = [
.layerMaxXMinYCorner,
.layerMinXMinYCorner,
.layerMaxXMaxYCorner,
.layerMinXMaxYCorner,
];

static func compute(
usingModalConfig modalConfig: AdaptiveModalConfig,
withTargetRect targetRect: CGRect,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// AdaptiveModalPropertyAnimator.swift
// swift-programmatic-modal
//
// Created by Dominic Go on 6/5/23.
//

import UIKit


struct AdaptiveModalKeyframePropertyAnimator {

var animator: UIViewPropertyAnimator;

private weak var component: UIView?;

init<T: UIView>(
interpolationPoints: [AdaptiveModalInterpolationPoint],
forComponent component: T,
animation: @escaping (
_ component: T,
_ interpolationPoint: AdaptiveModalInterpolationPoint
) -> Void
){
let animator = UIViewPropertyAnimator(duration: 1, curve: .linear);

self.animator = animator;
self.component = component;

animator.addAnimations {
UIView.addKeyframe(
withRelativeStartTime: 0,
relativeDuration: 1
){
component.transform = .identity;
};

for interpolationPoint in interpolationPoints {
UIView.addKeyframe(
withRelativeStartTime: interpolationPoint.percent,
relativeDuration: 0
){
animation(component, interpolationPoint);
};
};
};
};

func setFractionComplete(forPercent percent: CGFloat) {
self.animator.fractionComplete = 0;
};

func clear(){
self.animator.stopAnimation(true);
};
};
Loading

0 comments on commit ec6ad37

Please sign in to comment.