// // LNVoicePlayView.swift // Gami // // Created by OneeChan on 2026/3/9. // import Foundation import UIKit import SnapKit class LNVoicePlayView: UIView { let playIcon = UIImageView() let waveView = LNVoiceWaveView() let durationLabel = UILabel() private(set) var curUrl: String? private(set) var duration: Int = 0 override init(frame: CGRect) { super.init(frame: frame) setupViews() LNEventDeliver.addObserver(self) } func update(_ url: String?, duration: Int) { curUrl = url self.duration = duration if LNVoicePlayer.shared.playingUrl == url { playIcon.image = .icVoicePause } else { playIcon.image = .icVoicePlay } durationLabel.text = duration.durationDisplay } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupViews() { clipsToBounds = true let button = UIButton() button.setBackgroundImage(.primary_7, for: .normal) addSubview(button) button.snp.makeConstraints { make in make.edges.equalToSuperview() } playIcon.image = .icVoicePlay button.addSubview(playIcon) playIcon.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(3) make.width.height.equalTo(32) } waveView.isUserInteractionEnabled = false waveView.build() button.addSubview(waveView) waveView.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(playIcon.snp.trailing).offset(7) make.width.equalTo(30) make.height.equalTo(18) } durationLabel.font = .heading_h4 durationLabel.textColor = .text_1 button.addSubview(durationLabel) durationLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-8) } button.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } if LNVoicePlayer.shared.isPlaying { LNVoicePlayer.shared.stop() } else if let curUrl { if curUrl.starts(with: "http") { LNVoicePlayer.shared.play(curUrl) } else { LNVoicePlayer.shared.play(path: curUrl) } } }), for: .touchUpInside) } } extension LNVoicePlayView: LNVoicePlayerNotify { func onAudioUpdateDuration(path: String, cur: TimeInterval, total: TimeInterval) { guard curUrl == path else { return } durationLabel.text = (total - cur).durationDisplay } func onAudioStopPlay(path: String) { guard curUrl == path else { return } durationLabel.text = duration.durationDisplay playIcon.image = .icVoicePlay waveView.stopAnimate() } func onAudioStartPlay(path: String) { guard curUrl == path else { return } playIcon.image = .icVoicePause waveView.startAnimate() } }