add SearchablePickerView

This commit is contained in:
PangMo5 2021-06-25 18:49:54 +09:00
parent f308219c34
commit c27ebe5ad2
4 changed files with 114 additions and 27 deletions

View File

@ -108,6 +108,8 @@
621C638026672A30004216EA /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 621C637F26672A30004216EA /* NukeUI */; };
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; };
6228B1C22670EB010067FD35 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBFD263B596B003A4E83 /* PersistenceController.swift */; };
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
624C21762685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
625CB5682678B6FB00530A6E /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5672678B6FB00530A6E /* SplashView.swift */; };
625CB56A2678B71200530A6E /* SplashViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5692678B71200530A6E /* SplashViewModel.swift */; };
625CB56C2678C0FD00530A6E /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB56B2678C0FD00530A6E /* MainTabView.swift */; };
@ -289,6 +291,7 @@
621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
621338B22660A07800A81A2A /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallaxHeader.swift; sourceTree = "<group>"; };
624C21742685CF60007F1390 /* SearchablePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchablePickerView.swift; sourceTree = "<group>"; };
625CB5672678B6FB00530A6E /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
625CB5692678B71200530A6E /* SplashViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashViewModel.swift; sourceTree = "<group>"; };
625CB56B2678C0FD00530A6E /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
@ -599,6 +602,7 @@
6267B3D92671138200A7371D /* ImageExtensions.swift */,
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */,
62CB3F4A2685BB77003D0A6F /* DefaultsExtension.swift */,
624C21742685CF60007F1390 /* SearchablePickerView.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -952,6 +956,7 @@
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
5310695B2684E7EE00CFFDBA /* AudioView.swift in Sources */,
624C21762685CF60007F1390 /* SearchablePickerView.swift in Sources */,
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */,
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
@ -1021,6 +1026,7 @@
621338B32660A07800A81A2A /* LazyView.swift in Sources */,
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */,
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */,
62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */,
62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */,

View File

@ -50,22 +50,22 @@ struct SettingsView: View {
Section(header: Text("Accessibility")) {
Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles)
Picker("Subtitles Language preferences", selection: $autoSelectSubtitlesLangcode) {
Text("Auto")
.tag("Auto")
ForEach(viewModel.isoLanguageCodesPair, id: \.1) {
Text($0.0)
.tag($0.1)
}
}
Picker("Audio Language preferences", selection: $autoSelectAudioLangcode) {
Text("Auto")
.tag("Auto")
ForEach(viewModel.isoLanguageCodesPair, id: \.1) {
Text($0.0)
.tag($0.1)
}
}
SearchablePicker(label: "Subtitles Language preferences",
options: viewModel.langs,
optionToString: { $0.name },
selected:Binding<Lang>(
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectSubtitlesLangcode }) ?? .auto },
set: {autoSelectSubtitlesLangcode = $0.isoCode}
)
)
SearchablePicker(label: "Audio Language preferences",
options: viewModel.langs,
optionToString: { $0.name },
selected: Binding<Lang>(
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectAudioLangcode }) ?? .auto },
set: { autoSelectAudioLangcode = $0.isoCode}
)
)
}
Section {

View File

@ -0,0 +1,73 @@
//
/*
* SwiftFin is subject to the terms of the Mozilla Public
* License, v2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
*/
import Foundation
import SwiftUI
private struct SearchablePickerView<Selectable: Hashable>: View {
@Environment(\.presentationMode)
var presentationMode
let options: [Selectable]
let optionToString: (Selectable) -> String
let label: String
@State var text = ""
@Binding var selected: Selectable
var body: some View {
VStack {
SearchBar(text: $text)
List(options.filter {
guard !text.isEmpty else { return true }
return optionToString($0).lowercased().contains(text.lowercased())
}, id: \.self) { selectable in
Button(action: {
selected = selectable
presentationMode.wrappedValue.dismiss()
}) {
HStack {
Text(optionToString(selectable)).foregroundColor(Color.primary)
Spacer()
if selected == selectable {
Image(systemName: "checkmark").foregroundColor(.accentColor)
}
}
}
}.listStyle(GroupedListStyle())
}
}
}
struct SearchablePicker<Selectable: Hashable>: View {
let label: String
let options: [Selectable]
let optionToString: (Selectable) -> String
@Binding var selected: Selectable
var body: some View {
NavigationLink(destination: searchablePickerView()) {
HStack {
Text(label)
Spacer()
Text(optionToString(selected))
.foregroundColor(.gray)
.multilineTextAlignment(.trailing)
}
}
}
private func searchablePickerView() -> some View {
SearchablePickerView(options: options,
optionToString: optionToString,
label: label,
selected: $selected)
}
}

View File

@ -1,11 +1,11 @@
//
/*
* SwiftFin is subject to the terms of the Mozilla Public
* License, v2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
*/
/*
* SwiftFin is subject to the terms of the Mozilla Public
* License, v2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
*/
import Foundation
@ -23,10 +23,17 @@ struct Bitrates: Codable, Hashable {
public var value: Int
}
struct Lang: Hashable {
var name: String
var isoCode: String
static let auto = Lang(name: "Auto", isoCode: "Auto")
}
final class SettingsViewModel: ObservableObject {
let currentLocale = Locale.current
var bitrates: [Bitrates] = []
var isoLanguageCodesPair = [(name: String, isoCode: String)]()
var langs = [Lang]()
init() {
let url = Bundle.main.url(forResource: "bitrates", withExtension: "json")!
@ -41,10 +48,11 @@ final class SettingsViewModel: ObservableObject {
} catch {
print(error)
}
isoLanguageCodesPair = Locale.isoLanguageCodes.compactMap {
self.langs = Locale.isoLanguageCodes.compactMap {
guard let name = currentLocale.localizedString(forLanguageCode: $0) else { return nil }
return (name, $0)
return Lang(name: name, isoCode: $0)
}.sorted(by: { $0.name < $1.name })
self.langs.insert(.auto, at: 0)
}
}