Fix simulator crash & add conditional support for iOS 26+ native TabView (#209)

* add ability to use native tabbar on ios 26 or later

* add tabview lcalization

* fix library / home tab label and localization

* add missing localization & german translation
This commit is contained in:
undeaD_D 2025-06-23 21:03:37 +02:00 committed by GitHub
parent 82eec0688f
commit 78df4df73b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 121 additions and 87 deletions

View file

@ -16,37 +16,45 @@ struct ContentView_Previews: PreviewProvider {
}
struct ContentView: View {
@AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false
@StateObject private var tabBarController = TabBarController()
@State var selectedTab: Int = 0
@State var lastTab: Int = 0
@State private var searchQuery: String = ""
let tabs: [TabItem] = [
TabItem(icon: "square.stack", title: ""),
TabItem(icon: "arrow.down.circle", title: ""),
TabItem(icon: "gearshape", title: ""),
TabItem(icon: "magnifyingglass", title: "")
TabItem(icon: "square.stack", title: NSLocalizedString("LibraryTab", comment: "")),
TabItem(icon: "arrow.down.circle", title: NSLocalizedString("DownloadsTab", comment: "")),
TabItem(icon: "gearshape", title: NSLocalizedString("SettingsTab", comment: "")),
TabItem(icon: "magnifyingglass", title: NSLocalizedString("SearchTab", comment: ""))
]
var body: some View {
ZStack(alignment: .bottom) {
switch selectedTab {
case 0:
LibraryView()
.environmentObject(tabBarController)
case 1:
DownloadView()
.environmentObject(tabBarController)
case 2:
SettingsView()
.environmentObject(tabBarController)
case 3:
SearchView(searchQuery: $searchQuery)
.environmentObject(tabBarController)
default:
LibraryView()
.environmentObject(tabBarController)
private func tabView(for index: Int) -> some View {
switch index {
case 1: return AnyView(DownloadView())
case 2: return AnyView(SettingsView())
case 3: return AnyView(SearchView(searchQuery: $searchQuery))
default: return AnyView(LibraryView())
}
}
var body: some View {
if #available(iOS 26, *), useNativeTabBar == true {
TabView {
ForEach(Array(tabs.enumerated()), id: \.offset) { index, item in
Tab(item.title, systemImage: item.icon, role: index == 3 ? .search : nil) {
tabView(for: index)
}
}
}
.searchable(text: $searchQuery)
.environmentObject(tabBarController)
} else {
ZStack(alignment: .bottom) {
Group {
tabView(for: selectedTab)
}
.environmentObject(tabBarController)
TabBar(
tabs: tabs,
@ -61,3 +69,4 @@ struct ContentView: View {
.padding(.bottom, -20)
}
}
}

View file

@ -49,6 +49,9 @@
"Copied to Clipboard" = "In die Zwischenablage kopiert";
"Copy to Clipboard" = "In die Zwischenablage kopieren";
"Copy URL" = "URL kopieren";
"Collections" = "Sammlungen";
"No Collections" = "Keine Sammlungen vorhanden";
"Create a collection to organize your bookmarks" = "Erstelle eine Sammlung für mehr Organisation";
/* Episodes */
"%lld Episodes" = "%lld Folgen";
@ -84,7 +87,7 @@
"Download Episode" = "Folge herunterladen";
"Download Summary" = "Download-Übersicht";
"Download This Episode" = "Diese Folge herunterladen";
"Downloaded" = "Heruntergeladen";
"Downloaded" = "Geladen";
"Downloaded Shows" = "Heruntergeladene Serien";
"Downloading" = "Lädt herunter";
"Downloads" = "Downloads";
@ -115,6 +118,7 @@
"General events and activities." = "Allgemeine Aktivitäten.";
"General Preferences" = "Allgemeine Einstellungen";
"Hide Splash Screen" = "Startbildschirm ausblenden";
"Use Native Tab Bar" = "System Bar verwenden";
"HLS video downloading." = "HLS Video-Downloads.";
"Hold Speed" = "Geschwindigkeit halten";
@ -383,6 +387,12 @@
"Library cleared successfully" = "Bibliothek erfolgreich geleert";
"All downloads deleted successfully" = "Alle Downloads erfolgreich gelöscht";
/* TabView */
"LibraryTab" = "Bibliothek";
"DownloadsTab" = "Downloads";
"SettingsTab" = "Einstellungen";
"SearchTab" = "Suchen";
/* New additions */
"Recent searches" = "Letzte Suchanfragen";
"me frfr" = "Ich, ohne Witz";

View file

@ -49,6 +49,9 @@
"Copied to Clipboard" = "Copied to Clipboard";
"Copy to Clipboard" = "Copy to Clipboard";
"Copy URL" = "Copy URL";
"Collections" = "Collections";
"No Collections" = "No Collections";
"Create a collection to organize your bookmarks" = "Create a collection to organize your bookmarks";
/* Episodes */
"%lld Episodes" = "%lld Episodes";
@ -115,6 +118,7 @@
"General events and activities." = "General events and activities.";
"General Preferences" = "General Preferences";
"Hide Splash Screen" = "Hide Splash Screen";
"Use Native Tab Bar" = "Use Native Tabs";
"HLS video downloading." = "HLS video downloading.";
"Hold Speed" = "Hold Speed";
@ -384,6 +388,12 @@
"Library cleared successfully" = "Library cleared successfully";
"All downloads deleted successfully" = "All downloads deleted successfully";
/* TabView */
"LibraryTab" = "Library";
"DownloadsTab" = "Downloads";
"SettingsTab" = "Settings";
"SearchTab" = "Search";
/* New additions */
"Recent searches" = "Recent searches";
"me frfr" = "me frfr";

View file

@ -23,12 +23,17 @@ class DownloadManager: NSObject, ObservableObject {
}
private func initializeDownloadSession() {
#if targetEnvironment(simulator)
Logger.shared.log("Download Sessions are not available on Simulator", type: "Error")
#else
let configuration = URLSessionConfiguration.background(withIdentifier: "hls-downloader")
assetDownloadURLSession = AVAssetDownloadURLSession(
configuration: configuration,
assetDownloadDelegate: self,
delegateQueue: .main
)
#endif
}
func downloadAsset(from url: URL) {

View file

@ -43,6 +43,9 @@ extension JSController {
private static var progressUpdateTimer: Timer?
func initializeDownloadSession() {
#if targetEnvironment(simulator)
Logger.shared.log("Download Sessions are not available on Simulator", type: "Error")
#else
// Create a unique identifier for the background session
let sessionIdentifier = "hls-downloader-\(UUID().uuidString)"
@ -61,6 +64,8 @@ extension JSController {
)
print("Download session initialized with ID: \(sessionIdentifier)")
#endif
loadSavedAssets()
}

View file

@ -227,7 +227,6 @@ struct TabBar: View {
}
}
}) {
if tab.title.isEmpty {
Image(systemName: tab.icon + (selectedTab == index ? ".fill" : ""))
.frame(width: 28, height: 28)
.matchedGeometryEffect(id: tab.icon, in: animation)
@ -236,24 +235,6 @@ struct TabBar: View {
.padding(.horizontal, 10)
.frame(width: 70)
.opacity(selectedTab == index ? 1 : 0.5)
} else {
VStack {
Image(systemName: tab.icon + (selectedTab == index ? ".fill" : ""))
.frame(width: 36, height: 18)
.matchedGeometryEffect(id: tab.icon, in: animation)
.foregroundStyle(selectedTab == index ? .black : .gray)
Text(tab.title)
.font(.caption)
.frame(width: 60)
.lineLimit(1)
.truncationMode(.tail)
}
.padding(.vertical, 8)
.padding(.horizontal, 10)
.frame(width: 80)
.opacity(selectedTab == index ? 1 : 0.5)
}
}
.background(
selectedTab == index ?

View file

@ -154,6 +154,7 @@ struct SettingsViewGeneral: View {
@AppStorage("fetchEpisodeMetadata") private var fetchEpisodeMetadata: Bool = true
@AppStorage("analyticsEnabled") private var analyticsEnabled: Bool = false
@AppStorage("hideSplashScreen") private var hideSplashScreenEnable: Bool = false
@AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false
@AppStorage("metadataProvidersOrder") private var metadataProvidersOrderData: Data = {
try! JSONEncoder().encode(["TMDB","AniList"])
}()
@ -172,6 +173,10 @@ struct SettingsViewGeneral: View {
@EnvironmentObject var settings: Settings
@State private var showRestartAlert = false
private let isiOS26OrLater: Bool = {
if #available(iOS 26, *) { return true } else { return false }
}()
var body: some View {
ScrollView(showsIndicators: false) {
VStack(spacing: 24) {
@ -194,9 +199,18 @@ struct SettingsViewGeneral: View {
icon: "wand.and.rays.inverse",
title: NSLocalizedString("Hide Splash Screen", comment: ""),
isOn: $hideSplashScreenEnable,
showDivider: isiOS26OrLater
)
if isiOS26OrLater {
SettingsToggleRow(
icon: "inset.filled.bottomthird.rectangle",
title: NSLocalizedString("Use Native Tab Bar", comment: ""),
isOn: $useNativeTabBar,
showDivider: false
)
}
}
SettingsSection(title: NSLocalizedString("Language", comment: "")) {
SettingsPickerRow(