모바일 앱의 UI를 개발하는데 있어 동적인 요소들은 시각적으로 부드럽게 화면이 업데이트되는 경험을 줄 수 있다.
이번 챕터에서는 제스쳐와 같은 user interaction이 어떻게 추가되고 합쳐지고 custom 하게 전달되서 특별한 사용자 경험을 줄 수 있을 지에 대해서 알아본다.
Kuchi 앱에서 탭바를 추가해 새로운 단어를 배우는 화면을 추가할 것 이다.
Adding learn feature
두개의 탭으로 구성된 탭바를 만들 것 이다.
- 배우는 화면 (이번에 만들 것이다)
- 기존 challenge 화면
먼저 배우는 기능을 할 최상위 empty view 를 만들어 준다.
여러개의 파일로 구성될 것인데 Learn group으로 묶어준다.(practice folder 와 같은 level 에 위치한다.)
그리고 탭바를 host할 HomeView.swift 를 만든다.
body 에TabView 를 만든다.
tag(0) 을 통해 해당 tab 의 index 를 정하고 VStack으로 탭에 표시될 아이콘과 텍스트를 묶고 tabItem 에 넣어준다.
LearnView는 탭이 선택됬을 때 보여지는 view 이다.
두번째 탭을 추가해 주려면 먼저 리팩토링이 필요하다.
WelcomeView에서 PracticeView 를 방금만든 HomeView 로 바꿔줘야 한다.
그리고 PracticeView 를 HomeView 의 2번째 탭으로 넣어준다.
Creating a flashcard
Learn 탭에서 flash card 를 만들 것 이다.
보여지는 카드 (UI component) 와 카드 데이터 (state) 가 필요하다.
카드 UI 는 state 없이는 존재할 수 없기 때문에 state 를 표현할 수 있는 데이터 구조가 필요하다.
- 표시할 단어를 가지는 Challenge type card 변수이다.
- 여러장의 카드를 view에서 iterating 할 거기 때문에 Identifiable protocol 을 준수하는게 좋다.
예를들어 ForEach SwiftUI block 같은 경우 id 가 필요하다. - id 만드는 로직이 없으니 Foundation 의 UUID 메서드를 사용한다.
- filtering 을 위해 isActive 를 추가한다.
- Equatable 을 통해 비교 가능하게 만든다. (== 오퍼레이터 쓸 수 있게)
사용자는 카드 한장씩 공부하지 않을 것이기 때문에 카드 배열을 담을 수 있는 deck 같은 개념이 필요하다.
Building flash deck
- Flashcard의 배열인 cards 를 가지게 한다.
- 덱에 따라 UI 가 변하게 하기 위해 @published 로 선언하고 class 도 ObservableObject로 선언한다.
Final state
deck 을 가지고 있어 사용자가 deck 을 관리하고 UI 업데이트를 받을 수 있는 최상단 저장공간이 필요하다.
LearningStore 파일을 만든다.
And finally... building the UI
UI 는 3단계로 구성된다.
LearnView와 그 위의 deckView 와 그 위의 cardView 이다.
CardView.swift 부터 만들어 보자
다음 DeckView 를 만들자
다시 LearnView로 돌아가서 뷰 수정한다.
Adding LearningStore to the views
과정 생략..
Your first gesture
SwiftUI 의 제스쳐는 AppKit과 UIKit 과 비슷하지만 더 간단하다.
눙력은 비슷하지만 SwiftUI 는 더 쉽게 제스쳐를 만들 수 있다.
TapGesture
CardView 에서 Tap 해야 정답을 보여주는 기능을 만들어 본다.
Custom gestures
drag gesture 을 통해 사용자가 외운 단어인지 판단하는 기능을 만들어 본다.
DeckView.swift에서 방향을 나타내는 enum 을 만든다.
그리고 CardView에서 카드를 draggable 하게 만든다.
dragged 프로퍼티는 카드를 드래그 될 수 있게 해주고 어느 방향으로 드래그 됬는지 enum 결과를 반환해준다.
다음 init 에 dragged closure 를 파라미터로 받아준다.
다음 DeckView 에서 카드의 새로운 기능을 지원할 수 있도록 만들어 준다.
마지막 단계는 실제 drag 제스쳐를 적용하는 것 이다. CardView.swift 에서 offset 을 추가한다.
- ZStack을 리턴해야 그 위에 drag gesture 를 추가할 수 있다.
- body 안에 선언된 drag 는
- dragging 하면서 기록되는 움직임에 따라 onChagned event 가 발생한다.
이때 offset 값을 변경하면서 사용자의 drag motion 과 일치하도록 바꾼다.
예를들어 사용자가 (0, 0)에서 시작했고 사용자가 (200, -100) 으로 드래그 했을 때 onChanged 가 trigger 되어 x축으로 200, y축으로 -100 만큼 이동한다. 결론적으로 컴포넌트는 오른쪽 위로 사용자의 손가락에 맞춰 이동된다. - onEnded event 는 사용자가 dragging 을 멈췄을 때 발생한다.(손가락이 스크린에서 떨어졌을 때)
이 시점에 drag 가 어느 방향으로 이동했냐 (판단은 조건문에 따라) 판단되서 left 나 right 결과가 나온다.
- dragging 하면서 기록되는 움직임에 따라 onChagned event 가 발생한다.
- 마지막으로 geture modifier 를 적용하면 된다.
Combining gestures for more complex interactions
두개의 제스쳐를 합쳐보자
- Sequenced : 제스쳐 뒤 제스쳐
- Simultaneous : 제스쳐 동시에 active
- Exclusive : 둘다 동시에 추가되었지만 한번에 하나씩만 active
longpress 제스쳐를 drag 에 추가해본다.
@GestureState var isLongPressed = false
let longPress = LongPressGesture()
.updating($isLongPressed) { value, state, transition in
state = value
}
.simultaneously(with:drag)
.gesture(longPress)
.scaleEffect(isLongPressed ? 1.1 : 1)
Key points
- 기본 제스쳐 사용
- 커스텀 제스쳐 사용
- 두개의 제스쳐 합쳐서 사용
swiftUI gesture documentation
'iOS > SwiftUI' 카테고리의 다른 글
#12 Conditional Views (0) | 2020.10.23 |
---|---|
#11 Lists & Navigation (0) | 2020.10.22 |
#9 State & Data Flow (0) | 2020.10.22 |
#8 Introducing Stacks & Containers (0) | 2020.10.22 |
Inset이 뭘까 (0) | 2020.10.21 |
댓글