Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DevLog/UI/Common/Componeent/CheckBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
59 changes: 59 additions & 0 deletions DevLog/UI/Common/Componeent/SheetToolbar.swift
Original file line number Diff line number Diff line change
@@ -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, *) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

iOS 26.0 버전 체크는 현재 및 가까운 미래의 OS에서 항상 false를 반환하므로 else 블록의 코드만 실행됩니다. Button(role:)은 iOS 15.0부터 사용 가능하므로, 만약 iOS 버전을 기준으로 분기하려는 의도였다면 if #available(iOS 15.0, *)과 같이 더 낮은 버전으로 수정해야 합니다.

만약 visionOS를 지원하기 위한 코드라면, #if os(visionOS) 전처리기 매크로를 사용하거나 if #available(visionOS 1.0, *)으로 확인하는 것이 더 정확합니다. 다른 파일에서 glassEffect와 같은 visionOS 전용 API를 사용하는 것으로 보아 후자일 가능성이 높아 보입니다.

잘못된 버전 체크는 의도한 UI가 표시되지 않는 버그를 유발할 수 있으므로 수정이 필요합니다.

Suggested change
if #available(iOS 26.0, *) {
if #available(visionOS 1.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)
}
}
}
}
2 changes: 1 addition & 1 deletion DevLog/UI/Common/Componeent/WebItemRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct WebItemRow: View {
.multilineTextAlignment(.leading)
.lineLimit(2)
Text(item.displayURL)
.foregroundStyle(Color.accentColor)
.foregroundStyle(Color.blue)
.underline()
}
Spacer()
Expand Down
36 changes: 21 additions & 15 deletions DevLog/UI/Extension/View+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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, *) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

여기에서도 iOS 26.0 버전 체크가 사용되었습니다. glassEffectvisionOS에서만 사용 가능한 API이므로, 이 코드가 visionOS를 위한 것이라면 if #available(visionOS 1.0, *)으로 조건을 변경해야 합니다. 현재 코드는 visionOS에서도 if 블록이 실행되지 않아 glassEffect가 적용되지 않는 문제를 야기합니다.

Suggested change
if #available(iOS 26.0, *) {
if #available(visionOS 1.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)
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion DevLog/UI/Home/TodoDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ struct TodoDetailView: View {
viewModel.send(.setShowEditor(true))
} label: {
Text("수정")
.foregroundColor(.accentColor)
}
}
}
Expand Down
22 changes: 17 additions & 5 deletions DevLog/UI/Home/TodoEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
}
}

Expand Down Expand Up @@ -306,10 +315,13 @@ private struct TagEditor<Content: View>: 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))
}
}
Expand Down Expand Up @@ -344,7 +356,7 @@ private struct DueDatePicker<Content: View>: View {
)
.labelsHidden()
.datePickerStyle(.graphical)
.presentationDragIndicator(.hidden)
.presentationDragIndicator(.visible)
.presentationDetents([.height(height)])
.background {
GeometryReader { geometry in
Expand Down
7 changes: 4 additions & 3 deletions DevLog/UI/PushNotification/PushNotificationListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
45 changes: 8 additions & 37 deletions DevLog/UI/Setting/PushNotificationSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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())
Expand Down Expand Up @@ -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 {
Expand All @@ -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))
Expand Down