Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
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)
        }
    }
}