今までのチャットの実装の難しさとiOS14からの手法
UIKitでチャットアプリを実装する際、UITableViewを使うことが多いかと思います。
LINEのようなチャットの場合は特に高さの計算
、一番下へスクロール
の実装が難しいですね。
今までの経験により、実装も複雑になりバグも多かったりしました。
iOS14
になってからは、SwiftUIのScrollViewReader
が出てきて、だいぶチャットの実装が楽になりました。
環境
- Xcode12.2
- iOS14.2(SwiftUI2)
ScrollViewReaderとは
プログラム的にスクロールできるようになります。
ScrollViewReader - Apple公式ドキュメント
ScrollView {
ScrollViewReader { (proxy: ScrollViewProxy) in
...
}
}
ScrollViewReaderのクロージャーでScrollViewProxy
にアクセスが可能になります。これを保存しておき、
scrollTo
でプログラム的にスクロールします。
func scrollTo<ID>(_ id: ID, anchor: UnitPoint? = nil) where ID : Hashable
簡単なチャットで使ってみた
struct ContentView: View {
@ObservedObject private var viewModel: ViewModel = .init()
@State var text: String = ""
@State var value: ScrollViewProxy?
var body: some View {
VStack {
ScrollView {
ScrollViewReader { value in
LazyVStack(alignment: .center, spacing: 16) {
//チャットの表示
ForEach.init(self.viewModel.messages, id: \.id) { message in
ChatView.init(message: message)
}
}.onAppear {
self.value = value //ScrollViewProxyを保存する
self.value?.scrollTo(self.viewModel.messages.count, anchor: .bottom) //初めに表示された時に一番下までスクロールする
}.animation(.easeInOut)
}
}
//テキスト入力欄
VStack {
...
}.background(Color(white: 0.95))
}
}
}
extension ContentView {
//送信ボタンを押した時
func sendText() {
viewModel.send(text: text)
text = ""
guard let message = viewModel.messages.last else { return }
debugPrint(message)
//わずかにタイミングをずらさないと、スクロールできない
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
withAnimation {
//一番下にアニメーションする
self.value?.scrollTo(message.id, anchor: .bottom)
}
})
}
}
スクショ

ソースコード
参考
https://developer.apple.com/documentation/swiftui/scrollviewreader
https://qiita.com/giiiita/items/be38b9f0135a12bfd49c
https://medium.com/better-programming/build-a-chat-app-interface-with-swiftui-96609e605422
https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions
https://www.raywenderlich.com/5815412-getting-started-with-swiftui-animations