iOS 15+ contextMenu 中的反向 ScrollView 错误

2023-12-21

感谢您花时间帮助他人:)

例如,我已经检查了所有解决方案这个帖子 https://stackoverflow.com/questions/61726424/swiftui-chat-app-the-woes-of-reversed-list-and-context-menu,但没有一个对我们有用。

也发表在苹果开发者 https://developer.apple.com/forums/thread/723770,我在这里发表也是希望好运。谢谢!

错误描述:

当我们构建聊天时,我们需要以与 List/ScrollView 相反的顺序显示消息。这就是为什么我们要把一切颠倒过来。

在 iOS 15 和 iOS 16(14 中没有)上,在反向 List/ScrollView 上显示 contextMenu 时会出现奇怪的行为(似乎是错误)。

根据您使用的方法,它甚至会消失。让我们深入了解一下。

一些注意事项:

反转用户界面的要点是像聊天列表一样,在底部显示最后一条消息,然后滚动到顶部,而不是正常(相反)的行为)。

  • 我们确实尝试过反转数组,在出现时滚动到底部,但导致 iOS 14 上的scrollTo出现很多问题......性能不太好......
  • 如果我们尝试反转 UI,是因为我们已经看到它非常高效,而且还因为没有额外的错误(显然,除了这篇文章中暴露的错误)

编写简单的演示代码来展示会发生什么。

import SwiftUI

struct ContentView: View {
    var body: some View {
        ScrollView { // It also happens using List (instead ScrollView + LazyVStack)
            LazyVStack {
                ForEach(1..<101, id: \.self) { msg in
                    messageView(msg)
                        .upsideDown() // Upside down each element, you can try .upsideDownBis()
                }
            }
        }
        .upsideDown() // Upside down entire ScrollView, you can try .upsideDownBis()
    }

    private func messageView(_ msg: Int) -> some View {
        Text("Msg no. \(msg)")
            .padding()
            .background(Color.red.cornerRadius(8))
            .contextMenu {
                    Button(action: {
                        print("A")
                    }) {
                        Text("Button A")
                    }

                    Button(action: {
                        print("B")
                    }) {
                        Text("Button B")
                    }
            }
    }
}

// Just for setting upside down list/ ScrollView
extension View {
    func upsideDown() -> some View {
        self
            .rotationEffect(Angle(radians: .pi))
            .scaleEffect(x: -1, y: 1, anchor: .center)
    }

    // This one just rotates, does not disappear.
    func upsideDownBis() -> some View {
        self
            .rotationEffect(Angle(radians: .pi))
    }
}

iOS 15 和 16 上此行为的 GIF 资源:

iOS 15/16 应用 upsideDown() 修饰符时 -> 错误:

iOS 15/16 when applying upsideDownBis() modifier -> Undesired animation: iOS 15/16, upsideDownBis() modifier -> Undesired animation

我们检查了什么?

如前所述,每个解决方案发布于这个帖子 https://stackoverflow.com/questions/61726424/swiftui-chat-app-the-woes-of-reversed-list-and-context-menu和其他一些。除此之外的想法是:

  1. 由于scaleEffect确实导致了错误的最严重部分,因此在执行contextMenu手势时,我确实尝试将x:-1设置为x:1......但似乎比longPressGesture“识别器”更快。
  2. 与 1 类似,但在 upsideDownBis() 上尝试避免动画,将其设置为 nil,也不使修改器在这种情况下不应用... 因此,就目前情况而言,在 contextMenu 出现之前我无法识别 longPressGesture。

问题

  1. 有解决办法吗?不想用 upsideDownBis() 制作动画?
  2. 有解决办法吗?为了不让 upside Down() 出现这个错误?

要在 SwiftUI 中创建聊天应用程序,请使用以下关键 SwiftUI 功能:

  • Use a 滚动视图阅读器 https://developer.apple.com/documentation/swiftui/scrollviewreader访问所需的位置 滚动视图。
  • Use a 几何阅读器 https://developer.apple.com/documentation/swiftui/geometryreader来准确评估 特定设备显示的滚动区域大小 大小和方向。
  • 使用VStack中的Spacer()将内容推送到显示器 底部

