From fb731bceaed6717775537ee1a4d1eda43a041258 Mon Sep 17 00:00:00 2001 From: moral-life Date: Sat, 30 Nov 2024 17:01:39 +0900 Subject: [PATCH 01/21] =?UTF-8?q?feat:=20=EB=B0=A9=EC=9E=A5=EC=9D=B4=20?= =?UTF-8?q?=EB=AA=A8=EB=93=9C=20=EC=84=A0=ED=83=9D=EC=A4=91=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(=EC=8B=9C=EC=9E=91=ED=95=98=EA=B8=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/UIKitComponents/ASButton.swift | 6 ++-- .../Views/Lobby/LobbyViewController.swift | 28 +++++++++---------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift index 98a6f39f..da256ba8 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift @@ -85,7 +85,7 @@ final class ASButton: UIButton { func updateButton(_ type: ASButton.ASButtonType) { switch type { case .disabled: disable() - case .waitStart: setConfiguration(title: "시작 대기중..", backgroundColor: .asOrange) + case .needMorePlayers: setConfiguration(title: "인원 부족", backgroundColor: .asOrange) case let .idle(string, color): setConfiguration(title: string, backgroundColor: color) case .startRecord: setConfiguration(title: "녹음하기", backgroundColor: .systemRed) case .recording: setConfiguration(title: "녹음중..", backgroundColor: .asLightRed) @@ -96,12 +96,13 @@ final class ASButton: UIButton { setConfiguration(title: "제출 완료") disable() case .startGame: setConfiguration(systemImageName: "play.fill", title: "시작하기!", backgroundColor: .asMint) + case .hostSelecting: setConfiguration(title: "방장이 모드 선택중...", backgroundColor: .asMint) } } enum ASButtonType { case disabled - case waitStart + case needMorePlayers case idle(String, UIColor?) case startRecord case recording @@ -110,6 +111,7 @@ final class ASButton: UIButton { case submit case submitted case startGame + case hostSelecting } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 4e6ab120..6e0ffec3 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -53,22 +53,22 @@ final class LobbyViewController: UIViewController { } .store(in: &cancellables) - viewmodel.$isHost + viewmodel.$canBeginGame.combineLatest(viewmodel.$isHost) .receive(on: DispatchQueue.main) - .sink { [weak self] isHost in - self?.startButton.isEnabled = isHost - } - .store(in: &cancellables) - - viewmodel.$canBeginGame - .receive(on: DispatchQueue.main) - .sink { [weak self] canBeginGame in - if !canBeginGame { - self?.startButton.updateButton(.waitStart) + .sink { [weak self] canBeginGame, isHost in + if isHost { + if canBeginGame { + self?.startButton.updateButton(.startGame) + self?.startButton.isEnabled = true + } + else { + self?.startButton.updateButton(.needMorePlayers) + self?.startButton.updateButton(.disabled) + } + } + else { + self?.startButton.updateButton(.hostSelecting) self?.startButton.updateButton(.disabled) - } else { - self?.startButton.updateButton(.startGame) - self?.startButton.isEnabled = true } } .store(in: &cancellables) From 0edba66523a9717a25ff46202c80ef78878daf7a Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sat, 30 Nov 2024 21:35:36 +0900 Subject: [PATCH 02/21] =?UTF-8?q?chore:=20=EA=B8=B0=EB=8A=A5=20=EB=B3=84?= =?UTF-8?q?=20=ED=99=95=EC=9E=A5=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Utils/AudioHelper.swift | 116 ++++++++++-------- 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift index 8a4e04fb..f2d2bea4 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift @@ -1,12 +1,20 @@ import ASAudioKit +import ASLogKit import Combine import Foundation -import ASLogKit extension AnyPublisher: @unchecked @retroactive Sendable {} extension PassthroughSubject: @unchecked @retroactive Sendable {} +extension AudioHelper { + enum FileSource { + case imported + case recorded + } +} actor AudioHelper { + // MARK: - Private properties + static let shared = AudioHelper() private var recorder: ASAudioRecorder? private var player: ASAudioPlayer? @@ -14,6 +22,9 @@ actor AudioHelper { private var playType: PlayType = .full private var isConcurrent: Bool = false private var timer: Timer? + + // MARK: - Publishers + private let amplitudeSubject = PassthroughSubject() private let playerStateSubject = PassthroughSubject<(FileSource, Bool), Never>() private let recorderStateSubject = PassthroughSubject() @@ -48,31 +59,11 @@ actor AudioHelper { timer?.invalidate() timer = nil } +} - private func visualize() { - Task { [weak self] in - guard let self else { return } - await self.setTimer() - } - } - - private func setTimer() { - timer = Timer(timeInterval: 0.125, repeats: true) { [weak self] _ in - Task { - await self?.calculateRecorderAmplitude() - } - } - RunLoop.main.add(timer!, forMode: .common) - } - - private func calculateRecorderAmplitude() async { - await recorder?.updateMeters() - guard let averagePower = await recorder?.getAveragePower() else { return } - let newAmplitude = 1.8 * pow(10.0, averagePower / 20.0) - let clampedAmplitude = min(max(newAmplitude, 0), 1) - amplitudeSubject.send(clampedAmplitude) - } +// MARK: - Play Audio +extension AudioHelper { /// 여러 조건을 적용해 오디오를 재생하는 함수 /// - Parameters: /// - file: 재생할 오디오 데이터 @@ -92,9 +83,31 @@ actor AudioHelper { await player?.startPlaying(data: file, option: playType) } + func stopPlaying() async { + await player?.stopPlaying() + player = nil + sendDataThrough(playerStateSubject, (source, false)) + } + + private func makePlayer() { + player = ASAudioPlayer() + } + + private func checkPlayerState() async -> Bool { + if await isPlaying() { + await player?.stopPlaying() + sendDataThrough(playerStateSubject, (source, false)) + } + return true + } +} + +// MARK: - Record Audio + +extension AudioHelper { func startRecording() async { guard await checkRecorderState(), await checkPlayerState() else { return } - + makeRecorder() let tempURL = makeURL() recorderStateSubject.send(true) @@ -108,12 +121,6 @@ actor AudioHelper { } catch { Logger.error(error.localizedDescription) } } - func stopPlaying() async { - await player?.stopPlaying() - player = nil - sendDataThrough(playerStateSubject, (source, false)) - } - private func stopRecording() async -> Data? { let recordedData = await recorder?.stopRecording() recorderStateSubject.send(false) @@ -126,22 +133,10 @@ actor AudioHelper { return true } - private func checkPlayerState() async -> Bool { - if await isPlaying() { - await player?.stopPlaying() - sendDataThrough(playerStateSubject, (source, false)) - } - return true - } - private func makeRecorder() { recorder = ASAudioRecorder() } - private func makePlayer() { - player = ASAudioPlayer() - } - private func makeURL() -> URL { let tempCacheDirectory = FileManager.default .urls(for: .cachesDirectory, in: .userDomainMask).first! @@ -159,13 +154,6 @@ actor AudioHelper { } } -extension AudioHelper { - enum FileSource { - case imported - case recorded - } -} - // MARK: - Configure AudioHelper extension AudioHelper { @@ -195,3 +183,31 @@ extension AudioHelper { subject.send(data) } } + +// MARK: - Audio Visualize + +extension AudioHelper { + private func visualize() { + Task { [weak self] in + guard let self else { return } + await self.setTimer() + } + } + + private func setTimer() { + timer = Timer(timeInterval: 0.125, repeats: true) { [weak self] _ in + Task { + await self?.calculateRecorderAmplitude() + } + } + RunLoop.main.add(timer!, forMode: .common) + } + + private func calculateRecorderAmplitude() async { + await recorder?.updateMeters() + guard let averagePower = await recorder?.getAveragePower() else { return } + let newAmplitude = 1.8 * pow(10.0, averagePower / 20.0) + let clampedAmplitude = min(max(newAmplitude, 0), 1) + amplitudeSubject.send(clampedAmplitude) + } +} From fcd88a84d0954e86da70150e4bd7534ac2b72045 Mon Sep 17 00:00:00 2001 From: moral-life Date: Sun, 1 Dec 2024 01:42:56 +0900 Subject: [PATCH 03/21] =?UTF-8?q?feat:=20LobbyViewController=20=EB=92=A4?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EA=B8=B0=20=EB=B2=84=ED=8A=BC,=20?= =?UTF-8?q?=EB=B0=A9=20=EC=BD=94=EB=93=9C=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EB=B0=94=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alsongDalsong/Sources/SceneDelegate.swift | 8 +- .../Views/Lobby/LobbyViewController.swift | 79 +++++++------------ 2 files changed, 32 insertions(+), 55 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift index 8d5724ff..1d426d42 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift @@ -1,11 +1,11 @@ -import ASContainer import ASCacheKit -import ASRepository +import ASContainer +import ASLogKit import ASNetworkKit +import ASRepository import ASRepositoryProtocol import Firebase import UIKit -import ASLogKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? @@ -36,7 +36,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { ) let onboardingVC = OnboardingViewController(viewmodel: onboardingVM, inviteCode: inviteCode) let navigationController = UINavigationController(rootViewController: onboardingVC) - navigationController.navigationBar.isHidden = true + navigationController.navigationBar.isHidden = false navigationController.interactivePopGestureRecognizer?.isEnabled = false window?.rootViewController = navigationController window?.makeKeyAndVisible() diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 4e6ab120..62267c60 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -2,9 +2,6 @@ import Combine import SwiftUI final class LobbyViewController: UIViewController { - let topNavigationView = UIView() - let backButton = UIButton() - let codeLabel = UILabel() let inviteButton = ASButton() let startButton = ASButton() private var hostingController: UIHostingController? @@ -28,6 +25,7 @@ final class LobbyViewController: UIViewController { setupLayout() setAction() bindToComponents() + setupNavigationBar() } override func viewWillDisappear(_ animated: Bool) { @@ -48,8 +46,9 @@ final class LobbyViewController: UIViewController { private func bindToComponents() { viewmodel.$roomNumber .receive(on: DispatchQueue.main) - .sink { [weak self] roomNumber in - self?.codeLabel.text = "#\(roomNumber)" + .sink { [weak self] _ in + guard let self else { return } + navigationController?.navigationBar.topItem?.title = "#\(self.viewmodel.roomNumber)" } .store(in: &cancellables) @@ -74,25 +73,35 @@ final class LobbyViewController: UIViewController { .store(in: &cancellables) } - private func setupUI() { - view.backgroundColor = .asLightGray - topNavigationView.translatesAutoresizingMaskIntoConstraints = false + private func setupNavigationBar() { + navigationController?.navigationBar.tintColor = .asBlack + navigationItem.hidesBackButton = true - backButton.translatesAutoresizingMaskIntoConstraints = false - var configuration = UIButton.Configuration.plain() + navigationController?.navigationBar.titleTextAttributes = [.font: UIFont.font(.dohyeon, ofSize: 24)] - configuration.image = UIImage(systemName: "rectangle.portrait.and.arrow.forward")? + let backButtonImage = UIImage(systemName: "rectangle.portrait.and.arrow.forward")? .withRenderingMode(.alwaysTemplate) - .withTintColor(.label) .applyingSymbolConfiguration(.init(pointSize: 24, weight: .medium))? .rotate(radians: .pi) - backButton.configuration = configuration - backButton.tintColor = .asBlack - codeLabel.translatesAutoresizingMaskIntoConstraints = false - codeLabel.font = .font(forTextStyle: .title1) - codeLabel.textColor = .label - codeLabel.textAlignment = .center + let backButtonAction = UIAction { [weak self] _ in + let alert = ASAlertController( + style: .default, + titleText: .leaveRoom, + doneButtonTitle: .leave, + cancelButtonTitle: .cancel + ) { [weak self] _ in + self?.viewmodel.leaveRoom() + self?.navigationController?.popViewController(animated: true) + } + self?.presentAlert(alert) + } + let barButton = UIBarButtonItem(image: backButtonImage, primaryAction: backButtonAction) + navigationItem.leftBarButtonItem = barButton + } + + private func setupUI() { + view.backgroundColor = .asLightGray inviteButton.setConfiguration(systemImageName: "link", title: "초대하기!", backgroundColor: .asYellow) inviteButton.translatesAutoresizingMaskIntoConstraints = false @@ -102,22 +111,6 @@ final class LobbyViewController: UIViewController { } private func setAction() { - backButton.addAction( - UIAction { [weak self] _ in - let alert = ASAlertController( - style: .default, - titleText: .leaveRoom, - doneButtonTitle: .leave, - cancelButtonTitle: .cancel - ) { [weak self] _ in - self?.viewmodel.leaveRoom() - self?.navigationController?.popViewController(animated: true) - } - self?.presentAlert(alert) - }, - for: .touchUpInside - ) - inviteButton.addAction(UIAction { [weak self] _ in guard let roomNumber = self?.viewmodel.roomNumber else { return } if let url = URL(string: "alsongDalsong://invite/?roomnumber=\(roomNumber)") { @@ -155,27 +148,11 @@ final class LobbyViewController: UIViewController { view.addSubview(lobbyView.view) view.addSubview(startButton) view.addSubview(inviteButton) - view.addSubview(topNavigationView) - topNavigationView.addSubview(codeLabel) - topNavigationView.addSubview(backButton) let safeArea = view.safeAreaLayoutGuide NSLayoutConstraint.activate([ - topNavigationView.topAnchor.constraint(equalTo: safeArea.topAnchor), - topNavigationView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), - topNavigationView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor), - topNavigationView.heightAnchor.constraint(equalToConstant: 44), - - backButton.leadingAnchor.constraint(equalTo: topNavigationView.leadingAnchor, constant: 16), - backButton.centerYAnchor.constraint(equalTo: topNavigationView.centerYAnchor), - backButton.heightAnchor.constraint(equalToConstant: 44), - backButton.widthAnchor.constraint(equalToConstant: 44), - - codeLabel.centerYAnchor.constraint(equalTo: topNavigationView.centerYAnchor), - codeLabel.centerXAnchor.constraint(equalTo: topNavigationView.centerXAnchor), - - lobbyView.view.topAnchor.constraint(equalTo: topNavigationView.bottomAnchor), + lobbyView.view.topAnchor.constraint(equalTo: safeArea.topAnchor), lobbyView.view.bottomAnchor.constraint(equalTo: inviteButton.topAnchor, constant: -20), lobbyView.view.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), lobbyView.view.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor), From 34490fbf7c493d22e0c3afa0ea17c80af7249f91 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 01:43:29 +0900 Subject: [PATCH 04/21] =?UTF-8?q?feat:=20=EC=9E=AC=EC=83=9D=20=EC=8B=9C=20?= =?UTF-8?q?=ED=8C=8C=ED=98=95=20=EC=83=89=EC=83=81=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Utils/AudioHelper.swift | 63 +++++++++++++++---- .../Views/RecordingPanel/RecordingPanel.swift | 34 ++++++++-- .../RecordingPanelViewModel.swift | 17 ++++- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift index f2d2bea4..4589296b 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift @@ -21,12 +21,13 @@ actor AudioHelper { private var source: FileSource = .imported private var playType: PlayType = .full private var isConcurrent: Bool = false - private var timer: Timer? + private var cancellable: AnyCancellable? // MARK: - Publishers private let amplitudeSubject = PassthroughSubject() private let playerStateSubject = PassthroughSubject<(FileSource, Bool), Never>() + private let waveformUpdateSubject = PassthroughSubject() private let recorderStateSubject = PassthroughSubject() private let recorderDataSubject = PassthroughSubject() var amplitudePublisher: AnyPublisher { amplitudeSubject.eraseToAnyPublisher() } @@ -35,6 +36,10 @@ actor AudioHelper { playerStateSubject.eraseToAnyPublisher() } + var waveformUpdatePublisher: AnyPublisher { + waveformUpdateSubject.eraseToAnyPublisher() + } + var recorderStatePublisher: AnyPublisher { recorderStateSubject.eraseToAnyPublisher() } @@ -55,9 +60,17 @@ actor AudioHelper { return await player.isPlaying() } + private func removePlayer() async { + player = nil + } + + private func removeRecorder() { + recorder = nil + } + private func removeTimer() { - timer?.invalidate() - timer = nil + cancellable?.cancel() + cancellable = nil } } @@ -70,7 +83,7 @@ extension AudioHelper { /// - source: 녹음 파일/url에서 가져온 파일 /// - playType: 전체 또는 부분 재생 /// - allowsConcurrent: 녹음과 동시에 재생 - func startPlaying(_ file: Data?, sourceType type: FileSource = .imported) async { + func startPlaying(_ file: Data?, sourceType type: FileSource = .imported, needsWaveUpdate: Bool = false) async { guard await checkRecorderState(), await checkPlayerState() else { return } guard let file else { return } @@ -80,13 +93,31 @@ extension AudioHelper { await self?.stopPlaying() } sendDataThrough(playerStateSubject, (source, true)) + if needsWaveUpdate { + updatePlayIndex() + } await player?.startPlaying(data: file, option: playType) } func stopPlaying() async { await player?.stopPlaying() - player = nil + await removePlayer() sendDataThrough(playerStateSubject, (source, false)) + removeTimer() + } + + private func updatePlayIndex() { + cancellable = Timer.publish(every: 0.125, on: .main, in: .common) + .autoconnect() + .scan(0) { count, _ in + count + 1 + } + .sink { [weak self] value in + guard let self else { return } + Task { + await self.sendDataThrough(self.waveformUpdateSubject, value - 1) + } + } } private func makePlayer() { @@ -96,6 +127,7 @@ extension AudioHelper { private func checkPlayerState() async -> Bool { if await isPlaying() { await player?.stopPlaying() + await removePlayer() sendDataThrough(playerStateSubject, (source, false)) } return true @@ -113,6 +145,7 @@ extension AudioHelper { recorderStateSubject.send(true) await recorder?.startRecording(url: tempURL) visualize() + Logger.debug("녹음 시작") do { try await Task.sleep(nanoseconds: 6 * 1_000_000_000) let recordedData = await stopRecording() @@ -123,8 +156,9 @@ extension AudioHelper { private func stopRecording() async -> Data? { let recordedData = await recorder?.stopRecording() + Logger.debug("녹음 정지") recorderStateSubject.send(false) - recorder = nil + removeRecorder() return recordedData } @@ -190,17 +224,20 @@ extension AudioHelper { private func visualize() { Task { [weak self] in guard let self else { return } - await self.setTimer() + await self.calculateAmplitude() } } - private func setTimer() { - timer = Timer(timeInterval: 0.125, repeats: true) { [weak self] _ in - Task { - await self?.calculateRecorderAmplitude() + private func calculateAmplitude() { + Logger.debug("진폭계산 시작") + cancellable = Timer.publish(every: 0.125, on: .main, in: .common) + .autoconnect() + .sink { [weak self] value in + guard let self else { return } + Task { + await self.calculateRecorderAmplitude() + } } - } - RunLoop.main.add(timer!, forMode: .common) } private func calculateRecorderAmplitude() async { diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanel.swift index 2ea4536c..ad4b91b1 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanel.swift @@ -58,6 +58,14 @@ final class RecordingPanel: UIView { .receive(on: DispatchQueue.main) .sink { [weak self] state in self?.updateButtonImage(with: state) + self?.updateWaveForm(state: state) + } + .store(in: &cancellables) + viewModel.$playIndex + .receive(on: DispatchQueue.main) + .sink { [weak self] index in + guard let index else { return } + self?.updateWaveForm(index: index) } .store(in: &cancellables) } @@ -138,7 +146,15 @@ final class RecordingPanel: UIView { } private func updateWaveForm(amplitude: CGFloat) { - waveFormView.updateVisualizerView(with: amplitude) + waveFormView.updateAmplitude(with: amplitude) + } + + private func updateWaveForm(index: Int) { + waveFormView.updatePlayingIndex(index) + } + + private func updateWaveForm(state: AudioButtonState) { + if state == .idle { waveFormView.resetColor() } } private func reset() { @@ -165,14 +181,14 @@ private final class WaveForm: UIView { numOfColumns = 48 super.init(coder: coder) } - + override func layoutSubviews() { super.layoutSubviews() if columns.isEmpty { drawVisualizerCircles() } } - + func resetView() { removeVisualizerCircles() } @@ -206,7 +222,17 @@ private final class WaveForm: UIView { columns.removeAll() } - fileprivate func updateVisualizerView(with amplitude: CGFloat, direction: Direction = .LTR) { + fileprivate func resetColor() { + for column in columns { + column.fillColor = UIColor.white.cgColor + } + } + + fileprivate func updatePlayingIndex(_ index: Int) { + columns[index].fillColor = UIColor.black.cgColor + } + + fileprivate func updateAmplitude(with amplitude: CGFloat, direction: Direction = .LTR) { guard columns.count == numOfColumns, count < numOfColumns else { return } let index = direction == .LTR ? count : numOfColumns - count - 1 columns[index].path = computeNewPath(for: columns[index], with: amplitude) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift index 271f0100..344a82fb 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift @@ -5,6 +5,7 @@ final class RecordingPanelViewModel: @unchecked Sendable { @Published var recordedData: Data? @Published public private(set) var recorderAmplitude: Float = 0.0 @Published public private(set) var buttonState: AudioButtonState = .idle + @Published public private(set) var playIndex: Int? private var cancellables = Set() @@ -42,7 +43,7 @@ final class RecordingPanelViewModel: @unchecked Sendable { return } if self?.buttonState == .idle { - await AudioHelper.shared.startPlaying(self?.recordedData, sourceType: .recorded) + await AudioHelper.shared.startPlaying(self?.recordedData, sourceType: .recorded, needsWaveUpdate: true) return } } @@ -54,7 +55,7 @@ final class RecordingPanelViewModel: @unchecked Sendable { await AudioHelper.shared.stopPlaying() } } - + private func bindAudioHelper() { Task { await AudioHelper.shared.amplitudePublisher @@ -64,6 +65,7 @@ final class RecordingPanelViewModel: @unchecked Sendable { self.recorderAmplitude = newAmplitude } .store(in: &self.cancellables) + await AudioHelper.shared.playerStatePublisher .receive(on: DispatchQueue.main) .sink { [weak self] source, isPlaying in @@ -77,12 +79,23 @@ final class RecordingPanelViewModel: @unchecked Sendable { } } .store(in: &self.cancellables) + + await AudioHelper.shared.waveformUpdatePublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] index in + guard let self else { return } + if index > self.sampleCount - 1 || index < 0 { return } + self.playIndex = index + } + .store(in: &self.cancellables) + await AudioHelper.shared.recorderStatePublisher .receive(on: DispatchQueue.main) .sink { [weak self] isRecording in self?.updateButtonState(isRecording ? .recording : .idle) } .store(in: &self.cancellables) + await AudioHelper.shared.recorderDataPublisher .receive(on: DispatchQueue.main) .sink { [weak self] data in From 9ee9059d2978d5b4c2f70352e12fb191c57069bc Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 02:22:16 +0900 Subject: [PATCH 05/21] =?UTF-8?q?fix:=20=EC=82=AD=EC=A0=9C=ED=96=88?= =?UTF-8?q?=EB=8D=98=20=EB=B3=80=EC=88=98=20=EB=B3=B5=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Views/RecordingPanel/RecordingPanelViewModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift index 344a82fb..d3834037 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift @@ -2,6 +2,7 @@ import Combine import Foundation final class RecordingPanelViewModel: @unchecked Sendable { + let sampleCount: Int = 48 @Published var recordedData: Data? @Published public private(set) var recorderAmplitude: Float = 0.0 @Published public private(set) var buttonState: AudioButtonState = .idle From 5b25979f740da1c77d37ad8824ae1dee015c94dd Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 16:28:21 +0900 Subject: [PATCH 06/21] =?UTF-8?q?refactor:=20ASAlertController=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UIKitComponents/ASAlertController.swift | 380 ------------------ .../Alert/ASAlertController.swift | 203 ++++++++++ .../Alert/DefaultAlertController.swift | 33 ++ .../Alert/InputAlertController.swift | 85 ++++ .../Alert/LoadingAlertController.swift | 65 +++ .../Alert/SingleButtonAlertController.swift | 28 ++ .../Extensions/UIViewController+present.swift | 4 - .../Sources/Views/Humming/HummingView.swift | 6 +- .../Views/Lobby/LobbyViewController.swift | 12 +- .../Onboarding/OnboardingViewController.swift | 8 +- .../Views/Rehumming/RehummingView.swift | 6 +- .../Result/HummingResultViewController.swift | 6 +- .../SelectMusicViewController.swift | 6 +- .../SubmitAnswerViewController.swift | 8 +- 14 files changed, 440 insertions(+), 410 deletions(-) delete mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASAlertController.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASAlertController.swift deleted file mode 100644 index 7002a63b..00000000 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASAlertController.swift +++ /dev/null @@ -1,380 +0,0 @@ -import UIKit - -class ASAlertController: UIViewController { - var textField = ASTextField() - private var titleText: ASAlertText.Title? - private var textFieldPlaceholder: ASAlertText.Placeholder? - private var doneButtonTitle: ASAlertText.ButtonText? - private var cancelButtonTitle: ASAlertText.ButtonText? - private var progressText: ASAlertText.ProgressText? - private var isUppercased: Bool = false - private var textMaxCount: Int = 6 - private var style: ASAlertStyle = .default - var doneButtonCompletion: ((String) -> Void)? - var cancelButtonCompletion: (() -> Void)? - var load: (() async throws -> Void)? - var errorCompletion: ((Error) -> Void)? - var text: String { - textField.text ?? "" - } - - private var alertView = ASPanel() - private var stackView = UIStackView() - private lazy var buttonStackView = UIStackView() - private lazy var titleLabel = UILabel() - private lazy var doneButton = ASButton() - private lazy var cancelButton = ASButton() - private lazy var progressView = UIActivityIndicatorView() - - override func viewDidLoad() { - super.viewDidLoad() - setupUI() - setStackView() - setLayout() - setupStyle() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) { - self.alertView.transform = .identity - } - } - - private func setupUI() { - view.backgroundColor = .black.withAlphaComponent(0.3) - setAlertView() - } - - private func setAlertView() { - alertView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) - alertView.backgroundColor = .asLightGray - alertView.layer.borderWidth = 2.5 - alertView.layer.borderColor = UIColor.asBlack.cgColor - view.addSubview(alertView) - } - - private func setStackView() { - stackView.axis = .vertical - stackView.spacing = 16 - stackView.distribution = .fillProportionally - stackView.alignment = .center - alertView.addSubview(stackView) - } - - private func setLayout() { - alertView.translatesAutoresizingMaskIntoConstraints = false - stackView.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - alertView.centerXAnchor.constraint(equalTo: view.centerXAnchor), - alertView.centerYAnchor.constraint(equalTo: view.centerYAnchor), - alertView.widthAnchor.constraint(equalToConstant: style == .load ? 232 : 345), - - stackView.topAnchor.constraint(equalTo: alertView.topAnchor, constant: 16), - stackView.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -16), - stackView.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 16), - stackView.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: -16), - ]) - } - - private func setTitle() { - titleLabel.text = titleText?.description - titleLabel.textAlignment = .center - titleLabel.font = .font(forTextStyle: .title2) - titleLabel.numberOfLines = 0 - stackView.addArrangedSubview(titleLabel) - - titleLabel.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - titleLabel.centerXAnchor.constraint(equalTo: stackView.centerXAnchor), - titleLabel.topAnchor.constraint(equalTo: stackView.topAnchor), - ]) - } - - private func setTextField() { - textField.setConfiguration(placeholder: textFieldPlaceholder?.description) - stackView.addArrangedSubview(textField) - if isUppercased { - textField.delegate = self - } - textField.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - textField.widthAnchor.constraint(equalTo: stackView.widthAnchor), - textField.heightAnchor.constraint(equalToConstant: 44), - ]) - textField.heightAnchor.constraint(equalToConstant: 43).priority = .defaultHigh - } - - private func setButtonStackView() { - buttonStackView.axis = .horizontal - buttonStackView.spacing = 20 - buttonStackView.distribution = .fillEqually - buttonStackView.alignment = .center - stackView.addArrangedSubview(buttonStackView) - NSLayoutConstraint.activate([ - buttonStackView.widthAnchor.constraint(equalTo: stackView.widthAnchor), - buttonStackView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), - ]) - buttonStackView.heightAnchor.constraint(equalToConstant: 56).priority = .defaultHigh - } - - private func setDoneButton() { - doneButton.translatesAutoresizingMaskIntoConstraints = false - buttonStackView.addArrangedSubview(doneButton) - doneButton.setConfiguration( - title: doneButtonTitle?.description, - backgroundColor: .asLightSky, - textSize: 24 - ) - doneButton.addAction(UIAction { [weak self] _ in - self?.doneButtonCompletion?(self?.text ?? "") - self?.dismiss(animated: true) - }, for: .touchUpInside) - doneButton.heightAnchor.constraint(equalToConstant: 40).isActive = true - } - - private func setCancelButton() { - buttonStackView.addArrangedSubview(cancelButton) - cancelButton.translatesAutoresizingMaskIntoConstraints = false - cancelButton.setConfiguration( - title: cancelButtonTitle?.description, - backgroundColor: .asLightRed, - textSize: 24 - ) - cancelButton.addAction(UIAction { [weak self] _ in - self?.cancelButtonCompletion?() - self?.dismiss(animated: true) - }, for: .touchUpInside) - cancelButton.heightAnchor.constraint(equalToConstant: 40).isActive = true - } - - private func setProgressView() { - stackView.addArrangedSubview(progressView) - progressView.translatesAutoresizingMaskIntoConstraints = false - progressView.startAnimating() - progressView.style = .large - progressView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 12).isActive = true - progressView.hidesWhenStopped = true - Task { - if let load { - do { - try await load() - dismiss(animated: true) - } catch { - dismiss(animated: true) { [weak self] in - self?.errorCompletion?(error) - } - } - } - } - } - - private func setProgressText() { - let progressLabel = UILabel() - progressLabel.text = progressText?.description - progressLabel.font = .font(forTextStyle: .title2) - progressLabel.textColor = .label - stackView.addArrangedSubview(progressLabel) - progressLabel.translatesAutoresizingMaskIntoConstraints = false - progressLabel.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 12).isActive = true - } - - private func setupStyle() { - switch style { - case .input: - setTitle() - setTextField() - setButtonStackView() - setCancelButton() - setDoneButton() - case .default: - setTitle() - setButtonStackView() - setCancelButton() - setDoneButton() - case .confirm: - setTitle() - setButtonStackView() - setDoneButton() - case .load: - setProgressView() - setProgressText() - } - } -} - -// MARK: - Init - -extension ASAlertController { - convenience init( - style: ASAlertStyle = .input, - titleText: ASAlertText.Title, - doneButtonTitle: ASAlertText.ButtonText = .done, - cancelButtonTitle: ASAlertText.ButtonText = .cancel, - textFieldPlaceholder: ASAlertText.Placeholder, - isUppercased: Bool = false, - doneButtonCompletion: ((String) -> Void)? = nil, - cancelButtonTitleCompletion: (() -> Void)? = nil - ) { - self.init() - self.style = style - self.titleText = titleText - self.textFieldPlaceholder = textFieldPlaceholder - self.doneButtonTitle = doneButtonTitle - self.cancelButtonTitle = cancelButtonTitle - self.doneButtonCompletion = doneButtonCompletion - cancelButtonCompletion = cancelButtonTitleCompletion - self.isUppercased = isUppercased - - modalTransitionStyle = .crossDissolve - modalPresentationStyle = .overFullScreen - } - - convenience init( - style: ASAlertStyle = .default, - titleText: ASAlertText.Title, - doneButtonTitle: ASAlertText.ButtonText = .done, - cancelButtonTitle: ASAlertText.ButtonText = .cancel, - doneButtonCompletion: ((String) -> Void)? = nil, - cancelButtonTitleCompletion: (() -> Void)? = nil - ) { - self.init() - self.style = style - self.titleText = titleText - self.doneButtonTitle = doneButtonTitle - self.cancelButtonTitle = cancelButtonTitle - self.doneButtonCompletion = doneButtonCompletion - cancelButtonCompletion = cancelButtonTitleCompletion - - modalTransitionStyle = .crossDissolve - modalPresentationStyle = .overFullScreen - } - - convenience init( - style: ASAlertStyle = .confirm, - titleText: ASAlertText.Title, - doneButtonTitle: ASAlertText.ButtonText = .confirm, - doneButtonCompletion: ((String) -> Void)? = nil - ) { - self.init() - self.style = style - self.titleText = titleText - self.doneButtonTitle = doneButtonTitle - self.doneButtonCompletion = doneButtonCompletion - - modalTransitionStyle = .crossDissolve - modalPresentationStyle = .overFullScreen - } - - convenience init( - _ type: ASAlertStyle = .load, - progressText: ASAlertText.ProgressText, - load: (() async throws -> Void)? = nil, - errorCompletion: ((Error) -> Void)? = nil - ) { - self.init() - style = type - self.progressText = progressText - self.load = load - self.errorCompletion = errorCompletion - - modalTransitionStyle = .crossDissolve - modalPresentationStyle = .overFullScreen - } -} - -// MARK: - Delegate - -extension ASAlertController: UITextFieldDelegate { - func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - let uppercaseString = string.uppercased() - - if let text = textField.text, - let textRange = Range(range, in: text) - { - let updatedText = text.replacingCharacters(in: textRange, with: uppercaseString) - if updatedText.count <= textMaxCount { - textField.text = updatedText - } - return false - } - - return true - } -} - -// MARK: - Alert Type - -enum ASAlertStyle { - case input - case `default` - case confirm - case load -} - -enum ASAlertText { - enum Title: CustomStringConvertible { - case leaveRoom - case joinRoom - case joinFailed - case error(Error) - case needMorePlayer - - var description: String { - switch self { - case .leaveRoom: "방을 나가시겠습니까?" - case .joinRoom: "게임 입장 코드를 입력하세요" - case .joinFailed: "참가에 실패하였습니다." - case let .error(error): "\(error.localizedDescription)\n 잠시후에 다시 시도해주세요" - case .needMorePlayer: "알쏭달쏭은 여럿이서 할 수록\n재미있는 게임이에요!\n그래도 하시겠어요?" - } - } - } - - enum ButtonText: String, CustomStringConvertible { - case leave - case cancel - case done - case confirm - case keep - - var description: String { - switch self { - case .leave: "나가기" - case .cancel: "취소" - case .done: "완료" - case .confirm: "확인" - case .keep: "계속 하기" - } - } - } - - enum Placeholder: String, CustomStringConvertible { - case roomNumber - - var description: String { - switch self { - case .roomNumber: "000000" - } - } - } - - enum ProgressText: CustomStringConvertible { - case joinRoom - case startGame - case submitMusic - case submitHumming - case nextResult - var description: String { - switch self { - case .joinRoom: "방 정보를 가져오는 중..." - case .startGame: "게임을 시작하는 중..." - case .submitMusic: "노래를 전송하는 중..." - case .submitHumming: "허밍을 전송하는 중..." - case .nextResult: "다음 결과를 가져오는 중..." - } - } - } -} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift new file mode 100644 index 00000000..1d4330c1 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift @@ -0,0 +1,203 @@ +import UIKit + +class ASAlertController: UIViewController { + var titleText: ASAlertText.Title? + var doneButtonTitle: ASAlertText.ButtonText? + var cancelButtonTitle: ASAlertText.ButtonText? + var doneButtonCompletion: ((String) -> Void)? + var cancelButtonCompletion: (() -> Void)? + + var alertView = ASPanel() + var stackView = UIStackView() + lazy var buttonStackView = UIStackView() + lazy var titleLabel = UILabel() + lazy var doneButton = ASButton() + lazy var cancelButton = ASButton() + lazy var progressView = UIActivityIndicatorView() + + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + setStackView() + setLayout() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) { + self.alertView.transform = .identity + } + } + + private func setupUI() { + view.backgroundColor = .black.withAlphaComponent(0.3) + setAlertView() + } + + private func setAlertView() { + alertView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) + alertView.backgroundColor = .asLightGray + alertView.layer.borderWidth = 2.5 + alertView.layer.borderColor = UIColor.asBlack.cgColor + view.addSubview(alertView) + } + + private func setStackView() { + stackView.axis = .vertical + stackView.spacing = 16 + stackView.distribution = .fillProportionally + stackView.alignment = .center + alertView.addSubview(stackView) + } + + func alertViewWidthConstraint() -> NSLayoutConstraint { + return alertView.widthAnchor.constraint(equalToConstant: 345) + } + + private func setLayout() { + alertView.translatesAutoresizingMaskIntoConstraints = false + stackView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + alertView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + alertView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + alertViewWidthConstraint(), + + stackView.topAnchor.constraint(equalTo: alertView.topAnchor, constant: 16), + stackView.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -16), + stackView.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 16), + stackView.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: -16), + ]) + } + + func setTitle() { + titleLabel.text = titleText?.description + titleLabel.textAlignment = .center + titleLabel.font = .font(forTextStyle: .title2) + titleLabel.numberOfLines = 0 + stackView.addArrangedSubview(titleLabel) + + titleLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + titleLabel.centerXAnchor.constraint(equalTo: stackView.centerXAnchor), + titleLabel.topAnchor.constraint(equalTo: stackView.topAnchor), + ]) + } + + func setButtonStackView() { + buttonStackView.axis = .horizontal + buttonStackView.spacing = 20 + buttonStackView.distribution = .fillEqually + buttonStackView.alignment = .center + stackView.addArrangedSubview(buttonStackView) + NSLayoutConstraint.activate([ + buttonStackView.widthAnchor.constraint(equalTo: stackView.widthAnchor), + buttonStackView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), + ]) + buttonStackView.heightAnchor.constraint(equalToConstant: 56).priority = .defaultHigh + } + + func setDoneButton() { + doneButton.translatesAutoresizingMaskIntoConstraints = false + buttonStackView.addArrangedSubview(doneButton) + doneButton.setConfiguration( + title: doneButtonTitle?.description, + backgroundColor: .asLightSky, + textSize: 24 + ) + doneButton.addAction(UIAction { [weak self] _ in + self?.dismiss(animated: true) + }, for: .touchUpInside) + doneButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + } + + func setCancelButton() { + buttonStackView.addArrangedSubview(cancelButton) + cancelButton.translatesAutoresizingMaskIntoConstraints = false + cancelButton.setConfiguration( + title: cancelButtonTitle?.description, + backgroundColor: .asLightRed, + textSize: 24 + ) + cancelButton.addAction(UIAction { [weak self] _ in + self?.cancelButtonCompletion?() + self?.dismiss(animated: true) + }, for: .touchUpInside) + cancelButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + } +} + +// MARK: - Alert Type + +enum ASAlertStyle { + case input + case `default` + case confirm + case load +} + +enum ASAlertText { + enum Title: CustomStringConvertible { + case leaveRoom + case joinRoom + case joinFailed + case error(Error) + case needMorePlayer + + var description: String { + switch self { + case .leaveRoom: "방을 나가시겠습니까?" + case .joinRoom: "게임 입장 코드를 입력하세요" + case .joinFailed: "참가에 실패하였습니다." + case let .error(error): "\(error.localizedDescription)\n 잠시후에 다시 시도해주세요" + case .needMorePlayer: "알쏭달쏭은 여럿이서 할 수록\n재미있는 게임이에요!\n그래도 하시겠어요?" + } + } + } + + enum ButtonText: String, CustomStringConvertible { + case leave + case cancel + case done + case confirm + case keep + + var description: String { + switch self { + case .leave: "나가기" + case .cancel: "취소" + case .done: "완료" + case .confirm: "확인" + case .keep: "계속 하기" + } + } + } + + enum Placeholder: String, CustomStringConvertible { + case roomNumber + + var description: String { + switch self { + case .roomNumber: "000000" + } + } + } + + enum ProgressText: CustomStringConvertible { + case joinRoom + case startGame + case submitMusic + case submitHumming + case nextResult + var description: String { + switch self { + case .joinRoom: "방 정보를 가져오는 중..." + case .startGame: "게임을 시작하는 중..." + case .submitMusic: "노래를 전송하는 중..." + case .submitHumming: "허밍을 전송하는 중..." + case .nextResult: "다음 결과를 가져오는 중..." + } + } + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift new file mode 100644 index 00000000..f2081cb5 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift @@ -0,0 +1,33 @@ +import UIKit + +final class DefaultAlertController: ASAlertController { + override func viewDidLoad() { + super.viewDidLoad() + setupStyle() + } + + func setupStyle() { + setTitle() + setButtonStackView() + setCancelButton() + setDoneButton() + } + + convenience init( + titleText: ASAlertText.Title, + doneButtonTitle: ASAlertText.ButtonText = .done, + cancelButtonTitle: ASAlertText.ButtonText = .cancel, + doneButtonCompletion: ((String) -> Void)? = nil, + cancelButtonTitleCompletion: (() -> Void)? = nil + ) { + self.init() + self.titleText = titleText + self.doneButtonTitle = doneButtonTitle + self.cancelButtonTitle = cancelButtonTitle + self.doneButtonCompletion = doneButtonCompletion + cancelButtonCompletion = cancelButtonTitleCompletion + + modalTransitionStyle = .crossDissolve + modalPresentationStyle = .overFullScreen + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift new file mode 100644 index 00000000..509cd687 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift @@ -0,0 +1,85 @@ +import UIKit + +final class InputAlertController: ASAlertController { + var textField = ASTextField() + var textFieldPlaceholder: ASAlertText.Placeholder? + var textMaxCount: Int = 6 + var isUppercased: Bool = false + var text: String { + textField.text ?? "" + } + + override func viewDidLoad() { + super.viewDidLoad() + setupStyle() + } + + func setupStyle() { + setTitle() + setTextField() + setButtonStackView() + setCancelButton() + setDoneButton() + } + + override func setDoneButton() { + super.setDoneButton() + doneButton.addAction(UIAction { [weak self] _ in + self?.doneButtonCompletion?(self?.text ?? "") + }, for: .touchUpInside) + } + + private func setTextField() { + textField.setConfiguration(placeholder: textFieldPlaceholder?.description) + stackView.addArrangedSubview(textField) + if isUppercased { + textField.delegate = self + } + textField.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + textField.widthAnchor.constraint(equalTo: stackView.widthAnchor), + textField.heightAnchor.constraint(equalToConstant: 44), + ]) + textField.heightAnchor.constraint(equalToConstant: 43).priority = .defaultHigh + } + + convenience init( + titleText: ASAlertText.Title, + doneButtonTitle: ASAlertText.ButtonText = .done, + cancelButtonTitle: ASAlertText.ButtonText = .cancel, + textFieldPlaceholder: ASAlertText.Placeholder, + isUppercased: Bool = false, + doneButtonCompletion: ((String) -> Void)? = nil, + cancelButtonTitleCompletion: (() -> Void)? = nil + ) { + self.init() + self.titleText = titleText + self.textFieldPlaceholder = textFieldPlaceholder + self.doneButtonTitle = doneButtonTitle + self.cancelButtonTitle = cancelButtonTitle + self.doneButtonCompletion = doneButtonCompletion + cancelButtonCompletion = cancelButtonTitleCompletion + self.isUppercased = isUppercased + + modalTransitionStyle = .crossDissolve + modalPresentationStyle = .overFullScreen + } +} + +extension InputAlertController: UITextFieldDelegate { + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let uppercaseString = string.uppercased() + + if let text = textField.text, + let textRange = Range(range, in: text) + { + let updatedText = text.replacingCharacters(in: textRange, with: uppercaseString) + if updatedText.count <= textMaxCount { + textField.text = updatedText + } + return false + } + + return true + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift new file mode 100644 index 00000000..65d5c741 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift @@ -0,0 +1,65 @@ +import UIKit + +final class LoadingAlertController: ASAlertController { + var load: (() async throws -> Void)? + var errorCompletion: ((Error) -> Void)? + var progressText: ASAlertText.ProgressText? + + override func viewDidLoad() { + super.viewDidLoad() + setupStyle() + } + + override func alertViewWidthConstraint() -> NSLayoutConstraint { + return alertView.widthAnchor.constraint(equalToConstant: 232) + } + + func setupStyle() { + setProgressView() + setProgressText() + } + + func setProgressView() { + stackView.addArrangedSubview(progressView) + progressView.translatesAutoresizingMaskIntoConstraints = false + progressView.startAnimating() + progressView.style = .large + progressView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 12).isActive = true + progressView.hidesWhenStopped = true + Task { + guard let load else { return } + do { + try await load() + dismiss(animated: true) + } catch { + dismiss(animated: true) { [weak self] in + self?.errorCompletion?(error) + } + } + } + } + + private func setProgressText() { + let progressLabel = UILabel() + progressLabel.text = progressText?.description + progressLabel.font = .font(forTextStyle: .title2) + progressLabel.textColor = .label + stackView.addArrangedSubview(progressLabel) + progressLabel.translatesAutoresizingMaskIntoConstraints = false + progressLabel.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 12).isActive = true + } + + convenience init( + progressText: ASAlertText.ProgressText, + load: (() async throws -> Void)? = nil, + errorCompletion: ((Error) -> Void)? = nil + ) { + self.init() + self.progressText = progressText + self.load = load + self.errorCompletion = errorCompletion + + modalTransitionStyle = .crossDissolve + modalPresentationStyle = .overFullScreen + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift new file mode 100644 index 00000000..0ac6961c --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift @@ -0,0 +1,28 @@ +import UIKit + +final class SingleButtonAlertController: ASAlertController { + override func viewDidLoad() { + super.viewDidLoad() + setupStyle() + } + + func setupStyle() { + setTitle() + setButtonStackView() + setDoneButton() + } + + convenience init( + titleText: ASAlertText.Title, + doneButtonTitle: ASAlertText.ButtonText = .confirm, + doneButtonCompletion: ((String) -> Void)? = nil + ) { + self.init() + self.titleText = titleText + self.doneButtonTitle = doneButtonTitle + self.doneButtonCompletion = doneButtonCompletion + + modalTransitionStyle = .crossDissolve + modalPresentationStyle = .overFullScreen + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIViewController+present.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIViewController+present.swift index 4b79a6b0..c16ac134 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIViewController+present.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIViewController+present.swift @@ -4,8 +4,4 @@ extension UIViewController { func presentAlert(_ viewcontrollerToPresent: ASAlertController) { present(viewcontrollerToPresent, animated: true, completion: nil) } - - func presentLoadingView(_ viewcontrollerToPresent: ASAlertController) { - present(viewcontrollerToPresent, animated: true, completion: nil) - } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift index 21b9d43f..9d682e62 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift @@ -126,7 +126,7 @@ final class HummingViewController: UIViewController { extension HummingViewController { private func showSubmitHummingLoading() { - let alert = ASAlertController( + let alert = LoadingAlertController( progressText: .submitHumming, load: { [weak self] in try await self?.submitHumming() @@ -135,11 +135,11 @@ extension HummingViewController { self?.showFailSubmitMusic(error) } ) - presentLoadingView(alert) + presentAlert(alert) } private func showFailSubmitMusic(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 4e6ab120..53af570f 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -104,8 +104,7 @@ final class LobbyViewController: UIViewController { private func setAction() { backButton.addAction( UIAction { [weak self] _ in - let alert = ASAlertController( - style: .default, + let alert = DefaultAlertController( titleText: .leaveRoom, doneButtonTitle: .leave, cancelButtonTitle: .cancel @@ -131,8 +130,7 @@ final class LobbyViewController: UIViewController { UIAction { [weak self] _ in guard let playerCount = self?.viewmodel.players.count else { return } if playerCount < 3 { - let alert = ASAlertController( - style: .default, + let alert = DefaultAlertController( titleText: .needMorePlayer, doneButtonTitle: .keep, cancelButtonTitle: .cancel @@ -205,7 +203,7 @@ final class LobbyViewController: UIViewController { extension LobbyViewController { func showStartGameLoading() { - let alert = ASAlertController( + let alert = LoadingAlertController( progressText: .startGame, load: { [weak self] in try await self?.gameStart() @@ -214,11 +212,11 @@ extension LobbyViewController { self?.showStartGameFailed(error) } ) - presentLoadingView(alert) + presentAlert(alert) } func showStartGameFailed(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift index 1a0d23be..4870befa 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift @@ -225,7 +225,7 @@ extension OnboardingViewController { extension OnboardingViewController { private func showRoomNumerInputAlert() { - let alert = ASAlertController( + let alert = InputAlertController( titleText: .joinRoom, textFieldPlaceholder: .roomNumber, isUppercased: true @@ -236,12 +236,12 @@ extension OnboardingViewController { } private func showRoomFailedAlert(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } private func showCreateRoomLoading() { - let alert = ASAlertController( + let alert = LoadingAlertController( progressText: .joinRoom, load: { [weak self] in try await self?.setNicknameAndCreateRoom() @@ -250,7 +250,7 @@ extension OnboardingViewController { self?.showRoomFailedAlert(error) } ) - presentLoadingView(alert) + presentAlert(alert) } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift index 8e30a9eb..5b66b74e 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift @@ -130,7 +130,7 @@ final class RehummingViewController: UIViewController { extension RehummingViewController { private func showSubmitHummingLoading() { - let alert = ASAlertController( + let alert = LoadingAlertController( progressText: .submitHumming, load: { [weak self] in try await self?.submitHumming() @@ -139,11 +139,11 @@ extension RehummingViewController { self?.showFailSubmitMusic(error) } ) - presentLoadingView(alert) + presentAlert(alert) } private func showFailSubmitMusic(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift index 12f0575b..31fa7340 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift @@ -251,7 +251,7 @@ extension HummingResultViewController: UITableViewDataSource { extension HummingResultViewController { private func showNextResultLoading() { - let alert = ASAlertController( + let alert = LoadingAlertController( progressText: .nextResult, load: { [weak self] in try await self?.nextResultFetch() @@ -260,11 +260,11 @@ extension HummingResultViewController { self?.showFailNextLoading(error) } ) - presentLoadingView(alert) + presentAlert(alert) } private func showFailNextLoading(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift index 62c8a949..2abeacf2 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift @@ -102,7 +102,7 @@ class SelectMusicViewController: UIViewController { extension SelectMusicViewController { private func showSubmitMusicLoading() { - let alert = ASAlertController( + let alert = LoadingAlertController( progressText: .submitMusic, load: { [weak self] in try await self?.submitMusic() @@ -110,11 +110,11 @@ extension SelectMusicViewController { errorCompletion: { [weak self] error in self?.showFailSubmitMusic(error) }) - presentLoadingView(alert) + presentAlert(alert) } private func showFailSubmitMusic(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift index fbf4c8ed..3c5e962b 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift @@ -125,16 +125,18 @@ final class SubmitAnswerViewController: UIViewController { extension SubmitAnswerViewController { private func showSubmitAnswerLoading() { - let alert = ASAlertController(progressText: .submitMusic, load: { [weak self] in + let alert = LoadingAlertController( + progressText: .submitMusic, + load: { [weak self] in try await self?.submitAnswer() }) { [weak self] error in self?.showFailSubmitMusic(error) } - presentLoadingView(alert) + presentAlert(alert) } private func showFailSubmitMusic(_ error: Error) { - let alert = ASAlertController(titleText: .error(error)) + let alert = SingleButtonAlertController(titleText: .error(error)) presentAlert(alert) } } From 17c88e8adb0aa58ea25966d7422818641e83d5e6 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 17:01:24 +0900 Subject: [PATCH 07/21] =?UTF-8?q?refactor:=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Alert/ASAlertController.swift | 42 +++++++++---------- .../Alert/DefaultAlertController.swift | 20 ++++----- .../Alert/InputAlertController.swift | 28 ++++++------- .../Alert/LoadingAlertController.swift | 4 +- .../Alert/SingleButtonAlertController.swift | 10 ++--- .../Sources/Views/Humming/HummingView.swift | 2 +- .../Views/Lobby/LobbyViewController.swift | 10 ++--- .../Onboarding/OnboardingViewController.swift | 2 +- .../Views/Rehumming/RehummingView.swift | 2 +- .../Result/HummingResultViewController.swift | 2 +- .../SelectMusicViewController.swift | 2 +- .../SubmitAnswerViewController.swift | 2 +- 12 files changed, 63 insertions(+), 63 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift index 1d4330c1..2cd61c6d 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift @@ -2,17 +2,17 @@ import UIKit class ASAlertController: UIViewController { var titleText: ASAlertText.Title? - var doneButtonTitle: ASAlertText.ButtonText? - var cancelButtonTitle: ASAlertText.ButtonText? - var doneButtonCompletion: ((String) -> Void)? - var cancelButtonCompletion: (() -> Void)? + var primaryButtonText: ASAlertText.ButtonText? + var secondaryButtonText: ASAlertText.ButtonText? + var primaryButtonAction: ((String) -> Void)? + var secondaryButtonAction: (() -> Void)? var alertView = ASPanel() var stackView = UIStackView() lazy var buttonStackView = UIStackView() lazy var titleLabel = UILabel() - lazy var doneButton = ASButton() - lazy var cancelButton = ASButton() + lazy var primaryButton = ASButton() + lazy var secondaryButton = ASButton() lazy var progressView = UIActivityIndicatorView() override func viewDidLoad() { @@ -98,33 +98,33 @@ class ASAlertController: UIViewController { buttonStackView.heightAnchor.constraint(equalToConstant: 56).priority = .defaultHigh } - func setDoneButton() { - doneButton.translatesAutoresizingMaskIntoConstraints = false - buttonStackView.addArrangedSubview(doneButton) - doneButton.setConfiguration( - title: doneButtonTitle?.description, + func setPrimaryButton() { + primaryButton.translatesAutoresizingMaskIntoConstraints = false + buttonStackView.addArrangedSubview(primaryButton) + primaryButton.setConfiguration( + title: primaryButtonText?.description, backgroundColor: .asLightSky, textSize: 24 ) - doneButton.addAction(UIAction { [weak self] _ in + primaryButton.addAction(UIAction { [weak self] _ in self?.dismiss(animated: true) }, for: .touchUpInside) - doneButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + primaryButton.heightAnchor.constraint(equalToConstant: 40).isActive = true } - func setCancelButton() { - buttonStackView.addArrangedSubview(cancelButton) - cancelButton.translatesAutoresizingMaskIntoConstraints = false - cancelButton.setConfiguration( - title: cancelButtonTitle?.description, + func setSecondaryButton() { + buttonStackView.addArrangedSubview(secondaryButton) + secondaryButton.translatesAutoresizingMaskIntoConstraints = false + secondaryButton.setConfiguration( + title: secondaryButtonText?.description, backgroundColor: .asLightRed, textSize: 24 ) - cancelButton.addAction(UIAction { [weak self] _ in - self?.cancelButtonCompletion?() + secondaryButton.addAction(UIAction { [weak self] _ in + self?.secondaryButtonAction?() self?.dismiss(animated: true) }, for: .touchUpInside) - cancelButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + secondaryButton.heightAnchor.constraint(equalToConstant: 40).isActive = true } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift index f2081cb5..bf88f56a 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift @@ -9,23 +9,23 @@ final class DefaultAlertController: ASAlertController { func setupStyle() { setTitle() setButtonStackView() - setCancelButton() - setDoneButton() + setSecondaryButton() + setPrimaryButton() } convenience init( titleText: ASAlertText.Title, - doneButtonTitle: ASAlertText.ButtonText = .done, - cancelButtonTitle: ASAlertText.ButtonText = .cancel, - doneButtonCompletion: ((String) -> Void)? = nil, - cancelButtonTitleCompletion: (() -> Void)? = nil + primaryButtonText: ASAlertText.ButtonText = .done, + secondaryButtonText: ASAlertText.ButtonText = .cancel, + primaryButtonAction: ((String) -> Void)? = nil, + secondaryButtonAction: (() -> Void)? = nil ) { self.init() self.titleText = titleText - self.doneButtonTitle = doneButtonTitle - self.cancelButtonTitle = cancelButtonTitle - self.doneButtonCompletion = doneButtonCompletion - cancelButtonCompletion = cancelButtonTitleCompletion + self.primaryButtonText = primaryButtonText + self.secondaryButtonText = secondaryButtonText + self.primaryButtonAction = primaryButtonAction + self.secondaryButtonAction = secondaryButtonAction modalTransitionStyle = .crossDissolve modalPresentationStyle = .overFullScreen diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift index 509cd687..1182be1a 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift @@ -18,14 +18,14 @@ final class InputAlertController: ASAlertController { setTitle() setTextField() setButtonStackView() - setCancelButton() - setDoneButton() + setSecondaryButton() + setPrimaryButton() } - override func setDoneButton() { - super.setDoneButton() - doneButton.addAction(UIAction { [weak self] _ in - self?.doneButtonCompletion?(self?.text ?? "") + override func setPrimaryButton() { + super.setPrimaryButton() + primaryButton.addAction(UIAction { [weak self] _ in + self?.primaryButtonAction?(self?.text ?? "") }, for: .touchUpInside) } @@ -45,20 +45,20 @@ final class InputAlertController: ASAlertController { convenience init( titleText: ASAlertText.Title, - doneButtonTitle: ASAlertText.ButtonText = .done, - cancelButtonTitle: ASAlertText.ButtonText = .cancel, + primaryButtonText: ASAlertText.ButtonText = .done, + secondaryButtonText: ASAlertText.ButtonText = .cancel, textFieldPlaceholder: ASAlertText.Placeholder, isUppercased: Bool = false, - doneButtonCompletion: ((String) -> Void)? = nil, - cancelButtonTitleCompletion: (() -> Void)? = nil + primaryButtonAction: ((String) -> Void)? = nil, + secondaryButtonAction: (() -> Void)? = nil ) { self.init() self.titleText = titleText self.textFieldPlaceholder = textFieldPlaceholder - self.doneButtonTitle = doneButtonTitle - self.cancelButtonTitle = cancelButtonTitle - self.doneButtonCompletion = doneButtonCompletion - cancelButtonCompletion = cancelButtonTitleCompletion + self.primaryButtonText = primaryButtonText + self.secondaryButtonText = secondaryButtonText + self.primaryButtonAction = primaryButtonAction + self.secondaryButtonAction = secondaryButtonAction self.isUppercased = isUppercased modalTransitionStyle = .crossDissolve diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift index 65d5c741..95912a89 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift @@ -51,12 +51,12 @@ final class LoadingAlertController: ASAlertController { convenience init( progressText: ASAlertText.ProgressText, - load: (() async throws -> Void)? = nil, + loadAction: (() async throws -> Void)? = nil, errorCompletion: ((Error) -> Void)? = nil ) { self.init() self.progressText = progressText - self.load = load + self.load = loadAction self.errorCompletion = errorCompletion modalTransitionStyle = .crossDissolve diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift index 0ac6961c..b8affe03 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift @@ -9,18 +9,18 @@ final class SingleButtonAlertController: ASAlertController { func setupStyle() { setTitle() setButtonStackView() - setDoneButton() + setPrimaryButton() } convenience init( titleText: ASAlertText.Title, - doneButtonTitle: ASAlertText.ButtonText = .confirm, - doneButtonCompletion: ((String) -> Void)? = nil + primaryButtonText: ASAlertText.ButtonText = .confirm, + primaryButtonAction: ((String) -> Void)? = nil ) { self.init() self.titleText = titleText - self.doneButtonTitle = doneButtonTitle - self.doneButtonCompletion = doneButtonCompletion + self.primaryButtonText = primaryButtonText + self.primaryButtonAction = primaryButtonAction modalTransitionStyle = .crossDissolve modalPresentationStyle = .overFullScreen diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift index 9d682e62..9bfef4e4 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift @@ -128,7 +128,7 @@ extension HummingViewController { private func showSubmitHummingLoading() { let alert = LoadingAlertController( progressText: .submitHumming, - load: { [weak self] in + loadAction: { [weak self] in try await self?.submitHumming() }, errorCompletion: { [weak self] error in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 53af570f..12a7c9b9 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -106,8 +106,8 @@ final class LobbyViewController: UIViewController { UIAction { [weak self] _ in let alert = DefaultAlertController( titleText: .leaveRoom, - doneButtonTitle: .leave, - cancelButtonTitle: .cancel + primaryButtonText: .leave, + secondaryButtonText: .cancel ) { [weak self] _ in self?.viewmodel.leaveRoom() self?.navigationController?.popViewController(animated: true) @@ -132,8 +132,8 @@ final class LobbyViewController: UIViewController { if playerCount < 3 { let alert = DefaultAlertController( titleText: .needMorePlayer, - doneButtonTitle: .keep, - cancelButtonTitle: .cancel + primaryButtonText: .keep, + secondaryButtonText: .cancel ) { [weak self] _ in self?.showStartGameLoading() } @@ -205,7 +205,7 @@ extension LobbyViewController { func showStartGameLoading() { let alert = LoadingAlertController( progressText: .startGame, - load: { [weak self] in + loadAction: { [weak self] in try await self?.gameStart() }, errorCompletion: { [weak self] error in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift index 4870befa..40ee94e7 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift @@ -243,7 +243,7 @@ extension OnboardingViewController { private func showCreateRoomLoading() { let alert = LoadingAlertController( progressText: .joinRoom, - load: { [weak self] in + loadAction: { [weak self] in try await self?.setNicknameAndCreateRoom() }, errorCompletion: { [weak self] error in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift index 5b66b74e..decd68a4 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift @@ -132,7 +132,7 @@ extension RehummingViewController { private func showSubmitHummingLoading() { let alert = LoadingAlertController( progressText: .submitHumming, - load: { [weak self] in + loadAction: { [weak self] in try await self?.submitHumming() }, errorCompletion: { [weak self] error in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift index 31fa7340..5467f9c8 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift @@ -253,7 +253,7 @@ extension HummingResultViewController { private func showNextResultLoading() { let alert = LoadingAlertController( progressText: .nextResult, - load: { [weak self] in + loadAction: { [weak self] in try await self?.nextResultFetch() }, errorCompletion: { [weak self] error in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift index 2abeacf2..b990ef97 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift @@ -104,7 +104,7 @@ extension SelectMusicViewController { private func showSubmitMusicLoading() { let alert = LoadingAlertController( progressText: .submitMusic, - load: { [weak self] in + loadAction: { [weak self] in try await self?.submitMusic() }, errorCompletion: { [weak self] error in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift index 3c5e962b..f3eb7523 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift @@ -127,7 +127,7 @@ extension SubmitAnswerViewController { private func showSubmitAnswerLoading() { let alert = LoadingAlertController( progressText: .submitMusic, - load: { [weak self] in + loadAction: { [weak self] in try await self?.submitAnswer() }) { [weak self] error in self?.showFailSubmitMusic(error) From 9fe1170b1a751d1c1ccfe501645a971c2f31dc1d Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 19:34:35 +0900 Subject: [PATCH 08/21] =?UTF-8?q?fix:=20alert=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EC=97=86=EB=8A=94=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Alert/DefaultAlertController.swift | 14 ++++++++++++++ .../Alert/InputAlertController.swift | 7 +++++++ .../Alert/SingleButtonAlertController.swift | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift index bf88f56a..97ea8b97 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift @@ -13,6 +13,20 @@ final class DefaultAlertController: ASAlertController { setPrimaryButton() } + override func setPrimaryButton() { + super.setPrimaryButton() + primaryButton.addAction(UIAction { [weak self] _ in + self?.primaryButtonAction?("") + }, for: .touchUpInside) + } + + override func setSecondaryButton() { + super.setSecondaryButton() + secondaryButton.addAction(UIAction { [weak self] _ in + self?.secondaryButtonAction?() + }, for: .touchUpInside) + } + convenience init( titleText: ASAlertText.Title, primaryButtonText: ASAlertText.ButtonText = .done, diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift index 1182be1a..eedd4757 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift @@ -29,6 +29,13 @@ final class InputAlertController: ASAlertController { }, for: .touchUpInside) } + override func setSecondaryButton() { + super.setSecondaryButton() + secondaryButton.addAction(UIAction { [weak self] _ in + self?.secondaryButtonAction?() + }, for: .touchUpInside) + } + private func setTextField() { textField.setConfiguration(placeholder: textFieldPlaceholder?.description) stackView.addArrangedSubview(textField) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift index b8affe03..3a2e6454 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/SingleButtonAlertController.swift @@ -12,6 +12,13 @@ final class SingleButtonAlertController: ASAlertController { setPrimaryButton() } + override func setPrimaryButton() { + super.setPrimaryButton() + primaryButton.addAction(UIAction { [weak self] _ in + self?.primaryButtonAction?("") + }, for: .touchUpInside) + } + convenience init( titleText: ASAlertText.Title, primaryButtonText: ASAlertText.ButtonText = .confirm, From 4edc2b52ac755a1e3e65c6ce3985c5f479e0738c Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 19:34:52 +0900 Subject: [PATCH 09/21] =?UTF-8?q?fix:=20=EB=A1=9C=EB=94=A9=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=98=EB=A6=AC=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UIKitComponents/Alert/LoadingAlertController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift index 95912a89..84b3a483 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/LoadingAlertController.swift @@ -46,6 +46,7 @@ final class LoadingAlertController: ASAlertController { progressLabel.textColor = .label stackView.addArrangedSubview(progressLabel) progressLabel.translatesAutoresizingMaskIntoConstraints = false + progressLabel.adjustsFontSizeToFitWidth = true progressLabel.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 12).isActive = true } From 34a0ff51d6be2719f28e9b561513903c6387d28c Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 19:35:25 +0900 Subject: [PATCH 10/21] =?UTF-8?q?refactor:=20ASButton=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/UIKitComponents/ASButton.swift | 74 ++++++++++++++----- .../Alert/ASAlertController.swift | 13 ++-- .../Views/Lobby/LobbyViewController.swift | 12 ++- .../Onboarding/OnboardingViewController.swift | 4 +- .../Result/HummingResultViewController.swift | 6 +- .../SelectMusicViewController.swift | 2 +- .../SubmitAnswerViewController.swift | 4 +- 7 files changed, 81 insertions(+), 34 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift index 80e34e45..e1d00fbd 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift @@ -15,14 +15,15 @@ final class ASButton: UIButton { /// 버튼의 UI 관련한 Configuration을 설정하는 메서드 /// - Parameters: - /// - systemImageName: SF Symbol 이미지를 삽입을 원할 경우 "play.fill" 과 같이 systemName 입력. 입력 안할시 이미지 입력 안됨. - /// - title: 버튼에 쓰일 텍스트 - /// - backgroundColor: UIColor 형태로 색깔 입력. (ex. .asYellow) + /// - systemImageName: SF Symbol 이미지를 삽입을 원할 경우 "play.fill" 과 같이 systemName 입력. + /// - text: 버튼에 쓰일 텍스트 + /// - textStyle: 버튼에 쓰일 텍스트 스타일 + /// - backgroundColor: UIColor 형태로 색깔 입력. (ex) .asYellow) func setConfiguration( systemImageName: String? = nil, - title: String?, - backgroundColor: UIColor? = nil, - textSize: CGFloat = 32 + text: String?, + textStyle: UIFont.TextStyle = .largeTitle, + backgroundColor: UIColor? = nil ) { var config = UIButton.Configuration.gray() config.baseBackgroundColor = backgroundColor @@ -40,8 +41,8 @@ final class ASButton: UIButton { let imageConfig = UIImage.SymbolConfiguration(pointSize: 20, weight: .heavy) config.preferredSymbolConfigurationForImage = imageConfig - var titleAttr = AttributedString(title ?? "") - titleAttr.font = UIFont.font(.dohyeon, ofSize: textSize) + var titleAttr = AttributedString(text ?? "") + titleAttr.font = UIFont.font(forTextStyle: textStyle) config.attributedTitle = titleAttr config.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) @@ -85,17 +86,10 @@ final class ASButton: UIButton { func updateButton(_ type: ASButton.ASButtonType) { switch type { case .disabled: disable() - case .waitStart: setConfiguration(title: "시작 대기중..", backgroundColor: .asOrange) - case let .idle(string, color): setConfiguration(title: string, backgroundColor: color) - case .startRecord: setConfiguration(title: "녹음하기", backgroundColor: .systemRed) - case .recording: setConfiguration(title: "녹음중..", backgroundColor: .asLightRed) - case .reRecord: setConfiguration(systemImageName: "arrow.clockwise", title: "재녹음", backgroundColor: .asOrange) - case .submit: setConfiguration(title: "제출하기", backgroundColor: .asGreen) - case .complete: setConfiguration(title: "완료", backgroundColor: .asYellow) case .submitted: - setConfiguration(title: "제출 완료") + setConfiguration(text: "제출 완료") disable() - case .startGame: setConfiguration(systemImageName: "play.fill", title: "시작하기!", backgroundColor: .asMint) + default: setConfiguration(systemImageName: type.systemImage, text: type.text, backgroundColor: type.backgroundColor) } } @@ -110,6 +104,43 @@ final class ASButton: UIButton { case submit case submitted case startGame + + var text: String? { + switch self { + case .disabled: nil + case .waitStart: "시작 대기중.." + case let .idle(string, _): string + case .startRecord: "녹음하기" + case .recording: "녹음중.." + case .reRecord: "재녹음" + case .complete: "완료" + case .submit: "제출하기" + case .submitted: "제출 완료" + case .startGame: "시작하기!" + } + } + + var systemImage: String? { + switch self { + case .startGame: "play.fill" + case .reRecord: "arrow.clockwise" + default: nil + } + } + + var backgroundColor: UIColor? { + switch self { + case .waitStart: .asOrange + case let .idle(_, color): color + case .startRecord: .systemRed + case .recording: .asLightRed + case .reRecord: .asOrange + case .complete: .asYellow + case .submit: .asGreen + case .startGame: .asMint + default: nil + } + } } } @@ -117,10 +148,15 @@ struct ASButtonWrapper: UIViewRepresentable { let systemImageName: String let title: String let backgroundColor: UIColor - let textSize: CGFloat = 32 + let textStyle: UIFont.TextStyle = .largeTitle func makeUIView(context _: Context) -> ASButton { let view = ASButton() - view.setConfiguration(systemImageName: systemImageName, title: title, backgroundColor: backgroundColor, textSize: textSize) + view.setConfiguration( + systemImageName: systemImageName, + text: title, + textStyle: textStyle, + backgroundColor: backgroundColor + ) return view } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift index 2cd61c6d..830df711 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift @@ -102,9 +102,9 @@ class ASAlertController: UIViewController { primaryButton.translatesAutoresizingMaskIntoConstraints = false buttonStackView.addArrangedSubview(primaryButton) primaryButton.setConfiguration( - title: primaryButtonText?.description, - backgroundColor: .asLightSky, - textSize: 24 + text: primaryButtonText?.description, + textStyle: .title2, + backgroundColor: .asLightSky ) primaryButton.addAction(UIAction { [weak self] _ in self?.dismiss(animated: true) @@ -116,12 +116,11 @@ class ASAlertController: UIViewController { buttonStackView.addArrangedSubview(secondaryButton) secondaryButton.translatesAutoresizingMaskIntoConstraints = false secondaryButton.setConfiguration( - title: secondaryButtonText?.description, - backgroundColor: .asLightRed, - textSize: 24 + text: secondaryButtonText?.description, + textStyle: .title2, + backgroundColor: .asLightRed ) secondaryButton.addAction(UIAction { [weak self] _ in - self?.secondaryButtonAction?() self?.dismiss(animated: true) }, for: .touchUpInside) secondaryButton.heightAnchor.constraint(equalToConstant: 40).isActive = true diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 12a7c9b9..85c89a03 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -94,10 +94,18 @@ final class LobbyViewController: UIViewController { codeLabel.textColor = .label codeLabel.textAlignment = .center - inviteButton.setConfiguration(systemImageName: "link", title: "초대하기!", backgroundColor: .asYellow) + inviteButton.setConfiguration( + systemImageName: "link", + text: "초대하기!", + backgroundColor: .asYellow + ) inviteButton.translatesAutoresizingMaskIntoConstraints = false - startButton.setConfiguration(systemImageName: "play.fill", title: "시작하기!", backgroundColor: .asMint) + startButton.setConfiguration( + systemImageName: "play.fill", + text: "시작하기!", + backgroundColor: .asMint + ) startButton.translatesAutoresizingMaskIntoConstraints = false } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift index 40ee94e7..deef9433 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift @@ -123,12 +123,12 @@ final class OnboardingViewController: UIViewController { private func setupButton() { createRoomButton.setConfiguration( systemImageName: "", - title: Constants.craeteButtonTitle, + text: Constants.craeteButtonTitle, backgroundColor: .asYellow ) joinRoomButton.setConfiguration( systemImageName: "", - title: Constants.joinButtonTitle, + text: Constants.joinButtonTitle, backgroundColor: .asMint ) } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift index 5467f9c8..40b7b4bb 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift @@ -43,7 +43,11 @@ class HummingResultViewController: UIViewController { } private func setButton() { - button.setConfiguration(systemImageName: "play.fill", title: "다음으로", backgroundColor: .asMint) + button.setConfiguration( + systemImageName: "play.fill", + text: "다음으로", + backgroundColor: .asMint + ) view.addSubview(button) button.addAction(UIAction { [weak self] _ in guard let self, let viewModel else { return } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift index b990ef97..095f23ea 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SelectMusic/SelectMusicViewController.swift @@ -37,7 +37,7 @@ class SelectMusicViewController: UIViewController { private func setupUI() { view.backgroundColor = .asLightGray - submitButton.setConfiguration(title: "선택 완료", backgroundColor: .asGreen) + submitButton.setConfiguration(text: "선택 완료", backgroundColor: .asGreen) submitButton.updateButton(.disabled) let musicView = SelectMusicView(viewModel: viewModel) selectMusicView = UIHostingController(rootView: musicView) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift index f3eb7523..bce8b208 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift @@ -43,8 +43,8 @@ final class SubmitAnswerViewController: UIViewController { private func setupUI() { guideLabel.setText("허밍을 듣고 정답을 맞춰보세요!") - selectAnswerButton.setConfiguration(title: "정답 선택", backgroundColor: .asLightSky) - submitButton.setConfiguration(title: "정답 제출", backgroundColor: .asLightGray) + selectAnswerButton.setConfiguration(text: "정답 선택", backgroundColor: .asLightSky) + submitButton.setConfiguration(text: "정답 제출", backgroundColor: .asLightGray) submitButton.updateButton(.disabled) buttonStack.axis = .horizontal buttonStack.spacing = 16 From c4717d4a929812bfa29d76e7ebc091f8884f0c50 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 19:49:31 +0900 Subject: [PATCH 11/21] =?UTF-8?q?feat:=20Alert=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EB=B0=98=EC=A0=84=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/UIKitComponents/ASButton.swift | 27 ++++++++++--------- .../Alert/ASAlertController.swift | 7 ++--- .../Alert/DefaultAlertController.swift | 2 ++ .../Alert/InputAlertController.swift | 2 ++ .../Views/Lobby/LobbyViewController.swift | 3 ++- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift index e1d00fbd..09afceef 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift @@ -21,29 +21,32 @@ final class ASButton: UIButton { /// - backgroundColor: UIColor 형태로 색깔 입력. (ex) .asYellow) func setConfiguration( systemImageName: String? = nil, - text: String?, + text: String? = nil, textStyle: UIFont.TextStyle = .largeTitle, backgroundColor: UIColor? = nil ) { var config = UIButton.Configuration.gray() - config.baseBackgroundColor = backgroundColor config.baseForegroundColor = .asBlack - + config.background.strokeColor = .black + config.background.strokeWidth = 3 + if let systemImageName { config.imagePlacement = .leading config.image = UIImage(systemName: systemImageName) config.imagePadding = 10 + let imageConfig = UIImage.SymbolConfiguration(pointSize: 20, weight: .heavy) + config.preferredSymbolConfigurationForImage = imageConfig + } + + if let backgroundColor { + config.baseBackgroundColor = backgroundColor } - config.background.strokeColor = .black - config.background.strokeWidth = 3 - - let imageConfig = UIImage.SymbolConfiguration(pointSize: 20, weight: .heavy) - config.preferredSymbolConfigurationForImage = imageConfig - - var titleAttr = AttributedString(text ?? "") - titleAttr.font = UIFont.font(forTextStyle: textStyle) - config.attributedTitle = titleAttr + if let text { + var titleAttr = AttributedString(text) + titleAttr.font = UIFont.font(forTextStyle: textStyle) + config.attributedTitle = titleAttr + } config.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) config.cornerStyle = .medium diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift index 830df711..0db4ba37 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift @@ -6,7 +6,8 @@ class ASAlertController: UIViewController { var secondaryButtonText: ASAlertText.ButtonText? var primaryButtonAction: ((String) -> Void)? var secondaryButtonAction: (() -> Void)? - + var reversedColor: Bool = false + var alertView = ASPanel() var stackView = UIStackView() lazy var buttonStackView = UIStackView() @@ -104,7 +105,7 @@ class ASAlertController: UIViewController { primaryButton.setConfiguration( text: primaryButtonText?.description, textStyle: .title2, - backgroundColor: .asLightSky + backgroundColor: reversedColor ? .asLightRed : .asLightSky ) primaryButton.addAction(UIAction { [weak self] _ in self?.dismiss(animated: true) @@ -118,7 +119,7 @@ class ASAlertController: UIViewController { secondaryButton.setConfiguration( text: secondaryButtonText?.description, textStyle: .title2, - backgroundColor: .asLightRed + backgroundColor: reversedColor ? .asLightSky : .asLightRed ) secondaryButton.addAction(UIAction { [weak self] _ in self?.dismiss(animated: true) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift index 97ea8b97..29fe7972 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/DefaultAlertController.swift @@ -31,6 +31,7 @@ final class DefaultAlertController: ASAlertController { titleText: ASAlertText.Title, primaryButtonText: ASAlertText.ButtonText = .done, secondaryButtonText: ASAlertText.ButtonText = .cancel, + reversedColor: Bool = false, primaryButtonAction: ((String) -> Void)? = nil, secondaryButtonAction: (() -> Void)? = nil ) { @@ -38,6 +39,7 @@ final class DefaultAlertController: ASAlertController { self.titleText = titleText self.primaryButtonText = primaryButtonText self.secondaryButtonText = secondaryButtonText + self.reversedColor = reversedColor self.primaryButtonAction = primaryButtonAction self.secondaryButtonAction = secondaryButtonAction diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift index eedd4757..8cbfef2b 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/InputAlertController.swift @@ -56,6 +56,7 @@ final class InputAlertController: ASAlertController { secondaryButtonText: ASAlertText.ButtonText = .cancel, textFieldPlaceholder: ASAlertText.Placeholder, isUppercased: Bool = false, + reversedColor: Bool = false, primaryButtonAction: ((String) -> Void)? = nil, secondaryButtonAction: (() -> Void)? = nil ) { @@ -64,6 +65,7 @@ final class InputAlertController: ASAlertController { self.textFieldPlaceholder = textFieldPlaceholder self.primaryButtonText = primaryButtonText self.secondaryButtonText = secondaryButtonText + self.reversedColor = reversedColor self.primaryButtonAction = primaryButtonAction self.secondaryButtonAction = secondaryButtonAction self.isUppercased = isUppercased diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 85c89a03..87cd0860 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -115,7 +115,8 @@ final class LobbyViewController: UIViewController { let alert = DefaultAlertController( titleText: .leaveRoom, primaryButtonText: .leave, - secondaryButtonText: .cancel + secondaryButtonText: .cancel, + reversedColor: true ) { [weak self] _ in self?.viewmodel.leaveRoom() self?.navigationController?.popViewController(animated: true) From 2509596a3fc4bdabbab24946f9d6ea0b92f43059 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 19:52:02 +0900 Subject: [PATCH 12/21] =?UTF-8?q?feat:=20=EB=8B=A4=ED=81=AC=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20=ED=85=8D=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EA=B0=80=20=EA=B5=AC=EB=B6=84=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EC=83=89=EC=83=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets.xcassets/asLightRed.colorset/Contents.json | 6 +++--- .../Assets.xcassets/asLightSky.colorset/Contents.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightRed.colorset/Contents.json b/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightRed.colorset/Contents.json index d22c49c0..79137fa7 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightRed.colorset/Contents.json +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightRed.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.478", - "green" : "0.478", - "red" : "1.000" + "blue" : "0x52", + "green" : "0x52", + "red" : "0xF0" } }, "idiom" : "universal" diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightSky.colorset/Contents.json b/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightSky.colorset/Contents.json index cca75a4b..9b04837c 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightSky.colorset/Contents.json +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Resources/Assets.xcassets/asLightSky.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.969", - "green" : "0.922", - "red" : "0.624" + "blue" : "0xD9", + "green" : "0xC5", + "red" : "0x46" } }, "idiom" : "universal" From 4f20538a4a9238952701e9294d193263c4fb9333 Mon Sep 17 00:00:00 2001 From: moral-life Date: Sun, 1 Dec 2024 20:39:49 +0900 Subject: [PATCH 13/21] =?UTF-8?q?feat:=20=EB=82=98=EA=B0=80=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20+=20=ED=83=80=EC=9D=B4=ED=8B=80=20(?= =?UTF-8?q?=EC=B6=94=ED=9B=84=20=EC=A7=84=ED=96=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alsongDalsong/Sources/SceneDelegate.swift | 2 +- .../Views/Game/GameNavigationController.swift | 77 ++++++++++++++++++- .../Views/Lobby/LobbyViewController.swift | 30 +------- .../Onboarding/OnboardingViewController.swift | 8 +- 4 files changed, 82 insertions(+), 35 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift index 1d426d42..33a738fc 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/SceneDelegate.swift @@ -36,7 +36,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { ) let onboardingVC = OnboardingViewController(viewmodel: onboardingVM, inviteCode: inviteCode) let navigationController = UINavigationController(rootViewController: onboardingVC) - navigationController.navigationBar.isHidden = false + navigationController.navigationBar.isHidden = true navigationController.interactivePopGestureRecognizer?.isEnabled = false window?.rootViewController = navigationController window?.makeKeyAndVisible() diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift index e7961d21..1876fed3 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift @@ -1,5 +1,6 @@ import ASContainer import ASEntity +import ASLogKit import ASRepositoryProtocol import Combine import UIKit @@ -8,6 +9,7 @@ import UIKit final class GameNavigationController { private let navigationController: UINavigationController private let gameStateRepository: GameStateRepositoryProtocol + private let roomActionRepository: RoomActionRepositoryProtocol private var subscriptions: Set = [] private var gameInfo: GameState? { @@ -17,13 +19,19 @@ final class GameNavigationController { } } + private let roomNumber: String + init(navigationController: UINavigationController, - gameStateRepository: GameStateRepositoryProtocol) + gameStateRepository: GameStateRepositoryProtocol, + roomActionRepository: RoomActionRepositoryProtocol, + roomNumber: String) { self.navigationController = navigationController self.gameStateRepository = gameStateRepository + self.roomActionRepository = roomActionRepository + self.roomNumber = roomNumber } - + @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") @@ -38,7 +46,56 @@ final class GameNavigationController { } .store(in: &subscriptions) } + + private func setupNavigationBar(for viewController: UIViewController) { + Logger.debug("setupNavi 시작") + navigationController.navigationBar.isHidden = false + navigationController.navigationBar.tintColor = .asBlack + navigationController.navigationBar.titleTextAttributes = [.font: UIFont.font(.dohyeon, ofSize: 24)] + + let backButtonImage = UIImage(systemName: "rectangle.portrait.and.arrow.forward")? + .withRenderingMode(.alwaysTemplate) + .applyingSymbolConfiguration(.init(pointSize: 24, weight: .medium))? + .rotate(radians: .pi) + + let backButtonAction = UIAction { [weak self] _ in + let alert = ASAlertController( + style: .default, + titleText: .leaveRoom, + doneButtonTitle: .leave, + cancelButtonTitle: .cancel + ) { [weak self] _ in + // self?.viewmodel.leaveRoom() + self?.navigationController.popViewController(animated: true) // 아예 온보딩으로 가야함. + } + self?.navigationController.presentAlert(alert) + } + let backButton = UIBarButtonItem(image: backButtonImage, primaryAction: backButtonAction) + + viewController.navigationItem.leftBarButtonItem = backButton + viewController.title = setTitle() + } + + private func setTitle() -> String { + switch gameInfo?.resolveViewType() { + case .submitMusic: + return "노래 선택" + case .humming: + return "허밍" + case .rehumming: + return "리허밍" + case .submitAnswer: + return "정답 제출" + case .result: + return "결과 확인" + case .lobby: + return "#\(roomNumber)" + default: + return "" + } + } + private func updateViewControllers(state: GameState) { let viewType = state.resolveViewType() switch viewType { @@ -82,6 +139,7 @@ final class GameNavigationController { ) let vc = LobbyViewController(lobbyViewModel: vm) navigationController.pushViewController(vc, animated: true) + setupNavigationBar(for: vc) } private func navigateToSelectMusic() { @@ -98,6 +156,7 @@ final class GameNavigationController { ) let vc = SelectMusicViewController(selectMusicViewModel: vm) navigationController.pushViewController(vc, animated: true) + setupNavigationBar(for: vc) } private func navigateToHumming() { @@ -114,6 +173,7 @@ final class GameNavigationController { ) let vc = HummingViewController(viewModel: vm) navigationController.pushViewController(vc, animated: true) + setupNavigationBar(for: vc) } private func navigateToRehumming() { @@ -128,6 +188,7 @@ final class GameNavigationController { ) let vc = RehummingViewController(viewModel: vm) navigationController.pushViewController(vc, animated: true) + setupNavigationBar(for: vc) } private func navigateToSubmitAnswer() { @@ -146,6 +207,7 @@ final class GameNavigationController { ) let vc = SubmitAnswerViewController(viewModel: vm) navigationController.pushViewController(vc, animated: true) + setupNavigationBar(for: vc) } private func navigateToResult() { @@ -167,7 +229,18 @@ final class GameNavigationController { ) let vc = HummingResultViewController(viewModel: vm) navigationController.pushViewController(vc, animated: true) + setupNavigationBar(for: vc) } private func navigationToWaiting() {} + +// private func leaveRoom() { +// Task { +// do { +// try await roomActionRepository.leaveRoom() +// } catch { +// Logger.error(error.localizedDescription) +// } +// } +// } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 62267c60..4c63feed 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -25,7 +25,6 @@ final class LobbyViewController: UIViewController { setupLayout() setAction() bindToComponents() - setupNavigationBar() } override func viewWillDisappear(_ animated: Bool) { @@ -48,7 +47,7 @@ final class LobbyViewController: UIViewController { .receive(on: DispatchQueue.main) .sink { [weak self] _ in guard let self else { return } - navigationController?.navigationBar.topItem?.title = "#\(self.viewmodel.roomNumber)" +// navigationController?.navigationBar.topItem?.title = "#\(self.viewmodel.roomNumber)" } .store(in: &cancellables) @@ -73,33 +72,6 @@ final class LobbyViewController: UIViewController { .store(in: &cancellables) } - private func setupNavigationBar() { - navigationController?.navigationBar.tintColor = .asBlack - navigationItem.hidesBackButton = true - - navigationController?.navigationBar.titleTextAttributes = [.font: UIFont.font(.dohyeon, ofSize: 24)] - - let backButtonImage = UIImage(systemName: "rectangle.portrait.and.arrow.forward")? - .withRenderingMode(.alwaysTemplate) - .applyingSymbolConfiguration(.init(pointSize: 24, weight: .medium))? - .rotate(radians: .pi) - - let backButtonAction = UIAction { [weak self] _ in - let alert = ASAlertController( - style: .default, - titleText: .leaveRoom, - doneButtonTitle: .leave, - cancelButtonTitle: .cancel - ) { [weak self] _ in - self?.viewmodel.leaveRoom() - self?.navigationController?.popViewController(animated: true) - } - self?.presentAlert(alert) - } - let barButton = UIBarButtonItem(image: backButtonImage, primaryAction: backButtonAction) - navigationItem.leftBarButtonItem = barButton - } - private func setupUI() { view.backgroundColor = .asLightGray diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift index 1a0d23be..07d9af9b 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Onboarding/OnboardingViewController.swift @@ -35,7 +35,7 @@ final class OnboardingViewController: UIViewController { setupButton() hideKeyboard() bindViewModel() - viewModel?.authorizeAppleMusic() + viewModel?.authorizeAppleMusic() } override func viewWillAppear(_ animated: Bool) { @@ -152,12 +152,14 @@ final class OnboardingViewController: UIViewController { let mainRepository: MainRepositoryProtocol = DIContainer.shared.resolve(MainRepositoryProtocol.self) mainRepository.connectRoom(roomNumber: roomNumber) let gameStateRepository = DIContainer.shared.resolve(GameStateRepositoryProtocol.self) - + let roomActionRepository = DIContainer.shared.resolve(RoomActionRepositoryProtocol.self) guard let navigationController else { return } gameNavigationController = GameNavigationController( navigationController: navigationController, - gameStateRepository: gameStateRepository + gameStateRepository: gameStateRepository, + roomActionRepository: roomActionRepository, + roomNumber: roomNumber ) gameNavigationController?.setConfiguration() From bc5174430fd0d08b6f1dad4592f68d3eb59c66a8 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 20:42:30 +0900 Subject: [PATCH 14/21] =?UTF-8?q?feat:=20Alert=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/UIKitComponents/ASButton.swift | 7 ++++--- .../UIKitComponents/Alert/ASAlertController.swift | 13 ++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift index 09afceef..50f54fa3 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/ASButton.swift @@ -23,7 +23,8 @@ final class ASButton: UIButton { systemImageName: String? = nil, text: String? = nil, textStyle: UIFont.TextStyle = .largeTitle, - backgroundColor: UIColor? = nil + backgroundColor: UIColor? = nil, + cornerStyle: UIButton.Configuration.CornerStyle = .medium ) { var config = UIButton.Configuration.gray() config.baseForegroundColor = .asBlack @@ -49,7 +50,7 @@ final class ASButton: UIButton { } config.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) - config.cornerStyle = .medium + config.cornerStyle = cornerStyle setShadow() config.background.backgroundColorTransformer = UIConfigurationColorTransformer { color in @@ -90,7 +91,7 @@ final class ASButton: UIButton { switch type { case .disabled: disable() case .submitted: - setConfiguration(text: "제출 완료") + setConfiguration(text: type.text) disable() default: setConfiguration(systemImageName: type.systemImage, text: type.text, backgroundColor: type.backgroundColor) } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift index 0db4ba37..494ac0b4 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift @@ -46,7 +46,7 @@ class ASAlertController: UIViewController { private func setStackView() { stackView.axis = .vertical - stackView.spacing = 16 + stackView.spacing = 12 stackView.distribution = .fillProportionally stackView.alignment = .center alertView.addSubview(stackView) @@ -88,15 +88,16 @@ class ASAlertController: UIViewController { func setButtonStackView() { buttonStackView.axis = .horizontal - buttonStackView.spacing = 20 + buttonStackView.spacing = 12 buttonStackView.distribution = .fillEqually buttonStackView.alignment = .center stackView.addArrangedSubview(buttonStackView) + + buttonStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ buttonStackView.widthAnchor.constraint(equalTo: stackView.widthAnchor), buttonStackView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), ]) - buttonStackView.heightAnchor.constraint(equalToConstant: 56).priority = .defaultHigh } func setPrimaryButton() { @@ -105,7 +106,8 @@ class ASAlertController: UIViewController { primaryButton.setConfiguration( text: primaryButtonText?.description, textStyle: .title2, - backgroundColor: reversedColor ? .asLightRed : .asLightSky + backgroundColor: reversedColor ? .asLightRed : .asLightSky, + cornerStyle: .large ) primaryButton.addAction(UIAction { [weak self] _ in self?.dismiss(animated: true) @@ -119,7 +121,8 @@ class ASAlertController: UIViewController { secondaryButton.setConfiguration( text: secondaryButtonText?.description, textStyle: .title2, - backgroundColor: reversedColor ? .asLightSky : .asLightRed + backgroundColor: reversedColor ? .asLightSky : .asLightRed, + cornerStyle: .large ) secondaryButton.addAction(UIAction { [weak self] _ in self?.dismiss(animated: true) From 51f77f0b932505210868f84dd4a4371f50c0393b Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Sun, 1 Dec 2024 22:29:40 +0900 Subject: [PATCH 15/21] =?UTF-8?q?fix:=20=EB=8B=A4=ED=81=AC=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20=ED=8C=8C=ED=98=95=20=ED=9D=B0?= =?UTF-8?q?=EC=83=89=20=EC=95=88=20=EB=B3=B4=EC=9D=B4=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/UIKitComponents/AudioVisualizerView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/AudioVisualizerView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/AudioVisualizerView.swift index 6c930233..206115f3 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/AudioVisualizerView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/AudioVisualizerView.swift @@ -127,7 +127,7 @@ final class WaveFormView: UIView { let circleLayer = CAShapeLayer() circleLayer.path = circle.cgPath - circleLayer.fillColor = UIColor.asShadow.cgColor + circleLayer.fillColor = UIColor.white.cgColor layer.addSublayer(circleLayer) columns.append(circleLayer) From 0ac51cefba7ae84042e4d6ac97d3a6356a92db74 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Mon, 2 Dec 2024 00:30:48 +0900 Subject: [PATCH 16/21] =?UTF-8?q?feat:=20=EC=84=A0=ED=83=9D=ED=95=9C=20?= =?UTF-8?q?=EC=A0=95=EB=8B=B5=EC=9D=B4=20=EB=9C=A8=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Views/MusicPanel/MusicPanel.swift | 164 ++++++++++++++---- .../SubmitAnswerViewController.swift | 10 +- 2 files changed, 135 insertions(+), 39 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift index 7fcc32a5..c829bf3e 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift @@ -4,20 +4,31 @@ import ASRepositoryProtocol import Combine import UIKit +enum MusicPanelType { + case large, compact +} + final class MusicPanel: UIView { private let panel = ASPanel() - private let player = ASMusicPlayer() + private let player: ASMusicPlayer + private let noMusicLabel = UILabel() private let titleLabel = UILabel() private let artistLabel = UILabel() + private let labelStack = UIStackView() private var cancellables = Set() private let musicRepository: MusicRepositoryProtocol private var viewModel: MusicPanelViewModel? = nil + private var panelType: MusicPanelType = .large - init() { + init(_ type: MusicPanelType = .large) { + panelType = type musicRepository = DIContainer.shared.resolve(MusicRepositoryProtocol.self) + player = ASMusicPlayer(type) super.init(frame: .zero) setupUI() + setupNoMusicLabel() setupLayout() + setupNoMusicLayout() bindWithPlayer() } @@ -26,20 +37,30 @@ final class MusicPanel: UIView { fatalError("init(coder:) has not been implemented") } - func bind( - to dataSource: Published.Publisher - ) { + func bind(to dataSource: Published.Publisher) { dataSource .receive(on: DispatchQueue.main) .sink { [weak self] music in - self?.viewModel = MusicPanelViewModel( + guard let self else { return } + + if self.panelType == .compact, music == nil { + self.noMusicLabel.isHidden = false + self.labelStack.isHidden = true + self.player.isHidden = true + } else { + self.noMusicLabel.isHidden = true + self.labelStack.isHidden = false + self.player.isHidden = false + } + + self.viewModel = MusicPanelViewModel( music: music, - musicRepository: self?.musicRepository + musicRepository: self.musicRepository ) - self?.player.updateMusicPanel(color: music?.artworkBackgroundColor?.hexToCGColor()) - self?.bindViewModel() - self?.titleLabel.text = music?.title ?? "???" - self?.artistLabel.text = music?.artist ?? "????" + self.player.updateMusicPanel(color: music?.artworkBackgroundColor?.hexToCGColor()) + self.bindViewModel() + self.titleLabel.text = music?.title ?? "???" + self.artistLabel.text = music?.artist ?? "????" } .store(in: &cancellables) } @@ -64,27 +85,61 @@ final class MusicPanel: UIView { private func setupUI() { addSubview(panel) addSubview(player) - addSubview(titleLabel) - addSubview(artistLabel) + addSubview(labelStack) titleLabel.textColor = .label artistLabel.textColor = .secondaryLabel [titleLabel, artistLabel].forEach { label in label.font = .font(forTextStyle: .title3) - label.textAlignment = .center + label.textAlignment = panelType == .large ? .center : .left label.numberOfLines = 1 label.lineBreakMode = .byTruncatingTail label.adjustsFontSizeToFitWidth = false } + setupLabelStack() } private func setupLayout() { panel.translatesAutoresizingMaskIntoConstraints = false player.translatesAutoresizingMaskIntoConstraints = false - titleLabel.translatesAutoresizingMaskIntoConstraints = false - artistLabel.translatesAutoresizingMaskIntoConstraints = false + labelStack.translatesAutoresizingMaskIntoConstraints = false + + if panelType == .large { + largeLayout() + } else { + compactLayout() + } + } + + private func setupNoMusicLayout() { + noMusicLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + noMusicLabel.centerXAnchor.constraint(equalTo: centerXAnchor), + noMusicLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + noMusicLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 16), + noMusicLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -16), + ]) + } + + private func setupNoMusicLabel() { + noMusicLabel.text = "정답을 선택해 주세요." + noMusicLabel.textColor = .secondaryLabel + noMusicLabel.font = .font(forTextStyle: .title2) + noMusicLabel.textAlignment = .center + noMusicLabel.numberOfLines = 0 + noMusicLabel.isHidden = true + addSubview(noMusicLabel) + } + + private func setupLabelStack() { + labelStack.axis = .vertical + labelStack.spacing = 0 + labelStack.addArrangedSubview(titleLabel) + labelStack.addArrangedSubview(artistLabel) + } + private func largeLayout() { NSLayoutConstraint.activate([ panel.topAnchor.constraint(equalTo: topAnchor), panel.bottomAnchor.constraint(equalTo: bottomAnchor), @@ -94,14 +149,29 @@ final class MusicPanel: UIView { player.topAnchor.constraint(equalTo: topAnchor, constant: 24), player.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 24), player.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24), - player.bottomAnchor.constraint(equalTo: titleLabel.topAnchor, constant: -12), - - titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor), - titleLabel.widthAnchor.constraint(equalTo: player.widthAnchor, constant: -16), - artistLabel.centerXAnchor.constraint(equalTo: centerXAnchor), - artistLabel.widthAnchor.constraint(equalTo: player.widthAnchor, constant: -16), - artistLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 12), - artistLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24), + player.bottomAnchor.constraint(equalTo: labelStack.topAnchor, constant: -12), + + labelStack.centerXAnchor.constraint(equalTo: centerXAnchor), + labelStack.widthAnchor.constraint(equalTo: player.widthAnchor, constant: -16), + labelStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24), + ]) + } + + private func compactLayout() { + NSLayoutConstraint.activate([ + panel.topAnchor.constraint(equalTo: topAnchor), + panel.bottomAnchor.constraint(equalTo: bottomAnchor), + panel.leadingAnchor.constraint(equalTo: leadingAnchor), + panel.trailingAnchor.constraint(equalTo: trailingAnchor), + + player.topAnchor.constraint(equalTo: topAnchor, constant: 12), + player.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12), + player.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), + player.trailingAnchor.constraint(equalTo: trailingAnchor), + + labelStack.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 8), + labelStack.widthAnchor.constraint(equalToConstant: 160), + labelStack.centerYAnchor.constraint(equalTo: centerYAnchor), ]) } } @@ -110,10 +180,12 @@ private final class ASMusicPlayer: UIView { private var backgroundImageView = UIImageView() private var blurView = UIVisualEffectView() private var playButton = UIButton() + private var panelType: MusicPanelType var onPlayButtonTapped: (() -> Void)? private var cancellables = Set() - init() { + init(_ type: MusicPanelType = .large) { + panelType = type super.init(frame: .zero) setupUI() setupLayout() @@ -162,11 +234,13 @@ private final class ASMusicPlayer: UIView { private func setupUI() { backgroundImageView.contentMode = .scaleAspectFill backgroundImageView.clipsToBounds = true - backgroundImageView.layer.cornerRadius = 15 + backgroundImageView.layer.cornerRadius = panelType == .large ? 15 : 5 setupButton() setupBlurView() addSubview(backgroundImageView) - addSubview(blurView) + if panelType == .large { + addSubview(blurView) + } addSubview(playButton) let gradientLayer = makeGradientLayer() backgroundImageView.layer.addSublayer(gradientLayer) @@ -174,23 +248,37 @@ private final class ASMusicPlayer: UIView { private func setupLayout() { backgroundImageView.translatesAutoresizingMaskIntoConstraints = false - blurView.translatesAutoresizingMaskIntoConstraints = false + blurView.translatesAutoresizingMaskIntoConstraints = panelType == .large playButton.translatesAutoresizingMaskIntoConstraints = false + if panelType == .large { + NSLayoutConstraint.activate([ + backgroundImageView.topAnchor.constraint(equalTo: topAnchor), + backgroundImageView.bottomAnchor.constraint(equalTo: bottomAnchor), + backgroundImageView.leadingAnchor.constraint(equalTo: leadingAnchor), + backgroundImageView.trailingAnchor.constraint(equalTo: trailingAnchor), + backgroundImageView.heightAnchor.constraint(equalTo: widthAnchor), + + blurView.topAnchor.constraint(equalTo: topAnchor), + blurView.bottomAnchor.constraint(equalTo: bottomAnchor), + blurView.leadingAnchor.constraint(equalTo: leadingAnchor), + blurView.trailingAnchor.constraint(equalTo: trailingAnchor), + + playButton.centerYAnchor.constraint(equalTo: backgroundImageView.centerYAnchor), + playButton.centerXAnchor.constraint(equalTo: backgroundImageView.centerXAnchor), + ]) + } else { compactLayout() } + } + + private func compactLayout() { NSLayoutConstraint.activate([ backgroundImageView.topAnchor.constraint(equalTo: topAnchor), backgroundImageView.bottomAnchor.constraint(equalTo: bottomAnchor), backgroundImageView.leadingAnchor.constraint(equalTo: leadingAnchor), - backgroundImageView.trailingAnchor.constraint(equalTo: trailingAnchor), - backgroundImageView.heightAnchor.constraint(equalTo: widthAnchor), - - blurView.topAnchor.constraint(equalTo: topAnchor), - blurView.bottomAnchor.constraint(equalTo: bottomAnchor), - blurView.leadingAnchor.constraint(equalTo: leadingAnchor), - blurView.trailingAnchor.constraint(equalTo: trailingAnchor), + backgroundImageView.widthAnchor.constraint(equalTo: heightAnchor), playButton.centerYAnchor.constraint(equalTo: backgroundImageView.centerYAnchor), - playButton.centerXAnchor.constraint(equalTo: backgroundImageView.centerXAnchor), + playButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24), ]) } @@ -202,7 +290,7 @@ private final class ASMusicPlayer: UIView { animations: { [weak self] in self?.playButton.transform = .identity }, completion: { [weak self] _ in - self?.playButton.configuration?.baseForegroundColor = state.color + self?.playButton.configuration?.baseForegroundColor = self?.panelType == .large ? state.color : .asBlack self?.playButton.configuration?.image = state.symbol } ) @@ -228,7 +316,7 @@ private final class ASMusicPlayer: UIView { private func setupButton() { var buttonConfiguration = UIButton.Configuration.borderless() - let imageConfig = UIImage.SymbolConfiguration(pointSize: 60) + let imageConfig = UIImage.SymbolConfiguration(pointSize: panelType == .large ? 60 : 32) buttonConfiguration.preferredSymbolConfigurationForImage = imageConfig buttonConfiguration.baseForegroundColor = .white buttonConfiguration.contentInsets = .zero diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift index fbf4c8ed..ea0f2c85 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift @@ -5,7 +5,7 @@ final class SubmitAnswerViewController: UIViewController { private var progressBar = ProgressBar() private var guideLabel = GuideLabel() private var musicPanel = MusicPanel() - private var selectedSongView: UIHostingController? + private var selectedMusicPanel = MusicPanel(.compact) private var selectAnswerButton = ASButton() private var submitButton = ASButton() private var submissionStatus = SubmissionStatusView() @@ -38,6 +38,7 @@ final class SubmitAnswerViewController: UIViewController { submissionStatus.bind(to: viewModel.$submissionStatus) progressBar.bind(to: viewModel.$dueTime) musicPanel.bind(to: viewModel.$music) + selectedMusicPanel.bind(to: viewModel.$selectedMusic) submitButton.bind(to: viewModel.$musicData) } @@ -57,12 +58,14 @@ final class SubmitAnswerViewController: UIViewController { view.addSubview(progressBar) view.addSubview(guideLabel) view.addSubview(musicPanel) + view.addSubview(selectedMusicPanel) view.addSubview(buttonStack) view.addSubview(submissionStatus) progressBar.translatesAutoresizingMaskIntoConstraints = false guideLabel.translatesAutoresizingMaskIntoConstraints = false musicPanel.translatesAutoresizingMaskIntoConstraints = false + selectedMusicPanel.translatesAutoresizingMaskIntoConstraints = false submissionStatus.translatesAutoresizingMaskIntoConstraints = false buttonStack.translatesAutoresizingMaskIntoConstraints = false @@ -79,6 +82,11 @@ final class SubmitAnswerViewController: UIViewController { musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 48), musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -48), + selectedMusicPanel.topAnchor.constraint(equalTo: musicPanel.bottomAnchor, constant: 32), + selectedMusicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24), + selectedMusicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24), + selectedMusicPanel.heightAnchor.constraint(equalToConstant: 100), + submissionStatus.topAnchor.constraint(equalTo: buttonStack.topAnchor, constant: -16), submissionStatus.trailingAnchor.constraint(equalTo: buttonStack.trailingAnchor, constant: 16), From 24967bc5587bee4a54a23239bdb66773b7f2bf77 Mon Sep 17 00:00:00 2001 From: moral-life Date: Mon, 2 Dec 2024 01:30:33 +0900 Subject: [PATCH 17/21] =?UTF-8?q?feat:=20NavigationBar=20=EB=92=A4?= =?UTF-8?q?=EB=A1=9C=EA=B0=80=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=9D=BC?= =?UTF-8?q?=EA=B4=84=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EB=9D=BC=EC=9A=B4?= =?UTF-8?q?=EB=93=9C=EB=B3=84=20=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ASEntity/ASEntity/GameState.swift | 4 +- .../Protocols/RepositoryProtocols.swift | 2 +- .../Views/Game/GameNavigationController.swift | 47 ++++++++++++------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/alsongDalsong/ASEntity/ASEntity/GameState.swift b/alsongDalsong/ASEntity/ASEntity/GameState.swift index 1c6d2e7f..f5aa58f9 100644 --- a/alsongDalsong/ASEntity/ASEntity/GameState.swift +++ b/alsongDalsong/ASEntity/ASEntity/GameState.swift @@ -60,9 +60,9 @@ public struct GameState { return .result } - else if recordOrder == players.count - 1 { + else if recordOrder >= players.count - 1 { return .result - }else { + } else { return nil } diff --git a/alsongDalsong/ASRepository/ASRepository/Protocols/RepositoryProtocols.swift b/alsongDalsong/ASRepository/ASRepository/Protocols/RepositoryProtocols.swift index 568dd746..841bc962 100644 --- a/alsongDalsong/ASRepository/ASRepository/Protocols/RepositoryProtocols.swift +++ b/alsongDalsong/ASRepository/ASRepository/Protocols/RepositoryProtocols.swift @@ -51,7 +51,7 @@ public protocol AvatarRepositoryProtocol { func getAvatarData(url: URL) async -> Data? } -public protocol RoomActionRepositoryProtocol { +public protocol RoomActionRepositoryProtocol: Sendable { func createRoom(nickname: String, avatar: URL) async throws -> String func joinRoom(nickname: String, avatar: URL, roomNumber: String) async throws -> Bool func leaveRoom() async throws -> Bool diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift index 1876fed3..7f9e8f26 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift @@ -6,7 +6,7 @@ import Combine import UIKit @MainActor -final class GameNavigationController { +final class GameNavigationController: @unchecked Sendable { private let navigationController: UINavigationController private let gameStateRepository: GameStateRepositoryProtocol private let roomActionRepository: RoomActionRepositoryProtocol @@ -48,7 +48,6 @@ final class GameNavigationController { } private func setupNavigationBar(for viewController: UIViewController) { - Logger.debug("setupNavi 시작") navigationController.navigationBar.isHidden = false navigationController.navigationBar.tintColor = .asBlack navigationController.navigationBar.titleTextAttributes = [.font: UIFont.font(.dohyeon, ofSize: 24)] @@ -65,8 +64,9 @@ final class GameNavigationController { doneButtonTitle: .leave, cancelButtonTitle: .cancel ) { [weak self] _ in - // self?.viewmodel.leaveRoom() - self?.navigationController.popViewController(animated: true) // 아예 온보딩으로 가야함. + self?.leaveRoom() + self?.navigationController.popToRootViewController(animated: true) + self?.navigationController.navigationBar.isHidden = true } self?.navigationController.presentAlert(alert) } @@ -78,17 +78,24 @@ final class GameNavigationController { } private func setTitle() -> String { - switch gameInfo?.resolveViewType() { + guard let gameInfo else { return "" } + let viewType = gameInfo.resolveViewType() + print(viewType) + switch viewType { case .submitMusic: return "노래 선택" case .humming: return "허밍" case .rehumming: - return "리허밍" + guard let recordOrder = gameInfo.recordOrder else { return "" } + let rounds = gameInfo.players.count - 2 + return "리허밍 \(recordOrder)/\(rounds)" case .submitAnswer: - return "정답 제출" + return "정답 맞추기" case .result: - return "결과 확인" + guard let recordOrder = gameInfo.recordOrder else { return "" } + let currentRound = Int(recordOrder) - (gameInfo.players.count - 2) + return "결과 확인 \(currentRound)/\(gameInfo.players.count)" case .lobby: return "#\(roomNumber)" default: @@ -138,8 +145,8 @@ final class GameNavigationController { avatarRepository: avatarRepository ) let vc = LobbyViewController(lobbyViewModel: vm) - navigationController.pushViewController(vc, animated: true) setupNavigationBar(for: vc) + navigationController.pushViewController(vc, animated: true) } private func navigateToSelectMusic() { @@ -211,6 +218,10 @@ final class GameNavigationController { } private func navigateToResult() { + if navigationController.topViewController is HummingResultViewController { + navigationController.topViewController?.title = setTitle() + return + } let hummingResultRepository = DIContainer.shared.resolve(HummingResultRepositoryProtocol.self) let avatarRepository = DIContainer.shared.resolve(AvatarRepositoryProtocol.self) let gameStatusRepository = DIContainer.shared.resolve(GameStatusRepositoryProtocol.self) @@ -234,13 +245,13 @@ final class GameNavigationController { private func navigationToWaiting() {} -// private func leaveRoom() { -// Task { -// do { -// try await roomActionRepository.leaveRoom() -// } catch { -// Logger.error(error.localizedDescription) -// } -// } -// } + private func leaveRoom() { + Task { + do { + let _ = try await roomActionRepository.leaveRoom() + } catch { + Logger.error(error.localizedDescription) + } + } + } } From 64cf0ee67404dd94b7b49bffd266d885582ac9b1 Mon Sep 17 00:00:00 2001 From: psangwon62 Date: Mon, 2 Dec 2024 02:05:01 +0900 Subject: [PATCH 18/21] =?UTF-8?q?feat:=20MusicPanel=20compact=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Utils/AudioHelper.swift | 8 +- .../Views/MusicPanel/ASMusicPlayerView.swift | 186 +++++++++++++++++ .../Sources/Views/MusicPanel/MusicPanel.swift | 189 +----------------- .../MusicPanel/MusicPanelViewModel.swift | 46 +++-- .../RecordingPanelViewModel.swift | 2 +- 5 files changed, 226 insertions(+), 205 deletions(-) create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/ASMusicPlayerView.swift diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift index 8a4e04fb..ad69f0ea 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift @@ -10,7 +10,7 @@ actor AudioHelper { static let shared = AudioHelper() private var recorder: ASAudioRecorder? private var player: ASAudioPlayer? - private var source: FileSource = .imported + private var source: FileSource = .imported(.large) private var playType: PlayType = .full private var isConcurrent: Bool = false private var timer: Timer? @@ -79,7 +79,7 @@ actor AudioHelper { /// - source: 녹음 파일/url에서 가져온 파일 /// - playType: 전체 또는 부분 재생 /// - allowsConcurrent: 녹음과 동시에 재생 - func startPlaying(_ file: Data?, sourceType type: FileSource = .imported) async { + func startPlaying(_ file: Data?, sourceType type: FileSource = .imported(.large)) async { guard await checkRecorderState(), await checkPlayerState() else { return } guard let file else { return } @@ -160,8 +160,8 @@ actor AudioHelper { } extension AudioHelper { - enum FileSource { - case imported + enum FileSource: Equatable { + case imported(MusicPanelType) case recorded } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/ASMusicPlayerView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/ASMusicPlayerView.swift new file mode 100644 index 00000000..da84c249 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/ASMusicPlayerView.swift @@ -0,0 +1,186 @@ +import Combine +import UIKit + +final class ASMusicPlayerView: UIView { + private var backgroundImageView = UIImageView() + private var blurView = UIVisualEffectView() + private var playButton = UIButton() + private var panelType: MusicPanelType + var onPlayButtonTapped: ((MusicPanelType) -> Void)? + private var cancellables = Set() + + init(_ type: MusicPanelType = .large) { + panelType = type + super.init(frame: .zero) + setupUI() + setupLayout() + } + + func bind( + to dataSource: Published.Publisher + ) { + dataSource + .receive(on: DispatchQueue.main) + .sink { [weak self] state in + self?.updateButtonImage(with: state) + } + .store(in: &cancellables) + } + + override func layoutSubviews() { + super.layoutSubviews() + + backgroundImageView.layer.sublayers?.forEach { layer in + if let gradientLayer = layer as? CAGradientLayer { + gradientLayer.frame = backgroundImageView.bounds + } + } + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func updateMusicPanel(image: Data? = nil, color: CGColor? = nil) { + if let image, !image.isEmpty { + backgroundImageView.layer.sublayers?.removeAll() + backgroundImageView.image = UIImage(data: image) + return + } + + if let color { + backgroundImageView.layer.sublayers?.removeAll() + backgroundImageView.backgroundColor = UIColor(cgColor: color) + return + } + } + + private func setupUI() { + backgroundImageView.contentMode = .scaleAspectFill + backgroundImageView.clipsToBounds = true + backgroundImageView.layer.cornerRadius = panelType == .large ? 15 : 5 + setupButton() + setupBlurView() + addSubview(backgroundImageView) + if panelType == .large { + addSubview(blurView) + } + addSubview(playButton) + let gradientLayer = makeGradientLayer() + backgroundImageView.layer.addSublayer(gradientLayer) + } + + private func setupLayout() { + backgroundImageView.translatesAutoresizingMaskIntoConstraints = false + blurView.translatesAutoresizingMaskIntoConstraints = panelType == .large + playButton.translatesAutoresizingMaskIntoConstraints = false + + if panelType == .large { + largeLayout() + } else { compactLayout() } + } + + private func largeLayout() { + NSLayoutConstraint.activate([ + backgroundImageView.topAnchor.constraint(equalTo: topAnchor), + backgroundImageView.bottomAnchor.constraint(equalTo: bottomAnchor), + backgroundImageView.leadingAnchor.constraint(equalTo: leadingAnchor), + backgroundImageView.trailingAnchor.constraint(equalTo: trailingAnchor), + backgroundImageView.heightAnchor.constraint(equalTo: widthAnchor), + + blurView.topAnchor.constraint(equalTo: topAnchor), + blurView.bottomAnchor.constraint(equalTo: bottomAnchor), + blurView.leadingAnchor.constraint(equalTo: leadingAnchor), + blurView.trailingAnchor.constraint(equalTo: trailingAnchor), + + playButton.centerYAnchor.constraint(equalTo: backgroundImageView.centerYAnchor), + playButton.centerXAnchor.constraint(equalTo: backgroundImageView.centerXAnchor), + ]) + } + + private func compactLayout() { + NSLayoutConstraint.activate([ + backgroundImageView.topAnchor.constraint(equalTo: topAnchor), + backgroundImageView.bottomAnchor.constraint(equalTo: bottomAnchor), + backgroundImageView.leadingAnchor.constraint(equalTo: leadingAnchor), + backgroundImageView.widthAnchor.constraint(equalTo: heightAnchor), + + playButton.centerYAnchor.constraint(equalTo: backgroundImageView.centerYAnchor), + playButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24), + ]) + } + + private func updateButtonImage(with state: AudioButtonState) { + UIView.animate( + withDuration: 0.3, + delay: 0, + options: [.curveEaseOut], + animations: { [weak self] in + self?.playButton.transform = .identity + }, completion: { [weak self] _ in + self?.playButton.configuration?.baseForegroundColor = self?.panelType == .large ? state.color : .asBlack + self?.playButton.configuration?.image = state.symbol + } + ) + } + + private func didButtonTapped() { + onPlayButtonTapped?(panelType) + } + + private func makeGradientLayer() -> CAGradientLayer { + let gradientLayer = CAGradientLayer() + let colors: [CGColor] = [ + UIColor.asOrange.cgColor, + UIColor.asYellow.cgColor, + UIColor.asGreen.cgColor, + ] + gradientLayer.frame = backgroundImageView.bounds + gradientLayer.colors = colors + gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0) + gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) + return gradientLayer + } + + private func setupButton() { + var buttonConfiguration = UIButton.Configuration.borderless() + let imageConfig = UIImage.SymbolConfiguration(pointSize: panelType == .large ? 60 : 32) + buttonConfiguration.preferredSymbolConfigurationForImage = imageConfig + buttonConfiguration.baseForegroundColor = .white + buttonConfiguration.contentInsets = .zero + buttonConfiguration.background.backgroundColorTransformer = UIConfigurationColorTransformer { color in + color.withAlphaComponent(0.0) + } + + playButton.configurationUpdateHandler = { button in + UIView.animate( + withDuration: 0.15, + delay: 0, + usingSpringWithDamping: 0.5, + initialSpringVelocity: 0.5, + options: [.allowUserInteraction], + animations: { + if button.isHighlighted { + button.transform = CGAffineTransform(scaleX: 0.85, y: 0.85) + } else { + button.transform = .identity + } + } + ) + } + + playButton.configuration = buttonConfiguration + playButton.addAction(UIAction { [weak self] _ in + self?.didButtonTapped() + }, for: .touchUpInside) + } + + private func setupBlurView() { + let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark) + blurView = UIVisualEffectView(effect: blurEffect) + blurView.layer.cornerRadius = 15 + blurView.clipsToBounds = true + blurView.alpha = 0.6 + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift index c829bf3e..43a6b735 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanel.swift @@ -10,7 +10,7 @@ enum MusicPanelType { final class MusicPanel: UIView { private let panel = ASPanel() - private let player: ASMusicPlayer + private let player: ASMusicPlayerView private let noMusicLabel = UILabel() private let titleLabel = UILabel() private let artistLabel = UILabel() @@ -23,7 +23,7 @@ final class MusicPanel: UIView { init(_ type: MusicPanelType = .large) { panelType = type musicRepository = DIContainer.shared.resolve(MusicRepositoryProtocol.self) - player = ASMusicPlayer(type) + player = ASMusicPlayerView(type) super.init(frame: .zero) setupUI() setupNoMusicLabel() @@ -55,6 +55,7 @@ final class MusicPanel: UIView { self.viewModel = MusicPanelViewModel( music: music, + type: panelType, musicRepository: self.musicRepository ) self.player.updateMusicPanel(color: music?.artworkBackgroundColor?.hexToCGColor()) @@ -66,8 +67,8 @@ final class MusicPanel: UIView { } private func bindWithPlayer() { - player.onPlayButtonTapped = { [weak self] in - self?.viewModel?.togglePlayPause() + player.onPlayButtonTapped = { [weak self] type in + self?.viewModel?.togglePlayPause(type) } } @@ -175,183 +176,3 @@ final class MusicPanel: UIView { ]) } } - -private final class ASMusicPlayer: UIView { - private var backgroundImageView = UIImageView() - private var blurView = UIVisualEffectView() - private var playButton = UIButton() - private var panelType: MusicPanelType - var onPlayButtonTapped: (() -> Void)? - private var cancellables = Set() - - init(_ type: MusicPanelType = .large) { - panelType = type - super.init(frame: .zero) - setupUI() - setupLayout() - } - - func bind( - to dataSource: Published.Publisher - ) { - dataSource - .receive(on: DispatchQueue.main) - .sink { [weak self] state in - self?.updateButtonImage(with: state) - } - .store(in: &cancellables) - } - - override func layoutSubviews() { - super.layoutSubviews() - - backgroundImageView.layer.sublayers?.forEach { layer in - if let gradientLayer = layer as? CAGradientLayer { - gradientLayer.frame = backgroundImageView.bounds - } - } - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func updateMusicPanel(image: Data? = nil, color: CGColor? = nil) { - if let image, !image.isEmpty { - backgroundImageView.layer.sublayers?.removeAll() - backgroundImageView.image = UIImage(data: image) - return - } - - if let color { - backgroundImageView.layer.sublayers?.removeAll() - backgroundImageView.backgroundColor = UIColor(cgColor: color) - return - } - } - - private func setupUI() { - backgroundImageView.contentMode = .scaleAspectFill - backgroundImageView.clipsToBounds = true - backgroundImageView.layer.cornerRadius = panelType == .large ? 15 : 5 - setupButton() - setupBlurView() - addSubview(backgroundImageView) - if panelType == .large { - addSubview(blurView) - } - addSubview(playButton) - let gradientLayer = makeGradientLayer() - backgroundImageView.layer.addSublayer(gradientLayer) - } - - private func setupLayout() { - backgroundImageView.translatesAutoresizingMaskIntoConstraints = false - blurView.translatesAutoresizingMaskIntoConstraints = panelType == .large - playButton.translatesAutoresizingMaskIntoConstraints = false - - if panelType == .large { - NSLayoutConstraint.activate([ - backgroundImageView.topAnchor.constraint(equalTo: topAnchor), - backgroundImageView.bottomAnchor.constraint(equalTo: bottomAnchor), - backgroundImageView.leadingAnchor.constraint(equalTo: leadingAnchor), - backgroundImageView.trailingAnchor.constraint(equalTo: trailingAnchor), - backgroundImageView.heightAnchor.constraint(equalTo: widthAnchor), - - blurView.topAnchor.constraint(equalTo: topAnchor), - blurView.bottomAnchor.constraint(equalTo: bottomAnchor), - blurView.leadingAnchor.constraint(equalTo: leadingAnchor), - blurView.trailingAnchor.constraint(equalTo: trailingAnchor), - - playButton.centerYAnchor.constraint(equalTo: backgroundImageView.centerYAnchor), - playButton.centerXAnchor.constraint(equalTo: backgroundImageView.centerXAnchor), - ]) - } else { compactLayout() } - } - - private func compactLayout() { - NSLayoutConstraint.activate([ - backgroundImageView.topAnchor.constraint(equalTo: topAnchor), - backgroundImageView.bottomAnchor.constraint(equalTo: bottomAnchor), - backgroundImageView.leadingAnchor.constraint(equalTo: leadingAnchor), - backgroundImageView.widthAnchor.constraint(equalTo: heightAnchor), - - playButton.centerYAnchor.constraint(equalTo: backgroundImageView.centerYAnchor), - playButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24), - ]) - } - - private func updateButtonImage(with state: AudioButtonState) { - UIView.animate( - withDuration: 0.3, - delay: 0, - options: [.curveEaseOut], - animations: { [weak self] in - self?.playButton.transform = .identity - }, completion: { [weak self] _ in - self?.playButton.configuration?.baseForegroundColor = self?.panelType == .large ? state.color : .asBlack - self?.playButton.configuration?.image = state.symbol - } - ) - } - - private func didButtonTapped() { - onPlayButtonTapped?() - } - - private func makeGradientLayer() -> CAGradientLayer { - let gradientLayer = CAGradientLayer() - let colors: [CGColor] = [ - UIColor.asOrange.cgColor, - UIColor.asYellow.cgColor, - UIColor.asGreen.cgColor, - ] - gradientLayer.frame = backgroundImageView.bounds - gradientLayer.colors = colors - gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0) - gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) - return gradientLayer - } - - private func setupButton() { - var buttonConfiguration = UIButton.Configuration.borderless() - let imageConfig = UIImage.SymbolConfiguration(pointSize: panelType == .large ? 60 : 32) - buttonConfiguration.preferredSymbolConfigurationForImage = imageConfig - buttonConfiguration.baseForegroundColor = .white - buttonConfiguration.contentInsets = .zero - buttonConfiguration.background.backgroundColorTransformer = UIConfigurationColorTransformer { color in - color.withAlphaComponent(0.0) - } - - playButton.configurationUpdateHandler = { button in - UIView.animate( - withDuration: 0.15, - delay: 0, - usingSpringWithDamping: 0.5, - initialSpringVelocity: 0.5, - options: [.allowUserInteraction], - animations: { - if button.isHighlighted { - button.transform = CGAffineTransform(scaleX: 0.85, y: 0.85) - } else { - button.transform = .identity - } - } - ) - } - - playButton.configuration = buttonConfiguration - playButton.addAction(UIAction { [weak self] _ in - self?.didButtonTapped() - }, for: .touchUpInside) - } - - private func setupBlurView() { - let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark) - blurView = UIVisualEffectView(effect: blurEffect) - blurView.layer.cornerRadius = 15 - blurView.clipsToBounds = true - blurView.alpha = 0.6 - } -} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanelViewModel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanelViewModel.swift index 06fb04ca..c4bee06b 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanelViewModel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/MusicPanel/MusicPanelViewModel.swift @@ -4,6 +4,7 @@ import Combine import Foundation final class MusicPanelViewModel: @unchecked Sendable { + @Published var type: MusicPanelType @Published var music: Music? @Published var artwork: Data? @Published var preview: Data? @@ -11,8 +12,13 @@ final class MusicPanelViewModel: @unchecked Sendable { private let musicRepository: MusicRepositoryProtocol? private var cancellables = Set() - init(music: Music?, musicRepository: MusicRepositoryProtocol?) { + init( + music: Music?, + type: MusicPanelType = .large, + musicRepository: MusicRepositoryProtocol? + ) { self.music = music + self.type = type self.musicRepository = musicRepository getPreviewData() getArtworkData() @@ -24,13 +30,13 @@ final class MusicPanelViewModel: @unchecked Sendable { await AudioHelper.shared.playerStatePublisher .receive(on: DispatchQueue.main) .sink { [weak self] source, isPlaying in - if source == .imported { - self?.updateButtonState(isPlaying ? .playing : .idle) - return - } - if source == .recorded, isPlaying { - self?.updateButtonState(.idle) - return + switch source { + case let .imported(panelType): + self?.updateButtonState(type: panelType, isPlaying ? .playing : .idle) + default: if isPlaying { + self?.updateButtonState(type: .compact, .idle) + self?.updateButtonState(type: .large, .idle) + } } } .store(in: &cancellables) @@ -38,7 +44,8 @@ final class MusicPanelViewModel: @unchecked Sendable { .receive(on: DispatchQueue.main) .sink { [weak self] isRecording in if isRecording { - self?.updateButtonState(.idle) + self?.updateButtonState(type: .compact, .idle) + self?.updateButtonState(type: .large, .idle) } } .store(in: &cancellables) @@ -52,23 +59,30 @@ final class MusicPanelViewModel: @unchecked Sendable { } @MainActor - func togglePlayPause() { + func togglePlayPause(_ type: MusicPanelType) { guard preview != nil else { return } Task { [weak self] in - await self?.configureAudioHelper() - if self?.buttonState == .playing { + guard let self else { return } + await self.configureAudioHelper() + if buttonState == .playing { await AudioHelper.shared.stopPlaying() return } - if self?.buttonState == .idle { - await AudioHelper.shared.startPlaying(self?.preview, sourceType: .imported) + if buttonState == .idle { + await AudioHelper.shared.startPlaying( + self.preview, + sourceType: .imported(type) + ) return } } } - private func updateButtonState(_ state: AudioButtonState) { - buttonState = state + private func updateButtonState(type: MusicPanelType, _ state: AudioButtonState) { + if self.type == type { + buttonState = state + } + } private func getPreviewData() { diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift index 271f0100..ac2ff587 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/RecordingPanel/RecordingPanelViewModel.swift @@ -71,7 +71,7 @@ final class RecordingPanelViewModel: @unchecked Sendable { self?.updateButtonState(isPlaying ? .playing : .idle) return } - if source == .imported, isPlaying { + if isPlaying { self?.updateButtonState(.idle) return } From c27a7d1e342ed1cab852bb7c86ac2c1228704c3e Mon Sep 17 00:00:00 2001 From: SeungJae Son <46300191+Sonny-Kor@users.noreply.github.com> Date: Mon, 2 Dec 2024 02:20:28 +0900 Subject: [PATCH 19/21] =?UTF-8?q?feat:=20Guide=20ViewController=20?= =?UTF-8?q?=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ASEntity/ASEntity/GameState.swift | 87 ++++++++++++++--- .../Alert/ASAlertController.swift | 2 + .../UIKitComponents/CornerImageView.swift | 88 +++++++++++++++++ .../UIKitComponents/GuideLabel.swift | 7 +- .../Sources/Extensions/UIColor+Hex.swift | 38 ++++++++ .../Views/Game/GameNavigationController.swift | 31 +++++- .../Views/Guide/GuideViewController.swift | 95 +++++++++++++++++++ .../Sources/Views/Humming/HummingView.swift | 13 +-- .../Views/Rehumming/RehummingView.swift | 13 +-- .../Result/HummingResultViewController.swift | 3 +- .../Views/Result/HummingResultViewModel.swift | 3 +- .../SubmitAnswerViewController.swift | 15 +-- 12 files changed, 343 insertions(+), 52 deletions(-) create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/CornerImageView.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIColor+Hex.swift create mode 100644 alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift diff --git a/alsongDalsong/ASEntity/ASEntity/GameState.swift b/alsongDalsong/ASEntity/ASEntity/GameState.swift index 1c6d2e7f..4f41f129 100644 --- a/alsongDalsong/ASEntity/ASEntity/GameState.swift +++ b/alsongDalsong/ASEntity/ASEntity/GameState.swift @@ -48,24 +48,22 @@ public struct GameState { if players.count <= 2, round == 1, recordOrder == 1 { return .submitAnswer } - + if round == 1, recordOrder == players.count - 1 { return .submitAnswer - } - else if round == 1, recordOrder >= 1 { + } else if round == 1, recordOrder >= 1 { return .rehumming } case .result: if players.count <= 2, recordOrder == 1 { return .result } - + else if recordOrder == players.count - 1 { return .result - }else { + } else { return nil } - default: return .lobby } @@ -73,23 +71,22 @@ public struct GameState { } private func resolveHarmonyViewType(status: Status) -> GameViewType { - return .submitMusic + .submitMusic } private func resolveSyncViewType(status: Status) -> GameViewType { - return .submitMusic + .submitMusic } private func resolveInstantViewType(status: Status) -> GameViewType { - return .submitMusic + .submitMusic } private func resolveTTSViewType(status: Status) -> GameViewType { - return .submitMusic + .submitMusic } } - public enum GameViewType { case submitMusic case humming @@ -97,4 +94,72 @@ public enum GameViewType { case submitAnswer case result case lobby + + public var title: String { + switch self { + case .submitMusic: + "노래 선택" + case .humming: + "허밍" + case .rehumming: + "리허밍" + case .submitAnswer: + "정답 맞추기" + case .result: + "게임 종료!" + case .lobby: + "" + } + } + + public var description: String { + switch self { + case .submitMusic: + "문제로 제출할 노래를 고르세요" + case .humming: + "다음 사람에게 고른 노래를 전달하세요" + case .rehumming: + "다음 사람에게 허밍을 전달하세요" + case .submitAnswer: + "허밍을 듣고 무슨 노래인지 맞춰 보세요." + case .result: + "결과 화면으로 이동합니다.." + case .lobby: + "" + } + } + + public var caution: String? { + switch self { + case .submitMusic: + nil + case .humming: + "가사나 제목을 직접적으로 전달하지 않도록 주의하세요" + case .rehumming: + "가사나 제목을 직접적으로 전달하지 않도록 주의하세요" + case .submitAnswer: + nil + case .result: + nil + case .lobby: + nil + } + } + + public var symbol: (systemName: String, color: String)? { + switch self { + case .submitMusic: + (systemName: "music.note.list", color: "508DFD") + case .humming: + (systemName: "microphone", color: "FD5050") + case .rehumming: + (systemName: "microphone", color: "FD5050") + case .submitAnswer: + (systemName: "music.note.list", color: "508DFD") + case .result: + nil + case .lobby: + nil + } + } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift index 494ac0b4..bc7bbd54 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/Alert/ASAlertController.swift @@ -193,6 +193,7 @@ enum ASAlertText { case submitMusic case submitHumming case nextResult + case toLobby var description: String { switch self { case .joinRoom: "방 정보를 가져오는 중..." @@ -200,6 +201,7 @@ enum ASAlertText { case .submitMusic: "노래를 전송하는 중..." case .submitHumming: "허밍을 전송하는 중..." case .nextResult: "다음 결과를 가져오는 중..." + case . toLobby: "로비로 이동하는 중..." } } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/CornerImageView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/CornerImageView.swift new file mode 100644 index 00000000..46f328d3 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/CornerImageView.swift @@ -0,0 +1,88 @@ +import UIKit + +final class GuideIconView: UIView { + private let imageView = UIImageView() + private var animationCount = 0 + + init(image: UIImage?, backgroundColor: UIColor?) { + super.init(frame: .zero) + self.backgroundColor = backgroundColor + setupImageView(image: image) + applyCornerRadius(cornerRadius: 12) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupImageView(image: UIImage?) { + imageView.image = image + imageView.tintColor = .white + imageView.translatesAutoresizingMaskIntoConstraints = false + addSubview(imageView) + + NSLayoutConstraint.activate([ + imageView.widthAnchor.constraint(equalToConstant: 12), + imageView.heightAnchor.constraint(equalToConstant: 12), + imageView.centerXAnchor.constraint(equalTo: centerXAnchor), + imageView.centerYAnchor.constraint(equalTo: centerYAnchor) + ]) + } + + private func applyCornerRadius(cornerRadius: CGFloat) { + clipsToBounds = true + layer.cornerRadius = cornerRadius + if #available(iOS 11.0, *) { + self.layer.maskedCorners = [ + .layerMinXMinYCorner, + .layerMaxXMinYCorner, + .layerMaxXMaxYCorner + ] + } else { + let path = UIBezierPath( + roundedRect: bounds, + byRoundingCorners: [.topLeft, .topRight, .bottomRight], + cornerRadii: CGSize(width: cornerRadius, height: cornerRadius) + ) + let mask = CAShapeLayer() + mask.path = path.cgPath + layer.mask = mask + } + } + + private func animate(times: Int) { + guard animationCount < times else { return } + animationCount += 1 + + transform = CGAffineTransform.identity + transform = CGAffineTransform.identity + UIView.animate( + withDuration: 1.5, + delay: 0, + usingSpringWithDamping: 0.5, + initialSpringVelocity: 1.0, + options: .curveEaseInOut, + animations: { + self.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) + }, + completion: { _ in + UIView.animate(withDuration: 0.2) { + self.transform = CGAffineTransform.identity + } completion: { [weak self] _ in + self?.animate(times: times) + } + } + ) + } + + public func animateBounces(times: Int = 5) { + animationCount = 0 + animate(times: times) + } + + override func layoutSubviews() { + super.layoutSubviews() + applyCornerRadius(cornerRadius: layer.cornerRadius) + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/GuideLabel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/GuideLabel.swift index 4f7e7fa2..913b7367 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/GuideLabel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Components/UIKitComponents/GuideLabel.swift @@ -1,10 +1,12 @@ import UIKit final class GuideLabel: UILabel { - init() { + init(style: UIFont.TextStyle = .largeTitle) { super.init(frame: .zero) - font = .font(forTextStyle: .largeTitle) + font = .font(forTextStyle: style) textColor = .label + textAlignment = .center + numberOfLines = 0 } @available(*, unavailable) @@ -14,5 +16,6 @@ final class GuideLabel: UILabel { func setText(_ text: String) { self.text = text + sizeToFit() } } diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIColor+Hex.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIColor+Hex.swift new file mode 100644 index 00000000..051fb693 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Extensions/UIColor+Hex.swift @@ -0,0 +1,38 @@ +import UIKit + +extension UIColor { + convenience init?(hex: String?) { + guard let hex = hex else { + return nil + } + var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + + if hexSanitized.hasPrefix("#") { + hexSanitized.remove(at: hexSanitized.startIndex) + } + + let length = hexSanitized.count + guard length == 6 || length == 8 else { + return nil + } + + var rgbValue: UInt64 = 0 + Scanner(string: hexSanitized).scanHexInt64(&rgbValue) + + var red, green, blue, alpha: CGFloat + + if length == 6 { + red = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0 + green = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0 + blue = CGFloat(rgbValue & 0x0000FF) / 255.0 + alpha = 1.0 + } else { + red = CGFloat((rgbValue & 0xFF000000) >> 24) / 255.0 + green = CGFloat((rgbValue & 0x00FF0000) >> 16) / 255.0 + blue = CGFloat((rgbValue & 0x0000FF00) >> 8) / 255.0 + alpha = CGFloat(rgbValue & 0x000000FF) / 255.0 + } + + self.init(red: red, green: green, blue: blue, alpha: alpha) + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift index e7961d21..b260b6e6 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift @@ -97,7 +97,12 @@ final class GameNavigationController { gameStatusRepository: gameStatusRepository ) let vc = SelectMusicViewController(selectMusicViewModel: vm) - navigationController.pushViewController(vc, animated: true) + + let guideVC = GuideViewController(type: .submitMusic) { [weak self] in + guard let self else { return } + navigationController.pushViewController(vc, animated: true) + } + navigationController.pushViewController(guideVC, animated: true) } private func navigateToHumming() { @@ -113,7 +118,11 @@ final class GameNavigationController { recordsRepository: recordsRepository ) let vc = HummingViewController(viewModel: vm) - navigationController.pushViewController(vc, animated: true) + let guideVC = GuideViewController(type: .humming) { [weak self] in + guard let self else { return } + navigationController.pushViewController(vc, animated: true) + } + navigationController.pushViewController(guideVC, animated: true) } private func navigateToRehumming() { @@ -127,7 +136,11 @@ final class GameNavigationController { recordsRepository: recordsRepository ) let vc = RehummingViewController(viewModel: vm) - navigationController.pushViewController(vc, animated: true) + let guideVC = GuideViewController(type: .rehumming) { [weak self] in + guard let self else { return } + navigationController.pushViewController(vc, animated: true) + } + navigationController.pushViewController(guideVC, animated: true) } private func navigateToSubmitAnswer() { @@ -145,7 +158,11 @@ final class GameNavigationController { musicRepository: musicRepository ) let vc = SubmitAnswerViewController(viewModel: vm) - navigationController.pushViewController(vc, animated: true) + let guideVC = GuideViewController(type: .submitAnswer) { [weak self] in + guard let self else { return } + navigationController.pushViewController(vc, animated: true) + } + navigationController.pushViewController(guideVC, animated: true) } private func navigateToResult() { @@ -166,7 +183,11 @@ final class GameNavigationController { musicRepository: musicRepository ) let vc = HummingResultViewController(viewModel: vm) - navigationController.pushViewController(vc, animated: true) + let guideVC = GuideViewController(type: .result) { [weak self] in + guard let self else { return } + navigationController.pushViewController(vc, animated: true) + } + navigationController.pushViewController(guideVC, animated: true) } private func navigationToWaiting() {} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift new file mode 100644 index 00000000..501035a8 --- /dev/null +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift @@ -0,0 +1,95 @@ +import ASEntity +import UIKit + +final class GuideViewController: UIViewController { + private let type: GameViewType + private let titleLabel = GuideLabel(style: .largeTitle) + private let descriptionLabel = GuideLabel(style: .title2) + private let cautionLabel = GuideLabel(style: .callout) + private var imageContainerView: GuideIconView? + private var completion: (() -> Void)? + + init(type: GameViewType, completion: (() -> Void)? = nil) { + self.type = type + self.completion = completion + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + setupLayout() + setupImageView() + startAnimation() + } + + private func setupUI() { + view.backgroundColor = .asLightGray + titleLabel.text = type.title + descriptionLabel.text = type.description + cautionLabel.isHidden = true + + if let caution = type.caution { + cautionLabel.isHidden = false + cautionLabel.text = "* \(caution)" + cautionLabel.textColor = .systemRed + } + + if let symbol = type.symbol { + let image = UIImage(systemName: symbol.systemName) + let backgroundColor = UIColor(hex: symbol.color) + let corneredImageView = GuideIconView( + image: image, + backgroundColor: backgroundColor + ) + corneredImageView.translatesAutoresizingMaskIntoConstraints = false + imageContainerView = corneredImageView + } + } + + private func setupLayout() { + view.addSubview(titleLabel) + view.addSubview(descriptionLabel) + view.addSubview(cautionLabel) + titleLabel.translatesAutoresizingMaskIntoConstraints = false + descriptionLabel.translatesAutoresizingMaskIntoConstraints = false + cautionLabel.translatesAutoresizingMaskIntoConstraints = false + + let safeArea = view.safeAreaLayoutGuide + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 272), + titleLabel.centerXAnchor.constraint(equalTo: safeArea.centerXAnchor), + descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 48), + descriptionLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16), + descriptionLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -16), + cautionLabel.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 8), + cautionLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16), + cautionLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -16), + ]) + } + + private func setupImageView(){ + if let imageContainerView { + view.addSubview(imageContainerView) + NSLayoutConstraint.activate([ + imageContainerView.widthAnchor.constraint(equalToConstant: 24), + imageContainerView.heightAnchor.constraint(equalToConstant: 24), + imageContainerView.bottomAnchor.constraint(equalTo: titleLabel.topAnchor,constant: -4), + imageContainerView.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor,constant: 4), + ]) + } + + } + + private func startAnimation() { + imageContainerView?.animateBounces() + DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { + self.completion?() + } + } +} diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift index 9bfef4e4..b7741dda 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Humming/HummingView.swift @@ -2,7 +2,6 @@ import UIKit final class HummingViewController: UIViewController { private var progressBar = ProgressBar() - private var guideLabel = GuideLabel() private var musicPanel = MusicPanel() private var hummingPanel = RecordingPanel(.asYellow) private var recordButton = ASButton() @@ -41,7 +40,6 @@ final class HummingViewController: UIViewController { } private func setupUI() { - guideLabel.setText("노래를 따라해 보세요!") recordButton.updateButton(.startRecord) submitButton.updateButton(.submit) submitButton.updateButton(.disabled) @@ -51,7 +49,6 @@ final class HummingViewController: UIViewController { buttonStack.addArrangedSubview(submitButton) view.backgroundColor = .asLightGray view.addSubview(progressBar) - view.addSubview(guideLabel) view.addSubview(musicPanel) view.addSubview(hummingPanel) view.addSubview(buttonStack) @@ -76,7 +73,6 @@ final class HummingViewController: UIViewController { private func setupLayout() { progressBar.translatesAutoresizingMaskIntoConstraints = false - guideLabel.translatesAutoresizingMaskIntoConstraints = false musicPanel.translatesAutoresizingMaskIntoConstraints = false hummingPanel.translatesAutoresizingMaskIntoConstraints = false submissionStatus.translatesAutoresizingMaskIntoConstraints = false @@ -88,12 +84,9 @@ final class HummingViewController: UIViewController { progressBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), progressBar.heightAnchor.constraint(equalToConstant: 16), - guideLabel.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 20), - guideLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), - - musicPanel.topAnchor.constraint(equalTo: guideLabel.bottomAnchor, constant: 20), - musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 48), - musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -48), + musicPanel.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 32), + musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32), + musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32), hummingPanel.topAnchor.constraint(equalTo: musicPanel.bottomAnchor, constant: 36), hummingPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24), diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift index decd68a4..66733b0b 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Rehumming/RehummingView.swift @@ -2,7 +2,6 @@ import UIKit final class RehummingViewController: UIViewController { private var progressBar = ProgressBar() - private var guideLabel = GuideLabel() private var musicPanel = MusicPanel() private var hummingPanel = RecordingPanel(.asMint) private var recordButton = ASButton() @@ -45,7 +44,6 @@ final class RehummingViewController: UIViewController { } private func setupUI() { - guideLabel.setText("허밍을 듣고 따라하세요!") recordButton.updateButton(.idle("녹음하기", .systemRed)) submitButton.updateButton(.submit) submitButton.updateButton(.disabled) @@ -55,7 +53,6 @@ final class RehummingViewController: UIViewController { buttonStack.addArrangedSubview(submitButton) view.backgroundColor = .asLightGray view.addSubview(progressBar) - view.addSubview(guideLabel) view.addSubview(musicPanel) view.addSubview(hummingPanel) view.addSubview(buttonStack) @@ -80,7 +77,6 @@ final class RehummingViewController: UIViewController { private func setupLayout() { progressBar.translatesAutoresizingMaskIntoConstraints = false - guideLabel.translatesAutoresizingMaskIntoConstraints = false musicPanel.translatesAutoresizingMaskIntoConstraints = false hummingPanel.translatesAutoresizingMaskIntoConstraints = false submissionStatus.translatesAutoresizingMaskIntoConstraints = false @@ -92,12 +88,9 @@ final class RehummingViewController: UIViewController { progressBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), progressBar.heightAnchor.constraint(equalToConstant: 16), - guideLabel.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 20), - guideLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), - - musicPanel.topAnchor.constraint(equalTo: guideLabel.bottomAnchor, constant: 20), - musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 48), - musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -48), + musicPanel.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 32), + musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32), + musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32), hummingPanel.topAnchor.constraint(equalTo: musicPanel.bottomAnchor, constant: 36), hummingPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24), diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift index 40b7b4bb..6d25ba19 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewController.swift @@ -27,6 +27,7 @@ class HummingResultViewController: UIViewController { setButton() setConstraints() bind() + viewModel?.fetchResult() } override func viewDidDisappear(_ animated: Bool) { @@ -256,7 +257,7 @@ extension HummingResultViewController: UITableViewDataSource { extension HummingResultViewController { private func showNextResultLoading() { let alert = LoadingAlertController( - progressText: .nextResult, + progressText: .toLobby, loadAction: { [weak self] in try await self?.nextResultFetch() }, diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewModel.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewModel.swift index e6aed35b..5cca3cf8 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewModel.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Result/HummingResultViewModel.swift @@ -44,11 +44,10 @@ final class HummingResultViewModel: @unchecked Sendable { self.roomActionRepository = roomActionRepository self.roomInfoRepository = roomInfoRepository self.musicRepository = musicRepository - fetchResult() } // TODO: 함수 명이 바뀔 필요가 있는 듯함. - private func fetchResult() { + public func fetchResult() { hummingResultRepository.getResult() .receive(on: DispatchQueue.main) .sink { completion in diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift index bce8b208..f8a159b4 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/SubmitAnswer/SubmitAnswerViewController.swift @@ -3,7 +3,6 @@ import SwiftUI final class SubmitAnswerViewController: UIViewController { private var progressBar = ProgressBar() - private var guideLabel = GuideLabel() private var musicPanel = MusicPanel() private var selectedSongView: UIHostingController? private var selectAnswerButton = ASButton() @@ -42,7 +41,6 @@ final class SubmitAnswerViewController: UIViewController { } private func setupUI() { - guideLabel.setText("허밍을 듣고 정답을 맞춰보세요!") selectAnswerButton.setConfiguration(text: "정답 선택", backgroundColor: .asLightSky) submitButton.setConfiguration(text: "정답 제출", backgroundColor: .asLightGray) submitButton.updateButton(.disabled) @@ -55,13 +53,11 @@ final class SubmitAnswerViewController: UIViewController { private func setupLayout() { view.addSubview(progressBar) - view.addSubview(guideLabel) view.addSubview(musicPanel) view.addSubview(buttonStack) view.addSubview(submissionStatus) progressBar.translatesAutoresizingMaskIntoConstraints = false - guideLabel.translatesAutoresizingMaskIntoConstraints = false musicPanel.translatesAutoresizingMaskIntoConstraints = false submissionStatus.translatesAutoresizingMaskIntoConstraints = false buttonStack.translatesAutoresizingMaskIntoConstraints = false @@ -71,13 +67,10 @@ final class SubmitAnswerViewController: UIViewController { progressBar.leadingAnchor.constraint(equalTo: view.leadingAnchor), progressBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), progressBar.heightAnchor.constraint(equalToConstant: 16), - - guideLabel.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 20), - guideLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), - - musicPanel.topAnchor.constraint(equalTo: guideLabel.bottomAnchor, constant: 20), - musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 48), - musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -48), + + musicPanel.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 32), + musicPanel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32), + musicPanel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32), submissionStatus.topAnchor.constraint(equalTo: buttonStack.topAnchor, constant: -16), submissionStatus.trailingAnchor.constraint(equalTo: buttonStack.trailingAnchor, constant: 16), From 55626e6c60bf97d3d0b8aec6db986e178b483e18 Mon Sep 17 00:00:00 2001 From: SeungJae Son <46300191+Sonny-Kor@users.noreply.github.com> Date: Mon, 2 Dec 2024 02:50:33 +0900 Subject: [PATCH 20/21] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift index 405d1890..aef0d091 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Lobby/LobbyViewController.swift @@ -47,7 +47,6 @@ final class LobbyViewController: UIViewController { .receive(on: DispatchQueue.main) .sink { [weak self] _ in guard let self else { return } -// navigationController?.navigationBar.topItem?.title = "#\(self.viewmodel.roomNumber)" } .store(in: &cancellables) From c81e7da9fd674ab1b4961905051c6bacb22924f6 Mon Sep 17 00:00:00 2001 From: SeungJae Son <46300191+Sonny-Kor@users.noreply.github.com> Date: Mon, 2 Dec 2024 03:09:47 +0900 Subject: [PATCH 21/21] =?UTF-8?q?chore:=20=EA=B0=80=EC=9D=B4=EB=93=9C=20?= =?UTF-8?q?=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EB=B0=94=20?= =?UTF-8?q?=EA=B0=80=EB=A6=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alsongDalsong/Sources/Utils/AudioHelper.swift | 6 ------ .../Sources/Views/Game/GameNavigationController.swift | 1 - .../Sources/Views/Guide/GuideViewController.swift | 1 + 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift index 9e3818c7..862c0a03 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Utils/AudioHelper.swift @@ -5,12 +5,6 @@ import Foundation extension AnyPublisher: @unchecked @retroactive Sendable {} extension PassthroughSubject: @unchecked @retroactive Sendable {} -extension AudioHelper { - enum FileSource { - case imported - case recorded - } -} actor AudioHelper { // MARK: - Private properties diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift index b8bc36e3..b6770de1 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Game/GameNavigationController.swift @@ -210,7 +210,6 @@ final class GameNavigationController: @unchecked Sendable { } navigationController.pushViewController(guideVC, animated: true) - } private func navigateToSubmitAnswer() { diff --git a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift index 501035a8..8c74b5cb 100644 --- a/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift +++ b/alsongDalsong/alsongDalsong/alsongDalsong/Sources/Views/Guide/GuideViewController.swift @@ -29,6 +29,7 @@ final class GuideViewController: UIViewController { } private func setupUI() { + self.navigationController?.navigationBar.isHidden = true view.backgroundColor = .asLightGray titleLabel.text = type.title descriptionLabel.text = type.description