관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - SwiftUI] 튜토리얼 - 13. SwiftUI에서 UIKit 사용 방법 (UIViewRepresentable, UIViewControllerRepresentable) 본문

iOS 튜토리얼 (SwiftUI)

[iOS - SwiftUI] 튜토리얼 - 13. SwiftUI에서 UIKit 사용 방법 (UIViewRepresentable, UIViewControllerRepresentable)

jake-kim 2022. 7. 15. 22:23

SwiftUI에서 UIkit 사용 방법 - UIView

  • UIViewRepresentable 프로토콜을 구현하면 SwiftUI에서 UIView 사용 가능
@available(iOS 13.0, tvOS 13.0, *)
@available(macOS, unavailable)
@available(watchOS, unavailable)
public protocol UIViewRepresentable : View where Self.Body == Never {
  associatedtype UIViewType : UIView
  func makeUIView(context: Self.Context) -> Self.UIViewType
  func updateUIView(_ uiView: Self.UIViewType, context: Self.Context)
  
  static func dismantleUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator)
  
  associatedtype Coordinator = Void
  
  func makeCoordinator() -> Self.Coordinator
  
  typealias Context = UIViewRepresentableContext<Self>
}
  • UIViewControllerRepresentable의 3가지를 필수로 정의하여 사용
    • makeUIView(context:) -> Self.UIViewType: UIView를 생성하고 초기화
    • updateUIView(_:,context:): UIView 업데이트가 필요할 때 호출하는 메소드

ex) UIViewRepresentable를 이용하여 SwiftUI에서 UILabel 사용방법

  • UIViewRepresentable 프로토콜을 구현
  • makeUIView메소드와 updateUIView를 구현
struct MyUILabel: UIViewRepresentable {
  @Binding var text: String // @Bidning property: SwiftUI -> UIKit으로의 데이터 전달
  
  func makeUIView(context: Context) -> UILabel {
    let label = UILabel()
    label.textColor = .blue
    return label
  }
  func updateUIView(_ uiView: UILabel, context: Context) {
    uiView.text = text
  }
}
  • 사용할 땐 SwiftUI 사용하는 방법대로 그대로 사용
import UIKit
import SwiftUI

struct ContentView: View {
  @State var text: String
  var body: some View {
    VStack {
      MyUILabel(text: $text)
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView(text: "Example Text")
  }
}

Coordinator 개념

  • UIViewRepresentable에서 Coordinator 타입과 makeCoordinator()이 존재
    • Coordinator는 UIKit -> SwiftUI로의 데이터 전달 (= delegate 역할)
    • @Bidning property는 SwiftUI -> UIkit으로의 데이터 전달
  • UITableView에서의 UITableViewDataSource, UITableViewDelegate이 Coordinator와 동일한 역할

ex) UITableView에서 delegate까지 구현 방법

  • updateUIView(_:context:)에서, context로 Coordinator 인스턴스에 접근하여 델리게이트 할당
import UIKit
import SwiftUI

struct MyTableView: UIViewRepresentable {
  @Binding var isShowing: Bool
  
  func makeUIView(context: Context) -> UITableView {
    UITableView()
  }
  
  func updateUIView(_ uiView: UITableView, context: Context) {
    guard self.isShowing else { return }
    uiView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    uiView.delegate = context.coordinator // <-
    uiView.dataSource = context.coordinator // <-
  }
}
  • Coordinator에 delegate를 할당하고, makeCoordinator()의 반환값을 Coordinator 인스턴스로 반환
import UIKit
import SwiftUI

struct MyTableView: UIViewRepresentable {
  @Binding var isShowing: Bool
  
  func makeUIView(context: Context) -> UITableView {
    UITableView()
  }
  
  func updateUIView(_ uiView: UITableView, context: Context) {
    guard self.isShowing else { return }
    uiView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    uiView.delegate = context.coordinator
    uiView.dataSource = context.coordinator
  }
  
  func makeCoordinator() -> Coordinator { // <-
    Coordinator()
  }
  
  class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource { // <-
    var dataSource = (0...10).map(String.init(_:))
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      self.dataSource.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
      cell.textLabel?.text = self.dataSource[indexPath.row]
      return cell
    }
  }
}

SwiftUI에서 UIkit 사용 방법 - UIViewController

  • UIViewControllerRepresentable 프로토콜을 구현하면 SwiftUI에서 UIViewController 사용 가능
@available(iOS 13.0, tvOS 13.0, *)
@available(macOS, unavailable)
@available(watchOS, unavailable)
public protocol UIViewControllerRepresentable : View where Self.Body == Never {
  associatedtype UIViewControllerType : UIViewController
  
  func makeUIViewController(context: Self.Context) -> Self.UIViewControllerType
  func updateUIViewController(_ uiViewController: Self.UIViewControllerType, context: Self.Context)
  static func dismantleUIViewController(_ uiViewController: Self.UIViewControllerType, coordinator: Self.Coordinator)
  associatedtype Coordinator = Void
  func makeCoordinator() -> Self.Coordinator
  
  typealias Context = UIViewControllerRepresentableContext<Self>
}
  • UIView를 사용할때와 마찬가지로, UIViewControllerRepresentable의 2가지를 필수로 정의하여 사용
    • makeUIViewController(context:): UIViewController를 생성하고 초기화
    • updateUIViewController(_:context:): ViewController 업데이트가 필요할 때 호출되는 메소드 (ViewController에 필요한 데이터를 갱신)
  • Coordinator역시도 UIView에서 사용할때와 동일

* 전체 코드: https://github.com/JK0369/ExUIKit

* 참고

https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable

Comments