[iOS App Dev Tutorials] Scrumdinger

Woozoo·2023년 1월 13일
0

[이것 저것]

목록 보기
1/7

[iOS App Dev Tutorials]

SwiftUI로 처음 프로젝트를 만들게 되면 두개의 구조체가 있다.
body 라는 프로퍼티를 가지는 '뷰'와 이 뷰를 캔버스로 프리뷰하게 해주는 구조체!
이렇게 두가지가 있음

기본으로 있는 콘텐트뷰 리팩터 리네임 해서 이름 바꿔주고,
아래에 있는 프리뷰도 마찬가지로 바꿔준다!

ProgressView를 body 안에 선언해줌
이 프로그레스뷰는 time이 얼마나 흘러가는지 나타내줄 예정임

프로그레스 뷰를 커맨드 클릭해서 VStack에 임베드 해준다
VStack안에 HStack을 넣고, 텍스트도 넣어줌!


각각의 Text들을 VStack에 임베드 해줌.
Text랑 Label 어떤 차이점이 있는지 헷갈렸는데


텍스트는 그냥 텍스트이고

레이블은 이미지가 포함된 텍스트임

이 VStack 두개 사이에 Spacer로 공간을 채워줄 수 있음

고럼 요렇게 보인다

그리고 스택에 Alignment를 추가해줄 수 있음!

요렇게!!

이렇게 설정해주면 default로 설정되어 있는 .center를 override함
코드로 작성해줘도 되고, VStack 선택되어 있는 상태에서 Attribute inspector에서 설정을 줄 수도 있다.


SwiftUI에선 modifier를 사용해 customize가 가능함. 여러개 중첩해서 쌓을 수도 있고!! (근데 이렇게 사용할 경우엔 순서가 중요할 때가 종종 있음!)

이제 밑에다가 Circle을 추가해보자


호오 신기신기
이 Circle 약간 에펙에서 쉐입레이어 설정할 때랑 비슷한 느낌임!!

그리고 버튼을 추가해줌
action은 클로져 형태로 넣어줄 수 있음 그리고 Label이 들어가게 된다
버튼이 트리거 되는 이벤트는 플랫폼별로 정해져 있음!
맥은 클릭이고, 앱은 터치

텍스트와 버튼 사이에 Spacer로 간격을 주고,
전체를 감싸고 있는 VStack에 padding을 줬다

SwiftUI가 되게 직관적이라서 조금의 swift 지식만 알고 있어도 금방 개발이 가능하다는 생각이 든다


Supplement accessibility data

기본적으로 accessbility에 대한 접근이 SwiftUI에선 빌트인 되어 있다 (예를들면 VoiceOver 같은 것들)
여기에 추가적으로 커스텀도 가능함!


accessibility에 대한 설정을 안해줬다면 기본으로 voiceOver를 실행했을 때 Label에 들어있는 이미지의 이름 string 까지 읽었을 텐데
요렇게 childern들을 .ignore 시켜주고
accessibilityLabel을 직접 지정해서 Time remaining이라고 읽혀질 수 있게 만드는 게 가능함!

요렇게 Value도 추가가능함
예를들자면 볼륨 조절하는 슬라이더가 있다고 했을 때 Label에 볼륨을 지정해주고
볼륨의 값을 읽혀지게 하는 게 가능하다

var body: some View {
	Button(action: {}) {
    	Label("Skip song", systemImage: "forward")
    }
    .accessibilityLabel(Text("Skip song"))
}

요렇게 있다고 했을 때 VocieOver는 어떻게 읽힐까?!!

Skip song. Button 이라고 읽힘
기본으로 설정을 안해주면 VoiceOver는 systemImage 스트링을 읽고,
Button 같은 경우는 이미 있으니까 추가 안해줘도 읽어줌


Create a color theme


칼라 테마 추가해주고


모델이란 그룹을 만들고 Theme이란 swift 파일을 추가했다


enum 타입의 Theme을 만들고, rawValue 타입을 String으로 지정해줬다
rawValue는 껍질을 벗겨봤을 때 뭐가나오냐!! 물어보는 거라고 생각하면 됨

