SwiftUI에서 UIViewRepresntable 프로토콜을 이용하여 SwiftUI에서 UIKit 뷰를 래핑하고 코디네이터를 구현하여 SwiftUI와 UIKit 이벤트를 브릿징 하는 방법을 알아보도록 하자!
애플 공식문서 에서는 UIViewRepresentable을 SwiftUI에 UIKit 뷰 계층구조에 통합하는데 사용되는 래퍼클러스라고 소개하고 있다. SwiftUI가 빠르게 업데이트 되고 편리성이 많은것은 사실이지만 사실 이번에 팀프로젝트를 진행하면서 가장크게 느낀것은 UIKit은 이미 너무나도 많은 기능을 가지고 있고 실제로 모든것을 대체하기 까지는 아직은 시간이 걸릴것 같다는것을 몸으로 체감하게 되었다.
이번에 팀프로젝트에서 사용한 맵뷰와 같은 외부라이브러리 뿐만이 아니라 UI를 빌드할때도 종종 UIKit을 사용해야 하는일이 발생하는것같다.
import SwiftUI
struct MyUILabel: UIViewRepresentable {
var text: String
func makeUIView(context: Context) -> some UIView {
let myLabel = UILabel()
myLabel.text = text
return myLabel
}
func updateUIView(_ uiView: UIViewType, context: Context) {
// 필요한 업데이트 작업을 수행
}
}
#Preview {
MyUILabel(text: "Hello world!")
}
가장먼저 UIViewRepresentable 프로토콜을 채택해준다 해당 프로토콜을 채택하면 makeUIView 와 updateUIView 메서드를 반드시 구현해야 한다.
makeUIView는 사용하고 싶은 뷰를 리턴하는 역할을 한다 만약 UILabel이 아닌 UITableView를 사용하고 싶다면 UITalbeView를 생성해주고 함수에서 리턴해주면 된다.
updateUIView는 필요한 업데이트 작업을 수행 하는 역할을 한다.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
MyUILabel(text: "Presented with UIViewReprentable")
}
.padding()
}
}
#Preview {
ContentView()
}
실제로 사용할때는 SwiftUI 구조체와 동일하게 사용해주면 된다.
UILable 같은 경우는 단순히 텍스트를 나타내는 UIView 이다 하지만 우리가 실제로 앱을 구현하면서 중요한 부분은 사용자의 이벤트를 받아서 처리하는 작업일것이다 그래서 UIKit의 UIScrollView 뷰를 받아서 화면에 보여주고 이벤트까지 처리하는 방법을 알아보도록 하자.
struct MyScrollView: UIViewRepresentable {
var text: String
func makeUIView(context: Context) -> some UIView {
let scrollView = UIScrollView()
let myLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
scrollView.refreshControl = UIRefreshControl()
myLabel.text = text
scrollView.addSubview(myLabel)
return scrollView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
}
#Preview {
MyScrollView(text: "Swift")
}
UIScrollView를 반환해주는 MyScrollView라는 뷰파일을 만들었다 해당 뷰파일은 화면에 정상적으로 뷰파일을 보여준다
하지만 지금 상태에서 스크롤을 내리면 UIRefreshControll을 종료하지 않기때문에 계속 RefreshController가 보이게 된다.
SwiftUI와 UIKit 사이에 이벤트를 처리하기 위해서는 Coordinator 클래스를 이용하여 SwiftUI와 UIKit간의 이벤트를 브릿징하는 작업이 필요하다 Coordinator 라는게 대단한건 아니고 말그대로 중재자 역할을 하는 클래스를 구현한다고 생각하면 된다.
{
func makeCoordinator() -> Coordinator {
Coordinator(control: self) // 스크롤뷰를 코디네이터 클래스에 넘겨준다
}
var text: String
func makeUIView(context: Context) -> some UIView {
let scrollView = UIScrollView()
let myLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
scrollView.delegate = context.coordinator // 스크롤뷰의 delegate를 context.coordinator로 지정
scrollView.refreshControl = UIRefreshControl()
// refreshControll 타겟으로 handleRefresh 메서드를 추가한다
scrollView.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefresh), for: .valueChanged)
myLabel.text = text
scrollView.addSubview(myLabel)
return scrollView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
class Coordinator: NSObject, UIScrollViewDelegate {
var control: MyScrollView
init(control: MyScrollView) {
self.control = control
}
// 스크롤될때 호출되는 함수
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("View is Scrolling")
}
@objc func handleRefresh(sender: UIRefreshControl) {
sender.endRefreshing()
}
}
}
우선 Coordinator 클래스를 구현부를 보면 인스턴스가 초기화 될때 MyScrollView 구조체를 받도록 되어있다 그리고 UIScrollViewDelegate 프로토콜을 채택하여 UIScrollView의 이벤트를 위임받을 수 있도록 구현해준다.
makeCoordinator 에서는 Coordinator 인스턴스를 생성하고 뷰에 반환해준다 그리고 makeUIView 에서는 해당 컨텍스트의 코디네이터를 UIScrollView 의 delegate로 지정해주면 해당 코디네이터 클래스에서 스크롤뷰의 역할을 위임받아 스크롤뷰 관련된 이벤트를 처리가 가능해진다.
'iOS' 카테고리의 다른 글
SwiftUI 앱에서 Coordinator Pattern 적용하기 (0) | 2024.08.08 |
---|---|
[iOS/SwiftUI] firebase로 피드 데이터 가져오기 (1) | 2023.12.24 |
[iOS] Swift File I/O (1) | 2023.11.21 |
[iOS/SwiftUI] @StateObject, @ObservedObject의 차이점 (1) | 2023.11.20 |