LNIMChatVoiceWaveView.swift 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. //
  2. // LNIMChatVoiceWaveView.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/4.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNIMChatVoiceWaveView: UIView {
  11. private var waves: [Double] = []
  12. private let stackView = UIStackView()
  13. private var curCount = 0
  14. override init(frame: CGRect) {
  15. super.init(frame: frame)
  16. setupViews()
  17. }
  18. func clear() {
  19. stackView.arrangedSubviews.forEach {
  20. stackView.removeArrangedSubview($0)
  21. $0.removeFromSuperview()
  22. }
  23. }
  24. func add(_ wave: Double) {
  25. waves.append(wave)
  26. let waveView = LNIMChatVoiceWaveItemView()
  27. waveView.update(wave)
  28. waveView.frame = .init(
  29. x: stackView.bounds.width,
  30. y: 0,
  31. width: LNIMChatVoiceWaveItemView.perferWidth,
  32. height: bounds.height)
  33. waveView.layoutIfNeeded()
  34. stackView.addArrangedSubview(waveView)
  35. UIView.animate(withDuration: 0.2) { [weak self] in
  36. guard let self else { return }
  37. layoutIfNeeded()
  38. }
  39. if stackView.bounds.width - bounds.width > 2 * LNIMChatVoiceWaveItemView.perferWidth,
  40. let first = stackView.arrangedSubviews.first {
  41. stackView.removeArrangedSubview(first)
  42. first.removeFromSuperview()
  43. }
  44. }
  45. required init?(coder: NSCoder) {
  46. fatalError("init(coder:) has not been implemented")
  47. }
  48. }
  49. extension LNIMChatVoiceWaveView {
  50. private func setupViews() {
  51. clipsToBounds = true
  52. stackView.axis = .horizontal
  53. stackView.spacing = 0
  54. stackView.distribution = .fill
  55. addSubview(stackView)
  56. stackView.snp.makeConstraints { make in
  57. make.verticalEdges.equalToSuperview()
  58. make.trailing.equalToSuperview()
  59. make.width.equalTo(0).priority(.low)
  60. }
  61. }
  62. }
  63. private class LNIMChatVoiceWaveItemView: UIView {
  64. private let waveView = UIView()
  65. private static let waveWidth = 2.0
  66. private static let waveSpacing = 2.0
  67. private let minRatio = 0.65
  68. static var perferWidth: CGFloat {
  69. waveWidth + waveSpacing
  70. }
  71. override init(frame: CGRect) {
  72. super.init(frame: frame)
  73. waveView.layer.cornerRadius = Self.waveWidth * 0.5
  74. waveView.backgroundColor = .text_4
  75. addSubview(waveView)
  76. waveView.snp.makeConstraints { make in
  77. make.centerY.equalToSuperview()
  78. make.leading.equalToSuperview()
  79. make.trailing.equalToSuperview().offset(-Self.waveSpacing)
  80. make.width.equalTo(Self.waveWidth)
  81. make.height.equalTo(0)
  82. }
  83. }
  84. func update(_ wave: Double) {
  85. let ratio = max(max((wave - minRatio), 0) / (1 - minRatio), 0.1)
  86. waveView.snp.remakeConstraints { make in
  87. make.centerY.equalToSuperview()
  88. make.leading.equalToSuperview()
  89. make.trailing.equalToSuperview().offset(-Self.waveSpacing)
  90. make.width.equalTo(Self.waveWidth)
  91. if wave == 0.0 {
  92. make.height.equalTo(0)
  93. } else {
  94. make.height.equalToSuperview().multipliedBy(ratio)
  95. }
  96. }
  97. }
  98. required init?(coder: NSCoder) {
  99. fatalError("init(coder:) has not been implemented")
  100. }
  101. }