enum 자체는 나무이고, case는 나뭇가지들, rawValue랑 associated Value는 과일인데
associated Value가 과일 껍질이라면 rawValue는 속알맹이!


enum은 변수에 담겨질수 있는데

let colorPick: Theme

요런식으로 선언했으면 이 변수를 당장 사용하지는 못함. enum은 '값' 밸류 타입이기 때문에 initial value가 꼭 있어야함

let colorPick: Theme = .buttercup

이렇게!!
요렇게 설정이 되고나면 accentColor는 Color타입인데 switch를 타고 들어가서 .black이 설정되게 되겠죠?! 요런 플로우임 enum은

메인 칼라라는 컴퓨티드 프로퍼티 또한 만들었는데 이번엔 rawValue로 바로 만들어질 수 있게 해줌!


Create a daily scrum model

데일리 스크럼 모델을 만들어주자

그리고 샘플 데이터를 넣어준다!


목업 데이터를 준비해서 테스트할 때 쓸거 같음

Create the Card view

SwiftUI로 새로운 CardView를 만들어주자


아까 만든 모델을 constant로 선언해준다


프리뷰에다가 sampleData로 만들었던 데이터를 넣어준다!


그리고 프리뷰에 뷰를 조금 더 가시적으로 볼 수 있게 추가해줌

그리고 요렇게 Label을 추가해줬음

  • Label과 Image는 살짝 다른 파라미터를 사용함!
    지금 같은 경우 SF symbol을 사용하니까 systemImage라는 파라미터가 들어갔음


    그리고 Label에 패딩으로 trailing 20만큼 수치줌
    그리고 HStack 전부에 font를 .caption으로 먹여줌
  • Label에서 사용된 Label과 이미지들 SFSymbol은 font 크기와 두께에 맞춰서 스케일이 자동으로 맞춰짐


전체 VStack에 패딩을주고 foregroundColor를 맞춰줬다!
scrum의 theme 에 따라 변하게 되는 accentColor로!

Label Style 커스텀


새로운 TrailingIconLabelStyle이 될 struct를 선언하고
LabelStyle프로토콜을 채택해준다
Label Style 프로토콜은 내부에 항상 뷰로 그려질 수 있는 애가 존재해야 사용 가능한 듯


configuration을 설정해주는데 이 파라미터는 LabelStyleConfiguration이다.
icon 뷰와 title뷰를 현재 나타낸 거!


그리고 extension을 추가했는데
LabelStyle 프로토콜에 제약사항을 준거! TrailingIconLabelStyle일 때만 static으로 선언한 trailingIcon에 접근할 수 있게!

static var trailingIcon: Self { Self() } 는 trailingIcon이
TrailingIconLabelStyle.trailingIcon으로 바로 접근할 수 있게 만들어준 shorthand
레이블스타일을 채택하고 있는 애가 TrailingIconLabelStyle일 때 . 문법으로 trailingIcon에 인스턴스 생성 없이 바로 접근 가능함! 이렇게 나타낸거

Self는 타입의 이름을 반복해서 쓰는 대신에 현재 타입을 쓸 수 있도록 도와주는 키워드.
클래스, 구조체, enum 등에서 Self를 사용하면 그 타입 자체를 가리킨다

다시 카드뷰로 돌아와서

원래는 패딩으로 되어 있던 자리에 .labelStyle(.trailingIcon)을 해주면
title과 icon의 위치가 Label 내에서 바뀌게됨!


Make the card view accessible

제목이 되는 타이틀에 accessibility를 추가해준다!

인원수랑 시간도 추가 해줌!


List 뷰 만들기


새로운 ScrumsView를 만들고 DialyScrum 배열을 상수로 선언해줌


프리뷰에는 sample로 만들었던 데이터 넣어주고!

리스트 뷰를 먼저 구성해줌
그리고 ForEach뷰를 추가해준다
ForEach뷰는 콜렉션에 있는 각각의 아이템들을 식별가능한 방법이 필요함.
지금 같은 경우엔 샘플 데이터에 담긴 내용들이 다 다르니까 그냥 title의 경로를 인식 시켜줌

