UIView+Extension.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. //
  2. // UIView+Extension.swift
  3. // QuickSearchLocation
  4. //
  5. // Created by mac on 2024/4/10.
  6. //
  7. import Foundation
  8. import UIKit
  9. import Toast_Swift
  10. // MARK: - 屏幕尺寸常用的常量
  11. public extension UIView {
  12. // MARK: 1.1、是否是缺口屏幕(刘海屏)或者灵动岛的屏幕
  13. // 是否是缺口屏幕(刘海屏)或者灵动岛的屏幕
  14. var qsl_isIPhoneNotch: Bool {
  15. if #available(iOS 11.0, *) {
  16. if let window = UIApplication.keyWindow {
  17. return window.safeAreaInsets.bottom > 0
  18. } else {
  19. return false
  20. }
  21. } else {
  22. return UIApplication.shared.statusBarFrame.height > 20
  23. }
  24. }
  25. // MARK: 2.1、屏幕的宽
  26. /// 屏幕的宽
  27. var qsl_kScreenW: CGFloat { return UIScreen.main.bounds.width }
  28. // MARK: 2.2、屏幕的高
  29. /// 屏幕的高
  30. var qsl_kScreenH: CGFloat { return UIScreen.main.bounds.height }
  31. // MARK: 2.3、获取statusBar的高度
  32. /// 获取statusBar的高度
  33. var qsl_kStatusBarFrameH: CGFloat {
  34. if #available(iOS 13.0, *) {
  35. let window: UIWindow? = UIApplication.shared.windows.first
  36. let statusBarHeight = (window?.windowScene?.statusBarManager?.statusBarFrame.height) ?? 0
  37. return statusBarHeight
  38. } else {
  39. // 防止界面没有出来获取为0的情况
  40. return UIApplication.shared.statusBarFrame.height > 0 ? UIApplication.shared.statusBarFrame.height : 44
  41. }
  42. }
  43. // MARK: 2.4、获取导航栏的高度
  44. /// 获取导航栏的高度
  45. var qsl_kNavFrameH: CGFloat { return 44 + qsl_kStatusBarFrameH }
  46. // MARK: 2.5、屏幕底部Tabbar高度
  47. /// 屏幕底部Tabbar高度
  48. var qsl_kTabbarFrameH: CGFloat { return qsl_isIPhoneNotch ? 83 : 49 }
  49. // MARK: 2.6、屏幕底部刘海高度
  50. /// 屏幕底部刘海高度
  51. var qsl_kTabbarBottom: CGFloat { return qsl_isIPhoneNotch ? 34 : 0 }
  52. // MARK: 2.7、屏幕比例
  53. /// 屏幕比例
  54. var qsl_kPixel: CGFloat { return 1.0 / UIScreen.main.scale }
  55. var qsl_kScale: CGFloat { return qsl_kScreenW / CGFloat(375.0) }
  56. }
  57. // MARK: - UIView 有关 Frame 的扩展
  58. public extension UIView {
  59. // MARK: 3.1、x 的位置
  60. /// x 的位置
  61. var qsl_x: CGFloat {
  62. get {
  63. return frame.origin.x
  64. }
  65. set(newValue) {
  66. var tempFrame: CGRect = frame
  67. tempFrame.origin.x = newValue
  68. frame = tempFrame
  69. }
  70. }
  71. // MARK: 3.2、y 的位置
  72. /// y 的位置
  73. var qsl_y: CGFloat {
  74. get {
  75. return frame.origin.y
  76. }
  77. set(newValue) {
  78. var tempFrame: CGRect = frame
  79. tempFrame.origin.y = newValue
  80. frame = tempFrame
  81. }
  82. }
  83. // MARK: 3.3、height: 视图的高度
  84. /// height: 视图的高度
  85. var qsl_height: CGFloat {
  86. get {
  87. return frame.size.height
  88. }
  89. set(newValue) {
  90. var tempFrame: CGRect = frame
  91. tempFrame.size.height = newValue
  92. frame = tempFrame
  93. }
  94. }
  95. // MARK: 3.4、width: 视图的宽度
  96. /// width: 视图的宽度
  97. var qsl_width: CGFloat {
  98. get {
  99. return frame.size.width
  100. }
  101. set(newValue) {
  102. var tempFrame: CGRect = frame
  103. tempFrame.size.width = newValue
  104. frame = tempFrame
  105. }
  106. }
  107. // MARK: 3.5、size: 视图的zize
  108. /// size: 视图的zize
  109. var qsl_size: CGSize {
  110. get {
  111. return frame.size
  112. }
  113. set(newValue) {
  114. var tempFrame: CGRect = frame
  115. tempFrame.size = newValue
  116. frame = tempFrame
  117. }
  118. }
  119. // MARK: 3.6、centerX: 视图的X中间位置
  120. /// centerX: 视图的X中间位置
  121. var qsl_centerX: CGFloat {
  122. get {
  123. return center.x
  124. }
  125. set(newValue) {
  126. var tempCenter: CGPoint = center
  127. tempCenter.x = newValue
  128. center = tempCenter
  129. }
  130. }
  131. // MARK: 3.7、centerY: 视图的Y中间位置
  132. /// centerY: 视图Y的中间位置
  133. var qsl_centerY: CGFloat {
  134. get {
  135. return center.y
  136. }
  137. set(newValue) {
  138. var tempCenter: CGPoint = center
  139. tempCenter.y = newValue
  140. center = tempCenter
  141. }
  142. }
  143. // MARK: 3.9、top 上端横坐标(y)
  144. /// top 上端横坐标(y)
  145. var qsl_top: CGFloat {
  146. get {
  147. return frame.origin.y
  148. }
  149. set(newValue) {
  150. var tempFrame: CGRect = frame
  151. tempFrame.origin.y = newValue
  152. frame = tempFrame
  153. }
  154. }
  155. // MARK: 3.10、left 左端横坐标(x)
  156. /// left 左端横坐标(x)
  157. var qsl_left: CGFloat {
  158. get {
  159. return frame.origin.x
  160. }
  161. set(newValue) {
  162. var tempFrame: CGRect = frame
  163. tempFrame.origin.x = newValue
  164. frame = tempFrame
  165. }
  166. }
  167. // MARK: 3.11、bottom 底端纵坐标 (y + height)
  168. /// bottom 底端纵坐标 (y + height)
  169. var qsl_bottom: CGFloat {
  170. get {
  171. return frame.origin.y + frame.size.height
  172. }
  173. set(newValue) {
  174. frame.origin.y = newValue - frame.size.height
  175. }
  176. }
  177. // MARK: 3.12、right 底端纵坐标 (x + width)
  178. /// right 底端纵坐标 (x + width)
  179. var qsl_right: CGFloat {
  180. get {
  181. return frame.origin.x + frame.size.width
  182. }
  183. set(newValue) {
  184. frame.origin.x = newValue - frame.size.width
  185. }
  186. }
  187. // MARK: 3.13、origin 点
  188. /// origin 点
  189. var qsl_origin: CGPoint {
  190. get {
  191. return frame.origin
  192. }
  193. set(newValue) {
  194. var tempOrigin: CGPoint = frame.origin
  195. tempOrigin = newValue
  196. frame.origin = tempOrigin
  197. }
  198. }
  199. }
  200. // MARK: - 关于UIView的 圆角、阴影、边框、虚线 的设置
  201. public extension UIView {
  202. // MARK: 5.1、添加圆角
  203. /// 添加圆角
  204. /// - Parameters:
  205. /// - radius: 圆角的大小
  206. func addRadius(radius:CGFloat) {
  207. self.layer.cornerRadius = radius
  208. self.clipsToBounds = true
  209. }
  210. // MARK: 5.1、添加圆角
  211. /// 添加圆角
  212. /// - Parameters:
  213. /// - conrners: 具体哪个圆角
  214. /// - radius: 圆角的大小
  215. func addCorner(conrners: UIRectCorner , radius: CGFloat) {
  216. let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: conrners, cornerRadii: CGSize(width: radius, height: radius))
  217. let maskLayer = CAShapeLayer()
  218. maskLayer.frame = self.bounds
  219. maskLayer.path = maskPath.cgPath
  220. self.layer.mask = maskLayer
  221. }
  222. // MARK: 5.2、添加圆角和边框
  223. /// 添加圆角和边框
  224. /// - Parameters:
  225. /// - conrners: 具体哪个圆角
  226. /// - radius: 圆角的大小
  227. /// - borderWidth: 边框的宽度
  228. /// - borderColor: 边框的颜色
  229. func addCorner(conrners: UIRectCorner , radius: CGFloat, borderWidth: CGFloat, borderColor: UIColor) {
  230. let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: conrners, cornerRadii: CGSize(width: radius, height: radius))
  231. let maskLayer = CAShapeLayer()
  232. maskLayer.frame = self.bounds
  233. maskLayer.path = maskPath.cgPath
  234. self.layer.mask = maskLayer
  235. // Add border
  236. let borderLayer = CAShapeLayer()
  237. borderLayer.path = maskLayer.path
  238. borderLayer.fillColor = UIColor.clear.cgColor
  239. borderLayer.strokeColor = borderColor.cgColor
  240. borderLayer.lineWidth = borderWidth
  241. borderLayer.frame = self.bounds
  242. self.layer.addSublayer(borderLayer)
  243. }
  244. // MARK: 5.3、给继承于view的类添加阴影
  245. /// 给继承于view的类添加阴影
  246. /// - Parameters:
  247. /// - shadowColor: 阴影的颜色
  248. /// - shadowOffset: 阴影的偏移度:CGSizeMake(X[正的右偏移,负的左偏移], Y[正的下偏移,负的上偏移])
  249. /// - shadowOpacity: 阴影的透明度
  250. /// - shadowRadius: 阴影半径,默认 3
  251. func addShadow(shadowColor: UIColor, shadowOffset: CGSize, shadowOpacity: Float, shadowRadius: CGFloat = 3) {
  252. // 设置阴影颜色
  253. layer.shadowColor = shadowColor.cgColor
  254. // 设置透明度
  255. layer.shadowOpacity = shadowOpacity
  256. // 设置阴影半径
  257. layer.shadowRadius = shadowRadius
  258. // 设置阴影偏移量
  259. layer.shadowOffset = shadowOffset
  260. }
  261. // MARK: 5.4、添加阴影和圆角并存
  262. /// 添加阴影和圆角并存
  263. ///
  264. /// - Parameter superview: 父视图
  265. /// - Parameter conrners: 具体哪个圆角
  266. /// - Parameter radius: 圆角大小
  267. /// - Parameter shadowColor: 阴影的颜色
  268. /// - Parameter shadowOffset: 阴影的偏移度:CGSizeMake(X[正的右偏移,负的左偏移], Y[正的下偏移,负的上偏移])
  269. /// - Parameter shadowOpacity: 阴影的透明度
  270. /// - Parameter shadowRadius: 阴影半径,默认 3
  271. ///
  272. /// - Note1: 如果在异步布局(如:SnapKit布局)中使用,要在布局后先调用 layoutIfNeeded,再使用该方法
  273. /// - Note2: 如果在添加阴影的视图被移除,底部插入的父视图的layer是不会被移除的⚠️
  274. func addCornerAndShadow(superview: UIView, conrners: UIRectCorner , radius: CGFloat = 3, shadowColor: UIColor, shadowOffset: CGSize, shadowOpacity: Float, shadowRadius: CGFloat = 3) {
  275. let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: conrners, cornerRadii: CGSize(width: radius, height: radius))
  276. let maskLayer = CAShapeLayer()
  277. maskLayer.frame = self.bounds
  278. maskLayer.path = maskPath.cgPath
  279. self.layer.mask = maskLayer
  280. let subLayer = CALayer()
  281. let fixframe = self.frame
  282. subLayer.frame = fixframe
  283. subLayer.cornerRadius = radius
  284. subLayer.backgroundColor = shadowColor.cgColor
  285. subLayer.masksToBounds = false
  286. // shadowColor阴影颜色
  287. subLayer.shadowColor = shadowColor.cgColor
  288. // shadowOffset阴影偏移,x向右偏移3,y向下偏移2,默认(0, -3),这个跟shadowRadius配合使用
  289. subLayer.shadowOffset = shadowOffset
  290. // 阴影透明度,默认0
  291. subLayer.shadowOpacity = shadowOpacity
  292. // 阴影半径,默认3
  293. subLayer.shadowRadius = shadowRadius
  294. subLayer.shadowPath = maskPath.cgPath
  295. superview.layer.insertSublayer(subLayer, below: self.layer)
  296. }
  297. // MARK: 5.5、通过贝塞尔曲线View添加阴影和圆角
  298. /// 通过贝塞尔曲线View添加阴影和圆角
  299. ///
  300. /// - Parameter conrners: 具体哪个圆角(暂时只支持:allCorners)
  301. /// - Parameter radius: 圆角大小
  302. /// - Parameter shadowColor: 阴影的颜色
  303. /// - Parameter shadowOffset: 阴影的偏移度:CGSizeMake(X[正的右偏移,负的左偏移], Y[正的下偏移,负的上偏移])
  304. /// - Parameter shadowOpacity: 阴影的透明度
  305. /// - Parameter shadowRadius: 阴影半径,默认 3
  306. ///
  307. /// - Note: 提示:如果在异步布局(如:SnapKit布局)中使用,要在布局后先调用 layoutIfNeeded,再使用该方法或者在override func layoutSublayers(of layer: CALayer) {} 里面调用,也要使用 layoutIfNeeded
  308. func addViewCornerAndShadow(conrners: UIRectCorner , radius: CGFloat = 3, shadowColor: UIColor, shadowOffset: CGSize, shadowOpacity: Float, shadowRadius: CGFloat = 3) {
  309. // 切圆角
  310. layer.shadowColor = shadowColor.cgColor
  311. layer.shadowOffset = shadowOffset
  312. layer.shadowOpacity = shadowOpacity
  313. layer.shadowRadius = shadowRadius
  314. layer.cornerRadius = radius
  315. // 路径阴影
  316. let path = UIBezierPath.init(roundedRect: bounds, byRoundingCorners: conrners, cornerRadii: CGSize.init(width: radius, height: radius))
  317. layer.shadowPath = path.cgPath
  318. }
  319. // MARK: 5.6、添加边框
  320. /// 添加边框
  321. /// - Parameters:
  322. /// - width: 边框宽度
  323. /// - color: 边框颜色
  324. func addBorder(borderWidth: CGFloat, borderColor: UIColor) {
  325. layer.borderWidth = borderWidth
  326. layer.borderColor = borderColor.cgColor
  327. layer.masksToBounds = true
  328. }
  329. // MARK: 5.7、毛玻璃效果
  330. /// 毛玻璃效果
  331. /// - Parameters:
  332. /// - alpha: 可设置模糊的程度(0-1),越大模糊程度越大
  333. /// - size: 毛玻璃的size
  334. /// - style: 模糊效果
  335. func effectViewWithAlpha(alpha: CGFloat = 1.0, size: CGSize? = nil, style: UIBlurEffect.Style = .light) {
  336. // 模糊视图的大小
  337. var visualEffectViewSize = CGSize(width: 0, height: 0)
  338. if let weakSize = size {
  339. visualEffectViewSize = weakSize
  340. } else {
  341. visualEffectViewSize = self.qsl_size
  342. }
  343. let visualEffectView = UIVisualEffectView.visualEffectView(size: visualEffectViewSize, alpha: alpha, style: style, isAddVibrancy: false)
  344. self.addSubview(visualEffectView)
  345. }
  346. // 设置渐变颜色
  347. func gradientBackgroundColor(color1: UIColor, color2: UIColor, width: CGFloat, height: CGFloat, direction: ImageGradientDirection) {
  348. if let image = UIImage.gradient([color1, color2], size: CGSize(width: width, height: height), locations: [0, 1], direction: direction) {
  349. self.backgroundColor = UIColor(patternImage: image)
  350. }
  351. }
  352. }
  353. extension UIView {
  354. func addFourCornerAndBorder(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat, borderWidth: CGFloat, borderColor: UIColor){
  355. let cornerRadii = UIView.CornerRadii.init(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight)
  356. let path = createPathWithRoundedRect(bounds: self.bounds, cornerRadii:cornerRadii)
  357. let shapLayer = CAShapeLayer()
  358. shapLayer.frame = self.bounds
  359. shapLayer.path = path
  360. self.layer.mask = shapLayer
  361. // Add border
  362. let borderLayer = CAShapeLayer()
  363. borderLayer.path = shapLayer.path
  364. borderLayer.fillColor = UIColor.clear.cgColor
  365. borderLayer.strokeColor = borderColor.cgColor
  366. borderLayer.lineWidth = borderWidth
  367. borderLayer.frame = self.bounds
  368. self.layer.insertSublayer(borderLayer, at: 0)
  369. }
  370. //添加4个不同大小的圆角
  371. func addFourCorner(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat){
  372. let cornerRadii = UIView.CornerRadii.init(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight)
  373. let path = createPathWithRoundedRect(bounds: self.bounds, cornerRadii:cornerRadii)
  374. let shapLayer = CAShapeLayer()
  375. shapLayer.frame = self.bounds
  376. shapLayer.path = path
  377. self.layer.mask = shapLayer
  378. }
  379. //各圆角大小
  380. struct CornerRadii {
  381. var topLeft :CGFloat = 0
  382. var topRight :CGFloat = 0
  383. var bottomLeft :CGFloat = 0
  384. var bottomRight :CGFloat = 0
  385. }
  386. //切圆角函数绘制线条
  387. func createPathWithRoundedRect (bounds:CGRect,cornerRadii:CornerRadii) -> CGPath {
  388. let minX = bounds.minX
  389. let minY = bounds.minY
  390. let maxX = bounds.maxX
  391. let maxY = bounds.maxY
  392. //获取四个圆心
  393. let topLeftCenterX = minX + cornerRadii.topLeft
  394. let topLeftCenterY = minY + cornerRadii.topLeft
  395. let topRightCenterX = maxX - cornerRadii.topRight
  396. let topRightCenterY = minY + cornerRadii.topRight
  397. let bottomLeftCenterX = minX + cornerRadii.bottomLeft
  398. let bottomLeftCenterY = maxY - cornerRadii.bottomLeft
  399. let bottomRightCenterX = maxX - cornerRadii.bottomRight
  400. let bottomRightCenterY = maxY - cornerRadii.bottomRight
  401. //虽然顺时针参数是YES,在iOS中的UIView中,这里实际是逆时针
  402. let path :CGMutablePath = CGMutablePath();
  403. //顶 左
  404. path.addArc(center: CGPoint(x: topLeftCenterX, y: topLeftCenterY), radius: cornerRadii.topLeft, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 3 / 2, clockwise: false)
  405. //顶右
  406. path.addArc(center: CGPoint(x: topRightCenterX, y: topRightCenterY), radius: cornerRadii.topRight, startAngle: CGFloat.pi * 3 / 2, endAngle: 0, clockwise: false)
  407. //底右
  408. path.addArc(center: CGPoint(x: bottomRightCenterX, y: bottomRightCenterY), radius: cornerRadii.bottomRight, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: false)
  409. //底左
  410. path.addArc(center: CGPoint(x: bottomLeftCenterX, y: bottomLeftCenterY), radius: cornerRadii.bottomLeft, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: false)
  411. path.closeSubpath();
  412. return path;
  413. }
  414. }
  415. // MARK: - UIView的一些其他方法
  416. public extension UIView {
  417. // MARK: - 键盘收起来
  418. /// 键盘收起来
  419. func keyboardEndEditing() {
  420. self.endEditing(true)
  421. }
  422. // MARK: 将 View 转换成图片
  423. /// 将 View 转换成图片
  424. /// - Returns: 图片
  425. func toImage() -> UIImage {
  426. return UIGraphicsImageRenderer(size: self.frame.size).image { context in
  427. self.layer.render(in: context.cgContext)
  428. }
  429. }
  430. }
  431. // MARK: - Toast吐司🍞
  432. extension UIView {
  433. // MARK: 普通消息
  434. func toast(text: String) {
  435. var style = ToastStyle()
  436. style.messageFont = .textF(14)
  437. style.backgroundColor = UIColor.hexStringColor(hexString: "#000000", alpha: 0.85)
  438. style.cornerRadius = 8
  439. style.verticalPadding = 14
  440. style.horizontalPadding = 44
  441. self.makeToast(text, duration: 1.5, position: .center, style: style)
  442. }
  443. }