기존 글 보기 -> 새로운 글 작성 -> 수정
- 클래스 프로토콜
- 관찰 가능한 객체로 만들어줌
objectWillChange.send()
를 통해 변경사항을 전달할 수 있음
- ObservableObject 객체 내의 인스턴스에 선언
- 값이 변동되었을때 뷰에 즉시 알려주도록 하는 프로퍼티 래퍼
objectWillChange.send()
자동 실행
- 여러 뷰에서 같은 데이터를 공유하며 사용할 수 있도록 하는 property wrapper
- 싱글턴과 유사
- 최상위뷰(ContentView)에 할당하면 모든 뷰에서 사용 가능
- 전역적으로 사용하기 위해 최상위 App에서 .environmentObject(_:) 메서드를 통해 값 매칭
- 사용될 뷰에서 변수에 @EnvironmentObject 붙여 사용
📍앱 내에서 전반적으로 사용할 용도로 생성한 dummyData
class MemoData: ObservableObject {
@Published var dummyData: [Memo] = [
Memo(id: 0, isLocked: true, editedDate: "오늘", fullContent: "메모1\n하나둘셋넷"),
Memo(id: 1, isLocked: false, editedDate: "오늘", fullContent: "메모2\n하나둘셋넷다섯여섯일곱여덟아홉하나둘셋넷다섯여섯일곱여덟아홉")]
}
- MemoData라는 클래스를 앱 전반에서 변화에 대응해서 사용하기 위해 관찰 가능하도록 ObservableObject 프로토콜 상속
- MemoData 내에 dummyData라는 인스턴스의 값이 변하면 해당 데이터를 사용하는 뷰에게 전달하기 위해 @Published 선언
- ObservableObject인 MemoData를 전역적으로 사용할 수 있게 하기 위해 뷰의 최상위인 App에 .environmentObject()로 설정
- 사용할 뷰에서 @EnvironmentObject로 변수를 선언해서 접근
// MemoApp
@main
struct MemoApp: App {
var body: some Scene {
WindowGroup {
let memo = MemoData()
ContentView()
.environmentObject(memo)
}
}
}
// ContentView
struct ContentView: View {
@EnvironmentObject var memoData: MemoData
...
}
// MemoDetail
struct MemoDetail: View {
@EnvironmentObject var memoData: MemoData
...
}
- State(상태)가 변하면 뷰를 다시 그림
- 뷰 내부에서만 사용할 수 있기 때문에 private 선언
- 하위 뷰나 다른 뷰에서 참조하기 위해선 @Binding 필요
- TextField나 TextEditor에서 키보드 사용/해제를 인식해줌
- 키보드 올라옴/내려감 상태 변화에 따라 뷰 업데이트
- 키보드를 올리거나 내릴 수 있음
📍
- State 프로퍼티와 바인딩할 때는 변수 앞에
$
선언 - State 프로퍼티에 할당된 값을 그냥 사용하고자 할때는
$
를 붙이지 않고 사용
struct MemoDetail: View {
@State private var newMemo: Bool = false
@State private var memoContent: String = ""
@FocusState private var isFocused: Bool
var body: some View {
VStack {
TextEditor(text: $memoContent)
.focused($isFocused)
Spacer()
HStack {
...
Button(action: {
memoContent = ""
newMemo = true
isFocused = true
}, label: {
Image(systemName: "square.and.pencil").resizable().frame(width: 22, height: 22)
})
}
...
}
...
.toolbar {
if $isFocused.wrappedValue {
Button("완료") {
isFocused = false
if newMemo {
let new = Memo(id: (memoData.dummyData.last?.id ?? 0) + 1, isLocked: false, editedDate: "오늘", fullContent: memoContent)
memoData.dummyData.append(new)
} else {
memoData.dummyData[memoIndex].fullContent = memoContent
}
}
}
}
.onAppear {
if memoIndex > -1 {
memoContent = memoData.dummyData[memoIndex].fullContent
} else {
newMemo = true
}
}
}
}
UIKit의 viewLifeCycle 과 유사하게 사용할 수 있는 메소드
struct MemoDetail: View {
@State private var memoContent: String = ""
var body: some View {
VStack {
...
}
.onAppear {
// 뷰가 나타나기 전에 하고싶은 작업
}
}
}