Day 17에는 Day 16에서 배운 @State, Form, Picker를 실제로 적용해보고,
그 외 새로운 뷰와 프로퍼티 래퍼 등을 배운다.

어플에서 사용할 프로퍼티를 추가

이 어플에서 사용자가 입력해야 할 값들이다.

  • 지불해야 할 총 금액을 입력
  • 팁을 몇 퍼센트 줄지 설정
  • 몇 명으로 나눠서 값을 지불해야 할지 입력

3가지의 프로퍼티들을 어플에 입력해야 하고, 이 값들의 변화에 따라 어플의 ui도 같이 업데이트 되어야 한다.

contentView@State 프로퍼티 래퍼를 사용하여 각각에 대해 프로퍼티로 추가하고 기본값을 할당한다.

@State private var checkAmount = 0.0
@State private var numberOfPeople = 2
@State private var tipPercentage = 20

기본값으로 팁을 총 지불액의 20%로 설정했지만, 경우에 따라 다른 비율로 팁을 책정할 수 있기 때문에
팁에 대한 퍼센테이지를 배열에 저장하여 프로퍼티에 할당할 수 있다.

let tipPercentages = [10, 15, 20, 25, 0]

총 금액을 입력할 TextField 추가

지불해야 할 총 금액을 입력하기 위해 TextField가 필요하다.
body 프로퍼티 안에 Form을 추가하고, 그 안에 TextField를 입력한다.

주의해야 할 것은, TextField는 기본적으로 사용자의 모든 입력값을 받을 수 있다는 것이다.
우리는 숫자 (Double형)를 입력받기를 원하지만, 문자열이 들어오게 된다면 숫자로 변환하지 못할 가능성이 생길 수도 있다.

TextField의 format 속성을 변환하여 입력값을 숫자로 받게 하도록 설정할 수 있다.

어플의 확장성을 높이기 위해 통화 정보를 USD(미국 달러)애 고정하지 않고, 어플 사용자의 현재 위치하고 있는 지역의 통화 정보를 가져온다.

Form {
    Section {
        TextField("Amount", value: $checkAmount, format: .currency(code:Locale.current.currency?.identifier ?? "USD"))
            .keyboardType(.decimalPad)
    }
}
//TextField
//첫번째 매개변수: 플레이스 홀더에 표시될 문자열
//두번째 매개변수: 입력된 값의 양방향 바인딩 (checkAmount에 값 저장과 동시에 화면에 표시)
//세번째 매개변수: 입력값에 대한 서식을 지정. 여기서는 통화 정보

//현재 사용자의 위치 정보를 가져와 통화 정보를 요청하고 사용, 없는 경우 USD로 설정되도록 함.
  • .keyboardType
    TextField의 수정자 .keyboardType을 사용하여 입력 시 나타나는 가상 키보드의 종류를 바꿀 수 있다.
    괄호 안 매개변수에는 UIKeyboardType 열거형을 받는다.
    여기서는 정수와 소수를 입력할 수 있는 .decimalPad를 사용했다.

    이외에도 사용할 수 있는 키보드의 타입에는 .numberPad(정수), emailAddress(이메일) 등등이 있다.

    하드웨어 키보드의 입력이나 붙여넣은 값까지는 막지는 못하지만 TextField에서 잘못된 값을 필터링할 수 있다.

  • Locale
    사용자가 위치하고 있는 지역의 통화, 측정 단위, 달력 등에 대한 정보들을 담고 있는 거대한 구조체

인당 지불할 금액을 표시할 Text 추가

인당 지불할 금액에 대한 정보를 표시할 Text뷰를 추가한다.
TextField와 구분하기 위해 Form 안에 다른 Section을 작성하고 그 안에 배치한다.

Form {
    ...
    Section {
        Text(checkAmount, format: .currency(code:Locale.current.currency?.identifier ?? "USD"))
    }
}

현재는 총 금액을 표시하고 있지만 후반부에 인당 지불할 금액을 표시하는 것으로 수정한다.

팁의 퍼센테이지를 선택할 수 있는 Picker 추가

tipPercentages 배열에 있는 값들 중 하나를 선택하기 위해 Picker 뷰를 사용한다.
Picker는 여러 요소 중 하나를 선택할 수 있는 요소이다.

