이전 글: https://dev-doogie.tistory.com/24
Working with UI controls
.onAppear => ViewDidAppear
.onDisappear => ViewDidDisappear
같은 느낌인데 VC에서 사용하고 뭐 그런게 아니라 모든 뷰에 적용 가능 한듯
@Environment(\.editMode)
@Environment에는 \.editMode라는 기본적으로 제공하는 프로퍼티가 있는데 이는 EditButton()을 추가하면 이 버튼과 자동으로 연결됨
그리고 editMode프로퍼티는 wrappedValue가 .active인지 .inactive인지로 구별함
A뷰 안에 상위 뷰로 부터 주입되어야 하는 @Environment 프로퍼티를 사용하는 B뷰가 있다고 가정해볼 때
A뷰에서 해당 프로퍼티를 사용하지 않더라도 하위 뷰로 연결을 시켜야하기 때문에 A뷰 생성시 .environment로 해당 값을 전달하여야 함
Interfacing with UIKit
UIKit 연결 정리에 앞서 신기하거나 의문이 들었던 점
View를 구성할 때 프리뷰가 상위 뷰 까지 염두하고 보여주지 않으니 프리뷰에서 내가 나중에 실제로 보게 될 때와 비슷하게 조건을 걸어서 표시하기도 함
gradient를 추가했다고 외 Vstack이 왼쪽 아래로 가는지 잘 모르겠음
추측으로는 gradient를 추가하기 전에는 Zstack을 차지하는 범위가 Vstack 이고 그 크기가 fit(?)해서 중앙에 배치되었는데
gradient는 화면 전체를 차지하고 이에따라 ZStack의 범위가 화면 전체만큼 넓어져서 .bottomLeading이 명시적으로 보이는 듯?
UIKit의 기능을 SwiftUI에서 사용하기
온전히 SwiftUI로만 모든걸 구현하기에는 한계가 있어 일정 부분은 UIKit의 힘을 빌려야하는데 튜토리얼에서는 UIPageViewController와 UIPageControl을 예시로 들었다
- UIViewController
`UIViewControllerRepresentable`를 채택하는 객체를 만들고 `makeUIViewController`와 `updateUIViewController`라는 필수 메서드를 구현한다
그리고 dataSource, delegate등을 사용하기 위한 Coordinator도 구현한다(디자인 패턴 아님!)
makeUIViewController: 사용하려는 ViewController를 반환하며 화면에 보여질 때 딱 한 번만 호출됨
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
//dataSource, delegate 채택하는 부분
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
updateUIViewController: SwiftUI에서 사용하는 UIKit의 기능에 영향을 미치는 변경이 있으면 호출되고 해당 UIKit 기능에도 반영
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[context.coordinator.controllers[currentPage]], direction: .forward, animated: true)
}
makeCoordinator: makeUIViewController가 호출되기 전에 호출되는 메서드, 기본적으로는 아무것도 반환하지 않지만 delegate, dataSource등을 사용하기 위해(예를 들면 해당 튜토리얼에서는 UIPageViewController의 dataSource) 해당 delgate혹은 dataSource를 채택한 객체를 반환하면 위 makeUIViewControlle와 updateUIViewController메서드의 파라미터인 context의 coordinator에서 확인 가능
classCoordinator: NSObject <- 이 객체 만들기만 해도 필수 구현하라고 에러 뜸
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageViewController
var controllers = [UIViewController]()
init(_ parent: PageViewController) {
self.parent = parent
controllers = parent.pages.map { UIHostingController(rootView: $0) }
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController) else {
return nil
}
if index == 0 {
return controllers.last
}
return controllers[index - 1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController) else {
return nil
}
if index == controllers.count - 1 {
return controllers.first
}
return controllers[index + 1]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = controllers.firstIndex(of: visibleViewController) {
parent.currentPage = index
}
}
}
- UIView
UIViewController와 거의 비슷하게 UIViewRepresentable를 채택 하고 필수 메서드인 makeUIView와 updateUIView 메서드를 구현한다
여기서는 delegate와 dataSource가 아닌 @objc 메서드를 사용하기 위해 Coordinator를 구현했다
import SwiftUI
import UIKit
struct PageControl: UIViewRepresentable {
var numberOfPages: Int
@Binding var currenPage: Int
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIView(context: Context) -> UIPageControl {
let control = UIPageControl()
control.numberOfPages = numberOfPages
control.addTarget(
context.coordinator,
action: #selector(Coordinator.updateCurrentPage),
for: .valueChanged)
return control
}
func updateUIView(_ uiView: UIPageControl, context: Context) {
uiView.currentPage = currenPage
}
class Coordinator: NSObject {
var control: PageControl
init(_ control: PageControl) {
self.control = control
}
@objc func updateCurrentPage(sender: UIPageControl) {
control.currenPage = sender.currentPage
}
}
}
그리고 SwiftUI View에 @State 프로퍼티를, Representable객체에 @Binding프로퍼티를 만들고 변경되는 값을 바로 뷰에 반영할 수 있다
'Swift > SwiftUI' 카테고리의 다른 글
SwiftUI_Tutorial 간단 정리(2/3) - Drawing and animation (0) | 2024.05.13 |
---|---|
SwiftUI_Tutorial 간단 정리(1/3) - SwiftUI essentials (0) | 2024.05.11 |
흔하디 흔한 TCA 기본 개념 정리 (0) | 2024.05.09 |
SiwftUI iOS17 버전에서의 버그(bottomBar관련) (0) | 2023.12.19 |