Commit 25650a7
authored
Scale info data table text with Dynamic Type (#667)
* Remove Main.storyboard and migrate to SwiftUI app lifecycle
Replace UIKit storyboard/SceneDelegate architecture with SwiftUI App
entry point (LoopFollowApp.swift) and TabView (MainTabView.swift).
Convert MoreMenuViewController to SwiftUI (MoreMenuView.swift). Add
SwiftUI wrappers for Remote and Nightscout tabs. Remove 6 obsolete
UIKit wrapper view controllers and ~300 lines of tab management code
from MainViewController.
* Migrate info table from UITableView to SwiftUI List
Replace UITableView with SwiftUI InfoTableView hosted in
MainViewController. Make InfoManager an ObservableObject so data
updates trigger SwiftUI rebuilds automatically. Remove
UITableViewDataSource conformance and table delegate methods.
No changes needed to the 10 Nightscout controller files that
populate the table data.
* Migrate statistics and pie chart from UIKit to SwiftUI
Replace 7 UILabel properties and DGCharts PieChartView with a
StatsDisplayModel ObservableObject and hosted StatsDisplayView.
The pie chart uses a UIViewRepresentable wrapper for DGCharts
since the Charts pod name shadows Swift Charts. Remove ~60 lines
of UIKit stack layout code from MainViewController setupUI().
* Migrate BG display area from UIKit labels to SwiftUI
Replace BGText, DirectionText, DeltaText, MinAgoText, serverText,
LoopStatusLabel, and PredictionLabel with a SwiftUI BGDisplayView.
Add pull-to-refresh via .refreshable modifier. Move loop status and
prediction text updates to Observable values across DeviceStatus,
DeviceStatusLoop, DeviceStatusOpenAPS, and BGData. Remove
UIScrollView overlay and UIScrollViewDelegate conformance.
* Migrate main layout to SwiftUI with UIKit charts embedded
Replace UIStackView layout with MainHomeView SwiftUI view that composes
BGDisplayView, InfoTableView, LineChartWrapper (UIViewRepresentable for
DGCharts), and StatsDisplayView. MainViewController now hosts a single
UIHostingController instead of managing individual UIView containers.
Visibility of info table, small graph, and stats is now reactive via
Storage observables in SwiftUI, removing several Combine subscriptions.
BG text uses lineLimit + minimumScaleFactor instead of manual font sizing.
* Clean up migration artifacts and fix post-migration bugs
- Fix AVSpeechSynthesizer temporary in AppDelegate that would be
deallocated before speech completes; use stored property instead
- Fix appMovedToBackground tab switching to use Observable instead of
dead UIKit tabBarController reference
- Remove dead code: rebuildTabsIfNeeded(), updateNightscoutTabState(),
traitCollectionDidChange notification relay, UIViewExtension.addBorder
- Remove unused imports (Charts, UIKit, Combine) from migrated files
- Remove unused synthesizer from LoopFollowApp
- Remove redundant .appearanceDidChange subscription from NightscoutVC
- Add missing super calls in viewWillAppear/viewDidAppear
* Replace view hierarchy walking with MainViewController.shared
The getMainViewController() methods in TreatmentsView, SettingsMenuView,
and BackgroundRefreshManager tried to find MainViewController by casting
rootViewController as UITabBarController, which always fails with the
SwiftUI lifecycle. Add a weak static shared reference set during
viewDidLoad and use it everywhere instead.
* Fix MainViewController.shared references for stats and treatments
Pass MainViewController.shared instead of nil when creating
AggregatedStatsContentView in MainTabView and MoreMenuView. Replace
view-hierarchy-walking getMainViewController() in TreatmentsViewModel
with MainViewController.shared.
* Fix info table font size to match storyboard
The storyboard used system 17pt for both title and detail labels.
The SwiftUI migration used .subheadline (~15pt) making text smaller.
* Fix Share Logs sheet rendering blank
Present UIActivityViewController via UIApplication.topMost instead of
wrapping it in a SwiftUI .sheet, which rendered an empty view.
* Fix back navigation from Settings sub-pages
SettingsMenuView declared its own NavigationStack(path:) while already
being pushed onto the outer NavigationStack from MainTabView, so sub-page
back buttons popped the outer stack and jumped past Settings to Menu.
Drop the nested NavigationStack and route Settings entries through the
ambient stack: a single SettingsRoute enum drives a navigationDestination
attached at the MoreMenuView root. The Settings entry itself becomes a
NavigationLink(value:) so it doesn't compete with a navigationDestination
(isPresented:) modifier, which was re-asserting Settings as the top of
stack whenever a sub-page was pushed.
* Harden post-storyboard migration
* MainViewController is now a strong static singleton bootstrapped from
LoopFollowApp.init(). Lifecycle work in viewDidLoad (Combine sinks,
observers, scheduleAllTasks, migrations) runs at launch regardless of
whether the Home tab is rendered, and HomeContentView reuses the
singleton instead of instantiating a fresh VC each time.
* MoreMenuView's eight .navigationDestination(isPresented:) modifiers are
collapsed to a single MenuRoute enum routed through one
.navigationDestination(for:), preventing the same destination-slot
contention that previously caused Settings → Graph back navigation to
jump past Settings.
* MainTabView observes Storage.shared.appearanceMode so theme changes
propagate; the orphaned .appearanceDidChange notification name is
removed.
* OPEN_APP_ACTION notification taps now dismiss any presented modal
before switching to Home, matching prior SceneDelegate behavior.
* Drop the unused Core Data stack (NSPersistentCloudKitContainer,
saveContext) from AppDelegate, the dead AppDelegate.window property,
and the legacy UIRequiredDeviceCapabilities=armv7 /
UIStatusBarTintParameters keys from Info.plist. Switch
AlarmSound's keyWindow access to the connected-scenes API and
generalize UIApplication.topMost likewise so it works on Mac
Catalyst.
* Strip redundant inner NavigationView wrappers from settings sub-views
pushed onto the outer NavigationStack: Graph, General, Advanced,
Calendar, Contact, Dexcom, Nightscout, BackgroundRefresh,
InfoDisplay, ImportExport. Drop unused onBack parameters from
AlarmsContainerView and SettingsMenuView, the unused
isPresentedAsModal flag from MainViewController, and the leftover
debug print in ObservableValue.set.
* LineChartWrapper.updateUIView now flushes the chart on SwiftUI
re-render. MainViewController.deinit removes all observers, not
just the custom "refresh" one. MoreMenuView caches the app version
in @State instead of constructing AppVersionManager on every body
re-render. HomeModalView uses NavigationStack (not deprecated
NavigationView).
* MoreMenuView: render tab-switch buttons in primary color
Buttons in a List inherit the accent tint, so the Features rows that switch tabs appeared blue while the NavigationLink rows that push appeared white. Use .buttonStyle(.plain) to suppress the tint and drop the now-redundant .foregroundStyle(.primary) calls.
* Revert MainViewController singleton bootstrap
Constructing MainViewController.shared from LoopFollowApp.init() — and
reusing the same VC across HomeContentView re-creations — caused tapping
the BG chart to crash with `-[__NSArrayM insertObject:atIndex:]: object
cannot be nil`. Bisected to the singleton+bootstrap piece of the
post-storyboard hardening; the rest of that commit (programmatic UI,
MoreMenuView routing, NavigationView strip-out) is retained.
Restore the prior behavior: shared is a weak static set in viewDidLoad,
HomeContentView constructs a fresh MainViewController each time, and the
LoopFollowApp.init() bootstrap is removed.
Known follow-up: lifecycle work in viewDidLoad (Combine sinks,
scheduleAllTasks, migrations) again only runs when the Home view is
first rendered, so a user who has moved Home off the tab bar gets
degraded behavior until they navigate to it.
* MoreMenuView: make tab-switch rows full-row tappable
Wrap Button labels in an HStack with a trailing Spacer and contentShape
so the entire row is tappable, matching the hit area of NavigationLink
rows. Extract the pattern into a small FullRowButton helper, used for
both tab-switch rows and Share Logs.
* Align units-selection conflict resolution with integration branch
Move the Diagnostics section out of Section("Speak BG") (was nested at
the wrong indent), match StatsDisplayModel field order, and add the
spacing line in updateStats.
* MoreMenuView: fix cross-row tap routing in Features section
Mixing Button and NavigationLink rows in the same List ForEach caused
taps on a NavigationLink row to fire a sibling Button row's action —
e.g. tapping Alarms with Stats in the tab bar would switch to the Stats
tab instead of pushing the alarms detail.
Make every row in the menu's List a uniform FullRowButton and drive
pushes from state via .navigationDestination(isPresented:). Add an
opt-in chevron to FullRowButton so navigating rows render the standard
disclosure indicator.
* MoreMenuView: keep Settings as a value-based NavigationLink
Mixing .navigationDestination(isPresented:) with
.navigationDestination(for:) on the same view shadowed the value-based
SettingsRoute registration once SettingsMenuView was on the stack, so
sub-rows like Units and Metrics couldn't push.
Settings sits alone in its section, so it doesn't need the uniform-Button
treatment used in Features and Logging — restore it to a NavigationLink
and route it through the existing .navigationDestination(for:) channel.
* Fix navigation between alarms and menu
* Drive Before-First-Unlock recovery from AppDelegate
Move BFU recovery (Storage.reloadAll) out of MainViewController and into
AppDelegate so it runs even when the home tab's UIHostingController has
not yet materialized — necessary under the SwiftUI App lifecycle, where a
BG-only launch (BGAppRefreshTask, BLE wake, prewarming) may complete and
the device may unlock without MainViewController ever being created.
AppDelegate observes protectedDataDidBecomeAvailable (authoritative signal)
and willEnterForeground (fallback), with a race-guard re-check immediately
after observer registration. Recovery is idempotent via needsBFUReload.
MainViewController now reacts to a new .bfuReloadCompleted notification by
showing the loading overlay and rescheduling tasks; if it is not alive when
the notification fires, its viewDidLoad will later see the already-reloaded
Storage values and schedule tasks correctly on first load.
* Keep MainViewController alive regardless of tab layout
Make MainViewController.shared a strong, long-lived singleton created
once via bootstrap() on first foreground, so the data pipeline, alarms
and background audio run even when Home is moved into the Menu rather
than a tab. Home views reuse the single instance instead of creating
new ones, so the singleton is never displaced.
Defer the one-shot BG graph zoom until the chart has a real frame and
re-render the graph on every appearance, so the curve stays visible
when Home is reached from the Menu or moved between tab bar and Menu.
Restore the one-time telemetry consent prompt that was lost when
SceneDelegate was removed, presenting it from MainTabView on first
appearance for undecided installs.
* Scale info data table text with Dynamic Type
Drive the info table font size and row height from @ScaledMetric so the
top-right info data grows and shrinks with the iPhone's text-size setting,
keeping the existing 17pt/21pt look at the default size. Cap the scaling at
accessibility1 so the compact top strip stays within its layout.
* Use two rows if needed
* Info table adjustments1 parent 0841b1b commit 25650a7
3 files changed
Lines changed: 30 additions & 9 deletions
File tree
- LoopFollow
- Controllers/Nightscout
- InfoTable
- ViewControllers
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
201 | 201 | | |
202 | 202 | | |
203 | 203 | | |
204 | | - | |
| 204 | + | |
205 | 205 | | |
206 | 206 | | |
207 | 207 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
10 | 13 | | |
11 | 14 | | |
12 | 15 | | |
| |||
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
20 | | - | |
| 23 | + | |
21 | 24 | | |
22 | 25 | | |
23 | 26 | | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
29 | 47 | | |
30 | | - | |
31 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
32 | 52 | | |
33 | 53 | | |
34 | 54 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
| |||
0 commit comments