UIView+Ex.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. //
  2. // View+Ex.swift
  3. // TSLiveWallpaper
  4. //
  5. // Created by 100Years on 2024/12/20.
  6. //
  7. import UIKit
  8. public extension UIView {
  9. /// 增加渐变背景,需要先确定当前 view 的 frame
  10. /// - Parameters:
  11. /// - colors: 渐变背景颜色数组
  12. /// - startPoint: 渐变启示位置
  13. /// - endPoint: 渐变结束位置
  14. /// - layerSize: 指定 layer 的 size,否则和自己当前一样大
  15. public func addGradientBg(colors: [CGColor],
  16. startPoint: CGPoint = .zero,
  17. endPoint: CGPoint = CGPoint(x: 1, y: 1),
  18. layerSize: CGSize? = nil) {
  19. let gl = CAGradientLayer()
  20. gl.colors = colors
  21. gl.startPoint = startPoint
  22. gl.endPoint = endPoint
  23. gl.frame = CGRect(origin: .zero, size: layerSize ?? self.bounds.size)
  24. gl.zPosition = -1 // 将 gradientLayer 放在底层
  25. layer.insertSublayer(gl, at: 0)
  26. }
  27. /// 慎用
  28. /// 给 view 增加圆角阴影,会改变视图结构,慎用,
  29. /// 需add 且确定frame后再调用
  30. /// > Note: 慎用
  31. /// - Parameters:
  32. /// - cornerRadius: 视图圆角
  33. /// - shadowColor: 阴影颜色
  34. /// - shadowOffset: 阴影偏移量
  35. /// - shadowRadius: 阴影扩散距离
  36. /// - shadowOpacity: 阴影透明度
  37. @discardableResult
  38. public func addCornerRadiusShadow(cornerRadius: CGFloat,
  39. shadowColor: CGColor?,
  40. shadowOffset: CGSize,
  41. shadowRadius: CGFloat,
  42. shadowOpacity: Float) -> UIView? {
  43. guard let supView = superview else {
  44. debugPrint("必须要先add,且确定了frame")
  45. return nil
  46. }
  47. let frame = self.frame
  48. let shadowView = UIView()
  49. shadowView.frame = frame
  50. shadowView.layer.shadowColor = shadowColor
  51. shadowView.layer.shadowOffset = shadowOffset
  52. shadowView.layer.shadowOpacity = shadowOpacity
  53. shadowView.layer.shadowRadius = shadowRadius
  54. supView.insertSubview(shadowView, belowSubview: self)
  55. shadowView.addSubview(self)
  56. self.frame = CGRect(origin: .zero, size: frame.size)
  57. self.clipsToBounds = false
  58. self.layer.masksToBounds = true
  59. self.layer.cornerRadius = cornerRadius
  60. return shadowView
  61. }
  62. /// 给 view 增加阴影
  63. /// - Parameters:
  64. /// - cornerRadius: 视图圆角
  65. /// - shadowColor: 阴影颜色
  66. /// - shadowOffset: 阴影偏移量
  67. /// - shadowRadius: 阴影扩散距离
  68. /// - shadowOpacity: 阴影透明度
  69. @discardableResult
  70. public func addShadow(shadowColor: CGColor?,
  71. shadowOffset: CGSize,
  72. shadowRadius: CGFloat,
  73. shadowOpacity: Float){
  74. layer.shadowColor = shadowColor
  75. layer.shadowOffset = shadowOffset
  76. layer.shadowOpacity = shadowOpacity
  77. layer.shadowRadius = shadowRadius
  78. }
  79. }
  80. public extension UIView {
  81. public enum MaskContentMode {
  82. case scaleToFill
  83. case scaleAspectFit
  84. case scaleAspectFill
  85. }
  86. /// 设置图片蒙版,支持指定 `contentMode` 和缩小比例
  87. /// - Parameters:
  88. /// - image: 用作蒙版的图片
  89. /// - contentMode: 蒙版的内容模式
  90. /// - scaleFactor: 缩小比例,默认 1.0 表示不缩放,0.8 表示缩小为 80%
  91. public func setImageMask(image: UIImage, contentMode: MaskContentMode = .scaleAspectFit, scaleFactor: CGFloat = 1.0) {
  92. guard scaleFactor > 0 && scaleFactor <= 1 else {
  93. print("Invalid scaleFactor. It must be in the range (0, 1].")
  94. return
  95. }
  96. // 创建一个 CALayer 来作为蒙版
  97. let maskLayer = CALayer()
  98. maskLayer.contents = image.cgImage
  99. // 计算蒙版的 frame
  100. let viewSize = self.bounds.size
  101. let imageSize = image.size
  102. var maskFrame: CGRect = .zero
  103. switch contentMode {
  104. case .scaleToFill:
  105. // 拉伸填充整个视图
  106. maskFrame = CGRect(origin: .zero, size: viewSize)
  107. case .scaleAspectFit:
  108. // 按比例缩放,适配视图并居中
  109. let aspectWidth = viewSize.width / imageSize.width
  110. let aspectHeight = viewSize.height / imageSize.height
  111. let aspectRatio = min(aspectWidth, aspectHeight) // 按比例适配
  112. let scaledWidth = imageSize.width * aspectRatio * scaleFactor
  113. let scaledHeight = imageSize.height * aspectRatio * scaleFactor
  114. let xOffset = (viewSize.width - scaledWidth) / 2
  115. let yOffset = (viewSize.height - scaledHeight) / 2
  116. maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
  117. case .scaleAspectFill:
  118. // 按比例缩放,填充视图并裁剪多余部分
  119. let aspectWidth = viewSize.width / imageSize.width
  120. let aspectHeight = viewSize.height / imageSize.height
  121. let aspectRatio = max(aspectWidth, aspectHeight) // 按比例填充
  122. let scaledWidth = imageSize.width * aspectRatio * scaleFactor
  123. let scaledHeight = imageSize.height * aspectRatio * scaleFactor
  124. let xOffset = (viewSize.width - scaledWidth) / 2
  125. let yOffset = (viewSize.height - scaledHeight) / 2
  126. maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
  127. }
  128. maskLayer.frame = maskFrame
  129. // 设置蒙版
  130. self.layer.mask = maskLayer
  131. }
  132. }
  133. // Frame
  134. public extension UIView {
  135. var viewController: UIViewController? {
  136. var next = superview
  137. while next != nil {
  138. let nextResponder = next?.next
  139. if nextResponder is UINavigationController ||
  140. nextResponder is UIViewController {
  141. return nextResponder as? UIViewController
  142. }
  143. next = next?.superview
  144. }
  145. return nil
  146. }
  147. func convertedToImage(rect: CGRect = .zero) -> UIImage? {
  148. var size = bounds.size
  149. var origin = bounds.origin
  150. if !size.equalTo(rect.size) && !rect.isEmpty {
  151. size = rect.size
  152. origin = CGPoint(x: -rect.minX, y: -rect.minY)
  153. }
  154. let format = UIGraphicsImageRendererFormat()
  155. format.opaque = false
  156. format.scale = UIScreen._scale
  157. let renderer = UIGraphicsImageRenderer(size: size, format: format)
  158. let image = renderer.image { context in
  159. drawHierarchy(in: CGRect(origin: origin, size: bounds.size), afterScreenUpdates: true)
  160. }
  161. return image
  162. }
  163. func toImage() -> UIImage? {
  164. let renderer = UIGraphicsImageRenderer(bounds: self.bounds)
  165. let image = renderer.image { context in
  166. self.layer.render(in: context.cgContext)
  167. }
  168. return image
  169. }
  170. func cornersRound(radius: CGFloat, corner: UIRectCorner) {
  171. if #available(iOS 11.0, *) {
  172. layer.cornerRadius = radius
  173. layer.maskedCorners = corner.mask
  174. } else {
  175. let path = UIBezierPath(
  176. roundedRect: bounds,
  177. byRoundingCorners: corner,
  178. cornerRadii: CGSize(width: radius, height: radius)
  179. )
  180. let mask = CAShapeLayer()
  181. mask.path = path.cgPath
  182. layer.mask = mask
  183. }
  184. }
  185. }
  186. extension UIRectCorner {
  187. var mask: CACornerMask {
  188. switch self {
  189. case .allCorners:
  190. return [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  191. default:
  192. return .init(rawValue: rawValue)
  193. }
  194. }
  195. }
  196. public extension UIView {
  197. // 部分圆角(必须Frame已知)
  198. func makeCorner(_ corners: UIRectCorner, radius: CGFloat) {
  199. let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
  200. let maskLayer = CAShapeLayer()
  201. maskLayer.frame = bounds
  202. maskLayer.path = path.cgPath
  203. layer.mask = maskLayer
  204. }
  205. }
  206. public extension UIView {
  207. /// 为视图添加高斯模糊效果
  208. /// - Parameters:
  209. /// - blurRadius: 模糊半径(默认 10.0)
  210. /// - fillColor: 填充颜色(可选,默认透明)
  211. func applyGaussianBlur(blurRadius: CGFloat = 10.0, fillColor: UIColor? = nil) {
  212. // 移除已有的模糊效果
  213. self.removeBlurEffect()
  214. // 创建模糊效果
  215. let blurEffect = UIBlurEffect(style: .light)
  216. let blurView = UIVisualEffectView(effect: blurEffect)
  217. blurView.frame = self.bounds
  218. blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  219. // 设置模糊强度
  220. if blurRadius != 10.0 {
  221. let blurAnimator = CABasicAnimation(keyPath: "inputRadius")
  222. blurAnimator.fromValue = blurRadius
  223. blurAnimator.toValue = blurRadius
  224. blurAnimator.duration = 0
  225. blurView.layer.add(blurAnimator, forKey: nil)
  226. }
  227. // 如果指定了填充颜色,添加一个覆盖视图
  228. if let color = fillColor {
  229. let colorOverlay = UIView(frame: self.bounds)
  230. colorOverlay.backgroundColor = color
  231. colorOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  232. colorOverlay.isUserInteractionEnabled = false
  233. blurView.contentView.addSubview(colorOverlay)
  234. }
  235. self.addSubview(blurView)
  236. }
  237. /// 移除模糊效果
  238. func removeBlurEffect() {
  239. for subview in subviews {
  240. if subview is UIVisualEffectView {
  241. subview.removeFromSuperview()
  242. }
  243. }
  244. }
  245. }
  246. public extension UIView {
  247. static func creatColor(color:UIColor) -> UIView {
  248. let view = UIView()
  249. view.backgroundColor = color
  250. return view
  251. }
  252. }
  253. public extension UIView {
  254. func removeAllSubViews() {
  255. subviews.forEach { subview in
  256. subview.removeFromSuperview()
  257. }
  258. }
  259. }
  260. public func kGetSubFrame(superSize: CGSize, subViewSize: CGSize, contentMode: UIView.ContentMode = .scaleAspectFit, scaleFactor: CGFloat = 1.0)-> CGRect {
  261. guard scaleFactor > 0 && scaleFactor <= 1 else {
  262. print("Invalid scaleFactor. It must be in the range (0, 1].")
  263. return CGRect(origin: .zero, size: superSize)
  264. }
  265. var maskFrame: CGRect = .zero
  266. switch contentMode {
  267. case .scaleToFill:
  268. // 拉伸填充整个视图
  269. maskFrame = CGRect(origin: .zero, size: superSize)
  270. case .scaleAspectFit:
  271. // 按比例缩放,适配视图并居中
  272. let aspectWidth = superSize.width / subViewSize.width
  273. let aspectHeight = superSize.height / subViewSize.height
  274. let aspectRatio = min(aspectWidth, aspectHeight) // 按比例适配
  275. let scaledWidth = subViewSize.width * aspectRatio * scaleFactor
  276. let scaledHeight = subViewSize.height * aspectRatio * scaleFactor
  277. let xOffset = (superSize.width - scaledWidth) / 2
  278. let yOffset = (superSize.height - scaledHeight) / 2
  279. maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
  280. case .scaleAspectFill:
  281. // 按比例缩放,填充视图并裁剪多余部分
  282. let aspectWidth = superSize.width / subViewSize.width
  283. let aspectHeight = superSize.height / subViewSize.height
  284. let aspectRatio = max(aspectWidth, aspectHeight) // 按比例填充
  285. let scaledWidth = subViewSize.width * aspectRatio * scaleFactor
  286. let scaledHeight = subViewSize.height * aspectRatio * scaleFactor
  287. let xOffset = (superSize.width - scaledWidth) / 2
  288. let yOffset = (superSize.height - scaledHeight) / 2
  289. maskFrame = CGRect(x: xOffset, y: yOffset, width: scaledWidth, height: scaledHeight)
  290. default:
  291. maskFrame = CGRect(origin: .zero, size: superSize)
  292. }
  293. return maskFrame
  294. }
  295. public extension UIView {
  296. /// 添加渐变色边框
  297. /// - Parameters:
  298. /// - colors: 渐变色数组
  299. /// - width: 边框宽度
  300. /// - radius: 圆角半径(nil 时使用视图的现有圆角)
  301. /// - startPoint: 渐变起点 (默认从左到右)
  302. /// - endPoint: 渐变终点 (默认从左到右)
  303. func addGradientBorder(
  304. colors: [UIColor],
  305. width: CGFloat = 3,
  306. radius: CGFloat? = nil,
  307. startPoint: CGPoint = CGPoint(x: 0, y: 0.5),
  308. endPoint: CGPoint = CGPoint(x: 1, y: 0.5)
  309. ) {
  310. // 移除旧的渐变边框
  311. removeGradientBorder()
  312. let gradientLayer = CAGradientLayer()
  313. gradientLayer.name = "GradientBorder"
  314. gradientLayer.frame = bounds
  315. gradientLayer.colors = colors.map { $0.cgColor }
  316. gradientLayer.startPoint = startPoint
  317. gradientLayer.endPoint = endPoint
  318. let cornerRadius = radius ?? layer.cornerRadius
  319. let path = UIBezierPath(
  320. roundedRect: bounds.insetBy(dx: width/2, dy: width/2),
  321. cornerRadius: cornerRadius
  322. )
  323. let shapeLayer = CAShapeLayer()
  324. shapeLayer.name = "GradientBorderMask"
  325. shapeLayer.path = path.cgPath
  326. shapeLayer.lineWidth = width
  327. shapeLayer.fillColor = nil
  328. shapeLayer.strokeColor = UIColor.black.cgColor
  329. gradientLayer.mask = shapeLayer
  330. layer.addSublayer(gradientLayer)
  331. }
  332. /// 更新渐变色边框的frame(在layoutSubviews中调用)
  333. func updateGradientBorder() {
  334. guard let gradientLayer = layer.sublayers?.first(where: { $0.name == "GradientBorder" }) as? CAGradientLayer,
  335. let shapeLayer = gradientLayer.mask as? CAShapeLayer else {
  336. return
  337. }
  338. gradientLayer.frame = bounds
  339. let width = shapeLayer.lineWidth
  340. let cornerRadius = layer.cornerRadius
  341. let path = UIBezierPath(
  342. roundedRect: bounds.insetBy(dx: width/2, dy: width/2),
  343. cornerRadius: cornerRadius
  344. )
  345. shapeLayer.path = path.cgPath
  346. }
  347. /// 移除渐变色边框
  348. func removeGradientBorder() {
  349. layer.sublayers?.filter { $0.name == "GradientBorder" }.forEach {
  350. $0.removeFromSuperlayer()
  351. }
  352. }
  353. }