diff --git a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate index c26ba99..7c2929d 100644 Binary files a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate and b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/AcaMate/1. View/10. Common/DropDownView.swift b/AcaMate/1. View/10. Common/DropDownView.swift index 0b37b07..e166836 100644 --- a/AcaMate/1. View/10. Common/DropDownView.swift +++ b/AcaMate/1. View/10. Common/DropDownView.swift @@ -6,12 +6,13 @@ // import SwiftUI + class DropdownManager: ObservableObject { - static let shared = DropdownManager() - @Published var isPresented = false @Published var items: [String] = [] @Published var anchor: CGRect = .zero + @Published var font: Font = .body + var onSelect: ((String) -> Void)? func show(items: [String], anchor: CGRect, onSelect: @escaping (String) -> Void) { @@ -26,30 +27,49 @@ class DropdownManager: ObservableObject { } } +/// .coordinateSpace(name: "dropdownArea") 이거 지정을 해야 함 +/// GlobalDropdownOverlay(manager: dropdownManager) 이거도 ZStack으로 넣어야 함 struct DropdownButton: View { - let title: String + @ObservedObject var manager: DropdownManager + @State var title: String let items: [String] + + init(manager: DropdownManager, title: String, items: [String]) { + self.manager = manager + self.title = title + self.items = items + } + @State private var frame: CGRect = .zero var body: some View { Button { - DropdownManager.shared.show(items: items, anchor: frame) { selected in + manager.show(items: items, anchor: frame) { selected in print("선택한 항목:", selected) + title = selected } } label: { - HStack { + HStack(alignment: .center) { + Spacer() Text(title) - Image(systemName: "chevron.down") + .font(manager.font) + .lineLimit(1) + .minimumScaleFactor(0.5) + Image(systemName: /*manager.isPresented ? "chevron.up" : */ "chevron.down") + .resizable() + .frame(width: 8, height: 4) + Spacer() } - .padding() - .background(RoundedRectangle(cornerRadius: 8).stroke(Color.gray)) + .padding(4) + .background(RoundedRectangle(cornerRadius: 8).stroke(Color(.Normal.normal))) } + .buttonStyle(.plain) .background( GeometryReader { geo in Color.clear .onAppear { - frame = geo.frame(in: .global) + frame = geo.frame(in: .named("dropdownArea")) } } ) @@ -58,38 +78,78 @@ struct DropdownButton: View { struct GlobalDropdownOverlay: View { - @ObservedObject var manager = DropdownManager.shared + @ObservedObject var manager: DropdownManager + + @State private var scrollOffset: CGPoint = .zero + + private var maxVisibleItemCount: Int { + if manager.items.count > 5 { + return 5 + } else { return manager.items.count } + } + private let itemHeight: CGFloat = 42 + + private var heightForDropBox: CGFloat { + CGFloat(maxVisibleItemCount) * itemHeight + } + var body: some View { if manager.isPresented { ZStack(alignment: .topLeading) { - Color.black.opacity(0.001) + Color.black.opacity(0.1) .ignoresSafeArea() .onTapGesture { manager.dismiss() } - VStack(alignment: .leading, spacing: 0) { - ForEach(manager.items, id: \.self) { item in - Button { - manager.onSelect?(item) - manager.dismiss() - } label: { - Text(item) - .padding() - .frame(maxWidth: .infinity, alignment: .leading) + VStack(spacing: 0) { + if manager.items.count > maxVisibleItemCount { + OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { _ in + dropdownContent() } - .buttonStyle(.plain) - .background(Color.white) + } else { + dropdownContent() } } - .frame(width: manager.anchor.width) - .background(RoundedRectangle(cornerRadius: 8).stroke(Color.gray)) - .shadow(radius: 5) - .position(x: manager.anchor.midX, y: manager.anchor.maxY + 10) + .frame(width: manager.anchor.width, height: heightForDropBox) + .background{ + RoundedRectangle(cornerRadius: 8) + .fill(Color(.Normal.normal)) + .stroke(Color(.Second.normal)) + .strokeBorder(lineWidth: 2) + } + .position(x: manager.anchor.midX, + y: manager.anchor.maxY+heightForDropBox/2) + .animation(.easeOut(duration: 0.25), value: manager.isPresented) + } + } + } + + @ViewBuilder + private func dropdownContent() -> some View { + VStack(alignment: .leading, spacing: 0) { + ForEach(Array(manager.items.enumerated()), id: \.offset) { index, item in + Button { + manager.onSelect?(item) + manager.dismiss() + } label: { + Text(item) + .font(manager.font) + .lineLimit(1) + .minimumScaleFactor(0.5) + .frame(maxWidth: .infinity, minHeight: 40, maxHeight: 40, alignment: .center) + .padding([.leading,.trailing], 2) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + + if index < manager.items.count - 1 { + Rectangle() + .fill(Color(.Second.normal).opacity(0.3)) + .frame(maxWidth: .infinity, maxHeight: 2) + } } } } } - - diff --git a/AcaMate/1. View/11. Intro & Login/RegisterView.swift b/AcaMate/1. View/11. Intro & Login/RegisterView.swift index 9f7ca23..a5b966a 100644 --- a/AcaMate/1. View/11. Intro & Login/RegisterView.swift +++ b/AcaMate/1. View/11. Intro & Login/RegisterView.swift @@ -28,11 +28,13 @@ struct RegisterView: View { @State private var isSelectAddr: Bool = false + @StateObject private var dropdownManager = DropdownManager() + private let addressBtnID = UUID() private let registerBtnID = UUID() @State private var selected = "" - let options = ["Swift", "Kotlin", "Dart", "JavaScript"] + let options = ["Swift", "Kotlin", "Dart", "JavaScript", "C#","C++","C"] var body: some View { // MARK: TO-DO @@ -51,9 +53,6 @@ struct RegisterView: View { } .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 16)) - DropdownButton(title: "언어 선택", items: ["Swift", "Dart", "Kotlin"]) - - OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in // 이름 @@ -66,6 +65,7 @@ struct RegisterView: View { .foregroundStyle(Color(.Other.red)) } .frame(width: 60, alignment: .center) + .padding(.trailing,12) Spacer(minLength: 1) CustomTextField(placeholder: "최대 10글자 입력", text: $registerVM.nameText, alignment: .center) .frame(maxWidth: .infinity,maxHeight: 48) @@ -82,6 +82,7 @@ struct RegisterView: View { Text("생일") .font(.nps(size: 16)) .frame(width: 60, alignment: .center) + .padding(.trailing,12) Spacer(minLength: 1) DatePicker("", selection: $registerVM.selectDate, displayedComponents: [.date]) .datePickerStyle(.compact) @@ -99,10 +100,10 @@ struct RegisterView: View { .font(.nps(size: 16)) .foregroundStyle(Color(.Other.red)) } - + .frame(width: 60, alignment: .center) + .padding(.trailing,12) Spacer(minLength: 1) CustomTextField(placeholder: "앞부분 입력", text: $registerVM.emailFrontText, alignment: .center) - .frame(maxWidth: .infinity, maxHeight: .infinity) .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) .background { @@ -114,13 +115,25 @@ struct RegisterView: View { .font(.nps(size: 16)) .padding([.leading, .trailing], 4) // Spacer(minLength: 1) - CustomTextField(placeholder: "뒷부분 입력", text: $registerVM.emailTailText, alignment: .center) - .frame(maxWidth: .infinity,maxHeight: 48) + + DropdownButton(manager: dropdownManager, title: "도메인 선택", + items: registerVM.emailTailList) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + + // 직접 입력 선택시 나오게 할 부분 + /* + CustomTextField(placeholder: "뒷부분 입력", text: $registerVM.emailFrontText, alignment: .center) + .frame(maxWidth: .infinity, maxHeight: .infinity) .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) .background { RoundedRectangle(cornerRadius: 24) .foregroundStyle(Color(.Normal.light)) } + */ } .padding() @@ -133,10 +146,17 @@ struct RegisterView: View { .font(.nps(size: 16)) .foregroundStyle(Color(.Other.red)) } - - CustomTextField(placeholder: "000", text: $registerVM.nameText, alignment: .center) - .frame(maxWidth: .infinity,maxHeight: 48) - .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .frame(width: 60, alignment: .center) + .padding(.trailing,12) +// DropdownButton(manager: dropdownManager, title: "선택", items: options) +// CustomTextField(placeholder: "000", text: $registerVM.nameText, alignment: .center) +// .frame(maxWidth: .infinity,maxHeight: 48) +// .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) +// .background { +// RoundedRectangle(cornerRadius: 24) +// .foregroundStyle(Color(.Normal.light)) +// } + DropdownButton(manager: dropdownManager, title: "선택", items: registerVM.numberHeadList) .background { RoundedRectangle(cornerRadius: 24) .foregroundStyle(Color(.Normal.light)) @@ -144,6 +164,7 @@ struct RegisterView: View { Text("-") .font(.nps(size: 16)) .padding([.leading, .trailing], 4) + CustomTextField(placeholder: "0000", text: $registerVM.nameText, alignment: .center) .frame(maxWidth: .infinity,maxHeight: 48) .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) @@ -168,6 +189,7 @@ struct RegisterView: View { Text("주소") .font(.nps(size: 16)) .frame(width: 60, alignment: .center) + .padding(.trailing,12) Spacer(minLength: 1) VStack(spacing: 0) { @@ -189,8 +211,9 @@ struct RegisterView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) // .background(Color(.Normal.light)) } - GlobalDropdownOverlay() + GlobalDropdownOverlay(manager: dropdownManager) } + .coordinateSpace(name: "dropdownArea") .sheet(isPresented: $showWebView) { WebView(url: URL(string: "https://sean-59.github.io/Kakao-Postcode/")!, showWebView: $showWebView, @@ -217,6 +240,7 @@ struct RegisterView: View { btnVM.setAction(for: addressBtnID) { self.showWebView.toggle() } + dropdownManager.font = .nps(size: 16) } .onChange(of: registerVM.addressText) { _, new in diff --git a/AcaMate/3. ViewModel/RegisterViewModel.swift b/AcaMate/3. ViewModel/RegisterViewModel.swift index 0fe8166..2b4125c 100644 --- a/AcaMate/3. ViewModel/RegisterViewModel.swift +++ b/AcaMate/3. ViewModel/RegisterViewModel.swift @@ -28,5 +28,17 @@ class RegisterViewModel: ObservableObject { @Published var addressText: String = "주소 입력" @State var addrDetailText: String = "" + let numberHeadList = ["010","011","016","017","018","019"] + let emailTailList = ["gmail.com", + "naver.com", + "daum.net", + "hanmail.net", + "nate.com", + "outlook.com", + "icloud.com", + "kakao.com", + "yahoo.com", + "protonmail.com", + "직접 입력"] }