Path: blob/a-new-beginning/Folium-iOS/Extensions/UIView.swift
2 views
//
// UIView.swift
// Folium-iOS
//
// Created by Jarrod Norwell on 26/9/2025.
//
import Foundation
import UIKit
extension UIView {
func applyCutout(from button: UIButton, to imageView: UIImageView, expandBy padding: CGFloat) {
imageView.layoutIfNeeded()
button.layoutIfNeeded()
// Mask must match imageView size
let maskLayer = CAShapeLayer()
maskLayer.frame = imageView.bounds
// Convert UIButton frame → imageView coordinate space
var holeFrame = button.convert(button.bounds, to: imageView)
// Expand frame by padding
holeFrame = holeFrame.insetBy(dx: -padding, dy: -padding)
// Full mask area
let fullPath = UIBezierPath(rect: imageView.bounds)
// Circle (using expanded frame)
let circlePath = UIBezierPath(
roundedRect: holeFrame,
cornerRadius: holeFrame.width / 2 // stays perfectly circular
)
fullPath.append(circlePath)
fullPath.usesEvenOddFillRule = true
maskLayer.path = fullPath.cgPath
maskLayer.fillRule = .evenOdd
imageView.layer.mask = maskLayer
}
func expandAndShrink(by amount: Double, for duration: TimeInterval = 0.33, with completion: ((Bool) -> Void)? = nil) {
UIView.animate(withDuration: duration,
delay: 0,
usingSpringWithDamping: 0.4, // lower = more bounce
initialSpringVelocity: 6,
options: .allowUserInteraction,
animations: {
// Expand
self.transform = .init(scaleX: amount, y: amount)
}) { _ in
// Shrink back
UIView.animate(withDuration: 0.3,
delay: 0,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 6,
options: .allowUserInteraction,
animations: {
self.transform = .identity
}, completion: completion)
}
}
func shrinkAndExpand(by amount: Double, for duration: TimeInterval = 0.33, with completion: ((Bool) -> Void)? = nil) {
UIView.animate(withDuration: duration,
delay: 0,
usingSpringWithDamping: 0.4, // lower = more bounce
initialSpringVelocity: 6,
options: .allowUserInteraction,
animations: {
// Expand
self.transform = .init(scaleX: -amount, y: -amount)
}) { _ in
// Shrink back
UIView.animate(withDuration: 0.3,
delay: 0,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 6,
options: .allowUserInteraction,
animations: {
self.transform = .identity
}, completion: completion)
}
}
}