이제 아까 만든 CardView를 넣어주면?!


요호우 진짜 간단하다


그리고 Row의 백그라운드 칼라를 넣어줌
사실 뷰를 넣어주는 건데 칼라도 뷰라서 넣으면 색이 칠해지겠죠?


Color 문서를 읽다보면 나온다


Make scrums identifiable

지금 만들어준 ForEach뷰에 하나라도 똑같은 데이터가 들어가게되면 종종 크래쉬 남
타이머 만들다가 경험해봤죠
그래서 각각의 데이터들이 유니크하다는 걸 알려줘야하는데

Identifiable 프로토콜을 모델에 채택하면된다
이 프로토콜을 구현하기 위해선 id가 구조체 내에 선언되어 있어야함

이렇게만 했을 때 아직 컴파일이 되질 않음!
샘플data로 선언한 [DailyScrum] 에 id항목이 초기화되어 있지 않아서.
그럼? 초기화 해주면 되겠죠

요렇게 init 안에 id: UUID = UUID() 를 넣어주면 생략이 가능해짐

이제 ScrumsView에 있는 ForEach 뷰를 더 간단하게 표현이 가능해진다

경로를 얘기해주기위해 사용했던 .title을 지워도됨

App파일로 와서 루트뷰를 ScrumsView로 설정해주면 처음 실행할 때 뜨는 뷰가 ScrumsView가 되게된다


Creating Navigation hierarchy

App 파일에서 ScrumsView를 NavigationView에 임베드 해준다

ScrumsView에서도 프리뷰 될 때 NavigationView 넣어줌!


ForEach뷰에서 NavigationLink를 넣어줬다!
이렇게되면 각각의 뷰들을 터치했을 때 destination이 되는 뷰로 이동이 가능해진다


네비게이션링크 바깥으로 색이 칠해지게 해주고

리스트뷰에 navigationTitle을 추가했다

버튼도 추가해주자


accessibilityLabel도 버튼에 추가해주고!
이렇게 보니까 주석대신에 달아주는 느낌인 것 같기도 하다
어떤 동작을 하는 애인지 알려주는 용도로


Create the detail View

새로운 DetailView를 만들어주자
scrum 모델을 선언해주고
프리뷰에도 샘플데이터 꽂아줌

프리뷰 되는 곳에서 NavigationView로 감싸주고

ScrumsView로 돌아와서 NavigationLink detination을 DetailView로 변경해줬다


Add visual components to the detail view

디테일뷰로 돌아와서 body가 되는 뷰에 List를 추가해줬다

그리고 섹션도 추가해줌
이렇게 뷰를 구성해나가다 보니 선언형 프레임워크가 어떤 뜻인지 감이 잡힌다
여태까진 섹션을 주저리주저리 늘여놓고, 얘를 섹션으로 쓸거야! 명령해줬었는데
지금은 섹.션. 끝인 느낌 ㅋㅋㅋ

섹션 안에 Label을 구성해주고, 폰트와 ForegroundColor를 설정했다

HStack안에 레이블과 텍스트를 구성해줌
SwiftUI를 구성할 때 제일 습득을 빠르게 하는 방법은 머리속으로 코드를 작성하면서 직접 그려보는 일인 것 같다
프리뷰로 보는 게 아니라 머리로 그리면서 코드를 작성하면 속도도 빠를 듯


accessibility도 추가해줬음
지금처럼 VoiceOver가 읽어주는 내용을 묶는 작업도 가능하다
이렇게 하지 않았을 땐 ViceOver유저는 내용을 듣기 위해서 두 셀을 모두 터치했어야함


Theme 파일로 돌아와서, 새로운 변수를 선언해줬다
rawValue의 스트링을 대문자화 할 수 있는 프로퍼티!



텍스트 꾸며줌


Iterate through attendess


참석자들의 이름들이 같을 수 있다. 각각의 이름들이 유니크하게 만들어보자
Attendee라는 새로운 구조체를 DailyScrum의 extension으로 작성해줌

