Layout changes to support liquid glass and increase reading area (#465)

* UI improvements

* Revert accidental change

* Tidying up
This commit is contained in:
Ben Hughes
2026-03-26 20:35:10 +11:00
committed by GitHub
parent dae58882c2
commit 474f652674
6 changed files with 121 additions and 74 deletions
+6
View File
@@ -1,6 +1,7 @@
import CoreData
import CoreSpotlight
import Foundation
import HTMLEntities
import SharedLib
// import MobileCoreServices
@@ -80,4 +81,9 @@ extension Entry {
}
return Double(screenPosition)
}
var titleHtml: String {
let escapedTitle = (title ?? "").htmlEscape()
return "<h1>\(escapedTitle)</h1>"
}
}
+19 -11
View File
@@ -14,17 +14,23 @@ struct EntriesView: View {
@State private var showPaywallWallabagPlus = false
var body: some View {
VStack {
#if os(iOS)
PasteBoardView()
#endif
SearchView(searchViewModel: searchViewModel)
EntriesListView(
predicate: searchViewModel.predicate,
entriesSortedById: entriesSortedById,
entriesSortedByReadingTime: entriesSortedByReadingTime,
entriesSortedByAscending: entriesSortedByAscending
)
EntriesListView(
predicate: searchViewModel.predicate,
entriesSortedById: entriesSortedById,
entriesSortedByReadingTime: entriesSortedByReadingTime,
entriesSortedByAscending: entriesSortedByAscending
)
.scrollContentBackground(.hidden)
.safeAreaInset(edge: .top) {
VStack(spacing: 0) {
#if os(iOS)
PasteBoardView()
#endif
SearchView(searchViewModel: searchViewModel)
.padding(.bottom, 8)
Divider()
}
.background(.ultraThinMaterial)
}
.onChange(of: entriesSortedById) { _, newValue in
if newValue {
@@ -94,6 +100,8 @@ struct EntriesView: View {
PaywallView(displayCloseButton: true)
}
.navigationTitle("Entries")
.navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea(.all, edges: .bottom)
}
}
+11 -12
View File
@@ -23,18 +23,14 @@ struct EntryView: View {
#endif
var body: some View {
VStack(alignment: .leading) {
Text(entry.title?.htmlUnescape() ?? "Entry")
.font(.title)
.fontWeight(.black)
.lineLimit(2)
.padding(.horizontal)
ProgressView(value: min(progress, 1), total: 1)
WebView(entry: entry, progress: $progress)
}
.addSwipeToBack {
dismiss()
}
WebView(entry: entry, progress: $progress)
.ignoresSafeArea()
.safeAreaInset(edge: .top) {
ProgressView(value: max(0, min(progress, 1)), total: 1)
}
.addSwipeToBack {
dismiss()
}
.toolbar {
ToolbarItem(placement: toolbarPlacement) {
Menu(content: {
@@ -79,6 +75,9 @@ struct EntryView: View {
TagListFor(entry: entry)
.presentationDetents([.medium, .large])
}
.toolbarBackground(.ultraThinMaterial, for: .bottomBar)
.toolbarBackground(.visible, for: .bottomBar)
.ignoresSafeArea(.all, edges: .bottom)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
+43 -22
View File
@@ -6,7 +6,6 @@ import WebKit
#if os(iOS)
struct WebView: UIViewRepresentable {
var entry: Entry
private(set) var wkWebView = WKWebView(frame: .zero)
@EnvironmentObject var appSetting: AppSetting
@Binding var progress: Double
@@ -26,19 +25,27 @@ import WebKit
super.init()
}
func webViewToLastPosition() {
webView.wkWebView.scrollView.setContentOffset(
CGPoint(
x: 0.0,
y: webView.entry.screenPositionForWebView
),
animated: true
)
func webViewToLastPosition(in webView: WKWebView) {
let position = self.webView.entry.screenPositionForWebView
if position > 0 {
// Check if content is actually loaded by verifying contentSize
if webView.scrollView.contentSize.height > webView.bounds.height {
webView.scrollView.setContentOffset(
CGPoint(x: 0.0, y: position),
animated: true
)
} else {
// Content not ready yet, try again after a minimal delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.webViewToLastPosition(in: webView)
}
}
}
}
func webView(_ webView: WKWebView, didFinish _: WKNavigation!) {
webViewToLastPosition()
webView.fontSizePercent(appSetting.webFontSizePercent)
self.webViewToLastPosition(in: webView)
}
func webView(_: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
@@ -67,7 +74,12 @@ import WebKit
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
webView.progress = scrollView.contentOffset.y / (scrollView.contentSize.height - scrollView.bounds.height)
let scrollableHeight = scrollView.contentSize.height - scrollView.bounds.height
if scrollableHeight > 0 {
webView.progress = scrollView.contentOffset.y / scrollableHeight
} else {
webView.progress = 0
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
@@ -79,11 +91,17 @@ import WebKit
}
func makeUIView(context: Context) -> WKWebView {
wkWebView.navigationDelegate = context.coordinator
wkWebView.scrollView.delegate = context.coordinator
wkWebView.load(content: entry.content, justify: UserDefaults.standard.bool(forKey: "justifyArticle"))
let webView = WKWebView(frame: .zero)
webView.navigationDelegate = context.coordinator
webView.scrollView.delegate = context.coordinator
webView.isOpaque = false
webView.backgroundColor = .clear
webView.scrollView.backgroundColor = .clear
webView.scrollView.contentInsetAdjustmentBehavior = .always
webView.load(content: entry.titleHtml + (entry.content ?? ""), justify: UserDefaults.standard.bool(forKey: "justifyArticle"))
return wkWebView
return webView
}
func updateUIView(_ webView: WKWebView, context _: Context) {
@@ -95,17 +113,18 @@ import WebKit
#if os(macOS)
struct WebView: NSViewRepresentable {
var entry: Entry
private(set) var wkWebView = WKWebView(frame: .zero)
@EnvironmentObject var appSetting: AppSetting
@Binding var progress: Double
func makeNSView(context _: Context) -> WKWebView {
wkWebView.load(content: entry.content, justify: false)
func makeNSView(context: Context) -> WKWebView {
let webView = WKWebView(frame: .zero)
webView.navigationDelegate = context.coordinator
webView.load(content: entry.titleHtml + (entry.content ?? ""), justify: false)
return wkWebView
return webView
}
func updateNSView(_ nsView: WKWebView, context _: Context) {
nsView.load(content: entry.content, justify: false)
nsView.fontSizePercent(appSetting.webFontSizePercent)
}
@@ -186,10 +205,12 @@ struct WebView_Previews: PreviewProvider {
Group {
WebView(
entry: entry, progress: .constant(0.5)
).colorScheme(.light)
).environmentObject(AppSetting())
.colorScheme(.light)
WebView(
entry: entry, progress: .constant(0.5)
).colorScheme(.dark)
).environmentObject(AppSetting())
.colorScheme(.dark)
}
}
}
+3 -7
View File
@@ -4,20 +4,15 @@ struct RefreshButton: View {
@Environment(AppSync.self) var appSync: AppSync
var body: some View {
HStack {
ZStack {
if appSync.inProgress {
ProgressView(value: appSync.progress, total: 100)
#if os(iOS)
.progressViewStyle(.linear)
#else
ProgressView()
.progressViewStyle(.circular)
#endif
} else {
Button(
action: appSync.requestSync,
label: {
Image(systemName: "arrow.counterclockwise")
.frame(width: 34, height: 34, alignment: .center)
}
)
.buttonStyle(.plain)
@@ -26,6 +21,7 @@ struct RefreshButton: View {
.keyboardShortcut("r", modifiers: .command)
}
}
.frame(width: 34, height: 34, alignment: .center)
}
}
+39 -22
View File
@@ -14,7 +14,8 @@ html {
min-height: 100%;
}
html, body {
html,
body {
overscroll-behavior: none;
}
@@ -31,16 +32,20 @@ a {
font-weight: bold;
}
h2, h3, h4 {
h2,
h3,
h4 {
font-family: 'PT Sans', sans-serif;
text-transform: uppercase;
}
p, li {
p,
li {
color: #666;
}
a:hover, a:focus {
a:hover,
a:focus {
text-decoration: none;
}
@@ -58,11 +63,11 @@ h2:after {
========================================================================== */
#main {
margin-left: 13em;
position: relative;
z-index: 10;
padding-right: 5%;
padding-bottom: 1em;
margin-left: 13em;
position: relative;
z-index: 10;
padding-right: 5%;
padding-bottom: 1em;
}
#content {
@@ -86,7 +91,9 @@ blockquote {
margin: 0;
}
#article h2, #article h3, #article h4 {
#article h2,
#article h3,
#article h4 {
text-transform: none;
}
@@ -97,7 +104,7 @@ blockquote {
@media screen {
body > header {
body>header {
background: #333;
position: fixed;
top: 0;
@@ -117,35 +124,45 @@ blockquote {
}
#article h1 {
font-size: 1.2em;
font-size: 2.2em;
font-weight: 900;
line-height: 1.1;
margin-top: 1em;
margin-bottom: 0.8em;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
video {
width: 100%
width: 100%
}
}
@media (prefers-color-scheme: dark) {
body {
color: #FFF;
background: #000;
color: #FFF;
background: #000;
}
::selection {
color: #777;
background: #000;
color: #777;
background: #000;
}
p, li {
color: #FFF;
p,
li {
color: #FFF;
}
a {
color: #6CA0DC;
color: #6CA0DC;
}
blockquote {
background: #111;
background: #111;
}
}
@@ -157,4 +174,4 @@ blockquote {
a {
color: #000;
}
}
}