BFUIView+Ext.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. //
  2. // UICollectionView+Ext.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2020/6/6.
  6. // Copyright © 2020 BytesFlow. All rights reserved.
  7. //
  8. import KingfisherWebP
  9. import UIKit
  10. // MARK: - UIView的分类扩展
  11. /// UIView的分类扩展
  12. public extension UIView {
  13. func addCorner(roundingCorners: UIRectCorner = .allCorners, corner: CGFloat = 10) {
  14. if roundingCorners == .allCorners {
  15. layer.cornerRadius = corner
  16. layer.masksToBounds = true
  17. } else {
  18. let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: roundingCorners, cornerRadii: CGSize(width: corner, height: corner))
  19. let cornerLayer = CAShapeLayer()
  20. cornerLayer.frame = bounds
  21. cornerLayer.path = path.cgPath
  22. layer.mask = cornerLayer
  23. }
  24. }
  25. /// 添加阴影
  26. /// - Parameters:
  27. /// - color: <#color description#>
  28. /// - offset: <#offset description#>
  29. /// - Returns: <#description#>
  30. func addShadowLayer(isAll: Bool = false, color: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5), offset: CGSize = CGSize(width: 1, height: 1)) {
  31. layer.shadowColor = color.cgColor
  32. layer.shadowOffset = offset
  33. layer.shadowRadius = 0.5
  34. layer.shadowOpacity = 1.0
  35. if isAll {
  36. layer.shadowColor = color.cgColor
  37. layer.shadowOffset = CGSize.zero
  38. // 设置偏移量为0,四周都有阴影
  39. layer.shadowRadius = 0.5 // 阴影半径
  40. layer.shadowOpacity = 0.3 // 阴影透明度
  41. layer.masksToBounds = false
  42. layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius).cgPath
  43. }
  44. }
  45. /// 添加虚线条
  46. func addBorderToLayer(frame: CGRect? = nil) {
  47. // 线条颜色
  48. let borderLayer: CAShapeLayer = CAShapeLayer()
  49. borderLayer.strokeColor = UIColor.hexColor(hexadecimal: "#FFFFFF").cgColor
  50. borderLayer.fillColor = nil
  51. borderLayer.path = UIBezierPath(rect: frame == nil ? bounds : frame!).cgPath
  52. borderLayer.frame = bounds
  53. borderLayer.lineWidth = 2.0
  54. borderLayer.lineCap = .round
  55. // 第一位是 线条长度 第二位是间距 nil时为实线
  56. borderLayer.lineDashPattern = [5, 5]
  57. layer.addSublayer(borderLayer)
  58. }
  59. func animateZoom() {
  60. transform = CGAffineTransform(scaleX: 0.4, y: 0.4)
  61. UIView.animate(withDuration: 0.5, animations: {
  62. self.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
  63. }) { _ in
  64. UIView.animate(withDuration: 0.5, animations: {
  65. self.transform = .identity
  66. }) { _ in
  67. }
  68. }
  69. }
  70. /// 添加抖动功能
  71. /// - Parameters:
  72. /// - fromValue: <#fromValue description#>
  73. /// - toValue: <#toValue description#>
  74. /// - duration: <#duration description#>
  75. /// - repeatCount: <#repeatCount description#>
  76. /// - Returns: <#description#>
  77. func shakeAnimation(_ fromValue: Float, _ toValue: Float, _ duration: Float, _: Float) {
  78. layer.removeAllAnimations()
  79. let shake = CABasicAnimation(keyPath: "transform.rotation.z")
  80. shake.fromValue = fromValue
  81. shake.toValue = toValue
  82. shake.duration = CFTimeInterval(duration)
  83. shake.autoreverses = true
  84. shake.repeatCount = Float(CGFloat.greatestFiniteMagnitude)
  85. shake.isRemovedOnCompletion = false
  86. layer.add(shake, forKey: "imageView")
  87. // 增加锚点
  88. layer.anchorPoint = CGPoint(x: 0.5, y: 1)
  89. }
  90. /// 添加心跳动画
  91. /// - Parameters:
  92. /// - duration: <#duration description#>
  93. /// - isRepeat: <#isRepeat description#>
  94. /// - multiple: <#multiple description#>
  95. /// - Returns: <#description#>
  96. func heartbeatAnimate(duration: TimeInterval, isRepeat: Bool, multiple: CGFloat) {
  97. UIView.animateKeyframes(withDuration: duration, delay: 0, options: .allowUserInteraction, animations: {
  98. self.transform = CGAffineTransform(scaleX: 1.0 + multiple, y: 1.0 + multiple)
  99. }) { _ in
  100. UIView.animateKeyframes(withDuration: duration, delay: 0, options: .allowUserInteraction, animations: {
  101. self.transform = .identity
  102. }) { _ in
  103. UIView.animateKeyframes(withDuration: duration, delay: 0, options: .allowUserInteraction, animations: {
  104. self.transform = CGAffineTransform(scaleX: 1.0 + multiple * 2, y: 1.0 + multiple * 2)
  105. }) { _ in
  106. UIView.animateKeyframes(withDuration: duration, delay: 0, options: .allowUserInteraction, animations: {
  107. self.transform = .identity
  108. }) { _ in
  109. if isRepeat {
  110. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.3) {
  111. self.heartbeatAnimate(duration: duration, isRepeat: isRepeat, multiple: multiple)
  112. }
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119. /// 活动心跳动画
  120. /// - Returns: <#description#>
  121. func activityHeartbeatAnimate() {
  122. layer.removeAllAnimations()
  123. UIView.animateKeyframes(withDuration: 0.45, delay: 0, options: .allowUserInteraction, animations: {
  124. self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
  125. }) { _ in
  126. UIView.animateKeyframes(withDuration: 0.45, delay: 0, options: .allowUserInteraction, animations: {
  127. self.transform = .identity
  128. }) { _ in
  129. self.activityHeartbeatAnimate()
  130. }
  131. }
  132. }
  133. /// 活动心跳动画
  134. /// - Returns: <#description#>
  135. func heartbeatAnimate() {
  136. layer.removeAllAnimations()
  137. UIView.animateKeyframes(withDuration: 1, delay: 0, options: .allowUserInteraction, animations: {
  138. self.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
  139. }) { isFinished in
  140. UIView.animateKeyframes(withDuration: 1, delay: 0, options: .allowUserInteraction, animations: {
  141. self.transform = .identity
  142. }) { isFinished in
  143. if isFinished {
  144. self.heartbeatAnimate()
  145. }
  146. }
  147. }
  148. }
  149. func addScaleBasicAnimation() {
  150. let animation = CABasicAnimation(keyPath: "transform.scale")
  151. animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
  152. animation.duration = 0.5
  153. animation.repeatCount = 1000
  154. animation.autoreverses = true
  155. animation.fromValue = 0.8
  156. animation.toValue = 1.1
  157. layer.add(animation, forKey: nil)
  158. }
  159. func addScaleYBasicAnimation() {
  160. let animation = CAKeyframeAnimation(keyPath: "transform.translation.y")
  161. animation.duration = 0.5
  162. animation.repeatCount = 100
  163. animation.isRemovedOnCompletion = true
  164. // animation.
  165. animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
  166. layer.add(animation, forKey: nil)
  167. // CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
  168. // CGFloat duration = 1.f;
  169. // CGFloat height = 7.f;
  170. // CGFloat currentY = self.animationView.transform.ty;
  171. // animation.duration = duration;
  172. // animation.values = @[@(currentY),@(currentY - height/4),@(currentY - height/4*2),@(currentY - height/4*3),@(currentY - height),@(currentY - height/ 4*3),@(currentY - height/4*2),@(currentY - height/4),@(currentY)];
  173. // animation.keyTimes = @[ @(0), @(0.025), @(0.085), @(0.2), @(0.5), @(0.8), @(0.915), @(0.975), @(1) ];
  174. // animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  175. // animation.repeatCount = HUGE_VALF;
  176. // [self.animationView.layer addAnimation:animation forKey:@"kViewShakerAnimationKey"];
  177. // }
  178. }
  179. /// 将view生成一张图片
  180. /// - Returns: <#description#>
  181. func graphicsGetImage() -> UIImage? {
  182. UIGraphicsBeginImageContextWithOptions(frame.size, true, 0.0)
  183. layer.render(in: UIGraphicsGetCurrentContext()!)
  184. let newImage = UIGraphicsGetImageFromCurrentImageContext()
  185. UIGraphicsEndImageContext()
  186. return newImage
  187. }
  188. /// 动画显示View
  189. /// - Returns: <#description#>
  190. func showViewAnimate(duration: TimeInterval = 0.3, completion: ((Bool) -> Void)? = nil) {
  191. UIView.animate(withDuration: duration, animations: { [weak self] in
  192. self?.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - self!.frame.height, width: self!.frame.width, height: self!.frame.height)
  193. }) { isFinished in
  194. if completion != nil {
  195. completion!(isFinished)
  196. }
  197. }
  198. }
  199. /// 动画隐藏view
  200. /// - Returns: <#description#>
  201. func dismissViewAnimate(duration: TimeInterval = 0.3, completion: ((Bool) -> Void)? = nil) {
  202. UIView.animate(withDuration: duration, animations: { [weak self] in
  203. self?.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: self!.frame.width, height: self!.frame.height)
  204. }) { isFinished in
  205. if completion != nil {
  206. completion!(isFinished)
  207. }
  208. }
  209. }
  210. /// add by ak 添加虚线框
  211. /// - Parameter color: 框色
  212. /// - Parameter lineWidth: 框宽
  213. func addBorderToLayer(color: CGColor, lineWidth: CGFloat) {
  214. let border = CAShapeLayer()
  215. // 线条颜色
  216. border.strokeColor = color
  217. border.fillColor = nil
  218. border.path = UIBezierPath(rect: bounds).cgPath
  219. border.frame = bounds
  220. border.lineWidth = lineWidth
  221. border.lineCap = .square
  222. // 第一位是 线条长度 第二位是间距 nil时为实线
  223. border.lineDashPattern = [9, 4]
  224. layer.addSublayer(border)
  225. }
  226. }
  227. // MARK: - UICollectionView的分类扩展
  228. /// UICollectionView的分类扩展
  229. public extension UICollectionView {
  230. /// 获取当前cell
  231. /// - Returns: <#description#>
  232. func visibleCell() -> UICollectionViewCell? {
  233. let visibleRect = CGRect(origin: contentOffset, size: bounds.size)
  234. let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
  235. guard let visibleIndexPath = indexPathForItem(at: visiblePoint) else { return nil }
  236. return cellForItem(at: visibleIndexPath)
  237. }
  238. func indexPathsForElements(in rect: CGRect) -> [IndexPath] {
  239. let allLayoutAttributes = collectionViewLayout.layoutAttributesForElements(in: rect)!
  240. return allLayoutAttributes.map { $0.indexPath }
  241. }
  242. }
  243. // MARK: - UITabBar的分类扩展
  244. /// UITabBar的分类扩展
  245. public extension UITabBar {
  246. /// 展示小红点
  247. /// - Parameter index: <#index description#>
  248. /// - Returns: <#description#>
  249. func showPoint(index: Int) {
  250. let pointW: CGFloat = 8
  251. let pointView = UIView()
  252. pointView.tag = 11111 + index
  253. pointView.layer.cornerRadius = pointW / 2
  254. pointView.backgroundColor = UIColor.hexColor(hexadecimal: "#EE0051")
  255. let percentX: CGFloat = CGFloat(Double(index) + 0.7) / CGFloat(items?.count ?? 1)
  256. let pointX = ceil(percentX * frame.width)
  257. let pointY = ceil(0.1 * frame.height)
  258. pointView.frame = CGRect(x: pointX, y: pointY, width: pointW, height: pointW)
  259. addSubview(pointView)
  260. }
  261. /// 移除小红点
  262. /// - Parameter index: <#index description#>
  263. /// - Returns: <#description#>
  264. func removePoint(index: Int) {
  265. for item in subviews {
  266. if item.tag == 11111 + index {
  267. item.removeFromSuperview()
  268. }
  269. }
  270. }
  271. }
  272. // MARK: - UILabel的分类扩展
  273. /// UILabel的分类扩展
  274. extension UILabel {
  275. var isTruncated: Bool {
  276. guard let labelText = text else {
  277. return false
  278. }
  279. // 计算理论上显示所有文字需要的尺寸
  280. let rect = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
  281. let labelTextSize = (labelText as NSString)
  282. .boundingRect(with: rect, options: .usesLineFragmentOrigin,
  283. attributes: [NSAttributedString.Key.font: font as Any], context: nil)
  284. // 计算理论上需要的行数
  285. let labelTextLines = Int(ceil(CGFloat(labelTextSize.height) / font.lineHeight))
  286. // 实际可显示的行数
  287. var labelShowLines = Int(floor(CGFloat(bounds.size.height) / font.lineHeight))
  288. if numberOfLines != 0 {
  289. labelShowLines = min(labelShowLines, numberOfLines)
  290. }
  291. // 比较两个行数来判断是否需要截断
  292. return labelTextLines > labelShowLines
  293. }
  294. /// 添加阴影
  295. /// - Parameters:
  296. /// - color: <#color description#>
  297. /// - offset: <#offset description#>
  298. /// - Returns: <#description#>
  299. public func addShadow(color: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5), offset: CGSize = CGSize(width: 1, height: 1)) {
  300. shadowColor = color
  301. shadowOffset = offset
  302. }
  303. }
  304. public extension UIImageView {
  305. /// imageView加载网络图片
  306. /// - Parameters:
  307. /// - url: 网络url
  308. func setNetImage(url: String?, placeholder: UIImage = UIImage.moduleImage(named: "placehold_image", moduleName: "BFCommonKit") ?? UIImage()) {
  309. if url == nil || (url?.count ?? 0) <= 0 {
  310. return
  311. }
  312. kf.setImage(with: URL(string: url!), placeholder: placeholder, options: url?.suffix(5) == ".webp" ? [.processor(WebPProcessor.default), .cacheSerializer(WebPSerializer.default)] : nil, progressBlock: { _, _ in
  313. }) { _ in
  314. }
  315. }
  316. /// 展示加载中动画
  317. /// - Returns: <#description#>
  318. func showLoadingAnimation(duration: Double = 1) {
  319. let rotationAnim = CABasicAnimation(keyPath: "transform.rotation.z")
  320. rotationAnim.fromValue = 0
  321. rotationAnim.toValue = Double.pi * 2
  322. rotationAnim.repeatCount = MAXFLOAT
  323. rotationAnim.duration = duration
  324. rotationAnim.isRemovedOnCompletion = false
  325. layer.add(rotationAnim, forKey: nil)
  326. }
  327. /// 播放GIF
  328. /// - Parameters:
  329. /// - data : 图片二进制数据
  330. /// - images: 图片
  331. /// - repeatCount: 循环次数
  332. /// - duration: 时长
  333. /// - Returns: <#description#>
  334. func displayGIF(data: Data? = nil, images: [UIImage]? = nil, repeatCount: Int = Int.max, duration: Double = 1) {
  335. if images != nil, (images?.count ?? 0) > 0, !isAnimating {
  336. layer.removeAllAnimations()
  337. stopAnimating()
  338. animationImages = images
  339. animationDuration = duration
  340. animationRepeatCount = repeatCount
  341. startAnimating()
  342. } else if images == nil && data != nil {
  343. // PQPHAssetVideoParaseUtil.parasGIFImage(data: data!) { [weak self] _, images, duration in
  344. // if images != nil, (images?.count ?? 0) > 0 {
  345. // self?.displayGIF(images: images!, repeatCount: repeatCount, duration: duration ?? 1)
  346. // }
  347. // }
  348. }
  349. }
  350. /// 移除
  351. func removePlayGIF() {
  352. layer.removeAllAnimations()
  353. stopAnimating()
  354. }
  355. }
  356. public extension UIButton {
  357. /// UIButton加载网络图片
  358. /// - Parameters:
  359. /// - url: 网络url
  360. func setNetImage(url: String?, placeholder: UIImage = UIImage.moduleImage(named: "placehold_image", moduleName: "BFCommonKit") ?? UIImage()) {
  361. if url == nil || (url?.count ?? 0) <= 0 {
  362. // BFLog(message: "设置按钮网络图片地址为空")
  363. return
  364. }
  365. kf.setImage(with: URL(string: url!), for: .normal, placeholder: placeholder, options: url?.suffix(5) == ".webp" ? [.processor(WebPProcessor.default), .cacheSerializer(WebPSerializer.default)] : nil, progressBlock: { _, _ in
  366. }) { _ in
  367. }
  368. }
  369. /// UIButton加载网络背景图片
  370. /// - Parameters:
  371. /// - url: 网络url
  372. func setNetBackgroundImage(url: String, placeholder: UIImage = UIImage.moduleImage(named: "placehold_image", moduleName: "BFCommonKit") ?? UIImage()) {
  373. kf.setBackgroundImage(with: URL(string: url), for: .normal, placeholder: placeholder, options: url.suffix(5) == ".webp" ? [.processor(WebPProcessor.default), .cacheSerializer(WebPSerializer.default)] : nil, progressBlock: { _, _ in
  374. }) { _ in
  375. }
  376. }
  377. }