2022년 2월 9일 수요일

Building asynchronous views in SwiftUI 정리

self loading views
View model 사용하기
Combine을 사용한 AnyPublisher
refreshable with error : async/await
publisher에 의한 data를 감지하고 싶을 때 onReceive를 사용할 수 있다.

2022년 2월 4일 금요일

State management in SwiftUI 정리

State는 View의 내부 정보를 저장하고 싶을 때, State의 값이 변경되었을 때 View를 즉시 업데이트 하고 싶을 때 사용한다.
Binding은 State로부터 값을 받고, 이 값을 바꾸었을 때 기존 State도 같이 바뀌도록 하고 싶을 때 사용한다.

위 State와 Binding은 View 내에서 사용한다. 외부에서 같은 동작이 일어나도록 하고 싶으면 ObservableObject와 ObservedObject를 사용한다.

ObservableObject내에 notification을 받고 싶은 property를 Published로 선언한다. 또한, 주의해야 할점은 ObservedObject의 초기화가 View의 initializer에서 일어나야 한다는 것이다. 그렇게 하지 않으면 rerendering이 일어날 때마다 매번 새로 초기화될 수 있다. 이에 대한 해결로 iOS14에서 StateObject가 새로 나왔다. StateObject는 rerendering이 일어날 때 새로 초기화되지 않는다.

Parent와 바로 아래 children의 관계라면  Binding을 사용하는 것이 보통이다. 그러나, 만약 한창 아래의 view 계층에 있는 view에 environment를 전달하고 싶으면 EnvironmentObject를 사용한다. 상위의 view에서 .environmentObject(value)를 호출해주면 그 view 이하의 view에서 EnvironmentObject의 선언을 통해 값을 사용할 수 있다.
또는, EnvironmentKey를 사용한다. custom EnvironmentKey를 선언하고 EnvironmentValues에 관련 값의 get/set을 구현한다. 이 값은 아래와 같은 식으로 사용한다.

// EnvironmentValue에 선언된 값이 theme인 경우
@Environment(\.theme)

EnvironmentObject는 상위 view에서 값을 지정해주지 않으면 런타임에 크래시가 일어나고 EnvironmentKey는 컴파일 타임에 초기값을 지정해 주어야 하는 차이가 있다.

List에 Binding 사용하기
TabView와 NavigationView 에서 EnvironmetObject등 사용하기
View가 observe 하는 방식
Model - View 간의 연결에 Model을 변환해야 하는 경우
: View는 정렬이 된 item list가 필요하다.

1. item 리스트가 필요할 때마다 원 모델을 정렬한다.
 - 값이 필요할 때마다 정렬을 하게 된다.(불필요한 작업이 될 수 있다.)
2. View 생성시 정렬된 item 리스트를 가지고 있는다.
 - rerendering 마다 정렬이 새로 이루어진다.
3. ViewModel 안에 정렬된 item 리스트를 넣어둔다. 원 모델 변경시 마다 정렬된 item 리스트를 전달할 수 있다.
View 를 dismiss 하는 방법

1. @State와 @Binding 의 연결을 통한 isPresented 값 변경
2. @Environment(\.presentationMode) var presentationMode 의 사용
3. @Environment(\.dismiss) private var dismiss 의 사용
  - dismiss는 callAsFunction 이다.

2022년 1월 21일 금요일

[요약] Android Touch System — Part 1: Touch Functions and the View Hierarchy

https://proandroiddev.com/android-touch-system-part-1-touch-functions-and-the-view-hierarchy-1f6526e55d78

Android Touch System — Part 1: Touch Functions and the View Hierarchy


터치 스크린상에서의 모든 움직임은 MotionEvent로 알려진다.

MotionEvent는 다음의 값들을 가진다.

action : 수행한 action의 타입
x : 터치한 x좌표, view에 상대적인 위치
y : 터치한 y좌표,  view에 상대적인 위치
rawX : 터치한 절대 x좌표, 디바이스 화면에 상대적인 위치
rawY : 터치한 절대 y좌표, 디바이스 화면에 상대적인 위치
eventTime : 이벤트가 발생한 시간, SystemClock.uptimeMilllis()

안드로이드 화면은 좌상단이 (0, 0)이고 우하단이 (maxX, maxY)이다.