下面的示例是提供一种高效的仅 SwiftUI 方法,而不需要进行颠倒的 UI 和其他建议。

典型的聊天行为包括:

  • 列表会随着增长而向上滚动
  • 最旧的文本显示在顶部,最新的文本显示在底部
  • 滚动保持平滑和快速
  • 包含内容菜单

消息类型具有 ScrollViewReader 所需的唯一标识符:

struct Message: Codable, Hashable, Identifiable {
    var id: UUID
    var text: String
}

GeometryReader 和 minHeight 用于强制 VStack 填充可用的显示区域。如果列表不够大以填充显示,则 Spacer() 会将列表推到底部:

struct ChatStyleScrollView<Content: View>: View {
    let content: Content

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        GeometryReader { proxy in
            ScrollView(.vertical, showsIndicators: false) {
                VStack {
                    Spacer()
                    content
                }
                .frame(minHeight: proxy.size.height)
            }
        }
    }
}

您提供的 messageView() 函数已修改为使用 Message 类型而不是 Int。 ChatStyleScrollView 放置在 ScrollViewReader 中并使用 Message.id 滚动到底部:

struct ContentView: View {
    @State private var messages: [Message] = []
    @State private var text: String = ""
    @State private var targetMessage: Message?
    
    var body: some View {
        VStack(spacing: 0) {
            ScrollViewReader { scrollView in
                ChatStyleScrollView() {
                    ForEach(messages) { msg in
                        messageView(msg)
                    }
                }
                .onChange(of: targetMessage) { msg in
                    if let msg = msg {
                        withAnimation(.default) {
                            scrollView.scrollTo(msg.id)
                        }
                    }
                }
                
                HStack {
                    TextField("Message", text: $text)
                        .frame(height: 44)
                    Button(action: send) { Text("Send") }
                }
                .padding(.horizontal)
            }
        }
    }
    
    private func send() {
        guard !text.isEmpty else { return }
        let msg = Message(id: UUID(), text: text)
        messages.append(msg)
        text = ""
        targetMessage = msg
    }
    
    private func messageView(_ msg: Message) -> some View {
        HStack {
            Text("Msg no. \(msg.text)")
                .padding()
                .background(Color.red.cornerRadius(8))
                .contextMenu {
                    Button(action: {
                        print("A")
                    }) {
                        Text("Button A")
                    }
                    
                    Button(action: {
                        print("B")
                    }) {
                        Text("Button B")
                    }
                }
        }
        .frame(maxWidth: .infinity)
        .id(msg.id)
    }
}

该代码基于在 SwiftUI 中实现反向滚动 https://www.thirdrocktechkno.com/blog/implementing-reversed-scrolling-behaviour-in-swiftui/作者:拉特内什·简

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS 15+ contextMenu 中的反向 ScrollView 错误 的相关文章

