Skip to content

Commit 637506e

Browse files
authored
feat: リアルタイム音声録音時の自動スクロール機能を追加 (#89)
1 parent 7f1bc47 commit 637506e

File tree

1 file changed

+54
-26
lines changed

1 file changed

+54
-26
lines changed

MyLibrary/Sources/LiveTranslationFeature/LiveTranslationView.swift

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import SwiftUI
44
public struct LiveTranslationView: View {
55
let viewModel: ViewModel
66
@State var isSelectedLanguageSheet: Bool = false
7+
@State var isShowingLastChat: Bool = false
8+
9+
private let scrollContentBottomID: String = "atBottom"
710

811
public init(
912
roomNumber: String = ProcessInfo.processInfo.environment["LIVE_TRANSLATION_KEY"]
@@ -16,36 +19,61 @@ public struct LiveTranslationView: View {
1619
public var body: some View {
1720
NavigationStack {
1821
VStack {
19-
ScrollView {
20-
if self.viewModel.roomNumber.isEmpty {
21-
ContentUnavailableView("Room is unavailable", systemImage: "text.page.slash.fill")
22-
Spacer()
23-
} else if viewModel.chatList.isEmpty {
24-
ContentUnavailableView("Not started yet", systemImage: "text.page.slash.fill")
25-
Spacer()
26-
} else {
27-
LazyVStack {
28-
ForEach(viewModel.chatList) { item in
29-
Text(item.trItem?.content ?? item.item.text)
30-
.frame(maxWidth: .infinity, alignment: .leading)
31-
.multilineTextAlignment(.leading)
32-
.padding()
22+
ScrollViewReader { reader in
23+
ScrollView {
24+
if self.viewModel.roomNumber.isEmpty {
25+
ContentUnavailableView("Room is unavailable", systemImage: "text.page.slash.fill")
26+
Spacer()
27+
} else if viewModel.chatList.isEmpty {
28+
ContentUnavailableView("Not started yet", systemImage: "text.page.slash.fill")
29+
Spacer()
30+
} else {
31+
LazyVStack {
32+
ForEach(viewModel.chatList) { item in
33+
Text(item.trItem?.content ?? item.item.text)
34+
.frame(maxWidth: .infinity, alignment: .leading)
35+
.multilineTextAlignment(.leading)
36+
.padding()
37+
.onAppear {
38+
guard item == viewModel.chatList.last else { return }
39+
isShowingLastChat = true
40+
}
41+
.onDisappear {
42+
guard item == viewModel.chatList.last else { return }
43+
isShowingLastChat = false
44+
}
45+
}
3346
}
3447
}
48+
49+
HStack {
50+
Spacer()
51+
Text("Powered by", bundle: .module)
52+
.font(.caption)
53+
.foregroundStyle(Color(.secondaryLabel))
54+
Image(.flitto)
55+
.resizable()
56+
.offset(x: -10)
57+
.aspectRatio(contentMode: .fit)
58+
.frame(maxHeight: 30)
59+
Spacer()
60+
}
61+
.id(scrollContentBottomID)
62+
.padding(.bottom, 16)
3563
}
36-
HStack {
37-
Spacer()
38-
Text("Powered by", bundle: .module)
39-
.font(.caption)
40-
.foregroundStyle(Color(.secondaryLabel))
41-
Image(.flitto)
42-
.resizable()
43-
.offset(x: -10)
44-
.aspectRatio(contentMode: .fit)
45-
.frame(maxHeight: 30)
46-
Spacer()
64+
.onChange(of: viewModel.chatList.last) { old, new in
65+
guard old != .none else {
66+
reader.scrollTo(scrollContentBottomID, anchor: .bottom)
67+
return
68+
}
69+
70+
guard isShowingLastChat else { return }
71+
72+
guard new != .none else { return }
73+
withAnimation(.interactiveSpring) {
74+
reader.scrollTo(scrollContentBottomID, anchor: .center)
75+
}
4776
}
48-
.padding(.bottom, 16)
4977
}
5078
}
5179
.task {

0 commit comments

Comments
 (0)