action은 다음의 값을 가진다.


ACTION_DOWN : 터치가 처음으로 일어났을 때
ACTION_UP : 터치를 화면에서 떼었을 때
ACTION_MOVE : ACTION_DOWN과 ACTION_UP 사이에 터치가 이동할 때
ACTION_CANCEL : 현재 터치가 취소될 때. parent view가 child view의 이벤트를 인터셉트할 때 발생한다.

이벤트의 흐름


motion event가 발생하면 root view(예: Activity)로부터 중간에 인터셉트당하지 않으면 가장 하위의 view까지 내려간다. 내려갈 때, view의 dispatchTouchEvent()가 호출된다. 여기 안에서 onInterceptTouchEvent()가 호출되는데 false를 리턴하면 아래로 계속 내려가지만 true를 리턴하면 더이상 아래로 내려가지 않는다.(이벤트는 여기서 소비된다.) 이벤트가 leaf view까지 내려가거나 onInterceptTouchEvent()가 true를 리턴해서 더이상 아래로 내려가지 않으면 다시 위로 올라가면서 onTouchEvent()가 호출된다. onTouchEvent()가 true를 리턴하면 이벤트는 여기서 멈춘다.

dispatchTouchEvent()

View.dispatchTouchEvent : view는 children을 갖지 않는다. 따라서, 구현은 간단하다. onTouchEvent()를 호출하고, touch listener를 view에 설정한다. 이 리스너중 하나라도 true를 리턴하면 true를 리턴한다.
custom view는 dispatchTouchEvent 보다는 onTouchEvent를 override 하는 것이 좋다.

ViewGroup.dispatchTouchEvent : onInterceptTouchEvent()를 호출한다. 여기서 false를 리턴하면 추가된 반대의 순서로 child view들을 순회한다. 이중 터치가 child view의 안에서 발생한 것이면 child.dispatchTouchEvent()를 호출한다. 여기서 false를 리턴하면 다음 child에서 child.dispatchTouchEvent()를 호출한다.
ViewGroup은 dispatchTouchEvent 보다는 onInterceptTouchEvent를 override 하는 것이 좋다.

ScrollView.dispatchTouchEvent : ViewGroup과 같다.

Activity.dispatchTouchEvent : 자식들의 dispatchTouchEvent()를 호출한다. 

onInterceptTouchEvent()


View.onInterceptTouchEvent : 없다.

ViewGroup.onInterceptTouchEvent : 기본 구현은 false를 리턴한다. 이 함수를 override 하는 주 목적은 특정한 타입의 터치 이벤트만 다루고 나머지는 자식들이 다루도록 하기 위함이다. 예를 들어 ScrollView는 스크롤은 직접 다루고, 클릭 같은 것은 자식이 다루도록 한다.

ScrollView.onIntreceptTouchEvent : 이벤트가 ACTION_MOVE이고, velocity가 충분하면 true를 리ㅣ턴한다. 그리고 자식들은 ACTION_CANCELLEDD를 받게 된다. 또한 requestDisallowInterceptTouchEvent를 호출한다.

Activity.onInterceptTouchEvent : 없다.

onTouchEvent()


View.onTouchEvent : view가 clickable 하면 기본은 true를 리턴한다. overriding할거면 super.onTouchEvent()를 호출해주는 것이 좋다. 또는 click gesture만 다룰거라면 performClick()를 override하는 것이 좋다.

ViewGroup.onTouchEvent : View의 onTouchEvent와 같다.

ScrollView.onTouchEvent : event의 정보를 통해 얼마나 스크롤했는지를 알아내서 스크롤을 수행한다. 스크롤과 관련된 애니메이션도 다룬다. 또한 requestDisallowInterceptTouchEvent()를 호출한다.

Activity.onTouchEventv : 기본 구현은 항상 false를 리턴한다.

requestDisallowInterceptTouchEvent()

ViewParent 인터페이스의 함수. parent나 ancestor view가 터치 이벤트를 인터셉트하지 못하도록 할 때 사용한다.

Building asynchronous views in SwiftUI 정리

Handling loading states within SwiftUI views self loading views View model 사용하기 Combine을 사용한 AnyPublisher Making SwiftUI views refreshable r...