Skip to content

Commit b2f6419

Browse files
graycreateclaude
andcommitted
feat: optimize FeedDetailPage list with opacity-based fade-in
Use opacity animation instead of hiding/showing sections: - Sections remain in layout (no height jumps) - Fade in via opacity 0->1 when ready - Postscripts fade in when content is ready - Replies fade in 150ms after content - Faster easeIn animation (0.15s) This avoids layout recalculation and position jumping. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent efff2d3 commit b2f6419

File tree

1 file changed

+32
-25
lines changed

1 file changed

+32
-25
lines changed

V2er/View/FeedDetail/FeedDetailPage.swift

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct FeedDetailPage: StateView, KeyboardReadable, InstanceIdentifiable {
4444
@State var isKeyboardVisiable = false
4545
@State private var isLoadingMore = false
4646
@State private var contentReady = false
47+
@State private var repliesReady = false
4748
@FocusState private var replyIsFocused: Bool
4849
var initData: FeedInfo.Item? = nil
4950
var id: String
@@ -206,8 +207,12 @@ struct FeedDetailPage: StateView, KeyboardReadable, InstanceIdentifiable {
206207
// Content Section
207208
if !isContentEmpty {
208209
NewsContentView(state.model.contentInfo) {
209-
withAnimation {
210-
contentReady = true
210+
contentReady = true
211+
// Show replies after a short delay for smoother transition
212+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
213+
withAnimation(.easeInOut(duration: 0.2)) {
214+
repliesReady = true
215+
}
211216
}
212217
}
213218
.padding(.horizontal, 10)
@@ -216,31 +221,31 @@ struct FeedDetailPage: StateView, KeyboardReadable, InstanceIdentifiable {
216221
.listRowBackground(Color.itemBg)
217222
}
218223

219-
// Show postscripts and replies only after content is ready
220-
if contentReady || isContentEmpty {
221-
// Postscripts Section (附言)
222-
ForEach(state.model.postscripts) { postscript in
223-
PostscriptItemView(postscript: postscript)
224-
.listRowInsets(EdgeInsets())
225-
.listRowSeparator(.hidden)
226-
.listRowBackground(Color.itemBg)
227-
}
224+
// Postscripts Section (附言) - always in layout, fade in when ready
225+
ForEach(state.model.postscripts) { postscript in
226+
PostscriptItemView(postscript: postscript)
227+
.listRowInsets(EdgeInsets())
228+
.listRowSeparator(.hidden)
229+
.listRowBackground(Color.itemBg)
230+
.opacity(contentReady || isContentEmpty ? 1 : 0)
231+
}
228232

229-
// Reply Section Header with Sort Toggle
230-
if !state.model.replyInfo.items.isEmpty {
231-
replySectionHeader
232-
.listRowInsets(EdgeInsets())
233-
.listRowSeparator(.hidden)
234-
.listRowBackground(Color.itemBg)
235-
}
233+
// Reply Section Header with Sort Toggle
234+
if !state.model.replyInfo.items.isEmpty {
235+
replySectionHeader
236+
.listRowInsets(EdgeInsets())
237+
.listRowSeparator(.hidden)
238+
.listRowBackground(Color.itemBg)
239+
.opacity(repliesReady || isContentEmpty ? 1 : 0)
240+
}
236241

237-
// Reply Section
238-
ForEach(sortedReplies, id: \.floor) { item in
239-
ReplyItemView(info: item, topicId: id)
240-
.listRowInsets(EdgeInsets())
241-
.listRowSeparator(.hidden)
242-
.listRowBackground(Color.itemBg)
243-
}
242+
// Reply Section - always in layout, fade in when ready
243+
ForEach(sortedReplies, id: \.floor) { item in
244+
ReplyItemView(info: item, topicId: id)
245+
.listRowInsets(EdgeInsets())
246+
.listRowSeparator(.hidden)
247+
.listRowBackground(Color.itemBg)
248+
.opacity(repliesReady || isContentEmpty ? 1 : 0)
244249
}
245250

246251
// Load More Indicator
@@ -272,6 +277,8 @@ struct FeedDetailPage: StateView, KeyboardReadable, InstanceIdentifiable {
272277
.scrollContentBackground(.hidden)
273278
.background(Color.itemBg)
274279
.environment(\.defaultMinListRowHeight, 1)
280+
.animation(.easeIn(duration: 0.15), value: contentReady)
281+
.animation(.easeIn(duration: 0.15), value: repliesReady)
275282
.refreshable {
276283
await run(action: FeedDetailActions.FetchData.Start(id: instanceId, feedId: initData?.id))
277284
}

0 commit comments

Comments
 (0)