SwiftUI (3)
Dicee App 만들기
1. ZStack을 사용하여 배경화면 설정하기.
1
2
3
4
5
6
7
8
9
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
}
}
}
이렇게 해서 assets에 있는 이미지를 가져오고 설정을 해준다.
2. VStack을 사용하여 이미지 쌓기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
Image("dice1")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
}
}
이렇게 VStack을 사용하여 이미지를 쌓아주었다.
3. 모듈화하여 관리하기
주사위 이미지를 담당하는 부분을 따로 서브뷰로 추출하여 좀 더 관리하기 쉽게 만들어 본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
HStack {
DiceView(n: 1)
}
}
}
}
}
struct DiceView: View {
let n: Int
var body: some View {
Image("dice\(n)")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
그리고 주사위 이미지를 좀 더 유기적으로 관리하기위해서 n이라는 상수를 하나 만들어 주었다. 그리고 현재 n에는 1의 값을 부여 해줌으로써 주사위 이미지는 점이 1인 주사위가 나온다.
4. HStack을 사용하여 주사위를 하나 더 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
HStack {
DiceView(n: 1)
DiceView(n: 1)
}
}
}
}
}
이렇게 해주니 이제 좀 느낌이 나기 시작한다
5. Padding을 주어 이미지간의 간극 조절
1
2
3
4
5
6
7
8
VStack {
Image("diceeLogo")
HStack {
DiceView(n: 1)
DiceView(n: 1)
}
.padding(.horizontal)
}
이때 안에 어떠한 값도 주지 않으면 4변이 다 패딩이 들어가므로 이를 원하지 않는다면 특정 값을 주도록 하자.
여기선 가로만 패딩을 주기위해 horizontal을 사용.
6. Button을 추가하고 디자인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
HStack {
DiceView(n: 1)
DiceView(n: 1)
}
.padding(.horizontal)
Button("Roll")
{
}
.font(.system(size: 50))
.fontWeight(.heavy)
.foregroundStyle(.white)
.background(.red)
.padding()
}
}
}
}
struct DiceView: View {
let n: Int
var body: some View {
Image("dice\(n)")
.resizable()
.aspectRatio(contentMode: .fit)
.padding()
}
}
버튼 바로 다음에 .을 붙여서 해보았으나 되지 않았다.
강의 버전과는 달리 버튼의 text는 바로 적을 수 있고, 버튼의 폰트나 이런 외적인 요소는 액선 뒤에다가 해줘야 적용이 된다.
그리고 주사위 이미지간의 간격을 주기위해
Diceview에도 똑같이 패딩을 준다.
7. Spacer를 사용하여 이미지간 간격을 더 주기.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
Spacer()
HStack {
DiceView(n: 1)
DiceView(n: 1)
}
.padding(.horizontal)
Button("Roll")
{
}
.font(.system(size: 50))
.fontWeight(.heavy)
.foregroundStyle(.white)
.background(.red)
.padding()
}
}
}
}
이렇게 스페이서를 넣어주니
둘다 양끝으로 붙어 버린다.
그래서 버튼과 hstack 사이에도 스페이서를 주어 간격을 준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct ContentView: View {
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
Spacer()
HStack {
DiceView(n: 1)
DiceView(n: 1)
}
.padding(.horizontal)
Spacer() // new
Button("Roll")
{
}
.font(.system(size: 50))
.fontWeight(.heavy)
.foregroundStyle(.white)
.background(.red)
.padding()
}
}
}
}
이렇게 균등하게 분포가 되었음을 알 수 있다.
8. button을 활용하여 주사위의 숫자를 바꾸기
먼저 해야할것이 좌,우측 주사위의 숫자를 정해줘야한다.
그래서 변수를 하나 ContentView안에서 만들어 준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct ContentView: View {
var leftDiceNumber = 1
var rightDiceNumber = 1
var body: some View {
ZStack {
Image("background")
.resizable()
.ignoresSafeArea(.all)
VStack {
Image("diceeLogo")
Spacer()
HStack {
DiceView(n: leftDiceNumber)
DiceView(n: rightDiceNumber)
}
.padding(.horizontal)
Spacer()
Button("Roll")
{
}
.font(.system(size: 50))
.fontWeight(.heavy)
.foregroundStyle(.white)
.background(.red)
.padding()
}
}
}
}
이때 바뀐점은 HStack에서 DiceView의 n이 1이었지만 이제는 left,rightDiceNumber가 되었다는 것이다.
이것을 활용하여 버튼의 Action Section에 랜덤함수를 이용하여 숫자를 1~6 사이의 숫자로 바뀌게 할것이다.
아래는 Action에 관한 Code
1
2
3
4
5
Button("Roll")
{
leftDiceNumber = Int.random(in: 1...6)
rightDiceNumber = Int.random(in: 1...6)
}
하지만 에러가 발생한다.
바꿀 수 없다는 것이다.
- Struct안에 있는 변수는 Immutable이 라는 것이다.
- 값타입이기 때문이다.
그래서 이전에 작성한 글에서도 우리는 mutating을 사용하여 값을 바꿀 수 있게 하였다.
- 그러면 변수에도 똑같이 하면 되지않을까?
- 답은 No
애석하게도 mutating
이라는 녀석은 함수를 선언할때만 가능하다.
그렇다면 해결책은?
바로 @state
이다.
간단하게 정의를 하면 변수를 업데이트하고 contentview를 다시 생성하게 한다.
더 자세한 내용은 Docs 확인.