[] 회원가입 화면 추가 및 몇가지 동작 로직 수정

This commit is contained in:
김선규 2025-03-24 17:53:48 +09:00
parent 45a28e386d
commit 5a2c237e3a
17 changed files with 297 additions and 63 deletions

View File

@ -0,0 +1,78 @@
//
// WebView.swift
// AcaMate
//
// Created by TAnine on 3/24/25.
//
import SwiftUI
import WebKit
struct WebView: UIViewControllerRepresentable {
@Binding var isLoding: Bool
func updateUIViewController(_ uiViewController: WebViewController, context: Context) {
}
func makeUIViewController(context: Context) -> WebViewController {
return WebViewController()
}
}
class WebViewController: UIViewController, WKUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
webView()
}
func webView() {
let url = URL(string: "https://sean-59.github.io/Kakao-Postcode/")!
let request = URLRequest(url: url)
let configuration = WKWebViewConfiguration()
let contentController = WKUserContentController()
contentController.add(self, name: "callBackHandler")
configuration.userContentController = contentController
let webview = WKWebView(frame: view.bounds, configuration: configuration)
webview.uiDelegate = self
webview.navigationDelegate = self
webview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webview.isUserInteractionEnabled = true
webview.scrollView.isUserInteractionEnabled = true
webview.scrollView.delaysContentTouches = false
webview.load(request)
view.addSubview(webview)
}
}
extension WebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("웹뷰 로딩 시작")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("웹뷰 로딩 완료")
// (: ) .
}
}
extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
if let data = message.body as? [String: Any] {
print(data)
print(data["jibunAddress"] ?? "jibunAddress 없음")
print(data["roadAddress"] ?? "roadAddress 없음")
print(data["zonecode"] ?? "zonecode 없음")
}
}
}

View File