기존의 스트링 배열이었던 attendees를 방금 선언해준 구조체의 배열 형태로 업뎃해주고, init될 때 attendees 에 들어온 string배열을 Attendee의 name 프로퍼티에 매핑해주고, 이 배열을 다시 attendess 프로퍼티에 할당해준다
이제 identifiable 해졌다!

💡init의 파라미터의 타입은 원래의 타입과 다를 경우도 있다는 걸 처음 알았다! init의 본질적인 역할을 생각해보면 object의 초기 세팅을 설정하는 거임. 지속적으로 같은 상태를 초기에 전달해주는 겨

다시 디테일 뷰로 돌아와서 Attendees 섹션을 추가해주자



StartMeeting Label을 NavigationLink로 감싸주고, destination은 MeetingView()로 설정!
NavigationLink로 감싸주면 label은 gesture recognizer로 감싸지게 된다
그리고 navigationTitle을 scrum.title로 설정해줌
신기한건 navigationTitle은 어떤 scope에서 작성해도 똑같이 타이틀이 나옴

그리고 앱을 실행해보면!

Managing data flow between views

State

@State로 프로퍼티를 선언하게 되면 view와 함께 source of truth를 만들어주게 된다. 이 프로퍼티를 적용한 뷰들은 value들이 업데이트되면 ui도 같이 업데이트가 됨. 단하나의 진실!인 이 @State 프로퍼티를 만약에 다른 뷰의 계층에서 사용하고 싶다면?

Binding

@Binding으로 감싼 프로퍼티는 현재 존재하는 sourceof truth를 공유해서 읽고 쓰기의 접근이 가능해짐


Creating the edit view

DailyScrum의 extension으로 Data struct를 선언한다

여기서 lengthInMinutes를 Double타입으로 선언한 이유는 slider를 사용할 계획이라서! 그리고 지금처럼 DailyScrum.Data로 접근가능하게 내부에 선언해서 Foundation Framework에 선언되어 있는 Data타입과 구분 가능하게 해줌


Data 구조체에 기본 값을 넣어서 초기화될 때 값 안넣어줘도 되게 만들고,
data라는 computed property를 선언해줌!
Data struct타입인데 이 Data에는 DailyScrum 모델에 있는 프로퍼티의 값들이 들어가게 된다!

💡 살짝씩 변형해서 모델을 사용해야할 경우에 지금처럼 내부에 Data라고 부른 struct를 만들고, computedProperty를 활용해서 새로운 모델로 사용가능하겠다!

Add an edit view for scrum details

DetailEditView를 만들고
아까 작성한 Data구조체를 @State로 감싸준다
(여기서 private이 붙는 이유는 이 뷰 내에서만 접근이 가능하도록 해주기 위해서임, 안정성을 높이는 측면)

그리고 Form container안에 섹션을 만들어준다
Form container는 다른 플랫폼에서도 랜더를 해서 폼을 맞춰줌


그리고 텍스트필드를 만들어주는데 text의 내용은 data.title로전달될거임data.title로 전달될 거임 data.title?!

@State로 감싸진 단하나의 진실인 DailyScrum.Data()에 접근하고,
텍스트 필드의 텍스트는 여기랑 연결이 될거다~ 바인딩해줌
그럼 현재의 이 뷰는 data property를 관리하게 되겠죠

Slider를 추가해줬다.현재 Text는 보이지 않는데 이건 VoiceOver용으로 임의로 추가해둔거!

