// // LNIMChatVoiceWaveView.swift // Lanu // // Created by OneeChan on 2025/12/4. // import Foundation import UIKit import SnapKit class LNIMChatVoiceWaveView: UIView { private var waves: [Double] = [] private let stackView = UIStackView() private var curCount = 0 override init(frame: CGRect) { super.init(frame: frame) setupViews() } func clear() { stackView.arrangedSubviews.forEach { stackView.removeArrangedSubview($0) $0.removeFromSuperview() } } func add(_ wave: Double) { waves.append(wave) let waveView = LNIMChatVoiceWaveItemView() waveView.update(wave) waveView.frame = .init( x: stackView.bounds.width, y: 0, width: LNIMChatVoiceWaveItemView.perferWidth, height: bounds.height) waveView.layoutIfNeeded() stackView.addArrangedSubview(waveView) UIView.animate(withDuration: 0.2) { [weak self] in guard let self else { return } layoutIfNeeded() } if stackView.bounds.width - bounds.width > 2 * LNIMChatVoiceWaveItemView.perferWidth, let first = stackView.arrangedSubviews.first { stackView.removeArrangedSubview(first) first.removeFromSuperview() } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNIMChatVoiceWaveView { private func setupViews() { clipsToBounds = true stackView.axis = .horizontal stackView.spacing = 0 stackView.distribution = .fill addSubview(stackView) stackView.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.trailing.equalToSuperview() make.width.equalTo(0).priority(.low) } } } private class LNIMChatVoiceWaveItemView: UIView { private let waveView = UIView() private static let waveWidth = 2.0 private static let waveSpacing = 2.0 private let minRatio = 0.65 static var perferWidth: CGFloat { waveWidth + waveSpacing } override init(frame: CGRect) { super.init(frame: frame) waveView.layer.cornerRadius = Self.waveWidth * 0.5 waveView.backgroundColor = .text_4 addSubview(waveView) waveView.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview() make.trailing.equalToSuperview().offset(-Self.waveSpacing) make.width.equalTo(Self.waveWidth) make.height.equalTo(0) } } func update(_ wave: Double) { let ratio = max(max((wave - minRatio), 0) / (1 - minRatio), 0.1) waveView.snp.remakeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview() make.trailing.equalToSuperview().offset(-Self.waveSpacing) make.width.equalTo(Self.waveWidth) if wave == 0.0 { make.height.equalTo(0) } else { make.height.equalToSuperview().multipliedBy(ratio) } } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }