mirror of
https://github.com/jellyfin/Swiftfin.git
synced 2024-11-23 05:59:51 +00:00
[tvOS] Settings Cleanup (#1163)
* Settings Cleanup. Replace strings with labels. Enforce the same font. Ensure Forms don't get clipped by their boundries. Create consistent, reusable button sizing/coloring. Apply to all Settings Pages. * Remove custom Button/Form styling in exchange for just using .scrollClipDisabled() * Swap back to Jellyfin Purple from Purple. * Remove Check Button. Check all Section Inits where possible. Make Server Details Server non-focusable. Create a new menu for Server Details selection. This is a WIP awaiting feedback from https://github.com/jellyfin/Swiftfin/pull/1163#discussion_r1705957885 --------- Co-authored-by: Joseph Kribs <joseph@kribs.net>
This commit is contained in:
parent
0ae1324534
commit
e4fd98c244
@ -19,6 +19,7 @@ struct TextPairView: View {
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text(leading)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
|
@ -18,6 +18,8 @@ internal enum L10n {
|
||||
internal static let accentColorDescription = L10n.tr("Localizable", "accentColorDescription", fallback: "Some views may need an app restart to update.")
|
||||
/// Accessibility
|
||||
internal static let accessibility = L10n.tr("Localizable", "accessibility", fallback: "Accessibility")
|
||||
/// Add Server
|
||||
internal static let addServer = L10n.tr("Localizable", "addServer", fallback: "Add Server")
|
||||
/// Add URL
|
||||
internal static let addURL = L10n.tr("Localizable", "addURL", fallback: "Add URL")
|
||||
/// Advanced
|
||||
@ -30,6 +32,8 @@ internal enum L10n {
|
||||
internal static let allGenres = L10n.tr("Localizable", "allGenres", fallback: "All Genres")
|
||||
/// All Media
|
||||
internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media")
|
||||
/// All Servers
|
||||
internal static let allServers = L10n.tr("Localizable", "allServers", fallback: "All Servers")
|
||||
/// Appearance
|
||||
internal static let appearance = L10n.tr("Localizable", "appearance", fallback: "Appearance")
|
||||
/// App Icon
|
||||
@ -112,6 +116,8 @@ internal enum L10n {
|
||||
internal static let chapterSlider = L10n.tr("Localizable", "chapterSlider", fallback: "Chapter Slider")
|
||||
/// Cinematic
|
||||
internal static let cinematic = L10n.tr("Localizable", "cinematic", fallback: "Cinematic")
|
||||
/// Cinematic Background
|
||||
internal static let cinematicBackground = L10n.tr("Localizable", "cinematicBackground", fallback: "Cinematic Background")
|
||||
/// Cinematic Views
|
||||
internal static let cinematicViews = L10n.tr("Localizable", "cinematicViews", fallback: "Cinematic Views")
|
||||
/// Close
|
||||
@ -160,6 +166,10 @@ internal enum L10n {
|
||||
internal static let dark = L10n.tr("Localizable", "dark", fallback: "Dark")
|
||||
/// Default Scheme
|
||||
internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme")
|
||||
/// Delete
|
||||
internal static let delete = L10n.tr("Localizable", "delete", fallback: "Delete")
|
||||
/// Delete Server
|
||||
internal static let deleteServer = L10n.tr("Localizable", "deleteServer", fallback: "Delete Server")
|
||||
/// Delivery
|
||||
internal static let delivery = L10n.tr("Localizable", "delivery", fallback: "Delivery")
|
||||
/// DIRECTOR
|
||||
@ -176,6 +186,8 @@ internal enum L10n {
|
||||
internal static let downloads = L10n.tr("Localizable", "downloads", fallback: "Downloads")
|
||||
/// Edit Jump Lengths
|
||||
internal static let editJumpLengths = L10n.tr("Localizable", "editJumpLengths", fallback: "Edit Jump Lengths")
|
||||
/// Edit Server
|
||||
internal static let editServer = L10n.tr("Localizable", "editServer", fallback: "Edit Server")
|
||||
/// Empty Next Up
|
||||
internal static let emptyNextUp = L10n.tr("Localizable", "emptyNextUp", fallback: "Empty Next Up")
|
||||
/// Enabled
|
||||
@ -228,6 +240,8 @@ internal enum L10n {
|
||||
internal static let invertedLight = L10n.tr("Localizable", "invertedLight", fallback: "Inverted Light")
|
||||
/// Items
|
||||
internal static let items = L10n.tr("Localizable", "items", fallback: "Items")
|
||||
/// Jellyfin
|
||||
internal static let jellyfin = L10n.tr("Localizable", "jellyfin", fallback: "Jellyfin")
|
||||
/// Jump
|
||||
internal static let jump = L10n.tr("Localizable", "jump", fallback: "Jump")
|
||||
/// Jump Backward
|
||||
@ -338,6 +352,8 @@ internal enum L10n {
|
||||
}
|
||||
/// No title
|
||||
internal static let noTitle = L10n.tr("Localizable", "noTitle", fallback: "No title")
|
||||
/// Offset
|
||||
internal static let offset = L10n.tr("Localizable", "offset", fallback: "Offset")
|
||||
/// Ok
|
||||
internal static let ok = L10n.tr("Localizable", "ok", fallback: "Ok")
|
||||
/// 1 user
|
||||
@ -380,6 +396,8 @@ internal enum L10n {
|
||||
internal static let playback = L10n.tr("Localizable", "playback", fallback: "Playback")
|
||||
/// Playback Buttons
|
||||
internal static let playbackButtons = L10n.tr("Localizable", "playbackButtons", fallback: "Playback Buttons")
|
||||
/// Playback Quality
|
||||
internal static let playbackQuality = L10n.tr("Localizable", "playbackQuality", fallback: "Playback Quality")
|
||||
/// Playback settings
|
||||
internal static let playbackSettings = L10n.tr("Localizable", "playbackSettings", fallback: "Playback settings")
|
||||
/// Playback Speed
|
||||
@ -474,12 +492,16 @@ internal enum L10n {
|
||||
internal static let resetAppSettings = L10n.tr("Localizable", "resetAppSettings", fallback: "Reset App Settings")
|
||||
/// Reset User Settings
|
||||
internal static let resetUserSettings = L10n.tr("Localizable", "resetUserSettings", fallback: "Reset User Settings")
|
||||
/// Resume
|
||||
internal static let resume = L10n.tr("Localizable", "resume", fallback: "Resume")
|
||||
/// Resume 5 Second Offset
|
||||
internal static let resume5SecondOffset = L10n.tr("Localizable", "resume5SecondOffset", fallback: "Resume 5 Second Offset")
|
||||
/// Resume Offset
|
||||
internal static let resumeOffset = L10n.tr("Localizable", "resumeOffset", fallback: "Resume Offset")
|
||||
/// Resume content seconds before the recorded resume time
|
||||
internal static let resumeOffsetDescription = L10n.tr("Localizable", "resumeOffsetDescription", fallback: "Resume content seconds before the recorded resume time")
|
||||
/// Resume Offset
|
||||
internal static let resumeOffsetTitle = L10n.tr("Localizable", "resumeOffsetTitle", fallback: "Resume Offset")
|
||||
/// Retrieving media information
|
||||
internal static let retrievingMediaInformation = L10n.tr("Localizable", "retrievingMediaInformation", fallback: "Retrieving media information")
|
||||
/// Retry
|
||||
@ -540,6 +562,10 @@ internal enum L10n {
|
||||
internal static let showCastAndCrew = L10n.tr("Localizable", "showCastAndCrew", fallback: "Show Cast & Crew")
|
||||
/// Show Chapters Info In Bottom Overlay
|
||||
internal static let showChaptersInfoInBottomOverlay = L10n.tr("Localizable", "showChaptersInfoInBottomOverlay", fallback: "Show Chapters Info In Bottom Overlay")
|
||||
/// Show Favorited
|
||||
internal static let showFavorited = L10n.tr("Localizable", "showFavorited", fallback: "Show Favorited")
|
||||
/// Show Favorites
|
||||
internal static let showFavorites = L10n.tr("Localizable", "showFavorites", fallback: "Show Favorites")
|
||||
/// Flatten Library Items
|
||||
internal static let showFlattenView = L10n.tr("Localizable", "showFlattenView", fallback: "Flatten Library Items")
|
||||
/// Show Missing Episodes
|
||||
@ -548,6 +574,14 @@ internal enum L10n {
|
||||
internal static let showMissingSeasons = L10n.tr("Localizable", "showMissingSeasons", fallback: "Show Missing Seasons")
|
||||
/// Show Poster Labels
|
||||
internal static let showPosterLabels = L10n.tr("Localizable", "showPosterLabels", fallback: "Show Poster Labels")
|
||||
/// Show Progress
|
||||
internal static let showProgress = L10n.tr("Localizable", "showProgress", fallback: "Show Progress")
|
||||
/// Show Recently Added
|
||||
internal static let showRecentlyAdded = L10n.tr("Localizable", "showRecentlyAdded", fallback: "Show Recently Added")
|
||||
/// Show Unwatched
|
||||
internal static let showUnwatched = L10n.tr("Localizable", "showUnwatched", fallback: "Show Unwatched")
|
||||
/// Show Watched
|
||||
internal static let showWatched = L10n.tr("Localizable", "showWatched", fallback: "Show Watched")
|
||||
/// Signed in as %@
|
||||
internal static func signedInAsWithString(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "signedInAsWithString", String(describing: p1), fallback: "Signed in as %@")
|
||||
@ -594,6 +628,8 @@ internal enum L10n {
|
||||
internal static let subtitleOffset = L10n.tr("Localizable", "subtitleOffset", fallback: "Subtitle Offset")
|
||||
/// Subtitles
|
||||
internal static let subtitles = L10n.tr("Localizable", "subtitles", fallback: "Subtitles")
|
||||
/// Settings only affect some subtitle types
|
||||
internal static let subtitlesDisclaimer = L10n.tr("Localizable", "subtitlesDisclaimer", fallback: "Settings only affect some subtitle types")
|
||||
/// Subtitle Size
|
||||
internal static let subtitleSize = L10n.tr("Localizable", "subtitleSize", fallback: "Subtitle Size")
|
||||
/// Suggestions
|
||||
|
@ -31,6 +31,7 @@ struct SplitFormWindowView: View {
|
||||
.if(descriptionTopPadding) { view in
|
||||
view.padding(.top)
|
||||
}
|
||||
.scrollClipDisabled()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ struct SelectServerView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
FullScreenMenu("Servers") {
|
||||
FullScreenMenu(L10n.servers) {
|
||||
Section {
|
||||
Button {
|
||||
router.popLast {
|
||||
@ -47,7 +47,7 @@ struct SelectServerView: View {
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Add Server")
|
||||
L10n.addServer.text
|
||||
|
||||
Spacer()
|
||||
|
||||
@ -62,7 +62,7 @@ struct SelectServerView: View {
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Edit Server")
|
||||
L10n.editServer.text
|
||||
|
||||
Spacer()
|
||||
|
||||
@ -80,7 +80,7 @@ struct SelectServerView: View {
|
||||
router.popLast()
|
||||
} label: {
|
||||
HStack {
|
||||
Text("All Servers")
|
||||
L10n.allServers.text
|
||||
|
||||
Spacer()
|
||||
|
||||
@ -116,9 +116,10 @@ struct SelectServerView: View {
|
||||
.padding()
|
||||
}
|
||||
.buttonStyle(.card)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
} header: {
|
||||
Text("Servers")
|
||||
Text(L10n.servers)
|
||||
}
|
||||
.headerProminence(.increased)
|
||||
}
|
||||
|
@ -35,36 +35,61 @@ struct EditServerView: View {
|
||||
.frame(maxWidth: 400)
|
||||
}
|
||||
.contentView {
|
||||
|
||||
Section(L10n.server) {
|
||||
TextPairView(
|
||||
leading: L10n.name,
|
||||
trailing: viewModel.server.name
|
||||
)
|
||||
.focusable(false)
|
||||
}
|
||||
|
||||
Section("URL") {
|
||||
ForEach(viewModel.server.urls.sorted(using: \.absoluteString)) { url in
|
||||
if url == viewModel.server.currentURL {
|
||||
Button(url.absoluteString, systemImage: "checkmark") {}
|
||||
} else {
|
||||
Button(url.absoluteString) {
|
||||
Section(L10n.url) {
|
||||
Menu {
|
||||
ForEach(viewModel.server.urls.sorted(using: \.absoluteString), id: \.self) { url in
|
||||
Button(action: {
|
||||
guard viewModel.server.currentURL != url else { return }
|
||||
viewModel.setCurrentURL(to: url)
|
||||
}) {
|
||||
HStack {
|
||||
Text(url.absoluteString)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
if viewModel.server.currentURL == url {
|
||||
Image(systemName: "checkmark")
|
||||
.font(.body.weight(.regular))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text(viewModel.server.currentURL.absoluteString)
|
||||
.foregroundColor(.primary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Spacer()
|
||||
Image(systemName: "chevron.down")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isEditing {
|
||||
ListRowButton("Delete") {
|
||||
isPresentingConfirmDeletion = true
|
||||
Section {
|
||||
ListRowButton(L10n.delete) {
|
||||
isPresentingConfirmDeletion = true
|
||||
}
|
||||
.foregroundStyle(.primary, .red.opacity(0.5))
|
||||
}
|
||||
.foregroundStyle(.primary, .red.opacity(0.5))
|
||||
}
|
||||
}
|
||||
.withDescriptionTopPadding()
|
||||
.navigationTitle(L10n.server)
|
||||
.alert("Delete Server", isPresented: $isPresentingConfirmDeletion) {
|
||||
Button("Delete", role: .destructive) {
|
||||
.alert(L10n.deleteServer, isPresented: $isPresentingConfirmDeletion) {
|
||||
Button(L10n.delete, role: .destructive) {
|
||||
viewModel.delete()
|
||||
router.popLast()
|
||||
}
|
||||
|
@ -53,18 +53,16 @@ struct CustomizeViewsSettings: View {
|
||||
}
|
||||
.contentView {
|
||||
|
||||
Section {
|
||||
Section(L10n.missingItems) {
|
||||
|
||||
Toggle(L10n.showMissingSeasons, isOn: $shouldShowMissingSeasons)
|
||||
|
||||
Toggle(L10n.showMissingEpisodes, isOn: $shouldShowMissingEpisodes)
|
||||
} header: {
|
||||
L10n.missingItems.text
|
||||
}
|
||||
|
||||
Section {
|
||||
Section(L10n.posters) {
|
||||
|
||||
ChevronButton("Indicators")
|
||||
ChevronButton(L10n.indicators)
|
||||
.onSelect {
|
||||
router.route(to: \.indicatorSettings)
|
||||
}
|
||||
@ -82,23 +80,17 @@ struct CustomizeViewsSettings: View {
|
||||
InlineEnumToggle(title: L10n.search, selection: $searchPosterType)
|
||||
|
||||
InlineEnumToggle(title: L10n.library, selection: $libraryViewType)
|
||||
|
||||
} header: {
|
||||
Text("Posters")
|
||||
}
|
||||
|
||||
Section {
|
||||
Section(L10n.library) {
|
||||
|
||||
Toggle("Cinematic Background", isOn: $cinematicBackground)
|
||||
Toggle(L10n.cinematicBackground, isOn: $cinematicBackground)
|
||||
|
||||
Toggle("Random Image", isOn: $libraryRandomImage)
|
||||
Toggle(L10n.randomImage, isOn: $libraryRandomImage)
|
||||
|
||||
Toggle("Show Favorites", isOn: $showFavorites)
|
||||
Toggle(L10n.showFavorites, isOn: $showFavorites)
|
||||
|
||||
Toggle("Show Recently Added", isOn: $showRecentlyAdded)
|
||||
|
||||
} header: {
|
||||
L10n.library.text
|
||||
Toggle(L10n.showRecentlyAdded, isOn: $showRecentlyAdded)
|
||||
}
|
||||
}
|
||||
.withDescriptionTopPadding()
|
||||
|
@ -25,20 +25,15 @@ struct ExperimentalSettingsView: View {
|
||||
.frame(maxWidth: 400)
|
||||
}
|
||||
.contentView {
|
||||
Section {
|
||||
|
||||
Section("Video Player") {
|
||||
|
||||
Toggle("Force Direct Play", isOn: $forceDirectPlay)
|
||||
|
||||
} header: {
|
||||
Text("Video Player")
|
||||
}
|
||||
|
||||
Section {
|
||||
Section("Live TV") {
|
||||
|
||||
Toggle("Live TV Force Direct Play", isOn: $liveTVForceDirectPlay)
|
||||
|
||||
} header: {
|
||||
Text("Live TV")
|
||||
}
|
||||
}
|
||||
.navigationTitle(L10n.experimental)
|
||||
|
@ -32,18 +32,18 @@ struct IndicatorSettingsView: View {
|
||||
}
|
||||
.contentView {
|
||||
|
||||
Section {
|
||||
Section(L10n.posters) {
|
||||
|
||||
Toggle("Show Favorited", isOn: $showFavorited)
|
||||
Toggle(L10n.showFavorited, isOn: $showFavorited)
|
||||
|
||||
Toggle("Show Progress", isOn: $showProgress)
|
||||
Toggle(L10n.showProgress, isOn: $showProgress)
|
||||
|
||||
Toggle("Show Unwatched", isOn: $showUnwatched)
|
||||
Toggle(L10n.showUnwatched, isOn: $showUnwatched)
|
||||
|
||||
Toggle("Show Watched", isOn: $showWatched)
|
||||
Toggle(L10n.showWatched, isOn: $showWatched)
|
||||
}
|
||||
}
|
||||
.withDescriptionTopPadding()
|
||||
.navigationTitle("Indicators")
|
||||
.navigationTitle(L10n.indicators)
|
||||
}
|
||||
}
|
||||
|
@ -24,21 +24,19 @@ struct MaximumBitrateSettingsView: View {
|
||||
.frame(maxWidth: 400)
|
||||
}
|
||||
.contentView {
|
||||
|
||||
Section {
|
||||
CaseIterablePicker(
|
||||
L10n.maximumBitrate,
|
||||
selection: $appMaximumBitrate
|
||||
)
|
||||
|
||||
InlineEnumToggle(title: L10n.maximumBitrate, selection: $appMaximumBitrate)
|
||||
|
||||
if appMaximumBitrate == PlaybackBitrate.auto {
|
||||
CaseIterablePicker(
|
||||
L10n.testSize,
|
||||
selection: $appMaximumBitrateTest
|
||||
)
|
||||
InlineEnumToggle(title: L10n.testSize, selection: $appMaximumBitrateTest)
|
||||
}
|
||||
} header: {
|
||||
L10n.playbackQuality.text
|
||||
} footer: {
|
||||
if appMaximumBitrate == PlaybackBitrate.auto {
|
||||
Text(L10n.bitrateTestDescription)
|
||||
L10n.bitrateTestDescription.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ struct SettingsView: View {
|
||||
.frame(maxWidth: 400)
|
||||
}
|
||||
.contentView {
|
||||
Section {
|
||||
Section(L10n.jellyfin) {
|
||||
|
||||
Button {} label: {
|
||||
TextPairView(
|
||||
@ -51,14 +51,23 @@ struct SettingsView: View {
|
||||
Button {
|
||||
viewModel.signOut()
|
||||
} label: {
|
||||
L10n.switchUser.text
|
||||
.foregroundColor(.jellyfinPurple)
|
||||
HStack {
|
||||
|
||||
Text(L10n.switchUser)
|
||||
.foregroundColor(.jellyfinPurple)
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.body.weight(.regular))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Section(L10n.videoPlayer) {
|
||||
|
||||
InlineEnumToggle(title: "Video Player Type", selection: $videoPlayerType)
|
||||
InlineEnumToggle(title: L10n.videoPlayerType, selection: $videoPlayerType)
|
||||
|
||||
ChevronButton(L10n.videoPlayer)
|
||||
.onSelect {
|
||||
@ -69,12 +78,9 @@ struct SettingsView: View {
|
||||
.onSelect {
|
||||
router.route(to: \.maximumBitrateSettings)
|
||||
}
|
||||
|
||||
} header: {
|
||||
L10n.videoPlayer.text
|
||||
}
|
||||
|
||||
Section {
|
||||
Section(L10n.accessibility) {
|
||||
|
||||
ChevronButton(L10n.customize)
|
||||
.onSelect {
|
||||
@ -85,14 +91,11 @@ struct SettingsView: View {
|
||||
.onSelect {
|
||||
router.route(to: \.experimentalSettings)
|
||||
}
|
||||
|
||||
} header: {
|
||||
L10n.accessibility.text
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
ChevronButton("Logs")
|
||||
ChevronButton(L10n.logs)
|
||||
.onSelect {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
|
@ -43,46 +43,51 @@ struct VideoPlayerSettingsView: View {
|
||||
.contentView {
|
||||
|
||||
Section {
|
||||
|
||||
ChevronButton(
|
||||
"Resume Offset",
|
||||
L10n.offset,
|
||||
subtitle: resumeOffset.secondLabel
|
||||
)
|
||||
.onSelect {
|
||||
isPresentingResumeOffsetStepper = true
|
||||
}
|
||||
} header: {
|
||||
L10n.resume.text
|
||||
} footer: {
|
||||
Text("Resume content seconds before the recorded resume time")
|
||||
L10n.resumeOffsetDescription.text
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName)
|
||||
.onSelect {
|
||||
router.route(to: \.fontPicker, $subtitleFontName)
|
||||
}
|
||||
} header: {
|
||||
L10n.subtitles.text
|
||||
} footer: {
|
||||
Text("Settings only affect some subtitle types")
|
||||
L10n.subtitlesDisclaimer.text
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
Toggle("Pause on background", isOn: $pauseOnBackground)
|
||||
Toggle("Play on active", isOn: $playOnActive)
|
||||
Section(L10n.playback) {
|
||||
Toggle(L10n.pauseOnBackground, isOn: $pauseOnBackground)
|
||||
Toggle(L10n.playOnActive, isOn: $playOnActive)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Video Player")
|
||||
.blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) {
|
||||
StepperView(
|
||||
title: "Resume Offset",
|
||||
description: "Resume content seconds before the recorded resume time",
|
||||
value: $resumeOffset,
|
||||
range: 0 ... 30,
|
||||
step: 1
|
||||
)
|
||||
.valueFormatter {
|
||||
$0.secondLabel
|
||||
}
|
||||
.onCloseSelected {
|
||||
isPresentingResumeOffsetStepper = false
|
||||
.navigationTitle(L10n.videoPlayer.text)
|
||||
.blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) {
|
||||
StepperView(
|
||||
title: L10n.resumeOffsetTitle,
|
||||
description: L10n.resumeOffsetDescription,
|
||||
value: $resumeOffset,
|
||||
range: 0 ... 30,
|
||||
step: 1
|
||||
)
|
||||
.valueFormatter {
|
||||
$0.secondLabel
|
||||
}
|
||||
.onCloseSelected {
|
||||
isPresentingResumeOffsetStepper = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user