diff --git a/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalInterpolationPoint.swift b/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalInterpolationPoint.swift index b965d79c..942a5550 100644 --- a/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalInterpolationPoint.swift +++ b/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalInterpolationPoint.swift @@ -216,10 +216,17 @@ struct AdaptiveModalInterpolationPoint: Equatable { }; func apply(toModalView modalView: UIView){ - modalView.frame = self.computedRect; - modalView.layer.cornerRadius = self.modalCornerRadius; modalView.layer.maskedCorners = self.modalMaskedCorners; + + modalView.setNeedsLayout(); + }; + + func apply(toModalWrapperView modalWrapperView: UIView){ + modalWrapperView.frame = self.computedRect; + //modalWrapperView.transform = self.modalTransform; + + modalWrapperView.setNeedsLayout(); }; func apply(toDummyModalView dummyModalView: UIView){ diff --git a/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalManager.swift b/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalManager.swift index d7953391..67e7c68a 100644 --- a/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalManager.swift +++ b/experiments/swift-programmatic-modal/AdaptiveModal/AdaptiveModalManager.swift @@ -22,6 +22,7 @@ class AdaptiveModalManager { weak var eventDelegate: AdaptiveModalEventNotifiable?; lazy var dummyModalView = UIView(); + lazy var modalWrapperView = UIView(); weak var targetView: UIView?; weak var modalView: UIView?; @@ -70,14 +71,11 @@ class AdaptiveModalManager { var modalFrame: CGRect! { set { - guard let modalView = self.modalView, - let newValue = newValue - else { return }; - - self.prevModalFrame = modalView.frame; - modalView.frame = newValue; + guard let newValue = newValue else { return }; + self.prevModalFrame = self.dummyModalView.frame; self.dummyModalView.frame = newValue; + self.modalWrapperView.frame = newValue; } get { self.dummyModalView.frame; @@ -253,22 +251,26 @@ class AdaptiveModalManager { bgDimmingView.alpha = 0; }; - targetView.addSubview(modalView); + let modalWrapperView = self.modalWrapperView; + modalWrapperView.backgroundColor = .clear; + targetView.addSubview(modalWrapperView); + + modalWrapperView.addSubview(modalView); modalView.clipsToBounds = true; modalView.backgroundColor = .clear; if let modalBackgroundView = self.modalBackgroundView { - modalView.addSubview(modalBackgroundView); - modalView.sendSubviewToBack(modalBackgroundView); + modalWrapperView.addSubview(modalBackgroundView); + modalWrapperView.sendSubviewToBack(modalBackgroundView); modalBackgroundView.backgroundColor = .systemBackground; modalBackgroundView.isUserInteractionEnabled = false; }; if let modalBGVisualEffectView = self.modalBackgroundVisualEffectView { - modalView.addSubview(modalBGVisualEffectView); - modalView.sendSubviewToBack(modalBGVisualEffectView); + modalWrapperView.addSubview(modalBGVisualEffectView); + modalWrapperView.sendSubviewToBack(modalBGVisualEffectView); modalBGVisualEffectView.clipsToBounds = true; modalBGVisualEffectView.backgroundColor = .clear; @@ -281,6 +283,8 @@ class AdaptiveModalManager { let targetView = self.targetView else { return }; + let modalWrapperView = self.modalWrapperView; + if let bgVisualEffectView = self.backgroundVisualEffectView { bgVisualEffectView.translatesAutoresizingMaskIntoConstraints = false; @@ -303,14 +307,23 @@ class AdaptiveModalManager { ]); }; + modalView.translatesAutoresizingMaskIntoConstraints = false; + + NSLayoutConstraint.activate([ + modalView.topAnchor .constraint(equalTo: modalWrapperView.topAnchor ), + modalView.bottomAnchor .constraint(equalTo: modalWrapperView.bottomAnchor ), + modalView.leadingAnchor .constraint(equalTo: modalWrapperView.leadingAnchor ), + modalView.trailingAnchor.constraint(equalTo: modalWrapperView.trailingAnchor), + ]); + if let modalBGView = self.modalBackgroundView { modalBGView.translatesAutoresizingMaskIntoConstraints = false; NSLayoutConstraint.activate([ - modalBGView.topAnchor .constraint(equalTo: modalView.topAnchor ), - modalBGView.bottomAnchor .constraint(equalTo: modalView.bottomAnchor ), - modalBGView.leadingAnchor .constraint(equalTo: modalView.leadingAnchor ), - modalBGView.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), + modalBGView.topAnchor .constraint(equalTo: modalWrapperView.topAnchor ), + modalBGView.bottomAnchor .constraint(equalTo: modalWrapperView.bottomAnchor ), + modalBGView.leadingAnchor .constraint(equalTo: modalWrapperView.leadingAnchor ), + modalBGView.trailingAnchor.constraint(equalTo: modalWrapperView.trailingAnchor), ]); }; @@ -318,10 +331,10 @@ class AdaptiveModalManager { modalBGVisualEffectView.translatesAutoresizingMaskIntoConstraints = false; NSLayoutConstraint.activate([ - modalBGVisualEffectView.topAnchor .constraint(equalTo: modalView.topAnchor ), - modalBGVisualEffectView.bottomAnchor .constraint(equalTo: modalView.bottomAnchor ), - modalBGVisualEffectView.leadingAnchor .constraint(equalTo: modalView.leadingAnchor ), - modalBGVisualEffectView.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), + modalBGVisualEffectView.topAnchor .constraint(equalTo: modalWrapperView.topAnchor ), + modalBGVisualEffectView.bottomAnchor .constraint(equalTo: modalWrapperView.bottomAnchor ), + modalBGVisualEffectView.leadingAnchor .constraint(equalTo: modalWrapperView.leadingAnchor ), + modalBGVisualEffectView.trailingAnchor.constraint(equalTo: modalWrapperView.trailingAnchor), ]); }; }; @@ -447,6 +460,73 @@ class AdaptiveModalManager { ); }; + func interpolateModalTransform( + forInputPercentValue inputPercentValue: CGFloat, + rangeInput: [CGFloat]? = nil, + rangeOutput: [AdaptiveModalInterpolationPoint]? = nil + ) -> CGAffineTransform? { + + guard let interpolationSteps = rangeOutput ?? self.interpolationStepsSorted, + let interpolationRangeInput = rangeInput ?? self.interpolationRangeInput + else { return nil }; + + let clampConfig = modalConfig.interpolationClampingConfig; + + let nextModalRotation = Self.interpolate( + inputValue: inputPercentValue, + rangeInput: interpolationRangeInput, + rangeOutput: interpolationSteps.map { + $0.modalRotation + }, + shouldClampMin: clampConfig.shouldClampModalInitRotation, + shouldClampMax: clampConfig.shouldClampModalLastRotation + ); + + let nextScaleX = Self.interpolate( + inputValue: inputPercentValue, + rangeInput: interpolationRangeInput, + rangeOutput: interpolationSteps.map { + $0.modalScaleX; + }, + shouldClampMin: clampConfig.shouldClampModalLastScaleX, + shouldClampMax: clampConfig.shouldClampModalLastScaleX + ); + + let nextScaleY = Self.interpolate( + inputValue: inputPercentValue, + rangeInput: interpolationRangeInput, + rangeOutput: interpolationSteps.map { + $0.modalScaleY + }, + shouldClampMin: clampConfig.shouldClampModalLastScaleY, + shouldClampMax: clampConfig.shouldClampModalLastScaleY + ); + + let nextTransform: CGAffineTransform = { + var transforms: [CGAffineTransform] = []; + + if let rotation = nextModalRotation { + transforms.append( + .init(rotationAngle: rotation) + ); + }; + + if let nextScaleX = nextScaleX, + let nextScaleY = nextScaleY { + + transforms.append( + .init(scaleX: nextScaleX, y: nextScaleY) + ); + }; + + return transforms.reduce(.identity) { + $0.concatenating($1); + }; + }(); + + return nextTransform; + }; + func interpolateModalBackgroundOpacity( forInputPercentValue inputPercentValue: CGFloat, rangeInput: [CGFloat]? = nil, @@ -745,11 +825,14 @@ class AdaptiveModalManager { self.animator = animator; animator.addAnimations { + interpolationPoint.apply(toDummyModalView: self.dummyModalView); interpolationPoint.apply(toModalView: modalView); - interpolationPoint.apply(toDummyModalView: self.dummyModalView); interpolationPoint.apply(toModalBackgroundView: self.modalBackgroundView); interpolationPoint.apply(toBackgroundView: self.backgroundDimmingView); + + self.modalWrapperView.layoutIfNeeded(); + modalView.layoutIfNeeded(); }; if let completion = completion {