Section {
    ...
    Picker("Number of People", selection: $numberOfPeople) {
        ForEach(2..<100) {
            Text("\($0) people")
        }
    }
    .pickerStyle(.navigationLink)
}

ForEach는 2부터 99까지 반복하면서 행을 생성하고, numberOfPeople에는 2가 할당되어 있지만 ForEach로 생성된 행의 0번째는 “2 people”이므로, “4 people”이 보여지게 된다.

pickerStyle() 수정자를 사용하여 Picker의 스타일을 설정할 수 있다.
여기서는 새로운 창으로 이동하기 위해 .navigationLink 을 사용했다.
.navigationStack 을 사용하기 위해서는 상위 뷰에 navigationStack이 존재해야 한다.

var body: some View {
    navigationStack {
        Form {
            ...
            // 코드들 
        }
        .navigationTitle("WeSplit")
    }
}

마지막으로 Form에 .navigationTitle() 수정자를 사용하여 내비게이션 바에 제목을 붙인다.
내비게이션 스택이 이동할 수 있기 때문애, 스택 내에 제목을 입력하게 되면 스택이 이동하면서 타이틀을 각각 변경하면서 띄울 수 있다.


팁 퍼센테이지를 선택할 Picker 추가

tipPercentages 배열에 미리 정의해둔 지불할 팁의 퍼센테이지를 선택하기 위해 Picker를 사용한다.

선택할 수 있는 요소가 적은 경우 Picker를 세그먼트 스타일로 지정하면 훨씬 보기 좋다.

Section의 매개변수로 문자열을 전달하여 머리글 또는 바닥글을 추가할 수 있다. 여기서는 얼마를 팁으로 남길 것인지 안내하는 머리글을 추가하였다.

Section("How much do you leave?") {
    Picker("Tip Percentage", selection: $tipPercentage) {
        ForEach(tipPercentages, id: \.self) {
            Text($0, format: .percent)
        }
    }
    .pickerStyle(.segmented)
}

인당 분할 금액 계산

계산된 프로퍼티를 이용하여 인당 지불해야 할 금액을 계산한다.

인당 지불 금액 = 총 금액 + (총 금액 * 팁 퍼센테이지) / 인원 수

Double형의 tipPerPerson 계산된 프로퍼티를 사용하여 인당 지불할 금액을 계산한다.

인원 수를 선택하는 경우 인덱스와 실제 값이 2만큼 떨어져있기 때문에, 2를 더해 위치를 보정한다.

var totalPerPerson: Double {
    //선택한 인원수와 팁의 퍼센테이지
    let peopleCount = (numberOfPeople+2)
    let tipSelection = Double(tipPercentage)

    let tipValue = checkAmount / 100 * tipSlection 
    let grandTotal = checkAmount + tipValue
    let amountPerPerson = grandTotal / peopleCount

    return amountPerPerson
}

이후 세번째 Section에 작성되어 있는 Text의 총 금액 checkAmount 변수를 인당 지불 금액 amountPerPerson으로 변경한다.

어플이 입력값을 통해 인당 지불할 금액을 잘 계산하는 것을 볼 수 있다.

@FocusState

가상 키보드를 사용하는 경우, TextField에 값을 입력한 후 키보드가 닫히지 않는 현상이 일어난다.
숫자 키보드에는 리턴 키가 없어, 키보드를 해제할 수 없다.

@FocusState 는 TextField 등의 포커스(커서 깜빡임)을 처리하기 위해 설계된 프로퍼티 래퍼이다.

포커스 여부를 확인하기 위해 Bool형으로 프로퍼티를 정의한 후, TextField에 .focused() 수정자를 추가한다.

@FocusState private var amountIsFocused: Bool

...

TextField {
    ...
}
.focused($amountIsFocused)

TextField에 포커스가 있을 때 툴바에 버튼을 표시해, 버튼을 누르면 키보드가 닫히는 방식으로 동작하도록 한다.

Form의 .navigationTitle() 아래에 코드를 추가한다.

.toolbar {
    if amountIsFocused {
        Button("Done") {
            amountIsFocused = false
        }
    }
}

TextField에 포커스가 있을 때 값 입력 후 Done버튼을 누르면, amountIsFocused가 false로 바뀌면서 포커스가 해제되고, 키보드가 자동으로 닫히게 된다.


카테고리:

업데이트: