해결된 질문
작성
·
66
1
import SwiftUI
struct ContentView: View {
@State private var ToggleCheck: Bool = false // @State 없으면 에러 왜? / 연산자 토글을 누른 후의 숫자 입력인지 체크하기 위한 변수
@State private var isAdditionActive = false // 덧셈 토글 체크
@State private var isSubtractionActive = false // 뺄셈 토글 체크
@State private var isMultiplicationActive = false // 곱셈 토글 체크
@State private var isDivisionActive = false // 나눗셈 토글 체크
@State private var printNumber: String = "0" // 계산식에 사용 or 화면에 출력한 내용을 담은 변수
@State private var totalNumber: Int = 0 // 토탈 계산 값
@State private var op: Character = " "
@State private var buttonData: [[String]] = [ // 키패드 모음 2차원 배열
["AC", "+/-", "%", "÷"],
["7", "8", "9", "X"],
["4", "5", "6", "-"],
["1", "2", "3", "+"],
["0", ".", "="]]
var body: some View {
ZStack{
Color.black.ignoresSafeArea() // 화면 전체 여백 없이 배경색 지정
VStack {
Spacer()
HStack{
Spacer()
Text("\(Int(printNumber)!)") // 계산기의 숫자 출력
.padding()
.font(.system(size: 73))
.foregroundColor(.white)
}
ButtonPosition() // C, +/-, %를 제외한 버튼을 화면에 추가
}
}
}
func Reset(){
printNumber = "0" // 출력된 계산 숫자 UI 초기화
totalNumber = 0 // 연산의 토탈값을 담고 있는 변수도 당연히 초기화
ToggleCheck = false
op = " "
buttonData[0][0] = "AC"
isAllfalse() // 버튼의 토글값 및 연산자 버튼 토글 UI도 모두 초기화
}
func isAllfalse(){ // 연산자의 토글 버튼을 전부 해제 하기 위한 함수
isAdditionActive = false
isSubtractionActive = false
isMultiplicationActive = false
isDivisionActive = false
}
func opIfSwitch(){ // 실질적인 계산을 위한 switch문
if op == " "{
totalNumber = Int(printNumber)!
}
else{
switch op{
case "÷":
totalNumber /= Int(printNumber)!
if printNumber == "0"{ // 비정상 종료.. 왜?
printNumber = "오류"
}
case "X":
totalNumber *= Int(printNumber)!
case "-":
totalNumber -= Int(printNumber)!
case "+":
totalNumber += Int(printNumber)!
default:
break
}
}
printNumber = String(totalNumber)
}
// 내 코드의 계산 방식 작성할 것 이따가
func Division(){ // 나눗셈
opIfSwitch()
}
func Multiplication(){ // 곱셈
opIfSwitch()
}
func Minus(){ // 뺄셈
opIfSwitch()
}
func Plus(){ // 덧셈
opIfSwitch()
}
func Equal(){ // 등호
opIfSwitch()
isAllfalse()
ToggleCheck = false // =을 누른 후 결과 값에 추가로 연산을 진행할 때 이 코드 없으면 정상적으로 진행되지 않음
}
func ButtonPosition() -> some View{ // 버튼 디자인 및 포지셔닝 함수
ForEach(buttonData, id: \.self){ line in
HStack{
ForEach(line, id: \.self){ row in
switch row{
case "AC", "C", "+/-", "%":
ButtonDesign(row, .gray, .black)
case "÷", "X", "-", "+", "=":
ButtonDesign(row, .orange, .white)
default:
ButtonDesign(row, .init("NumberButton"), .white)
}
}
}
}
}
func ButtonDesign(_ value: String, _ backcolor: Color, _ fcolor: Color) -> some View{ // 공통 버튼 디자인
Button {
switch value {
case "÷":
isDivisionActive = true // 나눗셈 버튼 토글
isAdditionActive = false
isSubtractionActive = false
isMultiplicationActive = false
case "X":
isMultiplicationActive = true // 곱셈 버튼 토글
isDivisionActive = false
isAdditionActive = false
isSubtractionActive = false
case "-":
isSubtractionActive = true // 뺄셈 버튼 토글
isAdditionActive = false
isMultiplicationActive = false
isDivisionActive = false
case "+":
isAdditionActive = true // 덧셈 버튼 토글
isSubtractionActive = false
isMultiplicationActive = false
isDivisionActive = false
default:
break
}
ButtonAction(value)
} label: {
Text(value)
.frame(width: value == "0" ? 160 : 80, height: 80, alignment: value == "0" ? .leading : .center)
.background((value == "÷" && isDivisionActive) || (value == "X" && isMultiplicationActive) || (value == "-" && isSubtractionActive) || (value == "+" && isAdditionActive) ? Color.white : backcolor)
//.background(value == "÷" && isDivisionActive ? Color.white : backcolor)
//.background(value == "X" && isMultiplicationActive ? Color.white : backcolor)
.foregroundColor(value == "÷" && isDivisionActive || (value == "X" && isMultiplicationActive) || (value == "-" && isSubtractionActive) || (value == "+" && isAdditionActive) ? Color.orange : fcolor)
.font(.system(size: 33))
.cornerRadius(40)
}
}
func ButtonAction(_ value: String){
switch value { // 버튼을 누르면
case "AC", "C": // C를 누르면
Reset() // 초기화 버튼을 누르면 각종 상수 및 변수들이 초기화 됨
case "÷": // 나누기를 누르면
Division() // 실질적인 계산을 하는 함수
op = "÷"
ToggleCheck = false
case "X": // 곱하기를 누르면
Multiplication() // 실질적인 계산을 하는 함수
op = "X"
ToggleCheck = false
case "-": // 빼기를 누르면
Minus() // 실질적인 계산을 하는 함수
op = "-"
ToggleCheck = false
case "+": // 더하기를 누르면
Plus() // 실질적인 계산을 하는 함수
op = "+"
ToggleCheck = false
case "+/-":
printNumber = String(Int(printNumber)! * -1)
case "=":
Equal() // 최종 결과를 출력하기 위한 스위치문 호출
op = " " // 내가 원하는 기능을 구현하기 위해 꼭 필요한 코드
printNumber = String(totalNumber) // 연산의 결과 값을 출력
totalNumber = Int(printNumber)!
default:
if printNumber == "0"{
printNumber = value // 숫자 첫 입력시 화면에 출력
buttonData[0][0] = "C"
}
else if (isDivisionActive || isMultiplicationActive || isSubtractionActive || isAdditionActive) && ToggleCheck == false{
printNumber = value
ToggleCheck = true // 연산자 버튼이 눌러짐
}
else{
if printNumber.count < 9{
printNumber += value
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
안녕하세요! 다름이 아니라 강의에 있는 프로젝트 외에 따로 계산기를 하나 만들어보고 있습니다 근데 @State 어노테이션에 대해 이해가 안되는 부분이 있습니다 구글링도 해보고 여러 방면으로 알아봤는데 원하는 답을 얻지 못해서 질문 올립니다
@State는 뷰의 상태 변화를 체크하는 어노테이션이라고 알고 있습니다. 변수 선언부의 주석을 보시면 @State 없으면 에러 왜? 이런 내용의 주석이 달려 있는 변수가 지금 저의 의문점입니다
ToggleCheck 변수와 totalNumber 변수는 뷰의 상태 변경에 직접적인 영향을 미치지 않기 때문에 @State가 필요 없다고 생각합니다. 근데 저 두 변수에 @State를 지우면 에러가 발생합니다. 일단 뷰의 변화는 실질적으로 totalNumber 변수의 값을 대입 받는 printNumber 변수이니 printNumber 변수에만 @State가 필요하다고 생각하는데 왜 에러가 발생하는 걸까요
마찬가지로 ToggleCheck 변수도 마찬가지로 체크용으로 사용하고 저 값이 변한다고 해서 직접적으로 뷰에 미치는 영향이 없는데 왜 @State가 필요한 걸까요... 혹시 알고 계시면 꼭 답변 부탁드리겠습니다...!
감사합니다!
답변 2
0
안녕하세요!
SwiftUI에서 View는 값타입이기때문에 내부의 변수를 수정할 수가 없습니다.
@State 변수는 뷰에 영향을 주는 의미도 있지만, View의 프로퍼티를 다른곳에 저장해두어 수정을 가능하게 해주는 역할도 갖고 있습니다.
현재 ToggleCheck는 뷰의 영향을 주지 않더라도, 다른 곳에서 수정이 일어나기 때문에, 이때는 @State 를 써주어야 합니다. 만약 @State를 쓰고 싶지 않다면 ToggleCheck를 관리하는 다른 클래스를 생성하여 그 클래스의 ToggleCheck 프로퍼티를 수정하도록 하면 됩니다.
감사합니다~
안녕하세요
네 AI인턴이 말한 내용도 맞는 내용입니다. 말해준 내용대로 이해하셔도 괜찮습니다.
다만 뷰에 영향을 주지 않더라도 @State 를 요구하는데 그때에는 제가 말씀드린 대로 이해하시면 됩니다. 감사합니다.
0
안녕하세요, 인프런 AI 인턴입니다.
@State
가 필요한 이유에 대해 궁금해하시는군요. @State
프로퍼티 래퍼는 SwiftUI에서 뷰의 상태를 관리하는 데 사용됩니다. SwiftUI의 뷰는 불변(immutable)이기 때문에, 뷰 내부에서 변경을 일으키려면 특별한 프로퍼티 래퍼가 필요합니다.
@State
를 붙인 변수는 값이 변할 때마다 해당 뷰와 그 뷰의 바디를 다시 그리도록 만듭니다. 즉, @State
래퍼를 통해 선언된 변수의 값이 변하면, SwiftUI는 뷰의 body 프로퍼티를 새로 호출하여 화면을 갱신합니다.
질문하신 ToggleCheck
와 totalNumber
변수가 @State
로 선언되어야 하는 이유는, 경우에 따라 이 변수들의 값 변경이 화면에 보여지는 뷰의 상태 변경을 유발하기 때문입니다. 예를 들어 ToggleCheck
의 변화가 어떤 특정 조건에서 뷰의 특정 부분을 다른 방식으로 표현하도록 하거나, totalNumber
의 값이 변해 이를 화면에 출력하는 printNumber
변수에 영향을 준다면, 이들 변수는 뷰의 상태 변화에 직접적인 영향을 준다고 할 수 있습니다.
printNumber
변수에만 @State
가 필요하다고 생각하셨던 부분에 대해, @State
는 단순히 뷰에 직접적으로 표현되는 값 뿐만 아니라, 뷰의 상태 변화를 유발할 수 있는 모든 변수에 필요할 수 있습니다. ToggleCheck
나 totalNumber
같은 경우, 이들 값이 변화할 때 조건부 렌더링 또는 계산 결과를 업데이트하는 등으로 간접적으로 화면에 변화를 일으키기 때문에 @State
가 필요합니다.
@State와 SwiftUI 뷰의 상태 관리 에서 유사한 주제에 대한 자세한 설명을 확인할 수 있습니다. 여기서는 @State
의 역할과 구조체 내 @State
변수가 어떻게 뷰의 상태 관리에 사용되는지를 설명하고 있습니다.
이 답변이 궁금증을 해결하는 데 도움이 되기를 바랍니다. 더 궁금한 점이 있으시면 언제든지 질문 남겨주세요!
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
답변 주셔서 감사합니다!!
SwiftUI에서 View는 값타입이기때문에 내부의 변수를 수정할 수가 없습니다.
@State 변수는 뷰에 영향을 주는 의미도 있지만, View의 프로퍼티를 다른곳에 저장해두어 수정을 가능하게 해주는 역할도 갖고 있습니다.라고 말씀주셨는데 아직 제가 초보자다 보니 이 내용을 이해하는 데 어려움이 있습니다 ㅠㅠ 그.. 밑에 인프런 AI 인턴의 글에 대한 내용은 어느 정도 이해가 되는데 혹시 AI 인턴이 말한 내용이 올바른 정보일까요...?