SwiftUI can display a UIKit UIView. Adapting UIViews keeps UIKit programming to a minimum by allowing SwiftUI
views to supplement UIViews. For example, a WKWebView
with an overlaid
ProgressView.
The WebView declares a ZStack
, and places the ProgressView
on top of the
WebViewSwiftUIAdapter
at the bottom of the page.
The WebView
owns the estimatedProgress
and is annotated with the
@State
property wrapper.
struct WebView: View {
@State var estimatedProgress: Float = 0.0
let url: URL
var body: some View {
ZStack(alignment: .bottomLeading) {
WebViewSwiftUIAdapter(estimatedProgress: $estimatedProgress, url: url)
if estimatedProgress < 1.0 {
ProgressView(value: estimatedProgress)
.frame(maxWidth: .infinity)
.background(.white)
}
}
}
}
The SwiftUIAdapter suffix can be used to identify any Swift UI views that wrap a
UIView
.
These adapters implement UIViewRepresentable.
UIViewRepresentable
has two functions requiring implementation. SwiftUI calls
makeUIView
when it creates a view. SwiftUI calls updateUIView
when it updates the
view.
The makeCoordinator
function returns a Coordinator
. A Coordinator
instance is of an associatedtype
that allows communication between SwiftUI and UIKit.
struct WebViewSwiftUIAdapter: UIViewRepresentable {
@Binding var estimatedProgress: Float
let url: URL
let webView = WKWebView()
func makeCoordinator() -> WebViewCoordinator {
WebViewCoordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let request = URLRequest(url: url)
webView.load(request)
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {}
}
The WebViewCoordinator
has a reference to the parent WebViewSwiftUIAdapter
. The
WebViewCoordinator
can interact with the parent's webView
and
estimatedProgress
properties.
The WebViewCoordinator
subscribes to estimatedProgress
updates from the
webView
. When the webView
estimatedProgress
changes, the
WebViewCoordinator
updates the parent's estimatedProgress
. Since the parent's
estimatedProgress
is a Binding
to the SwiftUI view's State
property,
SwiftUI updates the view.
class WebViewCoordinator: NSObject {
let parent: WebViewSwiftUIAdapter
var cancellable: AnyCancellable?
init(_ parent: WebViewSwiftUIAdapter){
self.parent = parent
super.init()
cancellable = parent.webView.publisher(for: \.estimatedProgress)
.receive(on: RunLoop.main)
.sink{ [weak self] in
self?.parent.estimatedProgress = Float($0)
}
}
}