protocol UIViewRepresentable : View where Self.Body == Never

 

  • UIView๊ฐ์ฒด๋ฅผ SwiftUI ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ
  • SwiftUI์—์„œ์˜ View์™€ ๋™์ผํ•˜๊ฒŒ ์ƒ์„ฑ๋˜๊ณ  ์—…๋ฐ์ดํŠธ ๋จ
  • ์‹œ์Šคํ…œ์€ UIViewRepresentable์„ ์ฑ„ํƒํ•˜๋Š” ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์ ์ ˆํ•œ ํƒ€์ด๋ฐ์— ํ˜ธ์ถœํ•จ
  • ์‹œ์Šคํ…œ์€ ํ•ด๋‹น ๊ฐ์ฒด์˜ ๋ณ€ํ™”์— ๋Œ€ํ•ด ๋‹ค๋ฅธ View์™€ ์ž๋™์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์›ํ•˜๋Š” ๊ฒฝ์šฐ Coordinator์„ ์‚ฌ์šฉํ•ด์•ผ ํ•จ (target-action, delegate message ๋“ฑ)

 

makeUIView

@MainActor
func makeUIView(context: Self.Context) -> Self.UIViewType
  • View ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐํ™”
  • context parameter
    • UIViewRepresentableContext<Self> ํƒ€์ž…์œผ๋กœ์„œ ๋งŒ์•ฝ WKWebView๋ฅผ UIViewPresentable์„ ์ด์šฉํ•ด CustomWebView๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด context์˜ ํƒ€์ž…์€ UIViewRepresentableContext<CustomWebView>
    • context์˜ ๊ฐ’
      • coordinator
        • UIViewPresentable์˜ ์ƒํƒœ ๋ฐ ์ด๋ฒคํŠธ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
        • UIKit์˜ delegate ์™€ SwiftUI๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์—ญํ™œ
      • environment
        • ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ํ™˜๊ฒฝ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์™€์„œ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Œ
        • context.environment.local : ๋‚ ์งœ๋‚˜ ์ˆซ์ž ํ˜•์‹ ๋กœ์ปฌ๋ผ์ด์ง•์„ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
        • context.environment.colorScheme : ๋‹คํฌ๋ชจ๋“œ์ธ์ง€ ๋ผ์ดํŠธ๋ชจ๋“œ์ธ์ง€ ํ˜„์žฌ ๊ธฐ๊ธฐ ์„ค์ • ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์˜ฌ ์ˆ˜ ์žˆ์Œ
        • context.environment.accessibilityDifferentiateWithoutColor : ๋Œ€ํ˜• ํ…์ŠคํŠธ ์„ค์ •์ด ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  view์˜ ๊ฐ’์„ ์กฐ์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
      • transaction
        • UIView ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ SwiftUI์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋™๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ์Œ
        • SwiftUI์˜ ํ˜„์žฌ ๋ทฐ ์—…๋ฐ์ดํŠธ ์ •๋ณด ํฌํ•จ
        • ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ํŠธ๋žœ์ ์…˜ ์‚ฌ์šฉ๊ฐ€๋Šฅ
  • ๋ฐ˜ํ™˜๊ฐ’์ด UIView ์ด๋ฉฐ ์ด๋ฅผ SwiftUI์˜ View์— ํ‘œ์‹œํ•จ. View๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ํ•„์ˆ˜๋กœ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•˜๋Š” ๋ฉ”์†Œ๋“œ
  • ์ตœ์ดˆ๋กœ ๋ทฐ๊ฐ€ ํ•œ๋ฒˆ ์ƒ์„ฑ๋  ๋•Œ ํ•œ๋ฒˆ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜ (์ดˆ๊ธฐ ์„ค์ •). ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚œ ์ƒํƒœ์—์„œ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋‹ค๋ฅธ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ
  • ์ง์ ‘ ์‚ฌ์šฉํ•ด ๋ณด๋‹ˆ updateUIView๊ฐ€ ์ƒ๊ฐ๋ณด๋‹ค ์ž์ฃผ ํ˜ธ์ถœ ๋˜์–ด ์—…๋ฐ์ดํŠธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. SwiftUI์—์„œ ๋ทฐ ๊ณ„์ธต์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด updateUIView๋„ ๊ฐ™์ด ์—…๋ฐ์ดํŠธ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ ํ•ด์•ผ ํ•จ
    func makeUIView(context: Context) -> WKWebView {
        print("makeUIView")
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        return webView
    }

 

updateUIView

