I am engaged on a pair of Swift tasks, and one in all them, working on an iPad, refuses to cooperate with this webview. The picture under reveals the whitespace under the editor. This solely seems when the consumer scrolls right down to the underside of the editor’s content material. For context, the editor locks the physique peak to 100vh when in panorama mode so this may show an editor in a side-by-side scrollable container, and this whitespace would not seem when the iPad is in portrait mode with the editor hidden.
Additionally, I’ve enabled vim mode within the editor, and this appears to happen extra regularly, virtually solely when the editor is in vim mode and the consumer jumps to the underside of the observe’s content material.
That is my webview:
struct MarkdownTabView: View {
@Binding var editingNote: NoteModel?
@Binding var fullScreenCover: MainFullScreenCover?
var onNavigateToNote: (NoteModel) -> Void = { _ in }
@AppStorage(AppStorageKeys.editorThemeDark.rawValue) personal
var editorThemeDark: CodeSyntaxTheme = .dracula
@AppStorage(AppStorageKeys.editorThemeLight.rawValue) personal
var editorThemeLight: CodeSyntaxTheme = .githubLight
@AppStorage(AppStorageKeys.theme.rawValue) personal var theme: WebViewTheme =
.fluster
@AppStorage(AppStorageKeys.editorKeymap.rawValue) personal var editorKeymap: EditorKeymap = .base
@AppStorage(AppStorageKeys.hasLaunchedPreviously.rawValue) personal
var hasPreviouslyLaunched: Bool = false
@Atmosphere(ThemeManager.self) personal var themeManager: ThemeManager
var editorContainer: MdxEditorWebviewContainer
init(
editingNote: Binding<NoteModel?>, editorContainer: MdxEditorWebviewContainer,
onNavigateToNote: ((NoteModel) -> Void)?,
fullScreenCover: Binding<MainFullScreenCover?>
) {
self._editingNote = editingNote
self._fullScreenCover = fullScreenCover
self.editorContainer = editorContainer
if onNavigateToNote != nil {
self.onNavigateToNote = onNavigateToNote!
}
}
var physique: some View {
if let editingNoteBinding = Binding($editingNote) {
NavigationStack {
MdxEditorWebview(
url:
Bundle.foremost.url(
forResource: "index",
withExtension: "html",
subdirectory: "splitview_mdx_editor"
)!,
theme: $theme,
editorThemeDark: $editorThemeDark,
editorThemeLight: $editorThemeLight,
editingNote: editingNoteBinding,
editorKeymap: $editorKeymap,
container: editorContainer,
onNavigateToNote: onNavigateToNote,
fullScreenCover: $fullScreenCover
)
.body(maxWidth: .infinity, maxHeight: .infinity)
// .body(width: geo.measurement.width, peak: geo.measurement.peak, alignment: .topLeading)
// TODO: Take away this. That is only for straightforward growth.
.onAppear {
if let parsedMdx = editingNote?.markdown
.preParsedBody
{
editorContainer.setParsedEditorContentString(
content material: parsedMdx
)
}
UIScrollView.look().bounces = false
UIScrollView.look().isScrollEnabled =
false
}
.onDisappear {
UIScrollView.look().bounces = true
UIScrollView.look().isScrollEnabled = true
}
}
} else {
if hasPreviouslyLaunched {
SelectNoteToContinueView()
} else {
ProgressView()
.progressViewStyle(.round)
.scaleEffect(1.5)
.tint(themeManager.theme.main)
}
}
}
}
And the MdxEditorWebview:
@MainActor
public struct MdxEditorWebviewInternal: UIViewRepresentable {
@State personal var webView: WKWebView = WKWebView(
body: .zero,
configuration: getConfig()
)
@State personal var didSetInitialContent = false
@State var haveSetInitialContent: Bool = false
@Atmosphere(.openURL) var openURL
@Atmosphere(.modelContext) var modelContext
@Atmosphere(.colorScheme) var colorScheme
@Atmosphere(.createDataHandler) var dataHandler
@AppStorage(AppStorageKeys.webviewFontSize.rawValue) personal
var webviewFontSize: WebviewFontSize = .base
let url: URL
@Binding var present: Bool
@Binding var theme: WebViewTheme
@Binding var editorThemeDark: CodeSyntaxTheme
@Binding var editorThemeLight: CodeSyntaxTheme
@Binding var editingNote: NoteModel
@Binding var editorKeymap: EditorKeymap
@Binding var fullScreenCover: MainFullScreenCover?
var onNavigateToNote: (NoteModel) -> Void
let container: MdxEditorWebviewContainer
public init(
url: URL,
theme: Binding<WebViewTheme>,
editorThemeDark: Binding<CodeSyntaxTheme>,
editorThemeLight: Binding<CodeSyntaxTheme>,
editingNote: Binding<NoteModel>,
editorKeymap: Binding<EditorKeymap>,
container: MdxEditorWebviewContainer,
present: Binding<Bool>,
onNavigateToNote: @escaping (NoteModel) -> Void,
fullScreenCover: Binding<MainFullScreenCover?>
) {
self.url = url
self._theme = theme
self._editorThemeDark = editorThemeDark
self._editorThemeLight = editorThemeLight
self._editingNote = editingNote
self._editorKeymap = editorKeymap
self._fullScreenCover = fullScreenCover
self.container = container
self._show = present
self.onNavigateToNote = onNavigateToNote
}
public func makeUIView(context: Context) -> WKWebView {
let webView = container.webView
webView.isHidden = true
webView.navigationDelegate = context.coordinator
let editorContentControllers = [
SplitviewEditorWebviewActions.setWebviewLoaded.rawValue,
SplitviewEditorWebviewActions.onEditorChange.rawValue,
SplitviewEditorWebviewActions.requestSplitviewEditorData.rawValue,
SplitviewEditorWebviewActions.requestParsedMdxContent.rawValue,
SplitviewEditorWebviewActions.onTagClick.rawValue,
MdxPreviewWebviewActions.viewNoteByUserDefinedId.rawValue,
MdxPreviewWebviewActions.requestNoteData.rawValue
]
if colorScheme == .darkish {
webView.evaluateJavaScript(
"""
doc.physique.classList.add("darkish"); null;
"""
)
}
for controllerName in editorContentControllers {
addUserContentController(
controller: webView.configuration.userContentController,
coordinator: context.coordinator,
title: controllerName
)
}
// Loading the web page solely as soon as
webView.loadFileURL(url, allowingReadAccessTo: url)
if colorScheme == .darkish {
webView.evaluateJavaScript(
"""
doc.physique.classList.add("darkish"); null;
"""
)
}
return webView
}
public func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.isHidden = !present
// uiView.scrollView.contentInset = .zero
// uiView.scrollView.scrollIndicatorInsets = .zero
}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public func setInitialProperties() {
container.setInitialProperties(
editingNote: editingNote,
codeEditorTheme: colorScheme == .darkish
? editorThemeDark : editorThemeLight,
editorKeymap: editorKeymap,
theme: theme,
fontSize: webviewFontSize,
editorThemeDark: editorThemeDark,
editorThemeLight: editorThemeLight,
darkMode: colorScheme == .darkish,
modelContext: modelContext
)
}
public func setInitialContent() {
let s = editingNote.markdown.physique.toQuotedJavascriptString() ?? "''"
container.runJavascript(
"""
window.localStorage.setItem("(SplitviewEditorWebviewLocalStorageKeys.initialValue.rawValue)", (s))
window.setEditorContent((s))
"""
)
}
public func handleViewNoteByUserDefinedId(id: String) {
print("Right here...")
let fetchDescriptor = FetchDescriptor<NoteModel>(
predicate: #Predicate { observe in
observe.frontMatter.userDefinedId == id
})
if let notes = attempt? self.modelContext.fetch(fetchDescriptor) {
if !notes.isEmpty {
let observe = notes.first
self.editingNote = observe!
self.onNavigateToNote(observe!)
}
}
}
public func handleTagClick(tagBody: String) {
let fetchDescriptor = FetchDescriptor<TagModel>(
predicate: #Predicate<TagModel> { t in
t.worth == tagBody
})
if let tags = attempt? modelContext.fetch(fetchDescriptor) {
if !tags.isEmpty {
fullScreenCover = .tagSearch(tag: tags.first!)
}
}
}
}
public struct MdxEditorWebview: View {
@State personal var present: Bool = false
@State personal var showEditNoteTaggables: Bool = false
@Atmosphere(ThemeManager.self) personal var themeManager: ThemeManager
let url: URL
@Binding var theme: WebViewTheme
@Binding var editorThemeDark: CodeSyntaxTheme
@Binding var editorThemeLight: CodeSyntaxTheme
@Binding var editingNote: NoteModel
@Binding var editorKeymap: EditorKeymap
@Binding var fullScreenCover: MainFullScreenCover?
var onNavigateToNote: (NoteModel) -> Void
let container: MdxEditorWebviewContainer
public init(
url: URL,
theme: Binding<WebViewTheme>,
editorThemeDark: Binding<CodeSyntaxTheme>,
editorThemeLight: Binding<CodeSyntaxTheme>,
editingNote: Binding<NoteModel>,
editorKeymap: Binding<EditorKeymap>,
container: MdxEditorWebviewContainer,
onNavigateToNote: @escaping (NoteModel) -> Void,
fullScreenCover: Binding<MainFullScreenCover?>?
) {
self.url = url
self._theme = theme
self._editorThemeDark = editorThemeDark
self._editorThemeLight = editorThemeLight
self._editingNote = editingNote
self._editorKeymap = editorKeymap
self.container = container
self.onNavigateToNote = onNavigateToNote
if let fs = fullScreenCover {
self._fullScreenCover = fs
} else {
self._fullScreenCover = .fixed(nil)
}
self.onNavigateToNote = onNavigateToNote
}
public var physique: some View {
ZStack(alignment: present ? .bottomTrailing : .middle) {
MdxEditorWebviewInternal(
url: url,
theme: $theme,
editorThemeDark: $editorThemeDark,
editorThemeLight: $editorThemeLight,
editingNote: $editingNote,
editorKeymap: $editorKeymap,
container: container,
present: $present,
onNavigateToNote: onNavigateToNote,
fullScreenCover: $fullScreenCover,
)
.disableAnimations()
.body(
alignment: .backside
)
.scrollDisabled(true)
if !present {
ProgressView()
.progressViewStyle(.round)
.scaleEffect(1.5)
.tint(themeManager.theme.main)
} else {
FloatingButtonView(
buttons: [
FloatingButtonItem(
id: "addTaggable",
systemImage: "tag.fill",
action: {
withAnimation {
showEditNoteTaggables.toggle()
}
}
),
FloatingButtonItem(
id: "toggleBookmarked",
systemImage: editingNote.bookmarked ? "bookmark.fill" : "bookmark",
action: {
editingNote.bookmarked.toggle()
}
)
]
)
.padding()
}
}
.fullScreenCover(
isPresented: $showEditNoteTaggables,
content material: {
EditNoteTaggablesView(
editingNote: $editingNote,
open: $showEditNoteTaggables
)
},
)
}
func onLoad() async {
}
}
#endif
Thanks prematurely for any options. I’ve solely been working with Swift for a pair months so there’s nonetheless so much for me to study.


