192 lines
6.2 KiB
Swift
192 lines
6.2 KiB
Swift
//
|
|
// UIView+CALayer.swift
|
|
// VDS
|
|
//
|
|
// Created by Matt Bruce on 6/15/23.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import VDSFormControlsTokens
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Debug Borders
|
|
//--------------------------------------------------
|
|
extension UIView {
|
|
|
|
internal func removeDebugBorder() {
|
|
layer.remove(layerName: "debug")
|
|
}
|
|
|
|
internal func addDebugBorder(color: UIColor = .red) {
|
|
//ensure you remove existing
|
|
removeDebugBorder()
|
|
|
|
//add bounds border
|
|
let borderLayer = CALayer()
|
|
borderLayer.name = "debugAreaLayer"
|
|
borderLayer.frame = bounds
|
|
borderLayer.bounds = bounds
|
|
borderLayer.borderWidth = VDSFormControls.widthBorder
|
|
borderLayer.borderColor = color.cgColor
|
|
layer.addSublayer(borderLayer)
|
|
|
|
//add touchborder if applicable
|
|
if type(of: self) is AppleGuidelinesTouchable.Type {
|
|
let faultToleranceX: CGFloat = max((45 - bounds.size.width) / 2.0, 0)
|
|
let faultToleranceY: CGFloat = max((45 - bounds.size.height) / 2.0, 0)
|
|
|
|
let touchableAreaPath = UIBezierPath(rect: bounds.insetBy(dx: -faultToleranceX, dy: -faultToleranceY))
|
|
let touchLayer = CAShapeLayer()
|
|
touchLayer.path = touchableAreaPath.cgPath
|
|
touchLayer.strokeColor = color.cgColor
|
|
touchLayer.fillColor = UIColor.clear.cgColor
|
|
touchLayer.lineWidth = VDSFormControls.widthBorder
|
|
touchLayer.opacity = 1.0
|
|
touchLayer.name = "debugTouchableAreaLayer"
|
|
touchLayer.zPosition = 100
|
|
touchLayer.frame = bounds
|
|
touchLayer.bounds = bounds
|
|
layer.addSublayer(touchLayer)
|
|
}
|
|
}
|
|
|
|
public var hasDebugBorder: Bool {
|
|
guard let layers = layer.sublayers else { return false }
|
|
return layers.compactMap{$0.name}.filter{$0.hasPrefix("debug")}.count > 0
|
|
}
|
|
|
|
public func debugBorder(show shouldShow: Bool = true, color: UIColor = .red) {
|
|
if shouldShow {
|
|
addDebugBorder(color: color)
|
|
} else {
|
|
removeDebugBorder()
|
|
}
|
|
if let view = self as? ViewProtocol {
|
|
view.updateView()
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - CALayer
|
|
//--------------------------------------------------
|
|
extension CALayer {
|
|
|
|
/// Removes a sublayer of a specific name.
|
|
/// - Parameter layerName: Layer with the name you want to remove.
|
|
public func remove(layerName: String) {
|
|
guard let sublayers = sublayers else {
|
|
return
|
|
}
|
|
|
|
sublayers.forEach({ layer in
|
|
if layer.name?.hasPrefix(layerName) ?? false {
|
|
layer.removeFromSuperlayer()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Borders
|
|
//--------------------------------------------------
|
|
extension UIView {
|
|
|
|
/// Adds a border to a specific sides with the parameters.
|
|
/// - Parameters:
|
|
/// - side: Side in which to add the border.
|
|
/// - width: Width of the border.
|
|
/// - color: Color of the border.
|
|
/// - offset: offset of the border.
|
|
public func addBorder(side: UIRectEdge, width: CGFloat, color: UIColor, offset: CGFloat = 0) {
|
|
let layerName = borderLayerName(for: side)
|
|
layer.remove(layerName: layerName)
|
|
|
|
let borderLayer = CALayer()
|
|
borderLayer.backgroundColor = color.cgColor
|
|
borderLayer.name = layerName
|
|
|
|
switch side {
|
|
case .left:
|
|
borderLayer.frame = CGRect(x: 0, y: 0, width: width, height: frame.height)
|
|
case .right:
|
|
borderLayer.frame = CGRect(x: frame.width - width - offset, y: 0, width: width, height: frame.height)
|
|
case .top:
|
|
borderLayer.frame = CGRect(x: 0, y: 0, width: frame.width, height: width)
|
|
case .bottom:
|
|
borderLayer.frame = CGRect(x: 0, y: frame.height - width - offset, width: frame.width, height: width)
|
|
default:
|
|
break
|
|
}
|
|
|
|
layer.addSublayer(borderLayer)
|
|
}
|
|
|
|
/// Removes all of the borders addeding using the addBorder method.
|
|
public func removeBorders() {
|
|
layer.borderWidth = 0
|
|
layer.borderColor = nil
|
|
layer.remove(layerName: borderLayerName(for: .top))
|
|
layer.remove(layerName: borderLayerName(for: .left))
|
|
layer.remove(layerName: borderLayerName(for: .right))
|
|
layer.remove(layerName: borderLayerName(for: .bottom))
|
|
}
|
|
|
|
private func borderLayerName(for side: UIRectEdge) -> String {
|
|
switch side {
|
|
case .left:
|
|
return "leftBorderLayer"
|
|
case .right:
|
|
return "rightBorderLayer"
|
|
case .top:
|
|
return "topBorderLayer"
|
|
case .bottom:
|
|
return "bottomBorderLayer"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UIView {
|
|
|
|
fileprivate var bezierPathIdentifier: String { return "bezierPathBorderLayer" }
|
|
|
|
fileprivate var bezierPathBorder: CAShapeLayer? {
|
|
return (self.layer.sublayers?.filter({ (layer) -> Bool in
|
|
return layer.name == self.bezierPathIdentifier && (layer as? CAShapeLayer) != nil
|
|
}) as? [CAShapeLayer])?.first
|
|
}
|
|
|
|
public func bezierPathBorder(_ color: UIColor = .white, width: CGFloat = 1) {
|
|
|
|
var border = self.bezierPathBorder
|
|
let path = UIBezierPath(roundedRect: self.bounds, cornerRadius:self.layer.cornerRadius)
|
|
let mask = CAShapeLayer()
|
|
mask.path = path.cgPath
|
|
self.layer.mask = mask
|
|
|
|
if (border == nil) {
|
|
border = CAShapeLayer()
|
|
border!.name = self.bezierPathIdentifier
|
|
self.layer.addSublayer(border!)
|
|
}
|
|
|
|
border!.frame = self.bounds
|
|
let pathUsingCorrectInsetIfAny =
|
|
UIBezierPath(roundedRect: border!.bounds, cornerRadius:self.layer.cornerRadius)
|
|
|
|
border!.path = pathUsingCorrectInsetIfAny.cgPath
|
|
border!.fillColor = UIColor.clear.cgColor
|
|
border!.strokeColor = color.cgColor
|
|
border!.lineWidth = width * 2
|
|
}
|
|
|
|
public func removeBezierPathBorder() {
|
|
self.layer.mask = nil
|
|
self.bezierPathBorder?.removeFromSuperlayer()
|
|
}
|
|
|
|
}
|