随机推荐

  • 在数据存储查看器中使用数字 ID 进行 GQL 查询

    我想构建 GQL 查询以使用其数字 ID 获取对象 我在应用程序管理控制台的数据存储查看器中执行此操作 因此无法使用 Model get by id numeric id 就像是 SELECT FROM Model WHERE id
  • 由于缺乏 PCRE 支持,R 源构建失败

    我正在尝试在 Ubuntu 14 04 下编译 R 3 3 1 尽管尝试安装 ubuntu pcre3 软件包以及 稍后 从源代码安装 pcre 8 37 但当我在 R 3 3 1 中运行 configure 时 我仍然收到以下信息 che
  • 从本地 pypi 索引中删除包

    这与此类似question https stackoverflow com questions 20403387 how to remove a package from pypi但有一个例外 我想从本地 pypi 索引中删除该包的一些特定
  • 从 Excel 替换 Word 书签内的图像

    我有一个打开的 Word 文档 其中有一堆书签 每个书签都有一个先前从 Excel 导出的 Excel 表格的内嵌图像 现在 我需要更新 Word 文档中的表格 因为它们在 Excel 中发生了更改 我这样做的方法是将 Excel 中的表名
  • Gradle:如何通过gradle执行git pull?

    我想在编译开始之前从 git repo 中提取更改 我找到了这个Gradle 如何在任务中克隆 git 存储库 https stackoverflow com questions 13719676 gradle how to clone a
  • 在三元运算符中使用 return

    我尝试在三元运算符中使用 return 但收到错误 Parse error syntax error unexpected T RETURN 这是代码 e this gt return errors e return array false
  • 如何在不使用
    的情况下从 css 换行?

    如何在不使用的情况下实现相同的输出 br p hello br How are you p output hello How are you 您可以使用white space pre https developer mozilla org
  • 这个Array slice调用的原型,为什么呢?

    我正在阅读 JS Function 的 MDN 页面论点多变的 https developer mozilla org en JavaScript Reference Functions and function scope argumen
  • 在 PHP 中使用 preg_grep() 查找字符串之前或之后的字符

    我需要使用 PHP 查找单词之前或之后的任何字符preg grep 来自数组 我有一个如下所示的数组 findgroup array aphp phpb dephpfs potatoes 我需要从数组中查找在单词 php 之前或之后 两侧
  • TotalResults 计数与 YouTube v3 搜索 API 返回的实际结果不匹配

    我们正在使用 youtube v3 搜索 API 我们发现 totalResults 计数与response items 字段中返回的项目列表不匹配 我在请求中请求 50 个视频 返回的响应显示 TotalResults 计数为 65 但响
  • 请解释一下 Amazon RDS/Mysql 中的这种内存消耗模式?

    Folks 有人可以解释运行 Mysql 的 Amazon RDS 上的这种内存消耗模式吗 在此图中 我在 03 30 升级到了 db m2 2xlarge 具有 34GB 可用内存 您可以非常清楚地看到切换 当客户端开始连接并访问该实例时
  • 空间数据类型(几何)到 GeoJSON

    我想转换geom geometry 数据类型转换为 GeoJSON 我怎么能这么做呢 例如 WKT 中的几何图形 POLYGON 455216 346127297 4288433 28426224 455203 386722146 4288
  • 在 Groovy 中动态添加元素到 ArrayList

    我是 Groovy 的新手 尽管阅读了很多有关此的文章和问题 但我仍然不清楚发生了什么 据我目前的了解 当您在 Groovy 中创建一个新数组时 底层类型是 Java ArrayList 这意味着它应该是可调整大小的 您应该能够将其初始化为
  • 如何防止遗传算法收敛于局部极小值?

    我正在尝试使用遗传算法构建 4 x 4 数独求解器 我对值收敛到局部最小值有一些问题 我正在使用排名方法并删除排名底部的两个答案可能性 并将它们替换为排名最高的两个答案可能性之间的交叉 为了获得避免局部最小值的额外帮助 我还使用了突变 如果
  • 文字字符串 [Lua 5.1]

    所以我开始学习Lua 5 1 我看到了一个叫做文字字符串的东西 我不知道这些是做什么的 手册上说 a 是一个铃声 但是当我输入时 print hello athere IDE 打印一个奇怪的正方形 上面写着 bel 因此 如果有人可以帮助我
  • 为什么结构标签不是 C 中的类型名称? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 有人知道一个好的 MSI 日志查看器吗?

    相当简单的问题 有谁知道浏览 msi 日志文件的好实用程序吗 对任何提供过滤 不同标准和自定义操作 操作排序 属性和错误的良好视图的事物感兴趣 Thanks I found Rob Mensching 关于在 MSI 日志中查找的第一件事的
  • 如何使用 ReactJS 在 CKEditor 5 中使用 MathType 插件?

    我已经安装了三个包 ckeditor ckeditor5 react ckeditor ckeditor5 build classic wiris mathtype ckeditor5 src plugin 我可以设置简单的 ckedito
  • 如何优化具有数千个 WHERE 子句的 SQL 查询

    我对一个非常大的数据库进行了一系列查询 并且 WHERE 子句中有数十万个 OR 优化此类 SQL 查询的最佳且最简单的方法是什么 我找到了一些有关创建临时表和使用联接的文章 但我不确定 我是严肃 SQL 的新手 并且一直在将结果从一个SQ
  • iOS 15+ contextMenu 中的反向 ScrollView 错误

    感谢您花时间帮助他人 例如 我已经检查了所有解决方案这个帖子 https stackoverflow com questions 61726424 swiftui chat app the woes of reversed list and