LNOnlineView.swift 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //
  2. // LNOnlineView.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2026/1/4.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNOnlineView: UIView {
  11. var borderColor: UIColor = .primary_3 {
  12. didSet {
  13. reset()
  14. }
  15. }
  16. var borderWidth: CGFloat = 1 {
  17. didSet {
  18. reset()
  19. }
  20. }
  21. var duration: Double = 1.2 {
  22. didSet {
  23. reset()
  24. }
  25. }
  26. var offset = 5.0 {
  27. didSet {
  28. reset()
  29. }
  30. }
  31. private let borderLayer = CAShapeLayer()
  32. override init(frame: CGRect) {
  33. super.init(frame: frame)
  34. layer.borderWidth = borderWidth
  35. layer.borderColor = borderColor.cgColor
  36. clipsToBounds = false
  37. isUserInteractionEnabled = false
  38. borderLayer.borderWidth = borderWidth
  39. borderLayer.borderColor = borderColor.cgColor
  40. layer.addSublayer(borderLayer)
  41. startAnimate()
  42. }
  43. private func startAnimate() {
  44. let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
  45. scaleAnim.fromValue = 1.0
  46. scaleAnim.toValue = 1.0 + offset / CGFloat(bounds.width / 2)
  47. scaleAnim.duration = duration
  48. let opacityAnim = CABasicAnimation(keyPath: "opacity")
  49. opacityAnim.fromValue = 1.0
  50. opacityAnim.toValue = 0.0
  51. opacityAnim.duration = duration
  52. let animGroup = CAAnimationGroup()
  53. animGroup.animations = [scaleAnim, opacityAnim]
  54. animGroup.duration = duration
  55. animGroup.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
  56. animGroup.isRemovedOnCompletion = false
  57. animGroup.fillMode = .forwards
  58. animGroup.repeatCount = .infinity
  59. borderLayer.add(animGroup, forKey: "scaleAndFadeGroup")
  60. }
  61. override func layoutSubviews() {
  62. super.layoutSubviews()
  63. layer.cornerRadius = bounds.height * 0.5
  64. if borderLayer.frame.height != bounds.height {
  65. reset()
  66. }
  67. }
  68. private func reset() {
  69. borderLayer.frame = bounds
  70. borderLayer.cornerRadius = bounds.height * 0.5
  71. borderLayer.removeAllAnimations()
  72. startAnimate()
  73. }
  74. required init?(coder: NSCoder) {
  75. fatalError("init(coder:) has not been implemented")
  76. }
  77. }
  78. #if DEBUG
  79. import SwiftUI
  80. struct LNOnlineViewPreview: UIViewRepresentable {
  81. func makeUIView(context: Context) -> some UIView {
  82. let container = UIView()
  83. container.backgroundColor = .lightGray
  84. let view = LNOnlineView()
  85. container.addSubview(view)
  86. view.snp.makeConstraints { make in
  87. make.center.equalToSuperview()
  88. make.width.height.equalTo(50)
  89. }
  90. return container
  91. }
  92. func updateUIView(_ uiView: UIViewType, context: Context) { }
  93. }
  94. #Preview(body: {
  95. LNOnlineViewPreview()
  96. })
  97. #endif