I want to make a View in ScrollView includes WKWebView like Gmail.
Gmail's body view(webview) to read mail is below.
The WebView can zoom in/out but, top-area(subject, sender...etc) does not zoom.
If I scroll vertically, it will be scrolled with top-area and WebView together.
I am sorry for no good english skill.
struct TopInfoViewModel {
let subject: String
let name: String
let email: String
let date: String
}
private struct RootView: View {
let viewModel: TopInfoViewModel
var body: some View {
VStack(spacing: 0) {
TopInfoView(viewModel: viewModel)
.border(.blue)
Color.gray
.frame(height: 1)
BodyView()
}
.padding(.horizontal, 20)
}
}
struct TopInfoView: View {
@State private var isOpenedReceiverView: Bool = true
let viewModel: TopInfoViewModel
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// 제목
HStack(spacing: 0) {
Text(viewModel.subject)
.font(.system(size: 18, weight: .bold))
Spacer(minLength: 0)
}
// Sender
SenderInfoView(isOpened: $isOpenedReceiverView,
name: viewModel.name,
email: viewModel.email)
// 날짜, 글자크기 변경, 중요표시
HStack(spacing: 0) {
// 날짜
Text(viewModel.date)
.font(.system(size: 13))
}
}
.padding(.vertical, 20)
}
}
private struct SenderInfoView: View {
@Binding var isOpened: Bool
let name: String
let email: String
var body: some View {
HStack(spacing: 0) {
Button {
print("from label touched")
} label: {
HStack(spacing: 2) {
Text("from")
.font(.system(size: 15))
.frame(width: 52, alignment: .leading)
}
}
.padding(.vertical, 4)
// name button
Button {
print("name touched = \(email)")
} label: {
Text(name)
.lineLimit(1)
}
}
}
}
struct WebViewWrapper: UIViewRepresentable {
@State var isLoadedWebView: Bool = false
func makeUIView(context: Context) -> some WKWebView {
/// 자바스크립트 파일을 불러와 UserContentController에 추가
/// - Parameters:
/// - fileName: 추가할 자바스크립트 파일명. 확장자 제외
func importJavaScript(fileName: String, userContentController: inout WKUserContentController) {
if let scriptPath = Bundle.main.path(forResource: fileName, ofType: "js"),
let scriptSource = try? String(contentsOfFile: scriptPath) {
let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentStart, forMainFrameOnly: true)
userContentController.addUserScript(script)
}
}
let config = WKWebViewConfiguration()
var userContentController = WKUserContentController()
config.processPool = WKProcessPool()
config.dataDetectorTypes = .all
config.websiteDataStore = .nonPersistent()
userContentController.add(ContentController(), name: "eventNameTest")
// load javascript
importJavaScript(fileName: "script", userContentController: &userContentController)
config.userContentController = userContentController
let webView = WKWebView(frame: .zero, configuration: config)
let url = URL(string: "https://www.apple.com")!
let request = URLRequest(url: url)
webView.isOpaque = false
webView.backgroundColor = .blue
webView.navigationDelegate = context.coordinator
webView.scrollView.decelerationRate = .normal
webView.scrollView.minimumZoomScale = 1.0
webView.scrollView.maximumZoomScale = 1.0
webView.scrollView.isScrollEnabled = false
webView.backgroundColor = .white
webView.load(request)
return webView
}
func updateUIView(_ webView: some WKWebView, context: Context) {
}
func makeCoordinator() -> WebViewCoordinator {
WebViewCoordinator(self)
}
class WebViewCoordinator: NSObject, WKNavigationDelegate {
var webViewWrapper: WebViewWrapper
init(_ webView: WebViewWrapper) {
self.webViewWrapper = webView
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webViewWrapper.isLoadedWebView = true
}
}
class ContentController: NSObject, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
}
}
}
struct BodyView: View {
var body: some View {
WebViewWrapper()
.background(.yellow)
.border(.red)
}
}
struct RootView_Preview: PreviewProvider {
static var previews: some View {
RootView(viewModel: TopInfoViewModel(subject: "subject1", name: "name1", email: "email1", date: "date1"))
}
}


I assume you wanted the view to be scrollable, I don't understand very well your request, sorry. Anyway, what I did was to wrap the content of the body of your
RootViewin aGeometryReaderand used its size property to give the WebView, namedBodyViewin this case some witdth and height. Then I also wrapped it in aScrollView:Also, I changed this line to true too:
I don't exactly know how it is supposed to be set but, if you set it to false the rest View will scroll as if it was an image and you can still zoom in/out in the WebView anyway. Let me know if this is what you meant.