From 3a0ca5b945abc769516e6fe6fcf7c5ec80702486 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Thu, 9 Jan 2025 05:07:44 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20-=20=EC=B1=84=EB=84=90=20=EA=B2=80=EC=83=89=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 --- SodaLive/Sources/App/AppStep.swift | 2 + .../Content/Main/ContentMainView.swift | 27 ++++ SodaLive/Sources/ContentView.swift | 3 + .../Sources/CustomView/FocusedTextField.swift | 56 ++++++++ .../Sources/Explorer/ExplorerViewModel.swift | 1 + .../Extensions/UserDefaultsExtension.swift | 1 + .../SearchChannel/SearchChannelView.swift | 130 ++++++++++++++++++ 7 files changed, 220 insertions(+) create mode 100644 SodaLive/Sources/CustomView/FocusedTextField.swift create mode 100644 SodaLive/Sources/SearchChannel/SearchChannelView.swift diff --git a/SodaLive/Sources/App/AppStep.swift b/SodaLive/Sources/App/AppStep.swift index f1119df..62ec1de 100644 --- a/SodaLive/Sources/App/AppStep.swift +++ b/SodaLive/Sources/App/AppStep.swift @@ -139,4 +139,6 @@ enum AppStep { case auditionDetail(auditionId: Int) case auditionRoleDetail(roleId: Int) + + case searchChannel } diff --git a/SodaLive/Sources/Content/Main/ContentMainView.swift b/SodaLive/Sources/Content/Main/ContentMainView.swift index b55fb18..5fc4e5a 100644 --- a/SodaLive/Sources/Content/Main/ContentMainView.swift +++ b/SodaLive/Sources/Content/Main/ContentMainView.swift @@ -37,6 +37,33 @@ struct ContentMainView: View { .padding(.bottom, 26.7) .padding(.horizontal, 13.3) + HStack(spacing: 0) { + Image("ic_title_search_black") + + Text("채널명을 입력해 보세요") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color.gray55) + .keyboardType(.default) + .padding(.horizontal, 13.3) + + Spacer() + } + .padding(.horizontal, 21.3) + .frame(height: 50) + .frame(maxWidth: .infinity) + .background(Color.gray22) + .overlay( + RoundedRectangle(cornerRadius: 6.7) + .strokeBorder(lineWidth: 1) + .foregroundColor(Color.graybb) + ) + .padding(.bottom, 26.7) + .padding(.horizontal, 13.3) + .onTapGesture { + UserDefaults.set("", forKey: .searchChannel) + AppState.shared.setAppStep(step: .searchChannel) + } + ContentMainCreatorRankingView() .padding(.bottom, 26.7) .padding(.horizontal, 13.3) diff --git a/SodaLive/Sources/ContentView.swift b/SodaLive/Sources/ContentView.swift index bc9c410..c684285 100644 --- a/SodaLive/Sources/ContentView.swift +++ b/SodaLive/Sources/ContentView.swift @@ -209,6 +209,9 @@ struct ContentView: View { case .auditionRoleDetail(let roleId): AuditionRoleDetailView(roleId: roleId) + case .searchChannel: + SearchChannelView() + default: EmptyView() .frame(width: 0, height: 0, alignment: .topLeading) diff --git a/SodaLive/Sources/CustomView/FocusedTextField.swift b/SodaLive/Sources/CustomView/FocusedTextField.swift new file mode 100644 index 0000000..c8b7970 --- /dev/null +++ b/SodaLive/Sources/CustomView/FocusedTextField.swift @@ -0,0 +1,56 @@ +// +// FocusedTextField.swift +// SodaLive +// +// Created by klaus on 1/9/25. +// + +import SwiftUI + +struct FocusedTextField: UIViewRepresentable { + @Binding var text: String + var isFirstResponder: Bool + + class Coordinator: NSObject, UITextFieldDelegate { + var parent: FocusedTextField + + init(_ parent: FocusedTextField) { + self.parent = parent + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + parent.text = textField.text ?? "" + } + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + func makeUIView(context: Context) -> UITextField { + let textField = UITextField() + textField.delegate = context.coordinator + textField.font = UIFont(name: Font.medium.rawValue, size: 13.3) + textField.textColor = UIColor(hex: "eeeeee", alpha: 1.0) + textField.tintColor = UIColor(hex: "3bb9f1", alpha: 1.0) + textField.keyboardType = .default + textField.autocapitalizationType = .none + textField.autocorrectionType = .no + + let placeholder = "채널명을 입력해 보세요" + textField.placeholder = placeholder + textField.attributedPlaceholder = NSAttributedString( + string: placeholder, + attributes: [.foregroundColor: UIColor(hex: "555555", alpha: 1.0)] + ) + + return textField + } + + func updateUIView(_ uiView: UITextField, context: Context) { + uiView.text = text + if isFirstResponder { + uiView.becomeFirstResponder() + } + } +} diff --git a/SodaLive/Sources/Explorer/ExplorerViewModel.swift b/SodaLive/Sources/Explorer/ExplorerViewModel.swift index 7a4bfd4..1f669c4 100644 --- a/SodaLive/Sources/Explorer/ExplorerViewModel.swift +++ b/SodaLive/Sources/Explorer/ExplorerViewModel.swift @@ -29,6 +29,7 @@ final class ExplorerViewModel: ObservableObject { .sink { [unowned self] value in DEBUG_LOG("value: \(value)") if value.count > 1 { + UserDefaults.set(value, forKey: .searchChannel) searchChannel() } } diff --git a/SodaLive/Sources/Extensions/UserDefaultsExtension.swift b/SodaLive/Sources/Extensions/UserDefaultsExtension.swift index 51379c7..2865723 100644 --- a/SodaLive/Sources/Extensions/UserDefaultsExtension.swift +++ b/SodaLive/Sources/Extensions/UserDefaultsExtension.swift @@ -26,6 +26,7 @@ enum UserDefaultsKey: String, CaseIterable { case isAdultContentVisible case contentPreference case isAuditionNotification + case searchChannel } extension UserDefaults { diff --git a/SodaLive/Sources/SearchChannel/SearchChannelView.swift b/SodaLive/Sources/SearchChannel/SearchChannelView.swift new file mode 100644 index 0000000..abb3736 --- /dev/null +++ b/SodaLive/Sources/SearchChannel/SearchChannelView.swift @@ -0,0 +1,130 @@ +// +// SearchChannelView.swift +// SodaLive +// +// Created by klaus on 1/9/25. +// + +import SwiftUI +import Kingfisher + +struct SearchChannelView: View { + + @StateObject var viewModel = ExplorerViewModel() + @State private var isFocused: Bool = false + + var body: some View { + BaseView(isLoading: $viewModel.isLoading) { + VStack(spacing: 0) { + HStack(spacing: 0) { + Image("ic_back") + .resizable() + .frame(width: 20, height: 20) + .padding(13.3) + .onTapGesture { + AppState.shared.back() + } + + HStack(spacing: 0) { + Image("ic_title_search_black") + + FocusedTextField( + text: $viewModel.channel, + isFirstResponder: isFocused + ) + .padding(.horizontal, 13.3) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + isFocused = true + } + } + + if !viewModel.channel.isEmpty { + Image("ic_close_white") + .resizable() + .frame(width: 20, height: 20) + .onTapGesture { + viewModel.channel = "" + } + } + } + .padding(.horizontal, 21.3) + .frame(height: 50) + .frame(maxWidth: .infinity) + .background(Color.gray22) + .overlay( + RoundedRectangle(cornerRadius: 6.7) + .strokeBorder(lineWidth: 1) + .foregroundColor(Color.graybb) + ) + } + .padding(.horizontal, 13.3) + .frame(height: 50) + .background(Color.black) + .padding(.top, 13.3) + + ScrollView(.vertical, showsIndicators: false) { + if viewModel.channelResponses.count > 0 { + VStack(spacing: 26.7) { + ForEach(viewModel.channelResponses, id: \.self) { channel in + HStack(spacing: 13.3) { + KFImage(URL(string: channel.profileImageUrl)) + .cancelOnDisappear(true) + .downsampling(size: CGSize(width: 46.7, height: 46.7)) + .resizable() + .frame(width: 46.7, height: 46.7) + .clipShape(Circle()) + + Text(channel.nickname) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color.grayee) + Spacer() + } + .frame(maxWidth: .infinity) + .contentShape(Rectangle()) + .onTapGesture { + AppState.shared.setAppStep(step: .creatorDetail(userId: channel.id)) + } + } + } + } else { + Text("검색 결과가 없습니다.") + .font(.custom(Font.medium.rawValue, size: 18.3)) + .foregroundColor(.white) + .padding(.top, 40) + } + } + .padding(.vertical, 40) + .padding(.horizontal, 26.7) + } + .padding(.horizontal, 13.3) + .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { + GeometryReader { geo in + HStack { + Spacer() + Text(viewModel.errorMessage) + .padding(.vertical, 13.3) + .frame(width: geo.size.width - 66.7, alignment: .center) + .font(.custom(Font.medium.rawValue, size: 12)) + .background(Color.button) + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .cornerRadius(20) + .padding(.top, 66.7) + Spacer() + } + } + } + .onAppear { + viewModel.channel = UserDefaults.string(forKey: .searchChannel) + } + .onTapGesture { + hideKeyboard() + } + } + } +} + +#Preview { + SearchChannelView() +}