@ -22,13 +22,13 @@ struct NavigationView: View {
case .NONE: case .NONE:
EmptyView() EmptyView()
case .Intro: case .Intro:
IntroView(appVM: appVM) IntroView(appVM)
case .Login : case .Login :
LoginView(appVM: appVM) LoginView(appVM)
case .Register(let type, let id): case .Register(let type, let id):
RegisterView(type: type, snsID: id) RegisterView(appVM, type: type, snsID: id)
case .SelectAcademy: case .SelectAcademy:
SelectAcademyView() SelectAcademyView(appVM)
case .Main: case .Main:
MainView() MainView()
case .ChatRoom(let id): case .ChatRoom(let id):

View File

@ -8,15 +8,14 @@
import SwiftUI import SwiftUI
import Combine import Combine
struct IntroView: View {
// @EnvironmentObject var appVM: AppViewModel
@StateObject private var introVM : IntroViewModel
struct IntroView: View {
@EnvironmentObject var appVM: AppViewModel
@StateObject var introVM: IntroViewModel
@State var cancellables: Set<AnyCancellable> = [] @State var cancellables: Set<AnyCancellable> = []
init(appVM: AppViewModel){ init(_ appVM: AppViewModel) {
_introVM = StateObject(wrappedValue: IntroViewModel(appVM: appVM)) _introVM = StateObject(wrappedValue: IntroViewModel(appVM))
// self.introVM = IntroViewModel(appVM: appVM)
} }
var body: some View { var body: some View {

View File

@ -8,13 +8,16 @@
import SwiftUI import SwiftUI
import Combine import Combine
struct LoginView: View { struct LoginView: View {
// @EnvironmentObject var appVM: AppViewModel @EnvironmentObject var appVM: AppViewModel
@StateObject private var loginVM: LoginViewModel @StateObject var loginVM: LoginViewModel
init(appVM : AppViewModel) {
_loginVM = StateObject(wrappedValue: LoginViewModel(appVM: appVM)) init(_ appVM: AppViewModel) {
_loginVM = StateObject(wrappedValue: LoginViewModel(appVM))
} }
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
Spacer().frame(height: 100) Spacer().frame(height: 100)
@ -54,6 +57,9 @@ struct LoginView: View {
} }
.frame(maxWidth: .infinity,maxHeight: .infinity) .frame(maxWidth: .infinity,maxHeight: .infinity)
.fullDrawView(.Normal.normal) .fullDrawView(.Normal.normal)
.onAppear() {
}
// .onChange(of: loginVM.pathName){ _, new in // .onChange(of: loginVM.pathName){ _, new in
// appVM.naviState.set(act: .ADD, path: new) // appVM.naviState.set(act: .ADD, path: new)

View File

@ -11,37 +11,146 @@ struct RegisterView: View {
@EnvironmentObject var appVM: AppViewModel @EnvironmentObject var appVM: AppViewModel
@StateObject private var topVM = TopViewModel() @StateObject private var topVM = TopViewModel()
@StateObject var btnVM = ButtonViewModel() @StateObject var btnVM = ButtonViewModel()
@StateObject var registerVM: RegisterViewModel
private let responseValue: (SNSLoginType, String)
init(_ appVM: AppViewModel, type: SNSLoginType, snsID: String) {
_registerVM = StateObject(wrappedValue: RegisterViewModel(appVM))
self.responseValue.0 = type
self.responseValue.1 = snsID
}
@State private var scrollOffset: CGPoint = .zero @State private var scrollOffset: CGPoint = .zero
@State private var showWebView = false
let type: SNSLoginType
let snsID: String
@State private var selectDate: Date = {
let calendar = Calendar.current let addressBtnID = UUID()
return calendar.date(byAdding: .year, value: -12, to: Date()) ?? Date() let registerBtnID = UUID()
}()
var body: some View { var body: some View {
// MARK: TO-DO // MARK: TO-DO
// //
// , , , , // , , , ,
VStack(spacing: 0) { VStack(spacing: 0) {
TopView(topVM: topVM) TopView(topVM: topVM)
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
DatePicker("생년월일 입력", selection: $selectDate, displayedComponents: [.date]) //
HStack(spacing: 0){
HStack(spacing: 0) {
Text("이름")
.font(.nps(size: 16))
Text("*")
.font(.nps(size: 16))
.foregroundStyle(Color(.Other.red))
}
.frame(width: 60, alignment: .leading)
Spacer(minLength: 1)
CustomTextField(placeholder: "최대 10글자", text: $registerVM.nameText)
.frame(maxWidth: .infinity,maxHeight: 48)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 24)
.foregroundStyle(Color(.Normal.light))
}
}
.padding()
//
DatePicker("생일", selection: $registerVM.selectDate, displayedComponents: [.date])
.datePickerStyle(.compact) .datePickerStyle(.compact)
.environment(\.locale, Locale(identifier: "ko_KR")) .environment(\.locale, Locale(identifier: "ko_KR"))
.font(.nps(size: 16)) .font(.nps(size: 16))
.padding() .padding()
// E-Mail
HStack(spacing: 0){
Text("이메일")
.font(.nps(size: 16))
.frame(width: 60, alignment: .leading)
Spacer(minLength: 1)
CustomTextField(placeholder: "앞부분 입력", text: $registerVM.emailFrontText)
.frame(maxWidth: .infinity,maxHeight: 48)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 24)
.foregroundStyle(Color(.Normal.light))
}
// Spacer(minLength: 1)
Text("@")
.font(.nps(font: .bold, size: 16))
.padding([.leading, .trailing], 4)
// Spacer(minLength: 1)
CustomTextField(placeholder: "뒷부분 입력", text: $registerVM.emailTailText)
.frame(maxWidth: .infinity,maxHeight: 48)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 24)
.foregroundStyle(Color(.Normal.light))
}
}
.padding()
// Phone
HStack(spacing: 0){
Text("연락처")
.font(.nps(size: 16))
.frame(width: 60, alignment: .leading)
CustomTextField(placeholder: "000", text: $registerVM.nameText)
.frame(maxWidth: .infinity,maxHeight: 48)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 24)
.foregroundStyle(Color(.Normal.light))
}
Text("-")
.font(.nps(size: 16))
.padding([.leading, .trailing], 4)
CustomTextField(placeholder: "0000", text: $registerVM.nameText)
.frame(maxWidth: .infinity,maxHeight: 48)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 24)
.foregroundStyle(Color(.Normal.light))
}
Text("-")
.font(.nps(size: 16))
.padding([.leading, .trailing], 4)
CustomTextField(placeholder: "0000", text: $registerVM.nameText)
.frame(maxWidth: .infinity,maxHeight: 48)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
.background {
RoundedRectangle(cornerRadius: 24)
.foregroundStyle(Color(.Normal.light))
}
}
.padding()
HStack(spacing: 0){
Text("연락처")
.font(.nps(size: 16))
.frame(width: 60, alignment: .leading)
Spacer(minLength: 1)
SimpleBtnView(vm: btnVM, id: addressBtnID)
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
}
.padding()
// address
} }
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
.sheet(isPresented: $showWebView) {
WebView(isLoding: $showWebView)
.edgesIgnoringSafeArea(.all)
}
} }
.onAppear { .onAppear {
@ -49,6 +158,12 @@ struct RegisterView: View {
topVM.setLeftBtn(Image(.Icon.left), size: CGPoint(x: 40, y: 40), action: leftAct) topVM.setLeftBtn(Image(.Icon.left), size: CGPoint(x: 40, y: 40), action: leftAct)
topVM.setRightBtn(size: CGPoint(x: 40, y: 40), action: rightAct) topVM.setRightBtn(size: CGPoint(x: 40, y: 40), action: rightAct)
btnVM.setSize(for: addressBtnID, newWidth: 80, newHeight: 24)
btnVM.setText(for: addressBtnID, newText: "주소 입력", newFont: .nps(size: 16))
btnVM.setAction(for: addressBtnID) {
// self.appVM.isLoading.toggle()
self.showWebView.toggle()
}
} }
} }

