Skip to content

Commit 27862e4

Browse files
authored
Todo 세부 정보 시트에 Figma 디자인을 적용한다 (#932)
* ui: Tag가 사용 가능한 폭을 넘으면 말줄임 처리 * chore: 사용하지 않는 TodoInfoSheetView 제거 * ui: Todo 세부 정보 시트 카드 구조 적용
1 parent 13bd701 commit 27862e4

3 files changed

Lines changed: 111 additions & 153 deletions

File tree

‎Application/Presentation/PresentationShared/Sources/Common/Component/Tags.swift‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public struct Tag: View {
4646
.foregroundStyle(Color.accent)
4747
.bold()
4848
.lineLimit(1)
49-
.fixedSize()
5049
.padding(.vertical, 4)
5150
.padding(.leading, 8)
5251
.padding(.trailing, isEditing ? 0 : 8)
@@ -245,7 +244,7 @@ private struct TagLayout: Layout {
245244
var minX = bounds.minX
246245

247246
for index in row.indices {
248-
let size = subviews[index].sizeThatFits(.unspecified)
247+
let size = fittedSize(of: subviews[index], maxWidth: width)
249248
subviews[index].place(
250249
at: CGPoint(x: minX, y: minY),
251250
proposal: ProposedViewSize(size)
@@ -292,7 +291,7 @@ private struct TagLayout: Layout {
292291

293292
for index in contentIndices {
294293
let subview = subviews[index]
295-
let size = subview.sizeThatFits(.unspecified)
294+
let size = fittedSize(of: subview, maxWidth: availableWidth)
296295

297296
if currentWidth + size.width > availableWidth && !currentRow.indices.isEmpty {
298297
rows.append(currentRow)
@@ -330,7 +329,7 @@ private struct TagLayout: Layout {
330329
}
331330

332331
let overflowIndex = subviews.count - 1
333-
let overflowSize = subviews[overflowIndex].sizeThatFits(.unspecified)
332+
let overflowSize = fittedSize(of: subviews[overflowIndex], maxWidth: maxWidth)
334333
guard !limited.isEmpty else {
335334
return [Row(indices: [overflowIndex], maxHeight: overflowSize.height, width: overflowSize.width)]
336335
}
@@ -341,7 +340,7 @@ private struct TagLayout: Layout {
341340

342341
while !candidateIndices.isEmpty {
343342
let rowIndices = candidateIndices + [overflowIndex]
344-
let rowWidth = width(for: rowIndices, subviews: subviews)
343+
let rowWidth = width(for: rowIndices, subviews: subviews, maxWidth: maxWidth)
345344
if rowWidth <= maxWidth {
346345
lastRow.indices = rowIndices
347346
lastRow.maxHeight = max(lastRow.maxHeight, overflowSize.height)
@@ -360,15 +359,25 @@ private struct TagLayout: Layout {
360359

361360
private func width(
362361
for indices: [Int],
363-
subviews: Subviews
362+
subviews: Subviews,
363+
maxWidth: CGFloat
364364
) -> CGFloat {
365365
guard !indices.isEmpty else { return 0 }
366366
let widths = indices.reduce(CGFloat.zero) { partialResult, index in
367-
partialResult + subviews[index].sizeThatFits(.unspecified).width
367+
partialResult + fittedSize(of: subviews[index], maxWidth: maxWidth).width
368368
}
369369
return widths + CGFloat(max(0, indices.count - 1)) * horizontalSpacing
370370
}
371371

372+
private func fittedSize(
373+
of subview: LayoutSubview,
374+
maxWidth: CGFloat
375+
) -> CGSize {
376+
subview.sizeThatFits(
377+
ProposedViewSize(width: maxWidth.isFinite ? maxWidth : nil, height: nil)
378+
)
379+
}
380+
372381
private var usesOverflowIndicator: Bool {
373382
showsOverflowIndicator && (lineLimit ?? 0) > 0
374383
}

‎Application/Presentation/PresentationShared/Sources/Common/TodoInfoSheetView.swift‎

Lines changed: 0 additions & 105 deletions
This file was deleted.

‎Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailView.swift‎

Lines changed: 95 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -181,77 +181,131 @@ private struct InfoSheetView: View {
181181
private let calendar = Calendar.current
182182

183183
var body: some View {
184-
NavigationStack {
185-
List {
186-
Section(String(localized: "todo_options_section", bundle: PresentationResources.bundle)) {
187-
HStack {
188-
Text(String(localized: "todo_category", bundle: PresentationResources.bundle))
189-
Spacer()
184+
ScrollView {
185+
LazyVStack(alignment: .leading, spacing: 24) {
186+
VStack(alignment: .leading, spacing: 0) {
187+
optionRow(
188+
systemImage: "tag.fill",
189+
color: Color.accent,
190+
title: String(localized: "todo_category", bundle: PresentationResources.bundle)
191+
) {
190192
Text(TodoCategoryItem(from: todo.category).localizedName)
191-
.foregroundStyle(.secondary)
193+
.foregroundStyle(Color.accent)
194+
.lineLimit(1)
192195
}
193196

194-
statusRow(
195-
title: String(localized: "todo_completed", bundle: PresentationResources.bundle),
196-
systemImage: todo.isCompleted ? "checkmark.circle.fill" : "circle",
197-
color: todo.isCompleted ? .green : .secondary
198-
)
197+
Divider()
199198

200-
statusRow(
201-
title: String(localized: "todo_pinned", bundle: PresentationResources.bundle),
202-
systemImage: todo.isPinned ? "star.fill" : "star",
203-
color: todo.isPinned ? .orange : .secondary
204-
)
199+
optionRow(
200+
systemImage: "circle",
201+
color: Color.textSecondary,
202+
title: String(localized: "todo_completed", bundle: PresentationResources.bundle)
203+
) {
204+
Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
205+
.foregroundStyle(todo.isCompleted ? Color.accent : Color.textTertiary)
206+
}
207+
208+
Divider()
205209

206-
HStack {
207-
Text(String(localized: "todo_due_date", bundle: PresentationResources.bundle))
210+
optionRow(
211+
systemImage: "star.fill",
212+
color: Color.orange,
213+
title: String(localized: "todo_pinned", bundle: PresentationResources.bundle)
214+
) {
215+
Image(systemName: todo.isPinned ? "star.fill" : "star")
216+
.foregroundStyle(todo.isPinned ? Color.orange : Color.textTertiary)
217+
}
208218

209-
Spacer()
219+
Divider()
210220

221+
optionRow(
222+
systemImage: "calendar",
223+
color: Color.textSecondary,
224+
title: String(localized: "todo_due_date", bundle: PresentationResources.bundle)
225+
) {
211226
if let dueDate = todo.dueDate {
212227
Tag(dueDateText(for: dueDate), isEditing: false)
213-
.padding(.vertical, -4)
214228
} else {
215229
Text(String(localized: "todo_none", bundle: PresentationResources.bundle))
216-
.foregroundStyle(.secondary)
230+
.foregroundStyle(Color.textSecondary)
217231
}
218232
}
219233
}
234+
.background(Color.surface)
235+
.clipShape(.rect(cornerRadius: 16))
220236

221-
Section(String(localized: "todo_tags", bundle: PresentationResources.bundle)) {
222-
if todo.tags.isEmpty {
223-
Text(String(localized: "todo_no_tags", bundle: PresentationResources.bundle))
224-
.foregroundStyle(.secondary)
225-
.padding(.vertical, 4)
226-
} else {
227-
TagList(todo.tags)
237+
VStack(alignment: .leading, spacing: 12) {
238+
Text(String(localized: "todo_tags", bundle: PresentationResources.bundle))
239+
.font(.headline)
240+
.padding(.horizontal, 16)
241+
242+
Group {
243+
if todo.tags.isEmpty {
244+
Text(String(localized: "todo_no_tags", bundle: PresentationResources.bundle))
245+
.foregroundStyle(Color.textSecondary)
246+
} else {
247+
TagList(todo.tags, verticalSpacing: 4, horizontalSpacing: 4)
248+
}
228249
}
250+
.padding(16)
251+
.frame(maxWidth: .infinity, alignment: .leading)
252+
.background(Color.surface)
253+
.clipShape(.rect(cornerRadius: 16))
229254
}
230255
}
231-
.navigationTitle(String(localized: "todo_details", bundle: PresentationResources.bundle))
232-
.navigationBarTitleDisplayMode(.inline)
233-
.toolbar {
234-
ToolbarLeadingButton {
256+
.padding(.horizontal, 16)
257+
.padding(.bottom, 16)
258+
}
259+
.safeAreaInset(edge: .top, spacing: 0) { topBar }
260+
.background(Color.appBackground.ignoresSafeArea())
261+
.presentationDragIndicator(.visible)
262+
}
263+
264+
private var topBar: some View {
265+
ZStack {
266+
Text(String(localized: "todo_details", bundle: PresentationResources.bundle))
267+
.font(.headline)
268+
HStack {
269+
Button {
235270
onClose()
271+
} label: {
272+
if #available(iOS 26.0, *) {
273+
Image(systemName: "xmark")
274+
} else {
275+
Text(String(localized: "common_close", bundle: PresentationResources.bundle))
276+
}
236277
}
278+
.topBarButtonStyle()
279+
Spacer()
237280
}
238281
}
282+
.padding(.horizontal, 16)
283+
.padding(.vertical, 12)
284+
.background(Color.appBackground, ignoresSafeAreaEdges: .top)
239285
}
240286

241-
@ViewBuilder
242-
private func statusRow(
243-
title: String,
287+
private func optionRow<Trailing: View>(
244288
systemImage: String,
245-
color: Color
289+
color: Color,
290+
title: String,
291+
@ViewBuilder trailing: () -> Trailing
246292
) -> some View {
247-
HStack {
293+
HStack(spacing: 12) {
294+
Image(systemName: systemImage)
295+
.font(.title3)
296+
.foregroundStyle(color)
297+
.frame(width: 28, height: 28)
298+
248299
Text(title)
300+
.font(.headline)
301+
.layoutPriority(1)
249302

250-
Spacer()
303+
Spacer(minLength: 8)
251304

252-
Image(systemName: systemImage)
253-
.foregroundStyle(color)
305+
trailing()
254306
}
307+
.padding(.horizontal, 16)
308+
.padding(.vertical, 12)
255309
}
256310

257311
private func dueDateText(for dueDate: Date) -> String {

0 commit comments

Comments
 (0)