Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
SwiftUI/SwiftUI(Basic)

33. [SwiftUI] TextField(basic)

by Toughie 2023. 4. 29.

유저의 입력을 받을 수 있는 텍스트필드에 대해 알아보자.

유저의 입력 순간 순간을 감지하거나, 키보드의 리턴(엔터)버튼을 누르는 등

유저 입력부분에는 정말 다양한 커스터마이징이 가능하기 때문에 우선은 기본 기능을 살펴보자.

추가적으로 이전 시간에 배웠던 개념을 복습하는겸 알럿까지 띄워봤다.

 

 

먼저 두 글자 이상 받고 싶은 상황이다.

따라서 두 글자가 입력되기 전까지는 저장 버튼이 붉은색이었다가, 두 글자 이상인 경우 초록색으로 변한다.

 

(혹은 두 글자가 입력되기 전에는 버튼 자체를 disable 시키는 방법도 있다.)

하지만 조건을 만족하지 못한 경우에 알럿을 띄우는 방식을 사용했다.

 

색이나 애니메이션 등으로 UX를 이끌어 낼 수도 있지만, 만약 그냥 조건을 만족하기 전 버튼을 disable시킨다면

어 왜 저장이 안되지? 하고 의문을 가지는 사람도 있을 거 같아서.. 알럿이 더 명확하다는 생각이 들었기 때문.

 

//  Created by Toughie on 2023/04/29.
//

import SwiftUI

struct TextFieldPrac: View {
    //유저가 입력을 하면 변수에 바로 반영된다.
    @State var textFieldText: String = ""
    //저장 버튼을 누르면 텍스트를 담을 배열
    @State var dataArray: [String] = []
  	//알럿을 위한 변수
    @State var showAlert: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
            //placeHolder
                TextField("이름을 입력해 주세요. (예: 홍길동)", text: $textFieldText)
                //            .textFieldStyle(.roundedBorder)
                    .padding()
                    .background(Color.gray.opacity(0.2).cornerRadius(10))
                    .foregroundColor(Color.blue)
                    .font(.headline)
                
                Button {
                //입력값의 조건을 확인하고, 만족하면 저장하는 로직
                    if checkText() {
                        saveText()
                    }
                    
                } label: {
                    Text("저장".uppercased())
                        .padding()
                        .frame(maxWidth: .infinity)
                        //삼항 연산자를 활용한 컬러 변화
                        .background(checkText() ? Color.green.opacity(0.2) : Color.red.opacity(0.2))
                        .cornerRadius(10)
                        .foregroundColor(Color.blue)
                        .font(.headline)
                }
                //입력값의 조건이 만족되지 않았을 때 버튼 자체를 비활성화 하고 싶은 경우
//                .disabled(!checkText())
                
                ForEach(dataArray, id: \.self) { data in
                    Text(data)
                }
                
                Spacer()
            }
            .padding(.horizontal,10)
            .navigationTitle("회원 가입")
            //알럿
            .alert(isPresented: $showAlert) {
                Alert(title: Text("글자수 제한"), message: Text("두 글자 이상 입력해 주세요."), dismissButton: .default(Text("OK")))
            }
        }
    }
    
    private func checkText() -> Bool {
        if textFieldText.count >= 2 {
            return true
        } else {
            //for alert
            showAlert = true
            //저장 버튼을 눌렀을 때 텍스트필드를 빈문자열로 초기화 해주기 위함(지워지게)
            textFieldText = ""
            return false
        }
    }
    
    private func saveText() {
        dataArray.append(textFieldText)
        textFieldText = ""
    }
}

 

엇 근데 시뮬레이터를 돌리다 보니 처음보는 경고문구를 발견했다.

이 경고 메시지는 뷰 업데이트 중에 상태(state)를 수정하면 정의되지 않은 동작이 발생할 수 있다는 것을 나타낸다.

@State 변수를 변경하는 부분인데, 스유의 특징상 뷰의 상태가 변경되면 계속 새로 그리기 때문에 발생하는 에러이다.

뷰를 그리고 있는데 뭐가 도중에 바뀐다거나..

사실 이 부분은 동시성 스유의 뷰 드로잉 체계나, 동시성 프로그래밍을 공부해보면 더욱 와닿을 거 같다.

(공부할 거 추가 ㅎㅎ..)

 

위의 예시 코드에서 checkText()는 사실 문자열이 조건에 부합하는지 확인하고 참/거짓만 리턴하면 되는데

부가적인 역할도 많이 하고 있다.(@State 변수 변경)

따라서 코드를 간결하게 줄이고, 버튼에 역할을 이임하는 방식으로 코드를 수정했다.

(네이밍 이상한건 봐주세요..ㅋㅋ)

//  Created by Toughie on 2023/04/29.
//

import SwiftUI

struct TextFieldPrac: View {
    
    @State var textFieldText: String = ""
    @State var dataArray: [String] = []
    @State var showAlert: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("이름을 입력해 주세요. (예: 홍길동)", text: $textFieldText)
                //            .textFieldStyle(.roundedBorder)
                    .padding()
                    .background(Color.gray.opacity(0.2).cornerRadius(10))
                    .foregroundColor(Color.blue)
                    .font(.headline)
                
                Button {
                    if checkText() {
                        saveText()
                    } else {
                    //checkText()가 false인 경우 실행하는 코드
                        failSave()
                    }
                } label: {
                    Text("저장".uppercased())
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(checkText() ? Color.green.opacity(0.2) : Color.red.opacity(0.2))
                        .cornerRadius(10)
                        .foregroundColor(Color.blue)
                        .font(.headline)
                }
                //                .disabled(!checkText())
                
                ForEach(dataArray, id: \.self) { data in
                    Text(data)
                }
                
                Spacer()
            }
            .padding(.horizontal,10)
            .navigationTitle("회원 가입")
            .alert(isPresented: $showAlert) {
                Alert(title: Text("글자수 제한"), message: Text("두 글자 이상 입력해 주세요."), dismissButton: .default(Text("OK")))
            }
        }
    }
    
    //코드 단순화, 역할 명확히
    private func checkText() -> Bool {
        textFieldText.count >= 2
    }
    
    private func saveText() {
        dataArray.append(textFieldText)
        textFieldText = ""
    }
    
    private func failSave() {
        textFieldText = ""
        showAlert =  true
    }
}

코드를 수정해서 경고를 싹 없애버렸다!