그리고 두번째에 있는 Text("(Int(data.lengthInMinutes)) 부분은
읽기 작업만 해서 $ 를 안붙여줬음!!
$가 붙을 때는 쓰는 작업까지 할 경우에(유저가 상호작용을 할 경우에)!!!


Display attendees in the edit view

새로운 섹션을 만들어주자
섹션안에 ForEach 뷰를 구성하고
내용이 될 항목은 data.attendees로!


.onDelete modifier도 적용해줬다


새로운 single source of truth가 될 attendeeName을 선언해주고!


TextField를 구성해줌
이때 입력되는 텍스트는 방금 선언했던 newAttendeeName과 바인딩!
버튼도 추가해주자


버튼에 action을 추가해주는데

DailyScrum.Attendee의 name 프로퍼티에 새로입력되는 newAttendeeName을 넣어주고 data에도 추가를 해줌!

그리고 추가된 후에는 다시 초기화 해주는 걸로! 이렇게되면
newAttendeeName이 비워질 경우에 TextField도 비워지게 되겠죠?

그렇게 텍스트필드가 비워져있다면 button을 비활성화해줌!!


Add accessibility modifiers




Present the edit view

디테일뷰로 돌아와서, @State 로 새로운 변수 선언한다!

이 값에 따라서 화면이 뜨게 할 계획

리스트뷰의 끝자락에 .sheet 애니베이션을 넣어줬다

툴바를 추가하고, 버튼을 선언해서 이버튼이 탭되면
isPresenting 값이 변하게 되고, sheet 모달이 $isPresent 의 값을 바라보고 있으니까 DetailEditView가 뜨게 된다!!

DetailEditView에 NavigationView를 추가하고,
조금 더 사용자가 새로운 뷰에 진입했을 때 명확하게 알 수 있도록!
타이틀이랑 툴바 (캔슬버튼) 추가해줌~!

마찬가지로 Done 버튼도 추가하고!


Passing data with bindings

새로운 ThemeView를 만들어준다

list의 cell을 같은 파일내에 물론 작성할 수도 있지만
지금처럼 다른 파일로 작성하게 되면 재사용에 유리함!

Add a theme picker


@Binding을 하게 되면 parentView에 현재 뷰로 값을 전달해주고,
child View에서 이뤄지는 변경사항들을 전달해줄 수 있음


피커뷰 삽입!

Theme 을 선언해줬던 파일로 돌아와서 CaseIterable과 Identifiable을 채택해줌!
id는 name으로 구분가능하게 해주고


ForEach뷰로 Theme의 케이스들을 전부 넣어주고,

현재의 뷰가 다른뷰에서 값을 받아오고 이 값이 변경되는 걸 바로바로 UI업뎃 할 때 사용하는 게 @Binding


Pass the edit view a binding to data

Picker View를 따로 구성해보자!
먼저 ThemeView라는 새로운 SwiftUI파일을 작성하고,

ThemePicker 라는 Picker View를 구성해준다

이대로만 했을 땐 튜토리얼처럼 피커뷰가 구성이 안됨..
ios 16에서 바뀌었다고 하는데 튜토리얼 처럼 보여주려면 .pickerStyle(.navigationLink) 를 추가해줘야함

근데 이렇게되면 Detail Edit View에서 Picker 기능이 작동하지 않는 현상이 발생!
애플 포럼에 질문 올려놓고 우선 튜토리얼처럼 진행하기로 했다

여기서 @Binding은 언젠가 @State의 값을 찾아서 옮겨질거고, 이 값이 변경되게 되면 UI도 업뎃 되게 해주려는 계획!


만든 피커뷰 DetailEditView에 넣어주고

Detail Edit View에 있던 data property를 @State에서 @Binding으로 변경했다!

디테일뷰에다 @State로 data 프로퍼티를 구성해주고


Edit Button이 눌려질 때 data에 scrum.data를 옮겨줌!

그리고 DetailEditView를 띄워주는 쪽에서 $data로 값이 바인딩되게 해주고,

Done에선 scrum이 업데이트 될 수 있게 해줌


DailyScrum Model에 있는 update 메소드! 꼭 구현해줘야함
메소드를 보면 data를 받아서, DailyScrum의 프로퍼티들에 옮겨주고 있다!

그럼 데이터에 있던 값들이 본래의 scrum 으로 옮겨오게 되고,
scrum 또한 Binding되어 있으니까 또 연결해주는 거네


ScrumsView에 있는 scrums 프로퍼티도 @Binding으로 바꿔주고,
전달되는 값도 바인딩 될 수 있게 $ 붙여줌!


Pass a binding into the list view

ScrumdingerApp 파일로 와서
@State ScrumsView의 Single source of truth가 될 친구를 선언했다!
값도 binding되게 해주고!

🤔What is the type of $book.identifier?

struct Book {
	var title: String
    var identifier: Int
}
@State private var book = Book(title: "The Adventures of Smudge", identifier: 19237)

$가 붙게되면 Binding<Int> 타입으로 변함! 같은 값이 전달되니까 Int인줄 알았는데 프로퍼티래퍼로 감싸지게되면 타입이 변한다!!


🤔왜 Data 구조체를 만들었을까?


현재 DailyScrum이라는 모델을 잘 만들었다!
그런데 추후에 사용하는 뷰에 갑자기 Data로 모델을 바꿔서 사용하길래 의문이 들었음
처음부터 모든 모델들이 다 연결되어 있으면 되는거 아닌가? 굳이 만들필요가 없을 것 같은데??
라는 생각이 들었음

그러다가 뙇 깨달음을 얻었는데


여기 Data에 보면 lengthInMinutes가 Double타입인걸 볼 수 있음

DailyScrum에서는 Int타입인데 왜 Double로 바꿔서 새로 모델을 만들어줬느냐!!
Slider를 사용해서 value의 값을 변경하려는 계획이 있었기 때문이다!!!


Slider의 value프로퍼티의 설명을 보면 FloatingPoint, 즉 Double의 형태로 Binding된 값이 들어가야함

🤔 그럼 바인딩해서 넘겨줄 때 캐스팅하면 되는거 아님?

-> 못함. 이전에 Binding된 값의 타입은 어떤 형태인지 봤었다. Int 가 binding되면
Binding<Int> 타입임 이걸 Double로는 바꿀 수가 없음

그래서!!

💡 기존에 사용하던 모델의 타입이 바뀌어야될 경우에 새로운 모델로 만들어주고 다시 @State와 @Binding을 시작한 겨!


Making classes observable

지금까진 @State 와 @Binding을 사용하면서 value타입을 source of truth와 연결하고 뷰 계층들을 업데이트하는 걸 해봤다.
reference타입은 어떻게 할 수 있을까?!

Making a class observable

ObservableObject 프로토콜을 채택해서 가능함!

class ScrumTimer: ObservableObject {
	@Published var activeSpeaker = ""
    @Published var secondsElapsed = 0
    @Published var secondsRemaining = 0
}

Monotoring an object for changes

struct MeetingView: View {
	@StateObject var scrumTimer = ScrumTimer()
    //...
}
struct ChildView: View {
	@ObservedObject var timer: ScrumTimer
    //...
}
struct MeetingView: View {
	@StateObject var scrumTimer = ScrumTimer()
    var body: some View {
    	VStack {
        	ChildView(timer: scrumTimer)
        }
    }
}

요렇게 @StateObject로 선언하고 ChildView에 넘겨주는 방법도 있고

struct ParentView: View {
	@StateObject var scrumTimer = ScrumTimer()
    var body: some View {
    	VStack {
        	ChildView()
            	.environmentObject(scrumTimer)
        }
    }
}
struct ChildView: View {
	@EnvironmentObject var timer: ScrumTimer
    //...
}

요렇게 .environmentObject로 넘겨줄 수도 있음
이건 위에 @StateObject랑 @ObservedObject를 사용할 때랑 살짝 다른게
값을 두 뷰사이에 바로 전달하는게 아니라 .environmentObject로 childView의 계층들에 environment로 걸어놓는 느낌. ChildView들에선 @EnvironmentObject 프로퍼티 래퍼를 선언해서 접근이 가능해짐

ParentView의 프로퍼티 중 @StateObject로 감싸진 프로퍼티
ChildView.environmentObject(감싸진 프로퍼티)
ChildChildView 에서 사용하지 않았더라도
ChildChildChildView에서 @EnvironmentObject로 접근 가능함!


Responding to events

플랫폼에 따라서 보여주고 싶은 화면들이 같은앱이라도 다를 수있음
이것도 SwiftUI에선 핸들 가능함

그리고 Scene의 상태를 3가지로 볼 수 있는데

  • active - 활성화되어있음
  • inactive - 아이패드에서 멀티태스킹 모드 사용할 때처럼 화면은 켜져있는데 동작은 안하는
  • background - 백그라운드

이것들을 또 활용해서 앱의 데이터를 저장하는데 쓸수도 있겠다!

Events and state


Managing state and life cycle

Create an overlay view

MeetingView로 와서 전체를 ZStack에 감싸주고 패딩도 ZStack으로 옮겨줌

VStack 뒤로 RoundedRectangle을 넣어주고, scrum 모델에 들어있는 칼라로 채워주려고한다
이 값은 전의 부모뷰에서 넘어올거니까 @Binding으로 scrum 프로퍼티를 선언해주고

전의 부모뷰에서 DailyScrum 타입의 값을 바인딩해서 넘겨준다!

미팅뷰로 돌아오면

칼라가 잘 나오는걸 볼 수 있다. ZStack의 foregroundColor를 scrum.theme에 들어있는 accentColor로 맞춰줌

네비게이션 바 타이틀모드도 설정해줌

Extract the meeting header

Meeting HeaderView를 새로 만들어보자

MeetingView에 있던 헤더를 옮겨옴 그리고 VStack에 임베드
그리고 프리뷰에 .previewLayout(.sizeThatFits)를 추가해서
딱 이 크기만 볼수도 있음!

이제 여기 타임 값이 바뀔거니까 새로운 상수 추가

총 시간을 확인할 수 있는 totalSeconds 변수 추가!


그리고 progress를 나타낼 변수도 추가


VoiceOver용으로 사용할 분 표시할 변수도 추가

Add design elements to the meeting header

theme 추가!

커스텀하게 바꾼 프로그레스 스타일이 필요하다


이렇게 커스텀한 프로그레스뷰로 다시 그려주면됨

탑이랑 호리존탈로 패딩도 주고! (호리존탈은 왼쪽 오른쪽 패딩주는거)

Add a state object for a source of truth

ScrumTimer class를 구성해주자

꽤 많은 내용이 들어있는 클래스임
시간될때 분석해보기


MeetingView에서 @StateObject로 클래스를 선언해준다


onAppear일 때 초기화해줌
그리고 스크럼 시작 메소드 추가!


scrumTimer.stopScrum()으로 사라질때 멈추게끔 해준다



Meeting 하단에 있던 내용을 새로운 View로 만들어줌

새로운 speakers 추가해주고

skipAction 클로져 만들어줌



이 부분은 ScrumTimer 클래스가 어떻게 구성되어 있는지 이해해야 될 듯


Triger sound with AVFoundation


미팅뷰에 AVFoundation import 해준다


Updating app data






Add scrum history


History라는 모델을 새로 만들었다

그리고 DailyScrum에 변수로 하나 만들어줌


Persisting data


Theme enum에 codable추가해줌

History 모델에도 Codable을 추가해주는데 컴파일 에러가 발생한다
attendess가 codable하지않기 때문에!
Codable은 내부의 프로퍼티가 모두 Codable을 준수할 때 채택가능함


DailyScrum 모델 자체에도 Codable을 채택
결국 이 모든 Codable들은 DailyScrum에 Codable을 채택하기 위한 과정이었음

Create a data store

ObservableObject 프로토콜을 준수하는 ScrumStore 클래스를 작성하자

ObservableObject는 @State랑 비슷하면서 다름
@State는 밸류 타입에 붙는 프로퍼티 래퍼이고, 단일 뷰에서 선언하는 반면에
ObservableObject는 여러개의 뷰들에서 공유가능함

@Published로 선언한 프로퍼티는 이 값을 관찰하고 있는 쪽에서 값의 변경을 알아차릴 수 있게 해줌

파일매니저를 사용해서 데이터를 저장해보자

Add a method to load data


로드 하는 메소드를 작성해준다
completion형태의 closur를 프로퍼티로 받고
파일url에 접근해서 JSONDecoder로 디코딩 한후에 값을 가져옴

Add a method to save data

Save 메소드 작성!

Load data on app launch


기존에 있던 @State는 삭제하고 방금 만들었던 ScrumStore 클래스를
@StateObject로 감싸줌

onAppear될 때 로드 메소드를 작성해줌

Save data in inactive state




Adobting new API features

profile
우주형

0개의 댓글