BB Quotes (3)
Button에 Fetch 기능 적용하기
이제 비어있던 Button에 Fetch가 가능하도록 적용하자
1
2
3
4
5
Button {
Task {
await vm.getData(for: show)
}
}
뭐 이건 많이해봤지만 await는 단독으로 사용 될수 없기에 Task가 반드시 필요하다.
작동이 잘 되는걸 알 수 있다.
status 따라 작동을 나누기
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
VStack {
Spacer(minLength: 60)
switch vm.status {
case .notStarted:
EmptyView()
case .fetching:
ProgressView()
case .success:
Text("\"\(vm.quote.quote)\"")
//생략
ZStack(alignment: .bottom) {
//생략
}
//생략
case .failed(let error):
Text(error.localizedDescription)
}
Spacer()
//생략
}
.frame(width: geo.size.width, height: geo.size.height)
이렇게 구분을 해준다.
그러면 서버 통신에 상태에 따라 다르게 보여지게 된다.
사용자는 이제 버튼을 눌렀을때 어떻게 진행이 되는지 간접적으로 알 수 있게 되었다.
그리고 위의 사진을 보면 알겠지만 로딩 중일때 버튼이 위로 올라가게 되는데 그걸 막기 위해 Vstack을 하나 더 씌워준다.
1
2
3
4
5
6
7
8
9
10
11
12
VStack {
VStack { // added
Spacer(minLength: 60)
switch vm.status {
// 생략
}
// 생략
}
Button {
// 생략
}
}
아래 사진을 보면 Vstack을 추가해준 이유를 알 수 있다.
직전것과 달리 버튼이 고정되면서 ui가 더 안정적으로 되었다.
QuoteView 재사용
이제 키워드가 “Breaking Bad” 인것에 대해서는 끝났으니, 이제는 “Better Call Saul”에 대해서 처리를 하자
크게 달라지는 건 없다.
먼저 색을 만들어주고 이전에 BreakingBadgreen,yellow에서 button, shadow로 바꿔주었다.
그리고 이전글에서 배경화면에 적용했던것과 같은 방식으로 하면된다.
1
2
3
4
5
6
7
8
9
10
11
Button {
// 생략
} label: {
Text("Get Random Quote")
.font(.title)
.foregroundStyle(.white)
.padding()
.background(Color("\(show.replacingOccurrences(of: " ", with: ""))Button")) // changed
.clipShape(.rect(cornerRadius: 7))
.shadow(color: Color("\(show.replacingOccurrences(of: " ", with: ""))Button"), radius: 2) // changed
}
이렇게 적용을 해주면된다.
이젠 각 tab에 따라 다르게 되는걸 알 수 있다.
Character View 만들기
이젠 이렇게 fetch 를 했을때 나온 결과에 대해서 이미지를 클릭했을때 Character 정보가 나오는 Character View를 만들어 보도록 한다.
뭐 딱히 언급할만한것은 없다.
현재 이렇게 디자인이 된 상태.
QuoteView에서 화면 띄우기
이것도 크게 어렵지 않다.
이전에 했던것 그대로 하면된다.
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
39
40
41
42
43
44
45
struct QuoteView: View {
// 생략
@State var showCharacterInfo = false
var body: some View {
GeometryReader { geo in
ZStack {
// 생략
VStack {
VStack {
Spacer(minLength: 60)
switch vm.status {
case .notStarted:
EmptyView()
case .fetching:
ProgressView()
case .success:
// 생략
}
.frame(width: geo.size.width / 1.1, height: geo.size.height / 1.8)
.clipShape(.rect(cornerRadius: 50))
.onTapGesture { // new
showCharacterInfo.toggle()
}
case .failed(let error):
Text(error.localizedDescription)
}
Spacer()
}
// 생략
}
.frame(width: geo.size.width, height: geo.size.height)
}
.frame(width: geo.size.width, height: geo.size.height)
}
.ignoresSafeArea()
.sheet(isPresented: $showCharacterInfo) { // new
CharacterView(character: vm.character, show: show)
}
}
sheet의 활성 여부를 물어볼 변수 하나와, ontapgesture, sheet modifier를 사용하여 만들어 주면 된다.
실행하면 위와 같다.
DisclosureGroup 사용하기
이건 처음보는 내용이기에 Docs를 먼저 소개.
CharacterView 최하단에
1
2
3
4
5
6
7
8
9
10
11
VStack(alignment: .leading) {
// 생략
Divider()
DisclosureGroup("Status (spoiler alert!):") {
Text(character.status)
.font(.title2)
}
}
이렇게 DisclosureGroup을 만들어준다.
이런식으로 Folding을 하여 내용을 보여주게 하는 기능이다.
보완
여기서 조금더 보완을 해서 json 데이터를 가지고 죽었을때의 디테일 그리고 살아있을때를 구분해서 나누도록 한다.
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
DisclosureGroup("Status (spoiler alert!):") {
VStack(alignment: .leading) {
Text(character.status)
.font(.title2)
if let death = character.death {
AsyncImage(url: death.image) { image in
image
.resizable()
.scaledToFill()
.clipShape(.rect(cornerRadius: 15))
} placeholder: {
ProgressView()
}
Text("How: \(death.details)")
.padding(.bottom, 7)
Text("Last words: \"\(death.lastWords)\"")
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.tint(.primary)
if를 통해 character.death에 값이 있다면 추가적인 정보를 보여지게끔한다.
이렇게 더 자세한 정보가 나오게 된다.
하지만 작품속 인물이 아직 시즌 중 살아있다면 이렇게 Alive로 표시되고 끝
앱 이름 설정하기
지금은
이렇게 Display Name이 Blank라 프로젝트 명으로 앱 이름이 설정되는데, 여기에 “Say My Name”으로 해주면
이렇게 달라진걸 알 수 있다.