forked from AcaMate/AcaMate_iOS
[✨] 드롭다운 최종 완성, 커스텀 텍스트 필드 전체 변경, 회원가입 로직 추가 중
This commit is contained in:
parent
4875427e24
commit
63031172a1
Binary file not shown.
|
@ -45,6 +45,7 @@ struct CircleBtnView: View {
|
|||
}
|
||||
.frame(width: state.width, height: state.height)
|
||||
.onTapGesture {
|
||||
endTextEditing()
|
||||
guard let action = state.action else {return}
|
||||
action()
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ struct SimpleBtnView: View {
|
|||
.frame(width: state.width, height: state.height)
|
||||
.onTapGesture {
|
||||
if state.isUsable {
|
||||
endTextEditing()
|
||||
guard let action = state.action else { return }
|
||||
action()
|
||||
}
|
||||
|
@ -30,6 +31,7 @@ struct SimpleBtnView: View {
|
|||
}
|
||||
else {
|
||||
Button{
|
||||
endTextEditing()
|
||||
guard let action = state.action else { return }
|
||||
action()
|
||||
} label: {
|
||||
|
|
|
@ -13,9 +13,10 @@ class DropdownManager: ObservableObject {
|
|||
@Published var anchor: CGRect = .zero
|
||||
@Published var font: Font = .body
|
||||
|
||||
var onSelect: ((String) -> Void)?
|
||||
var onSelect: ((Int) -> Void)?
|
||||
|
||||
func show(items: [String], anchor: CGRect, onSelect: @escaping (String) -> Void) {
|
||||
func show(items: [String], anchor: CGRect,
|
||||
onSelect: @escaping (Int) -> Void) {
|
||||
self.items = items
|
||||
self.anchor = anchor
|
||||
self.onSelect = onSelect
|
||||
|
@ -32,22 +33,28 @@ class DropdownManager: ObservableObject {
|
|||
struct DropdownButton: View {
|
||||
@ObservedObject var manager: DropdownManager
|
||||
@State var title: String
|
||||
|
||||
var onSelect: ((Int) -> Void)?
|
||||
let items: [String]
|
||||
|
||||
|
||||
init(manager: DropdownManager, title: String, items: [String]) {
|
||||
init(manager: DropdownManager, title: String, items: [String],
|
||||
onSelect: @escaping (Int) -> Void) {
|
||||
self.manager = manager
|
||||
self.title = title
|
||||
self.items = items
|
||||
self.onSelect = onSelect
|
||||
}
|
||||
|
||||
@State private var frame: CGRect = .zero
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
manager.show(items: items, anchor: frame) { selected in
|
||||
print("선택한 항목:", selected)
|
||||
title = selected
|
||||
endTextEditing()
|
||||
manager.show(items: items, anchor: frame) { index in
|
||||
self.onSelect?(index)
|
||||
print("선택한 항목: \(items[index])")
|
||||
title = items[index]
|
||||
}
|
||||
} label: {
|
||||
HStack(alignment: .center) {
|
||||
|
@ -62,7 +69,10 @@ struct DropdownButton: View {
|
|||
Spacer()
|
||||
}
|
||||
.padding(4)
|
||||
.background(RoundedRectangle(cornerRadius: 8).stroke(Color(.Normal.normal)))
|
||||
.background{
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(Color.clear)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(
|
||||
|
@ -131,16 +141,16 @@ struct GlobalDropdownOverlay: View {
|
|||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(manager.items.enumerated()), id: \.offset) { index, item in
|
||||
Button {
|
||||
manager.onSelect?(item)
|
||||
manager.onSelect?(index)
|
||||
manager.dismiss()
|
||||
} label: {
|
||||
Text(item)
|
||||
.font(manager.font)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.minimumScaleFactor(0.4)
|
||||
.padding([.leading, .trailing], 8)
|
||||
.frame(maxWidth: .infinity, minHeight: 40, maxHeight: 40, alignment: .center)
|
||||
.padding([.leading,.trailing], 2)
|
||||
.contentShape(Rectangle())
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
|
|
|
@ -13,12 +13,10 @@ struct RegisterView: View {
|
|||
@StateObject var btnVM = ButtonViewModel()
|
||||
@StateObject var registerVM: RegisterViewModel
|
||||
|
||||
private let responseValue: (SNSLoginType, String)
|
||||
// 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
|
||||
_registerVM = StateObject(wrappedValue: RegisterViewModel(appVM, type: type, snsID: snsID))
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,11 +28,9 @@ struct RegisterView: View {
|
|||
|
||||
@StateObject private var dropdownManager = DropdownManager()
|
||||
|
||||
private let addressBtnID = UUID()
|
||||
private let registerBtnID = UUID()
|
||||
|
||||
@State private var selected = ""
|
||||
let options = ["Swift", "Kotlin", "Dart", "JavaScript", "C#","C++","C"]
|
||||
@State private var onDomainTxf = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
// MARK: TO-DO
|
||||
|
@ -67,13 +63,13 @@ struct RegisterView: View {
|
|||
.frame(width: 60, alignment: .center)
|
||||
.padding(.trailing,12)
|
||||
Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "최대 10글자 입력", text: $registerVM.nameText, alignment: .center)
|
||||
CustomTxfView(placeholder: "이름 입력 (10 글자)", text: $registerVM.nameText, maxLength: 10, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}.clipped()
|
||||
}
|
||||
.padding(EdgeInsets(top: 8, leading: 16, bottom: 16, trailing: 16))
|
||||
|
||||
|
@ -103,37 +99,48 @@ struct RegisterView: View {
|
|||
.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 {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
// Spacer(minLength: 1)
|
||||
Text("@")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
// Spacer(minLength: 1)
|
||||
|
||||
DropdownButton(manager: dropdownManager, title: "도메인 선택",
|
||||
items: registerVM.emailTailList)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
VStack (spacing: 4) {
|
||||
CustomTxfView(placeholder: "이메일 입력(30 글자)", text: $registerVM.emailFrontText, maxLength: 30, alignment: .center)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.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))
|
||||
HStack(spacing: 0) {
|
||||
Text("@")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
// Spacer(minLength: 1)
|
||||
|
||||
DropdownButton(manager: dropdownManager, title: "도메인 선택",
|
||||
items: registerVM.emailTailList){ index in
|
||||
if registerVM.emailTailList.count-1 == index {
|
||||
onDomainTxf = true
|
||||
} else {
|
||||
registerVM.emailTailText = registerVM.emailTailList[index]
|
||||
onDomainTxf = false
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
*/
|
||||
if onDomainTxf {
|
||||
CustomTxfView(placeholder: "도메인 직접 입력(30 글자)", text: $registerVM.emailTailText, maxLength: 30, alignment: .center)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
@ -148,26 +155,20 @@ struct RegisterView: View {
|
|||
}
|
||||
.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))
|
||||
}
|
||||
DropdownButton(manager: dropdownManager, title: "선택", items: registerVM.numberHeadList){ index in
|
||||
registerVM.phoneTextSet.0 = registerVM.numberHeadList[index]
|
||||
}
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
Text("-")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
|
||||
CustomTextField(placeholder: "0000", text: $registerVM.nameText, alignment: .center)
|
||||
CustomTxfView(placeholder: "0000", text: $registerVM.phoneTextSet.1, maxLength: 4, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
|
@ -175,9 +176,9 @@ 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))
|
||||
CustomTxfView(placeholder: "0000", text: $registerVM.phoneTextSet.2, maxLength: 4, alignment: .center)
|
||||
.frame(maxWidth: .infinity, maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
|
@ -185,31 +186,44 @@ struct RegisterView: View {
|
|||
}
|
||||
.padding()
|
||||
|
||||
HStack(spacing: 0){
|
||||
HStack(alignment: .center, spacing: 0){
|
||||
Text("주소")
|
||||
.font(.nps(size: 16))
|
||||
.frame(width: 60, alignment: .center)
|
||||
.padding(.trailing,12)
|
||||
Spacer(minLength: 1)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
SimpleBtnView(vm: btnVM, id: addressBtnID)
|
||||
.padding(.bottom, isSelectAddr ? 4:0)
|
||||
CustomTextField(placeholder: "상세 주소 입력", text: $registerVM.addrDetailText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,
|
||||
maxHeight: isSelectAddr ? 48 : 0)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
SimpleBtnView(vm: btnVM, id: registerVM.addressBtnID)
|
||||
.frame(maxWidth: .infinity, maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
.opacity(isSelectAddr ? 1.0 : 0.0)
|
||||
.padding(.bottom, isSelectAddr ? 4:0)
|
||||
if isSelectAddr {
|
||||
CustomTxfView(placeholder: "상세 주소 입력 (50 글자)", text: $registerVM.addrDetailText, maxLength: 50, alignment: .center)
|
||||
.frame(maxWidth: .infinity,
|
||||
maxHeight: isSelectAddr ? 48 : 0)
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
// .background(Color(.Normal.light))
|
||||
|
||||
SimpleBtnView(vm: btnVM, id: registerVM.registerBtnID)
|
||||
.frame(maxWidth: .infinity, maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||
// .background {
|
||||
// RoundedRectangle(cornerRadius: 24)
|
||||
// .foregroundStyle(Color(.Normal.light))
|
||||
// }
|
||||
}
|
||||
GlobalDropdownOverlay(manager: dropdownManager)
|
||||
}
|
||||
|
@ -221,30 +235,36 @@ struct RegisterView: View {
|
|||
if let result = result as? (String, String) {
|
||||
print(result.0)
|
||||
isSelectAddr = true
|
||||
// printLog(registerVM.addressText)
|
||||
registerVM.addressText = result.0
|
||||
|
||||
btnVM.setSize(for: addressBtnID, newWidth: .infinity, newHeight: .infinity)
|
||||
// printLog(registerVM.addressText)
|
||||
btnVM.setText(for: addressBtnID, newText: "\(registerVM.addressText)", newFont: .nps(size: 16))
|
||||
btnVM.setSize(for: registerVM.addressBtnID, newWidth: .infinity, newHeight: .infinity)
|
||||
btnVM.setText(for: registerVM.addressBtnID, newText: "\(registerVM.addressText)", newFont: .nps(size: 16))
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
topVM.titleName = "회원가입"
|
||||
|
||||
topVM.setLeftBtn(Image(.Icon.left), size: CGPoint(x: 40, y: 40), action: leftAct)
|
||||
topVM.setRightBtn(size: CGPoint(x: 40, y: 40), action: rightAct)
|
||||
|
||||
btnVM.setSize(for: addressBtnID, newWidth: 80, newHeight: 24)
|
||||
btnVM.setText(for: addressBtnID, newText: "\(registerVM.addressText)", newFont: .nps(size: 16))
|
||||
btnVM.setAction(for: addressBtnID) {
|
||||
self.showWebView.toggle()
|
||||
}
|
||||
btnVM.setSize(for: registerVM.addressBtnID, newWidth: 80, newHeight: 24)
|
||||
btnVM.setText(for: registerVM.addressBtnID, newText: "\(registerVM.addressText)", newFont: .nps(size: 16))
|
||||
btnVM.setTextColor(for: registerVM.addressBtnID, newColor: Color.Text.black)
|
||||
btnVM.setAction(for: registerVM.addressBtnID) { self.showWebView.toggle() }
|
||||
|
||||
btnVM.setSize(for: registerVM.registerBtnID, newWidth: .infinity, newHeight: 48)
|
||||
btnVM.setText(for: registerVM.registerBtnID, newText: "회원가입", newFont: .nps(font: .bold, size: 24))
|
||||
btnVM.setTextColor(for: registerVM.registerBtnID, newColor: Color.Point.dark)
|
||||
btnVM.setAction(for: registerVM.registerBtnID) { registerVM.registerUser() }
|
||||
|
||||
|
||||
dropdownManager.font = .nps(size: 16)
|
||||
}
|
||||
.onChange(of: registerVM.addressText) { _, new in
|
||||
|
||||
}
|
||||
// .onChange(of: dropdownManager.onSelect) { _, new in
|
||||
// printLog("CHANGE: \(new)")
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
func leftAct() {
|
||||
|
|
|
@ -16,7 +16,7 @@ class AppViewModel: ObservableObject {
|
|||
@Published var menuName: MenuName = .Home
|
||||
@Published var naviState: NaviState = .init(act: .NONE, path: .Intro)
|
||||
|
||||
var alertData: AlertData = .init(body: "")
|
||||
@Published var alertData: AlertData = .init(body: "")
|
||||
|
||||
/// 항상 최신값을 가지고 있다가 구독자 추가 되면 그 즉시 값을 전달하고 이후 업데이트 되는 값을 계속 보내주는 역할을 함
|
||||
let alertAction = CurrentValueSubject<String?, Never>(nil)
|
||||
|
|
|
@ -9,24 +9,38 @@ import SwiftUI
|
|||
import Combine
|
||||
|
||||
class RegisterViewModel: ObservableObject {
|
||||
private let appVM: AppViewModel
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private let appVM: AppViewModel
|
||||
private let responseValue: (SNSLoginType, String)
|
||||
|
||||
init(_ appVM: AppViewModel) {
|
||||
init(_ appVM: AppViewModel, type: SNSLoginType, snsID: String) {
|
||||
self.appVM = appVM
|
||||
self.responseValue = (type, snsID)
|
||||
}
|
||||
|
||||
@State var selectDate: Date = {
|
||||
let calendar = Calendar.current
|
||||
return calendar.date(byAdding: .year, value: -12, to: Date()) ?? Date()
|
||||
}()
|
||||
let addressBtnID = UUID()
|
||||
let registerBtnID = UUID()
|
||||
|
||||
@State var nameText: String = ""
|
||||
@State var emailFrontText: String = ""
|
||||
@State var emailTailText: String = ""
|
||||
@State var phoneArray: [Int] = []
|
||||
@Published var selectDate: Date = {
|
||||
let calendar = Calendar.current
|
||||
return calendar.date(byAdding: .year, value: 0, to: Date()) ?? Date()
|
||||
}() {
|
||||
didSet {
|
||||
if selectDate != oldValue {
|
||||
changeDate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var changeDate: Bool = false
|
||||
|
||||
@Published var nameText: String = ""
|
||||
@Published var emailFrontText: String = ""
|
||||
@Published var emailTailText: String = ""
|
||||
@Published var numberHead: String = ""
|
||||
@Published var phoneTextSet: (String,String,String) = ("","","")
|
||||
@Published var addressText: String = "주소 입력"
|
||||
@State var addrDetailText: String = ""
|
||||
@Published var addrDetailText: String = ""
|
||||
|
||||
let numberHeadList = ["010","011","016","017","018","019"]
|
||||
let emailTailList = ["gmail.com",
|
||||
|
@ -41,4 +55,30 @@ class RegisterViewModel: ObservableObject {
|
|||
"protonmail.com",
|
||||
"직접 입력"]
|
||||
|
||||
func registerUser() {
|
||||
// 필수 값
|
||||
|
||||
if nameText != "" && emailFrontText != "" && emailTailText != "" && phoneTextSet.0 != "" && phoneTextSet.1 != "" && phoneTextSet.2 != "" {
|
||||
if !changeDate || addressText == "주소 입력" {
|
||||
appVM.alertData = AlertData(
|
||||
title: "알림",
|
||||
body: "\(changeDate ? "":"[생일]")\((addressText != "주소 입력") ? "":"[주소]")의 내용이 없습니다.\n 계속해서 진행할까요?",
|
||||
button: [ButtonType(name: "확인", role: .cancel, function: nil)])
|
||||
appVM.showAlert.toggle()
|
||||
// 넘어가는 로직도 추가
|
||||
} else {
|
||||
//정상 동작
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
appVM.alertData = AlertData(
|
||||
title: "경고", body: "필수 입력 사항이 누락되었습니다.",
|
||||
button: [ButtonType(name: "확인", role: .cancel, function: nil)])
|
||||
appVM.showAlert.toggle()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,10 +8,69 @@
|
|||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct CustomTextField: UIViewRepresentable {
|
||||
struct FixedSizeWrapper<Content: UIView>: UIViewRepresentable {
|
||||
let content: () -> Content
|
||||
let width: CGFloat
|
||||
let height: CGFloat
|
||||
|
||||
func makeUIView(context: Context) -> Content {
|
||||
let view = content()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
view.widthAnchor.constraint(equalToConstant: width),
|
||||
view.heightAnchor.constraint(equalToConstant: height)
|
||||
])
|
||||
return view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: Content, context: Context) {}
|
||||
}
|
||||
|
||||
struct CustomTxfView: View {
|
||||
|
||||
var placeholder: String
|
||||
@Binding var text: String
|
||||
|
||||
var maxLength: Int = 100
|
||||
var isSecure: Binding<Bool> = .constant(false)
|
||||
var alignment: TextAlignment = .leading
|
||||
|
||||
var textColor = Color(.Text.detail)
|
||||
var font: Font = .nps(size:16)
|
||||
// UIFont(name: "NPS-font-Regular", size: 16)
|
||||
|
||||
|
||||
var body: some View {
|
||||
TextField(placeholder, text: $text)
|
||||
// Binding<String>(
|
||||
// get: { text },
|
||||
// set: { newValue in
|
||||
// text = String(newValue.prefix(maxLength))
|
||||
// }
|
||||
// ))
|
||||
.font(font)
|
||||
.tint(textColor)
|
||||
.lineLimit(1)
|
||||
.multilineTextAlignment(alignment)
|
||||
.minimumScaleFactor(0.5)
|
||||
.truncationMode(.tail)
|
||||
.clipped()
|
||||
|
||||
.onChange(of: text) { old, new in
|
||||
if new.count > maxLength {
|
||||
text = String(new.prefix(maxLength))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CustomTextField: UIViewRepresentable {
|
||||
var placeholder: String
|
||||
@Binding var text: String
|
||||
var maxLength: Int?
|
||||
var isSecure: Binding<Bool> = .constant(false)
|
||||
|
||||
var textColor = UIColor(Color(.Text.detail))
|
||||
|
@ -21,24 +80,31 @@ struct CustomTextField: UIViewRepresentable {
|
|||
// [필수] 초기화 시 UIView 생성
|
||||
func makeUIView(context: Context) -> UITextField {
|
||||
let txf = UITextField()
|
||||
// txf.placeholder = placeholder
|
||||
txf.attributedPlaceholder = NSAttributedString(string: "\(placeholder)", attributes: [NSAttributedString.Key.foregroundColor : UIColor(.Text.border)])
|
||||
|
||||
|
||||
// txf.placeholder = placeholder
|
||||
txf.attributedPlaceholder = NSAttributedString(
|
||||
string: "\(placeholder)",
|
||||
attributes: [NSAttributedString.Key.foregroundColor : UIColor(.Text.border)])
|
||||
|
||||
txf.isSecureTextEntry = isSecure.wrappedValue
|
||||
|
||||
txf.adjustsFontSizeToFitWidth = true
|
||||
txf.minimumFontSize = 4
|
||||
|
||||
txf.textColor = textColor
|
||||
txf.font = font
|
||||
|
||||
txf.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let height = (font?.lineHeight ?? 10) + 8
|
||||
txf.frame.size.height = height
|
||||
|
||||
txf.borderStyle = .none
|
||||
txf.autocorrectionType = .no
|
||||
txf.autocapitalizationType = .none
|
||||
txf.smartInsertDeleteType = .no
|
||||
txf.textContentType = .oneTimeCode
|
||||
txf.textAlignment = self.alignment
|
||||
txf.translatesAutoresizingMaskIntoConstraints = false
|
||||
txf.setContentHuggingPriority(.required, for: .horizontal)
|
||||
txf.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
|
||||
txf.delegate = context.coordinator
|
||||
return txf
|
||||
|
@ -51,19 +117,34 @@ struct CustomTextField: UIViewRepresentable {
|
|||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
Coordinator(self, max: maxLength)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, UITextFieldDelegate {
|
||||
var parent: CustomTextField
|
||||
var maxLength: Int?
|
||||
|
||||
init(_ parent: CustomTextField) {
|
||||
init(_ parent: CustomTextField, max: Int?) {
|
||||
self.parent = parent
|
||||
self.maxLength = max
|
||||
}
|
||||
|
||||
func textFieldDidChangeSelection(_ textField: UITextField) {
|
||||
parent.text = textField.text ?? ""
|
||||
}
|
||||
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
let current = textField.text ?? ""
|
||||
guard let stringRange = Range(range, in: current) else { return false}
|
||||
let updated = current.replacingCharacters(in: stringRange, with: string)
|
||||
return updated.count <= (maxLength ?? 9999)
|
||||
}
|
||||
|
||||
// func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
// let currentText = textField.text ?? ""
|
||||
// guard let stringRange = Range(range, in: currentText) else { return false }
|
||||
// let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
|
||||
// return updatedText.count <= maxLength
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user