@MainActor
func updateUIView(
    _ uiView: Self.UIViewType,
    context: Self.Context
)

 

  • SwiftUI์™€ View ์‚ฌ์ด์—์„œ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ํ˜ธ์ถœ. SwiftUI์˜ state๋‚˜ binding, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์‹œ UIKit์— ๋ฐ˜์˜
  • uiView๋Š” UIViewRepresentable์„ ์ฑ„ํƒํ•œ Custom View 
  • context ๋ฐ”๋กœ ์œ„ ๋‚ด์šฉ ์ฐธ๊ณ 
  • ๋‹จ๋ฐฉํ–ฅ. SwiftUI์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ UIKit์— ํ•ด๋‹น ํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•˜์—ฌ UIKit์˜ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ๋˜๋ฉฐ ๋ฐ˜๋Œ€๋กœ UIKit์˜ ๋ณ€ํ™”๋ฅผ SwiftUI์˜ View๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹˜
  
    @Binding var url: URL?
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        if let url = url {
            let request = URLRequest(url: url)
            uiView.load(request)
        }
    }
  • ๋ฐ”์ธ๋”ฉ ๋œ url์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด WKWebView๋ฅผ ์—…๋ฐ์ดํŠธ ํ•จ
import SwiftUI

struct ContentView: View {
    @State private var testValue = false
    @State private var currentURLString = "https://tistory.com"
    
    var body: some View {
        VStack {
            CustomWebView(urlString: $currentURLString)
            Button {
                testValue.toggle()
                currentURLString = testValue ? "https://naver.com" : "https://tistory.com"
            } label: {
                Text("ํ† ๊ธ€ ๋ฒ„ํŠผ")
            }
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

(๋„ˆ๋ฌด๋‚˜ ์Šฌํ”„๊ฒŒ๋„ ๊ฐ‘์ž๊ธฐ ํ•„๋ฐ›์•„ ํฐ์„ ๋ฒ ํƒ€๋กœ ๋Œ๋ฆฐ ํ›„ ํฐ์ด ๋ฒฝ๋Œ์ด ๋ฌ์Œ ํ˜„ ์‹œ์  ๋ฒ ํƒ€ XCode 15.0์„ ์“ฐ๊ณ  ์žˆ๋Š”๋ฐ Preview ์ฝ”๋“œ๊ฐ€ ์˜ˆ์ˆ ์ž…๋‹ˆ๋‹ค. #Preview๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด PreviewProvider ๋ถ€๋ถ„์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์ฝ”๋”ฉํ•˜๊ณ  canvas๋กœ ๋ณผ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

import SwiftUI
import WebKit

struct CustomWebView: UIViewRepresentable {
    @Binding var urlString: String
    
    func makeUIView(context: Context) -> WKWebView {
        print("makeUIView")
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        print("updateUIView")
        if let url = URL(string: urlString) {
            let request = URLRequest(url: url)
            uiView.load(request)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: CustomWebView
        
        init(_ parent: CustomWebView) {
            self.parent = parent
        }
    }
}

  • ์ฒ˜์Œ ํ™”๋ฉด์— ๋กœ๋“œ ๋˜๋ฉด mkaeUIView์™€ updateUIView๊ฐ€ ํ˜ธ์ถœ๋จ -> ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด SwiftUI View์˜ State ๊ฐ’(url)์ด ๋ณ€๊ฒฝ๋จ -> ์ด๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  ์žˆ๋Š” UIKit์˜ WKWebView๋Š” updateUIView์—์„œ ๋ณ€๊ฒฝ๋œ State ๊ฐ’์„ WKWebView์— ๋ฐ˜์˜ํ•จ

 

Coordinator Class

  • Coordinator ํด๋ž˜์Šค๋Š” UIKit์˜ delegate ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋จ
import SwiftUI
import WebKit

struct CustomWebView: UIViewRepresentable {
    @Binding var urlString: String
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: CustomWebView
        
        init(_ parent: CustomWebView) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            print(parent.urlString)
         }
    }
}
  • WKWebView๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด UIViewRepresentable์„ ์ฑ„ํƒํ•˜๋Š” CustomView๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ WKNavigationDelegate๋ฅผ Coordinator ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
  • ์ด๋•Œ parent๋Š” ์ปค์Šคํ…€ํ•œ ๋ทฐ ํƒ€์ž…์ด๋ฉฐ SwiftUI๋กœ ๋ถ€ํ„ฐ ์ „๋‹ฌ๋œ ๋ฐ”์ธ๋”ฉ ๊ฐ’์„ parent๋กœ ๋ถ€ํ„ฐ ์ ‘๊ทผํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ

 

Pexels์—์„œ Tima Miroshnichenko๋‹˜์˜ ์‚ฌ์ง„: https://www.pexels.com/ko-kr/photo/6826795/

 

 

์ฐธ๊ณ  ์‚ฌ์ดํŠธ

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

'iOS ๐ŸŽ > UIViewRepresentable' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[SwiftUI] UIViewRepresentable UITextField์— done ๋ฒ„ํŠผ ์ถ”๊ฐ€ํ•˜๊ธฐ  (0) 2023.08.29