swiftui - SwiftUI 聊天应用:反向列表和上下文菜单的困境
问题描述
我正在 SwiftUI 中构建一个聊天应用程序。要在聊天中显示消息,我需要一个反向列表(在底部显示最新条目并自动滚动到底部的列表)。我通过翻转列表及其每个条目(标准的做法)制作了一个反向列表。
现在我想将上下文菜单添加到消息中。但长按后,菜单显示消息翻转。我认为这是有道理的,因为它会从列表中提取一条翻转的消息。
关于如何让它发挥作用的任何想法?
import SwiftUI
struct TestView: View {
var arr = ["1aaaaa","2bbbbb", "3ccccc", "4aaaaa","5bbbbb", "6ccccc", "7aaaaa","8bbbbb", "9ccccc", "10aaaaa","11bbbbb", "12ccccc"]
var body: some View {
List {
ForEach(arr.reversed(), id: \.self) { item in
VStack {
Text(item)
.height(100)
.scaleEffect(x: 1, y: -1, anchor: .center)
}
.contextMenu {
Button(action: { }) {
Text("Reply")
}
}
}
}
.scaleEffect(x: 1, y: -1, anchor: .center)
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
解决方案
翻转的问题是您需要翻转上下文菜单,而 SwiftUI 并没有提供这么多的控制。
处理此问题的更好方法是访问嵌入式 UITableView(您将拥有更多控制权)并且您无需添加额外的 hack。
这是演示代码:
import SwiftUI
import UIKit
struct TestView: View {
@State var arr = ["1aaaaa","2bbbbb", "3ccccc", "4aaaaa","5bbbbb", "6ccccc", "7aaaaa","8bbbbb", "9ccccc", "10aaaaa","11bbbbb", "12ccccc"]
@State var tableView: UITableView? {
didSet {
self.tableView?.adaptToChatView()
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.tableView?.scrollToBottom(animated: true)
}
}
}
var body: some View {
NavigationView {
List {
UIKitView { (tableView) in
DispatchQueue.main.async {
self.tableView = tableView
}
}
ForEach(arr, id: \.self) { item in
Text(item).contextMenu {
Button(action: {
// change country setting
}) {
Text("Choose Country")
Image(systemName: "globe")
}
Button(action: {
// enable geolocation
}) {
Text("Detect Location")
Image(systemName: "location.circle")
}
}
}
}
.navigationBarTitle(Text("Chat View"), displayMode: .inline)
.navigationBarItems(trailing:
Button("add chat") {
self.arr.append("new Message: \(self.arr.count)")
self.tableView?.adaptToChatView()
DispatchQueue.main.async {
self.tableView?.scrollToBottom(animated: true)
}
})
}
}
}
extension UITableView {
func adaptToChatView() {
let offset = self.contentSize.height - self.visibleSize.height
if offset < self.contentOffset.y {
self.tableHeaderView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: self.contentSize.width, height: self.contentOffset.y - offset))
}
}
}
extension UIScrollView {
func scrollToBottom(animated:Bool) {
let offset = self.contentSize.height - self.visibleSize.height
if offset > self.contentOffset.y {
self.setContentOffset(CGPoint(x: 0, y: offset), animated: animated)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
final class UIKitView : UIViewRepresentable {
let callback: (UITableView) -> Void //return TableView in CallBack
init(leafViewCB: @escaping ((UITableView) -> Void)) {
callback = leafViewCB
}
func makeUIView(context: Context) -> UIView {
let view = UIView.init(frame: CGRect(x: CGFloat.leastNormalMagnitude,
y: CGFloat.leastNormalMagnitude,
width: CGFloat.leastNormalMagnitude,
height: CGFloat.leastNormalMagnitude))
view.backgroundColor = .clear
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
if let tableView = uiView.next(UITableView.self) {
callback(tableView) //return tableview if find
}
}
}
extension UIResponder {
func next<T: UIResponder>(_ type: T.Type) -> T? {
return next as? T ?? next?.next(type)
}
}
推荐阅读
- flutter - 按钮名称未显示在应用栏中,而标题谷歌地图可见
- d3.js - 如何按年份累积 d3.rollup 数据
- python-3.x - SQL Alchemy 枚举验证与验证函数冲突
- c# - 如何在 ZOHO CRM 中制作公式字段?
- sql - SQL Select FOR XML PATH - 将排序和连接字符串与递增计数器组合
- android - 在 Square.Retrofit 上,如何设置 CallAdapter.Factory 以便 Retrofit 使用我的 CallAdapter
>> - python - 从一个不规则纬度网格重新网格化到另一个不规则纬度网格
- python - python文件反编译加密文件
- android - 当ChannelResult为channelResult = Value(失败)时如何重试kotlin通道trySend
- powerbi - PowerBI - 根据最近的日期选择度量的最后一个非空白值