diff --git a/DevLog/UI/Common/Componeent/CheckBox.swift b/DevLog/UI/Common/Componeent/CheckBox.swift index 295cd95..0858186 100644 --- a/DevLog/UI/Common/Componeent/CheckBox.swift +++ b/DevLog/UI/Common/Componeent/CheckBox.swift @@ -21,7 +21,7 @@ struct CheckBox: View { if isChecked { Image(systemName: "checkmark.circle.fill") .symbolRenderingMode(.palette) - .foregroundStyle(Color.white, Color.accentColor) + .foregroundStyle(Color.white, Color.blue) } else { Image(systemName: "circle") .foregroundStyle(Color.gray) diff --git a/DevLog/UI/Common/Componeent/SheetToolbar.swift b/DevLog/UI/Common/Componeent/SheetToolbar.swift new file mode 100644 index 0000000..b42e149 --- /dev/null +++ b/DevLog/UI/Common/Componeent/SheetToolbar.swift @@ -0,0 +1,59 @@ +// +// SheetToolbar.swift +// DevLog +// +// Created by 최윤진 on 2/27/26. +// + +import SwiftUI + +struct SheetToolbar: ToolbarContent { + let onCancel: () -> Void + let onConfirm: () -> Void + let isConfirmEnabled: Bool + + init( + onCancel: @escaping () -> Void, + onConfirm: @escaping () -> Void, + isConfirmEnabled: Bool = true + ) { + self.onCancel = onCancel + self.onConfirm = onConfirm + self.isConfirmEnabled = isConfirmEnabled + } + + var body: some ToolbarContent { + if #available(iOS 26.0, *) { + ToolbarItem(placement: .topBarLeading) { + Button(role: .cancel) { + onCancel() + } + } + + ToolbarItem(placement: .topBarTrailing) { + Button(role: .confirm) { + onConfirm() + } + .disabled(!isConfirmEnabled) + } + } else { + ToolbarItem(placement: .topBarLeading) { + Button { + onCancel() + } label: { + Text("취소") + } + } + + ToolbarItem(placement: .topBarTrailing) { + Button { + onConfirm() + } label: { + Text("확인") + .bold() + } + .disabled(!isConfirmEnabled) + } + } + } +} diff --git a/DevLog/UI/Common/Componeent/WebItemRow.swift b/DevLog/UI/Common/Componeent/WebItemRow.swift index ba87c39..a2dc07d 100644 --- a/DevLog/UI/Common/Componeent/WebItemRow.swift +++ b/DevLog/UI/Common/Componeent/WebItemRow.swift @@ -30,7 +30,7 @@ struct WebItemRow: View { .multilineTextAlignment(.leading) .lineLimit(2) Text(item.displayURL) - .foregroundStyle(Color.accentColor) + .foregroundStyle(Color.blue) .underline() } Spacer() diff --git a/DevLog/UI/Extension/View+.swift b/DevLog/UI/Extension/View+.swift index a7f6a7d..f8d1129 100644 --- a/DevLog/UI/Extension/View+.swift +++ b/DevLog/UI/Extension/View+.swift @@ -9,25 +9,31 @@ import SwiftUI extension View { @ViewBuilder - func adaptiveButtonStyle(_ color: Color? = nil) -> some View { - if #available(iOS 26.0, *), color == nil { - self.buttonStyle(.glass) + func adaptiveButtonStyle( + shape: some Shape = .capsule, + color: Color = .clear) + -> some View { + if #available(iOS 26.0, *) { + self.foregroundStyle(Color(.label)) + .padding(8) + .glassEffect(.regular.tint(color), in: shape) } else { self.foregroundStyle(Color(.label)) - .font(.footnote) - .padding(.vertical, 8) - .padding(.horizontal, 16) + .padding(8) .background { - Capsule() - .fill(.ultraThinMaterial) - .background { - Capsule() - .fill(color ?? Color.clear) - } - .overlay { - Capsule() - .stroke(Color.white.opacity(0.2), lineWidth: 1) + Group { + if color == .clear { + shape + .fill(.ultraThinMaterial) + } else { + shape + .fill(color) } + } + .overlay { + shape + .stroke(Color.white.opacity(0.2), lineWidth: 1) + } } } } diff --git a/DevLog/UI/Home/TodoDetailView.swift b/DevLog/UI/Home/TodoDetailView.swift index 6364e57..575301c 100644 --- a/DevLog/UI/Home/TodoDetailView.swift +++ b/DevLog/UI/Home/TodoDetailView.swift @@ -74,7 +74,6 @@ struct TodoDetailView: View { viewModel.send(.setShowEditor(true)) } label: { Text("수정") - .foregroundColor(.accentColor) } } } diff --git a/DevLog/UI/Home/TodoEditorView.swift b/DevLog/UI/Home/TodoEditorView.swift index bc90055..e5f9505 100644 --- a/DevLog/UI/Home/TodoEditorView.swift +++ b/DevLog/UI/Home/TodoEditorView.swift @@ -46,7 +46,16 @@ struct TodoEditorView: View { .navigationTitle(viewModel.navigationTitle) .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.background, for: .navigationBar) - .toolbar { toolBar } + .toolbar { + SheetToolbar( + onCancel: { dismiss() }, + onConfirm: { + onSubmit?(viewModel.upsertTodo()) + dismiss() + }, + isConfirmEnabled: viewModel.state.isValidToSave + ) + } } } @@ -306,10 +315,13 @@ private struct TagEditor: View { tag = "" } label: { Image(systemName: "plus") - .font(.title.bold()) - .padding(.vertical, 5) + .font(.largeTitle) + .foregroundStyle(Color.white) } - .adaptiveButtonStyle((!tag.isEmpty && !tags.contains(tag)) ? .blue : .clear) + .adaptiveButtonStyle( + shape: .circle, + color: (!tag.isEmpty && !tags.contains(tag)) ? Color.blue : .gray.opacity(0.4) + ) .disabled(tag.isEmpty || tags.contains(tag)) } } @@ -344,7 +356,7 @@ private struct DueDatePicker: View { ) .labelsHidden() .datePickerStyle(.graphical) - .presentationDragIndicator(.hidden) + .presentationDragIndicator(.visible) .presentationDetents([.height(height)]) .background { GeometryReader { geometry in diff --git a/DevLog/UI/PushNotification/PushNotificationListView.swift b/DevLog/UI/PushNotification/PushNotificationListView.swift index ca0ec84..e2dfb05 100644 --- a/DevLog/UI/PushNotification/PushNotificationListView.swift +++ b/DevLog/UI/PushNotification/PushNotificationListView.swift @@ -123,7 +123,7 @@ struct PushNotificationListView: View { } label: { Text("정렬: \(viewModel.state.query.sortOrder.title)") } - .adaptiveButtonStyle(viewModel.state.query.sortOrder == .oldest ? .blue : .clear) + .adaptiveButtonStyle(color: viewModel.state.query.sortOrder == .oldest ? .blue : .clear) Menu { ForEach(PushNotificationQuery.TimeFilter.availableOptions, id: \.id) { option in @@ -144,14 +144,15 @@ struct PushNotificationListView: View { } label: { Text("기간") } - .adaptiveButtonStyle(viewModel.state.query.timeFilter == .none ? .clear : .blue) + .adaptiveButtonStyle(color: viewModel.state.query.timeFilter == .none ? .clear : .blue) Button { viewModel.send(.toggleUnreadOnly) } label: { Text("읽지 않음") + .foregroundStyle(viewModel.state.query.unreadOnly ? .white : Color(.label)) } - .adaptiveButtonStyle(viewModel.state.query.unreadOnly ? .blue : .clear) + .adaptiveButtonStyle(color: viewModel.state.query.unreadOnly ? .blue : .clear) } } .scrollIndicators(.never) diff --git a/DevLog/UI/Setting/PushNotificationSettingsView.swift b/DevLog/UI/Setting/PushNotificationSettingsView.swift index 181ed29..643ed3a 100644 --- a/DevLog/UI/Setting/PushNotificationSettingsView.swift +++ b/DevLog/UI/Setting/PushNotificationSettingsView.swift @@ -32,7 +32,7 @@ struct PushNotificationSettingsView: View { if viewModel.state.pushNotificationHour == hour && viewModel.state.pushNotificationMinute == 0 { Image(systemName: "checkmark") - .foregroundStyle(Color.accentColor) + .foregroundStyle(Color.blue) } } .contentShape(Rectangle()) @@ -48,7 +48,7 @@ struct PushNotificationSettingsView: View { .foregroundStyle(.secondary) if viewModel.state.pushNotificationMinute != 0 { Image(systemName: "checkmark") - .foregroundStyle(Color.accentColor) + .foregroundStyle(Color.blue) } } .contentShape(Rectangle()) @@ -88,7 +88,12 @@ struct PushNotificationSettingsView: View { .presentationDetents([.height(viewModel.state.sheetHeight)]) .onAppear { UIDatePicker.appearance().minuteInterval = 5 } .onDisappear { UIDatePicker.appearance().minuteInterval = 1 /* 기본값으로 복원 */ } - .toolbar { toolbar } + .toolbar { + SheetToolbar( + onCancel: { viewModel.send(.rollbackUpdate) }, + onConfirm: { viewModel.send(.confirmUpdate) } + ) + } .background( GeometryReader { geometry in Color.clear.onAppear { @@ -100,40 +105,6 @@ struct PushNotificationSettingsView: View { } } - @ToolbarContentBuilder - private var toolbar: some ToolbarContent { - if #available(iOS 26.0, *) { - ToolbarItem(placement: .topBarLeading) { - Button(role: .cancel) { - viewModel.send(.rollbackUpdate) - } - } - - ToolbarItem(placement: .topBarTrailing) { - Button(role: .confirm) { - viewModel.send(.confirmUpdate) - } - } - } else { - ToolbarItem(placement: .topBarLeading) { - Button { - viewModel.send(.rollbackUpdate) - } label: { - Text("취소") - } - } - - ToolbarItem(placement: .topBarTrailing) { - Button { - viewModel.send(.confirmUpdate) - } label: { - Text("확인") - .bold() - } - } - } - } - private func formattedTimeString(_ date: Date) -> String { let minuteValue = Calendar.current.component(.minute, from: date) let formatStyle: Date.FormatStyle = .dateTime.hour(.twoDigits(amPM: .wide))