View File

@ -9,7 +9,11 @@ import SwiftUI
struct SelectAcademyView: View { struct SelectAcademyView: View {
@EnvironmentObject var appVM: AppViewModel @EnvironmentObject var appVM: AppViewModel
@StateObject var saVM = SelectAcademyViewModel() @StateObject var saVM: SelectAcademyViewModel
init(_ appVM: AppViewModel) {
_saVM = StateObject(wrappedValue: SelectAcademyViewModel(appVM))
}
@State private var scrollOffset: CGPoint = .zero @State private var scrollOffset: CGPoint = .zero

View File

@ -10,7 +10,6 @@ import Combine
struct MainView: View { struct MainView: View {
@EnvironmentObject var appVM: AppViewModel @EnvironmentObject var appVM: AppViewModel
@EnvironmentObject var alertController: AlertController
@State var cancellables: Set<AnyCancellable> = [] @State var cancellables: Set<AnyCancellable> = []
// @Binding var naviState : NaviState // @Binding var naviState : NaviState

View File

@ -1,19 +0,0 @@
//
// AlertController.swift
// AcaMate
//
// Created by Sean Kim on 12/13/24.
//
import SwiftUI
import Combine
class AlertViewModel2: ObservableObject {
@Published var showAlert: Bool = false
var alertData: AlertData = .init(body: "")
let alertAction = CurrentValueSubject<String?, Never>(nil)
}

View File

@ -9,6 +9,8 @@ import SwiftUI
import Combine import Combine
class AppViewModel: ObservableObject { class AppViewModel: ObservableObject {
// public static let shared = AppViewModel()
@Published var isLoading: Bool = false @Published var isLoading: Bool = false
@Published var showAlert: Bool = false @Published var showAlert: Bool = false
@Published var menuName: MenuName = .Home @Published var menuName: MenuName = .Home
@ -18,5 +20,10 @@ class AppViewModel: ObservableObject {
/// ///
let alertAction = CurrentValueSubject<String?, Never>(nil) let alertAction = CurrentValueSubject<String?, Never>(nil)
var apiManager: APIManager = APIManager()
// private init() {
//
// }
//
} }

View File

@ -6,9 +6,17 @@
// //
import SwiftUI import SwiftUI
final class ChatVM: ObservableObject {
@Published var vm: ChatViewModel?
}
class ChatViewModel: ObservableObject { class ChatViewModel: ObservableObject {
private let appVM: AppViewModel
@Published var messages: [ChatMesage] = [] @Published var messages: [ChatMesage] = []
init(_ appVM: AppViewModel) {
self.appVM = appVM
}
} }

View File

@ -8,15 +8,15 @@
import SwiftUI import SwiftUI
import Combine import Combine
class IntroViewModel: ObservableObject { class IntroViewModel: ObservableObject {
var appVM : AppViewModel private let appVM: AppViewModel
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
// @Published var toggleLoading: Bool = false
// @Published var pathName: PathName = .NONE
@UserDefault(key: "header", defaultValue: "headerValue") var headerValue @UserDefault(key: "header", defaultValue: "headerValue") var headerValue
init(appVM: AppViewModel) { init(_ appVM: AppViewModel) {
self.appVM = appVM self.appVM = appVM
} }
@ -83,7 +83,7 @@ class IntroViewModel: ObservableObject {
decoding: APIResponse<Header>.self) decoding: APIResponse<Header>.self)
APIManager.shared.loadAPIData(request) appVM.apiManager.loadAPIData(request)
.sink { completion in .sink { completion in
switch completion { switch completion {
case .failure(let error): case .failure(let error):
@ -133,7 +133,7 @@ class IntroViewModel: ObservableObject {
parameters: ["type":"I"], parameters: ["type":"I"],
decoding: APIResponse<VersionData>.self) decoding: APIResponse<VersionData>.self)
APIManager.shared.loadAPIData(request) appVM.apiManager.loadAPIData(request)
.sink { completion in .sink { completion in
switch completion { switch completion {
case .failure(let error): case .failure(let error):

View File

@ -8,9 +8,9 @@
import SwiftUI import SwiftUI
import Combine import Combine
class LoginViewModel: ObservableObject { class LoginViewModel: ObservableObject {
let appVM: AppViewModel private let appVM: AppViewModel
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
// @Published var toggleLoading: Bool = false // @Published var toggleLoading: Bool = false
@ -22,7 +22,8 @@ class LoginViewModel: ObservableObject {
var bidArray: [String] = [] var bidArray: [String] = []
init(appVM: AppViewModel) {
init(_ appVM: AppViewModel) {
self.appVM = appVM self.appVM = appVM
} }
@ -30,7 +31,7 @@ class LoginViewModel: ObservableObject {
appVM.isLoading = true appVM.isLoading = true
LoginController().login(type) LoginController().login(type)
.flatMap{ snsId in .flatMap{ snsId in
APIManager.shared.loadAPIData(APIRequest(path: "/api/v1/in/user/login", self.appVM.apiManager.loadAPIData(APIRequest(path: "/api/v1/in/user/login",
headers: [API_HEADER : self.headerValue], headers: [API_HEADER : self.headerValue],
parameters: [ parameters: [
"acctype": "\(type == .Apple ? "ST00": "ST01")", "acctype": "\(type == .Apple ? "ST00": "ST01")",

View File

@ -0,0 +1,31 @@
//
// RegisterViewModel.swift
// AcaMate
//
// Created by TAnine on 3/24/25.
//
import SwiftUI
import Combine
class RegisterViewModel: ObservableObject {
private let appVM: AppViewModel
private var cancellables = Set<AnyCancellable>()
init(_ appVM: AppViewModel) {
self.appVM = appVM
}
@State var selectDate: Date = {
let calendar = Calendar.current
return calendar.date(byAdding: .year, value: -12, to: Date()) ?? Date()
}()
@State var nameText: String = ""
@State var emailFrontText: String = ""
@State var emailTailText: String = ""
@State var phoneArray: [Int] = []
@State var addressText: String = ""
}

View File

@ -9,8 +9,13 @@ import SwiftUI
import Combine import Combine
class SelectAcademyViewModel: ObservableObject { class SelectAcademyViewModel: ObservableObject {
private var appVM: AppViewModel
private var cancellables: Set<AnyCancellable> = [] private var cancellables: Set<AnyCancellable> = []
init(_ appVM: AppViewModel) {
self.appVM = appVM
}
@Published var academyCode: String = "" @Published var academyCode: String = ""
@Published var academyList: [AcademyName] = [] @Published var academyList: [AcademyName] = []
@Published var selectNum: Int = -1 @Published var selectNum: Int = -1
@ -23,7 +28,7 @@ class SelectAcademyViewModel: ObservableObject {
parameters: ["token": token, "refresh": refresh], parameters: ["token": token, "refresh": refresh],
decoding: APIResponse<[AcademyName]>.self) decoding: APIResponse<[AcademyName]>.self)
APIManager.shared.loadAPIData(request) appVM.apiManager.loadAPIData(request)
.sink { completion in .sink { completion in
switch completion { switch completion {
case .failure(let error): case .failure(let error):

View File

@ -8,18 +8,18 @@
import Foundation import Foundation
import Combine import Combine
import Alamofire import Alamofire
public class APIManager { public class APIManager {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
public static let shared = APIManager() // public static let shared = APIManager()
@UserDefault(key: "refresh", defaultValue: "refreshToken") var refresh @UserDefault(key: "refresh", defaultValue: "refreshToken") var refresh
@UserDefault(key: "token", defaultValue: "accToken") var accToken @UserDefault(key: "token", defaultValue: "accToken") var accToken
private init(cancellables: Set<AnyCancellable> = Set<AnyCancellable>()) { // private init(cancellables: Set<AnyCancellable> = Set<AnyCancellable>()) {
self.cancellables = cancellables // self.cancellables = cancellables
} // }
public func loadAPIData<T: APIResponseProtocol>(_ request: APIRequest<T>) -> Future<T, Error> { public func loadAPIData<T: APIResponseProtocol>(_ request: APIRequest<T>) -> Future<T, Error> {
let encoding: ParameterEncoding = (request.method == .get) ? URLEncoding.default : JSONEncoding.default let encoding: ParameterEncoding = (request.method == .get) ? URLEncoding.default : JSONEncoding.default
@ -58,7 +58,7 @@ public class APIManager {
parameters: ["refresh": refresh], parameters: ["refresh": refresh],
decoding: APIResponse<Access>.self) decoding: APIResponse<Access>.self)
APIManager.shared.loadAPIData(request) APIManager().loadAPIData(request)
.sink { completion in .sink { completion in
switch completion { switch completion {
case .failure(let error): case .failure(let error):
@ -105,7 +105,7 @@ public class APIManager {
self.accToken = (response as! Access).access self.accToken = (response as! Access).access
var updateRequest = request var updateRequest = request
updateRequest.parameters["token"] = self.accToken updateRequest.parameters["token"] = self.accToken
return APIManager.shared.loadAPIData(updateRequest) return APIManager().loadAPIData(updateRequest)
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
.eraseToAnyPublisher() .eraseToAnyPublisher()

View File

@ -29,7 +29,7 @@ struct AcaMateApp: App {
_ = AuthController.handleOpenUrl(url: url) _ = AuthController.handleOpenUrl(url: url)
} }
} }
.environmentObject(self.appVM) .environmentObject(appVM)
} }
} }
} }